Skip to content

Commit 2faa9bc

Browse files
authored
Add foreign proc def (#371)
* Added foreign proc def * Ensure resource declaration order is maintained * Require params be typed in foreign proc defs * Add foreign proc def to WYBE.md * Specify more on resources and modifiers * Coalesce repeated resources into one; improve test case to cover all resource flow cases
1 parent 7fc963a commit 2faa9bc

16 files changed

+781
-102
lines changed

WYBE.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ Wybe also supports an alternative syntax for invoking procedures, functions, or
736736
constructors that puts the (first) argument first, and the procedure, function,
737737
or constructor name, with its other arguments, if any, second:
738738

739-
> *arg*`^`*operation*(*other args*)
739+
> *arg*`^`*operation*`(`*other args*`)`
740740
741741
and in the special case of operations taking only one argument:
742742

@@ -2064,7 +2064,7 @@ so be careful to get the call right.
20642064
20652065
The form of a foreign call is:
20662066
2067-
> `foreign` *language* *function*(*arg*, *arg*, ...)
2067+
> `foreign` *language* *function*`(`*arg*, *arg*, ...`)`
20682068
20692069
where *language* is the name of the foreign language the function is written in,
20702070
*function* is the name of the foreign function to call, and the *arg*s are the
@@ -2092,7 +2092,7 @@ Foreign calls may optionally specify *modifiers* to provide extra information
20922092
useful to the Wybe compiler. If modifiers are to be specified, they are written
20932093
after the *language* name:
20942094
2095-
> `foreign` *language* `{`*modifiers*`}` *function*(*arg*, *arg*, ...)
2095+
> `foreign` *language* `{`*modifiers*`}` *function*`(`*arg*, *arg*, ...`)`
20962096
20972097
where *modifiers* is a comma-separate sequence of identifiers specifying this
20982098
information. Supported modifiers in foreign calls are:
@@ -2123,6 +2123,35 @@ and the less than test for integers is defined as
21232123
pub def {test} (x:_ < y:_) { foreign llvm {test} icmp_slt(x,y) }
21242124
```
21252125
2126+
##### Foreign procedure definition short-hand
2127+
2128+
For convenince, a short-hand syntax is provided to define a Wybe procedure to interface
2129+
with a foriegn procedure. This syntax is as follows:
2130+
2131+
> `def` `foreign` *language* *function*`(`*param*, *param*, ...`)`
2132+
2133+
which is equivalent to defining the following:
2134+
2135+
> `def` *function*`(`*param*, *param*, ...`)` `{` `foreign` *language* *function*`(`*param*, *param*, ...`)` `}`
2136+
2137+
Note that all parameters must be typed.
2138+
2139+
Resources can optionally be specified with a `use` clause, that follows the same syntax
2140+
for a regular Wybe procedure definition. Resources are added as additonal arguments to
2141+
the foreign call after the other arguments, in the order specified in the source code.
2142+
If resources are repeated in the `use` clause, they are added as multiple arguments in
2143+
the generated foreign call.
2144+
2145+
Modifiers can also optionally be specified, with the syntax being identical to a Wybe
2146+
procedure definition, and are added to the foreign procedure call.
2147+
In addition, inlining (`inline`, `noinline`) can be specified for the for a
2148+
foreign procedure, but only inline/don't inline the generated Wybe procedure, and don't
2149+
apply to the generated foreign call.
2150+
2151+
With both resources and modifiers, this would be as follows:
2152+
2153+
> `def` `foreign` *language* `{` *modifiers* `}` *function*`(`*param*, *param*, ...`)` `use` *resources*
2154+
21262155
21272156
#### Using LLVM instructions
21282157

src/AST.hs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ data Item
179179
| ImportForeign [FilePath] OptPos
180180
| ImportForeignLib [Ident] OptPos
181181
| ResourceDecl Visibility ResourceName TypeSpec (Maybe (Placed Exp)) OptPos
182-
-- The Bool in the next two indicates whether inlining is forced
183182
| FuncDecl Visibility ProcModifiers ProcProto TypeSpec (Placed Exp) OptPos
184183
| ProcDecl Visibility ProcModifiers ProcProto [Placed Stmt] OptPos
184+
| ForeignProcDecl Visibility Ident ProcModifiers ProcProto OptPos
185185
| StmtDecl Stmt OptPos
186186
| PragmaDecl Pragma
187187
deriving (Generic, Eq)
@@ -2169,9 +2169,9 @@ univGlobalFlows = GlobalFlows UniversalSet UniversalSet UniversalSet
21692169
-- In the case we have a higher order resourceful argument, we may not know
21702170
-- exactly which global variables flow into or out of a procedure, and as such
21712171
-- we take a conservative approach and assume all do.
2172-
makeGlobalFlows :: [(ParameterID, PrimParam)] -> Set ResourceFlowSpec -> GlobalFlows
2172+
makeGlobalFlows :: [(ParameterID, PrimParam)] -> [ResourceFlowSpec] -> GlobalFlows
21732173
makeGlobalFlows params resFlows =
2174-
Set.fold addGlobalResourceFlows
2174+
List.foldr addGlobalResourceFlows
21752175
(emptyGlobalFlows{globalFlowsParams=pFlows}) resFlows
21762176
where
21772177
pFlows = FiniteSet $ Set.fromList $ catMaybes $ List.map (uncurry paramFlow) params
@@ -2761,7 +2761,7 @@ data Constant = Int Int
27612761
data ProcProto = ProcProto {
27622762
procProtoName::ProcName,
27632763
procProtoParams::[Placed Param],
2764-
procProtoResources::Set.Set ResourceFlowSpec
2764+
procProtoResources::[ResourceFlowSpec]
27652765
} deriving (Eq, Generic)
27662766

27672767

@@ -3053,7 +3053,7 @@ data Exp
30533053
-- these two must be the same.
30543054
| Global GlobalInfo
30553055
-- The following are eliminated during flattening
3056-
| AnonProc ProcModifiers [Param] [Placed Stmt] (Maybe VarDict) (Maybe (Set ResourceFlowSpec))
3056+
| AnonProc ProcModifiers [Param] [Placed Stmt] (Maybe VarDict) (Maybe [ResourceFlowSpec])
30573057
| AnonFunc (Placed Exp)
30583058
| AnonParamVar (Maybe Integer) FlowDirection
30593059
| Where [Placed Stmt] (Placed Exp)
@@ -3758,7 +3758,14 @@ instance Show Item where
37583758
++ showOptPos pos
37593759
++ " {"
37603760
++ showBody 4 stmts
3761-
++ "\n }"
3761+
++ "\n }"
3762+
show (ForeignProcDecl vis lang modifiers proto pos) =
3763+
visibilityPrefix vis
3764+
++ "def foreign "
3765+
++ lang ++ " "
3766+
++ showProcModifiers' modifiers
3767+
++ show proto
3768+
++ showOptPos pos
37623769
show (StmtDecl stmt pos) =
37633770
showStmt 4 stmt ++ showOptPos pos
37643771
show (PragmaDecl prag) =
@@ -3925,11 +3932,10 @@ instance Show TypeSpec where
39253932

39263933

39273934
-- |Show the use declaration for a set of resources, if it's non-empty.
3928-
showResources :: Set.Set ResourceFlowSpec -> String
3935+
showResources :: [ResourceFlowSpec] -> String
39293936
showResources resources
3930-
| Set.null resources = ""
3931-
| otherwise = " use " ++ intercalate ", "
3932-
(List.map show $ Set.elems resources)
3937+
| List.null resources = ""
3938+
| otherwise = " use " ++ intercalate ", " (List.map show resources)
39333939

39343940

39353941
-- |How to show a proc prototype.

src/Builder.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,9 +1137,9 @@ buildMain :: [[ModSpec]] -> Compiler Item
11371137
buildMain sccs = do
11381138
logBuild "Generating main executable code"
11391139
let cmdResource name = ResourceFlowSpec (ResourceSpec cmdLineModSpec name)
1140-
let mainRes = Set.fromList [cmdResource "argc" ParamIn,
1141-
cmdResource "argv" ParamIn,
1142-
cmdResource "exit_code" ParamOut]
1140+
let mainRes = [ cmdResource "argc" ParamIn
1141+
, cmdResource "argv" ParamIn
1142+
, cmdResource "exit_code" ParamOut]
11431143
initPairs <- mapM sccInits sccs
11441144
let initRes = concatMap fst initPairs
11451145
let body = concatMap snd initPairs

src/Flatten.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,23 @@ flattenProcBody pd _ = do
6666
$ List.map paramName
6767
$ List.filter (flowsIn . paramFlow)
6868
$ content <$> params
69-
resources = Set.map (resourceName . resourceFlowRes)
69+
resources = List.map (resourceName . resourceFlowRes)
7070
$ procProtoResources proto
7171
ProcDefSrc body = procImpln pd
7272

7373
detism = procDetism pd
7474
inlining = procInlining pd
7575
impurity = procImpurity pd
7676
variant = procVariant pd
77-
resourceful = not $ Set.null resources
77+
resourceful = not $ List.null resources
7878
mods = ProcModifiers detism inlining impurity variant resourceful
7979

8080
logMsg Flatten
8181
$ "** Flattening def " ++ showProcModifiers' mods ++ show proto
8282
++ " {" ++ showBody 4 body ++ "}"
8383
mapM_ (placedApply $ flip explicitTypeSpecificationWarning . paramType) params
8484

85-
(body',tmpCtr) <- flattenBody body (inParams `Set.union` resources) detism
85+
(body',tmpCtr) <- flattenBody body (inParams `Set.union` Set.fromList resources) detism
8686

8787
return pd{procTmpCount = tmpCtr, procImpln = ProcDefSrc body'}
8888

@@ -647,7 +647,7 @@ flattenExp (Typed exp ty castFrom) _ _ pos = do
647647

648648
-- | Flatten something, and produce an anonymous procedure from the resultant flattened
649649
-- statements
650-
flattenAnon :: ProcModifiers -> (Maybe VarDict) -> (Maybe (Set ResourceFlowSpec))
650+
flattenAnon :: ProcModifiers -> (Maybe VarDict) -> (Maybe [ResourceFlowSpec])
651651
-> TypeSpec -> Maybe TypeSpec -> OptPos
652652
-> Flattener () -> Flattener (Placed Exp)
653653
flattenAnon mods clsd res ty castFrom pos inner = do

src/Normalise.hs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ normaliseItem (FuncDecl vis mods (ProcProto name params resources)
111111
[result, varSet outputVariableName `maybePlace` pos])
112112
pos]
113113
pos)
114+
normaliseItem (ForeignProcDecl vis lang mods proto@(ProcProto name params resources) pos) = do
115+
when (mods{modifierImpurity=Pure, modifierDetism=Det, modifierInline=MayInline} /= defaultProcModifiers)
116+
$ errmsg pos
117+
$ "Foreign procedure declaration of " ++ name
118+
++ " has illegal procedure modifiers. Only purity, determinism, and inlining can be specified."
119+
mapM_ ((\(Param{paramType=ty, paramName=name}, pos) -> do
120+
when (ty == AnyType)
121+
$ errmsg pos
122+
$ "Foreign procedure declaration parameters must be typed, but " ++ name ++ " is untyped."
123+
) . unPlace) params
124+
125+
normaliseItem
126+
(ProcDecl vis mods proto
127+
[maybePlace (ForeignCall lang name mods' exps) pos] pos)
128+
where
129+
mods' = List.filter (not . List.null)
130+
[ impurityName (modifierImpurity mods)
131+
, determinismName (modifierDetism mods) ]
132+
exps = List.map (uncurry (flip rePlace . paramToVar) . unPlace) params
133+
++ List.map (\(ResourceFlowSpec rs@(ResourceSpec _ r) dir) ->
134+
Var r dir (Resource rs) `maybePlace` pos)
135+
resources
114136
normaliseItem item@ProcDecl{} = do
115137
logNormalise $ "Recording proc without flattening:" ++ show item
116138
addProc 0 item
@@ -288,7 +310,7 @@ validateModuleName what pos name =
288310

289311
-- |Check that the specified module name is valid, reporting and error if not.
290312
validateModSpec :: OptPos -> ModSpec -> Compiler ()
291-
validateModSpec pos = mapM_ (validateModuleName "module" pos)
313+
validateModSpec pos = mapM_ (validateModuleName "module" pos)
292314

293315

294316
-- | Information about a non-constant constructor
@@ -400,7 +422,7 @@ nonConstCtorInfo (vis, placedProto) tag = do
400422
logNormalise $ "Analysing non-constant ctor "
401423
++ show tag ++ ": " ++ show placedProto
402424
let (proto,pos) = unPlace placedProto
403-
unless (Set.null $ procProtoResources proto)
425+
unless (List.null $ procProtoResources proto)
404426
$ shouldnt $ "Constructor with resources: " ++ show placedProto
405427
let name = procProtoName proto
406428
let params = procProtoParams proto
@@ -470,7 +492,7 @@ normaliseModMain modSCC = do
470492
-- assumes that all resource initialisations have already been completed, and
471493
-- all are permitted to be modified by the initialisation code, so all
472494
-- visible initialised resources flow both in and out.
473-
initResources :: [ModSpec] -> Compiler (Set ResourceFlowSpec)
495+
initResources :: [ModSpec] -> Compiler [ResourceFlowSpec]
474496
initResources modSCC = do
475497
thisMod <- getModule modSpec
476498
mods <- getModuleImplementationField (Map.keys . modImports)
@@ -489,7 +511,7 @@ initResources modSCC = do
489511
-- because that would overwrite them.
490512
let cmdlineResources =
491513
if cmdLineModSpec == thisMod
492-
then let cmdline = ResourceSpec cmdLineModSpec
514+
then let cmdline = ResourceSpec cmdLineModSpec
493515
in [ResourceFlowSpec (cmdline "argc") ParamInOut
494516
,ResourceFlowSpec (cmdline "argv") ParamInOut]
495517
else []
@@ -498,7 +520,7 @@ initResources modSCC = do
498520
<$> Set.toList visibleInitSet)
499521
logNormalise $ "In initResources for module " ++ showModSpec thisMod
500522
++ ", resources = " ++ show resources
501-
return (Set.fromList resources)
523+
return resources
502524

503525

504526

@@ -538,7 +560,7 @@ constCtorItems typeSpec ((vis, placedProto), num) =
538560
constName = procProtoName proto
539561
in [ProcDecl vis (inlineModifiers (ConstructorProc constName) Det)
540562
(ProcProto constName
541-
[Param outputVariableName typeSpec ParamOut Ordinary `maybePlace` pos] Set.empty)
563+
[Param outputVariableName typeSpec ParamOut Ordinary `maybePlace` pos] [])
542564
[lpvmCastToVar (castTo (iVal num) typeSpec) outputVariableName] pos
543565
]
544566

@@ -652,7 +674,7 @@ constructorItems vis ctorName typeSpec params fields size tag tagLimit pos =
652674
(ProcProto ctorName
653675
((placedApply (\p -> maybePlace p {paramFlow=ParamIn, paramFlowType=Ordinary}) <$> params)
654676
++ [Param outputVariableName typeSpec ParamOut Ordinary `maybePlace` pos])
655-
Set.empty)
677+
[])
656678
-- Code to allocate memory for the value
657679
([maybePlace (ForeignCall "lpvm" "alloc" []
658680
[Unplaced $ iVal size,
@@ -702,7 +724,7 @@ deconstructorItems uniq vis ctorName typeSpec params numConsts numNonConsts tag
702724
(ProcProto ctorName
703725
((contentApply (\p -> p {paramFlow=ParamOut, paramFlowType=Ordinary}) <$> params)
704726
++ [Param outputVariableName typeSpec ParamIn Ordinary `maybePlace` pos])
705-
Set.empty)
727+
[])
706728
-- Code to check we have the right constructor
707729
(tagCheck pos numConsts numNonConsts tag tagBits tagLimit
708730
(Just size) outputVariableName
@@ -810,7 +832,7 @@ unboxedConstructorItems vis ctorName typeSpec tag nonConstBit fields pos =
810832
([Param name paramType ParamIn Ordinary `maybePlace` pPos
811833
| FieldInfo name pPos _ paramType _ _ _ <- fields]
812834
++ [Param outputVariableName typeSpec ParamOut Ordinary `maybePlace` pos])
813-
Set.empty
835+
[]
814836
in [ProcDecl vis (inlineModifiers (ConstructorProc ctorName) Det) proto
815837
-- Initialise result to 0
816838
([ForeignCall "llvm" "move" []
@@ -863,7 +885,7 @@ unboxedDeconstructorItems vis uniq ctorName recType numConsts numNonConsts tag
863885
(List.map (\(FieldInfo n pPos _ fieldType _ _ _) -> Param n fieldType ParamOut Ordinary `maybePlace` pPos)
864886
fields
865887
++ [Param outputVariableName recType ParamIn Ordinary `maybePlace` pos])
866-
Set.empty)
888+
[])
867889
-- Code to check we have the right constructor
868890
(tagCheck pos numConsts numNonConsts tag tagBits (wordSizeBytes-1) Nothing
869891
outputVariableName
@@ -965,13 +987,13 @@ getterSetterItems numConsts numNonConsts recType field infos = do
965987
[-- The getter:
966988
ProcDecl fieldVis (setInline inline $ inlineModifiers (GetterProc field fieldType) detism)
967989
(ProcProto field [Param recName recType ParamIn Ordinary `maybePlace` pos,
968-
Param outputVariableName fieldType ParamOut Ordinary `maybePlace` pos] Set.empty)
990+
Param outputVariableName fieldType ParamOut Ordinary `maybePlace` pos] [])
969991
getBody
970992
pos,
971993
-- The setter:
972994
ProcDecl fieldVis (setInline inline $ inlineModifiers (SetterProc field fieldType) detism)
973995
(ProcProto field [Param recName recType ParamInOut Ordinary `maybePlace` pos,
974-
Param fieldName fieldType ParamIn Ordinary `maybePlace` pos] Set.empty)
996+
Param fieldName fieldType ParamIn Ordinary `maybePlace` pos] [])
975997
setBody
976998
pos]
977999

@@ -1008,7 +1030,7 @@ implicitEquality pos typespec consts nonconsts rep = do
10081030
else do
10091031
let eqProto = ProcProto "=" [Param leftName typespec ParamIn Ordinary `maybePlace` pos,
10101032
Param rightName typespec ParamIn Ordinary `maybePlace` pos]
1011-
Set.empty
1033+
[]
10121034
let (body,inline) = equalityBody pos consts nonconsts rep
10131035
return [ProcDecl Public (setInline inline
10141036
$ setDetism SemiDet defaultProcModifiers)
@@ -1024,7 +1046,7 @@ implicitDisequality pos typespec consts nonconsts _ = do
10241046
else do
10251047
let neProto = ProcProto "~=" [Param leftName typespec ParamIn Ordinary `maybePlace` pos,
10261048
Param rightName typespec ParamIn Ordinary `maybePlace` pos]
1027-
Set.empty
1049+
[]
10281050
let neBody = [maybePlace (Not $
10291051
ProcCall (First [] "=" Nothing) SemiDet False
10301052
[varGetTyped leftName typespec `maybePlace` pos,

0 commit comments

Comments
 (0)