Home My Page Projects Code Snippets Project Openings diderot
Summary Activity Tracker Tasks SCM

SCM Repository

[diderot] Annotation of /branches/vis15/src/compiler/target-cpu/gen-outputs.sml
ViewVC logotype

Annotation of /branches/vis15/src/compiler/target-cpu/gen-outputs.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3958 - (view) (download)

1 : jhr 3909 (* gen-outputs.sml
2 :     *
3 :     * This code is part of the Diderot Project (http://diderot-language.cs.uchicago.edu)
4 :     *
5 :     * COPYRIGHT (c) 2016 The University of Chicago
6 :     * All rights reserved.
7 :     *
8 :     * Generate strand output functions. The output formats always have a single axis for the
9 :     * data elements followed by one, or more, axes for the output structure. There are four
10 :     * cases that we handle:
11 :     *
12 :     * grid, fixed-size elements:
13 :     * nrrd has object axis followed by grid axes
14 :     *
15 :     * collection, fixed-size elements
16 :     * nrrd has object axis followed by a single axis
17 :     *
18 :     * grid, dynamic-size elements
19 :     * nLengths nrrd has size 2 for objects (offset, length) followed by grid axes
20 :     * nData nrrd has object axis followed by a single axis
21 :     *
22 :     * collection, dynamic-size elements
23 :     * nLengths nrrd has size 2 for objects (offset, length) followed by a single axis
24 :     * nData nrrd has object axis followed by a single axis
25 :     *
26 :     * The object axis kind depends on the output type, but it will either be one of the tensor types
27 :     * that Teem knows about or else nrrdKindList. In any case, the data elements are written as a
28 :     * flat vector following the in-memory layout. The other axes in the file will have nrrdKindSpace
29 :     * as their kind.
30 :     *
31 :     * TODO: some of this code will be common across all targets (e.g., writing outputs to files), so
32 :     * we will want to refactor it.
33 :     *
34 :     * TODO: for sequences of tensors (e.g., tensor[3][2]), we should use a separate axis for the
35 :     * sequence dimension with kind nrrdKindList.
36 :     *
37 :     * TODO: since the runtime tracks numbers of strands in various states, we should be
38 :     * able to use that information directly from the world without having to recompute it!
39 :     *)
40 :    
41 :     structure GenOutputs : sig
42 :    
43 : jhr 3958 (* gen (props, nAxes, outputs)
44 : jhr 3909 * returns a list of function declarations for getting the output/snapshot nrrds from
45 :     * the program state. The arguments are:
46 :     * props - the target information
47 :     * nAxes - the number of axes in the grid of strands (NONE for a collection)
48 :     * outputs - the list of output state variables paired with their API types
49 :     *)
50 : jhr 3917 val gen : CodeGenEnv.t * int option * OutputUtil.output_info list -> CLang.decl list
51 : jhr 3909
52 :     end = struct
53 :    
54 :     structure IR = TreeIR
55 :     structure V = TreeVar
56 :     structure Ty = APITypes
57 :     structure CL = CLang
58 :     structure Nrrd = NrrdEnums
59 :     structure U = OutputUtil
60 : jhr 3917 structure RN = CxxNames
61 :     structure Env = CodeGenEnv
62 : jhr 3909
63 :     fun mapi f l = let
64 :     fun mapf (i, [], l) = List.rev l
65 :     | mapf (i, x::xs, l) = mapf (i+1, xs, f(i, x)::l)
66 :     in
67 :     mapf (0, l, [])
68 :     end
69 :    
70 :     val nrrdPtrTy = CL.T_Ptr(CL.T_Named "Nrrd")
71 :     val sizeTy = CL.T_Named "size_t"
72 : jhr 3917 val wrldPtr = RN.worldPtrTy
73 : jhr 3909 fun mkInt i = CL.mkInt(IntInf.fromInt i)
74 :    
75 :     (* variables in the generated code *)
76 :     val wrldV = CL.mkVar "wrld"
77 :     val sizesV = CL.mkVar "sizes"
78 :     val iV = CL.mkVar "i"
79 :     val nV = CL.mkVar "n"
80 :     val cpV = CL.mkVar "cp"
81 :     val ipV = CL.mkVar "ip"
82 :     val msgV = CL.mkVar "msg"
83 :     val offsetV = CL.mkVar "offset"
84 :     val nDataV = CL.mkVar "nData"
85 :     val nLengthsV = CL.mkVar "nLengths"
86 :     val numStableV = CL.mkVar "numStable"
87 :     val numElemsV = CL.mkVar "numElems"
88 :     val outSV = CL.mkVar "outS"
89 : jhr 3917 val DIDEROT_DEAD = CL.mkVar "diderot::kDead"
90 :     val DIDEROT_STABLE = CL.mkVar "diderot::kStable"
91 : jhr 3909 val NRRD = CL.mkVar "NRRD"
92 :    
93 :     (* dymanic sequence operations *)
94 : jhr 3917 fun seqLength arg = CL.mkApply("diderot::DynSeqLength", [arg])
95 :     fun seqCopy (elemSz, dst, seq) = CL.mkApply("diderot::DynSeqCopy", [elemSz, dst, seq])
96 : jhr 3909
97 :     (* utility functions for initializing the sizes array *)
98 :     fun sizes i = CL.mkSubscript(sizesV, mkInt i)
99 :     fun setSizes (i, v) = CL.mkAssign(sizes i, v)
100 :    
101 :     (* code to access state variable
102 :     wrld->outState[i]->name
103 :     * or
104 :     wrld->state[i].name
105 :     *)
106 : jhr 3917 fun stateVar spec name = if TargetSpec.dualState spec
107 : jhr 3924 then CL.mkIndirect(CL.mkSubscript(CL.mkIndirect(wrldV, "_outState"), iV), "sv_"^name)
108 :     else CL.mkSelect(CL.mkSubscript(CL.mkIndirect(wrldV, "_state"), iV), "sv_"^name)
109 : jhr 3909
110 :     (* code fragment to loop over strands
111 : jhr 3924 for (unsigned int i = 0; i < wrld->_nstrands; i++) ...
112 : jhr 3909 *)
113 :     fun forStrands stm = CL.mkFor(
114 :     [(CL.uint32, "i", mkInt 0)],
115 : jhr 3924 CL.mkBinOp(iV, CL.#<, CL.mkIndirect(wrldV, "_nstrands")),
116 : jhr 3909 [CL.mkPostOp(iV, CL.^++)],
117 :     stm)
118 :    
119 :     (* code fragment to test for stable strands in a loop
120 : jhr 3917 if (wrld->_status[i] == diderot::kStable)
121 : jhr 3909 ...
122 :     *)
123 :     fun ifStable stm = CL.mkIfThen(
124 : jhr 3917 CL.mkBinOp(CL.mkSubscript(CL.mkIndirect(wrldV, "_status"), iV), CL.#==, DIDEROT_STABLE),
125 : jhr 3909 stm)
126 :    
127 :     (* code fragment to test for active strands in a loop; note that NEW strands are considered active.
128 : jhr 3917 if (wrld->_status[i] != DIDEROT_DIE)
129 : jhr 3909 ...
130 :     *)
131 :     fun ifActive stm = CL.mkIfThen(
132 : jhr 3917 CL.mkBinOp(CL.mkSubscript(CL.mkIndirect(wrldV, "_status"), iV), CL.#!=, DIDEROT_DEAD),
133 : jhr 3909 stm)
134 :    
135 :     (* code fragment to initialize the axes kinds; the data axis (axis[0]) is given, but we skip it
136 :     * (by convention) if it is scalar. The other axes are the specified domAxisKind.
137 :     *)
138 :     fun initAxisKinds (nrrd, dataAxisKind, nAxes, domAxisKind) = let
139 :     (* nData->axis[0].kind *)
140 :     fun axisKind i = CL.mkSelect(CL.mkSubscript(CL.mkIndirect(nrrd, "axis"), mkInt i), "kind")
141 :     fun init (i, k) = CL.mkAssign (axisKind i, CL.mkVar(Nrrd.kindToEnum k))
142 :     val (firstSpace, dataAxis) = (case dataAxisKind
143 :     of Nrrd.KindScalar => (0, [])
144 :     | _ => (1, [init(0, dataAxisKind)])
145 :     (* end case *))
146 :     in
147 :     dataAxis @ List.tabulate(nAxes, fn i => init(i+firstSpace, domAxisKind))
148 :     end
149 :    
150 :     (* create the body of an output function for dynamic-size outputs. The structure of the
151 :     * function body is:
152 :     *
153 :     * declarations
154 :     * compute sizes array for nLengths
155 :     * allocate nrrd for nLengths
156 :     * compute sizes array for nData
157 :     * allocate nrrd for nData
158 :     * copy data from strands to nrrd
159 :     *)
160 : jhr 3917 fun genDynOutput (env, snapshot, nAxes, ty, name) = let
161 :     val spec = Env.target env
162 :     val (elemCTy, nrrdType, axisKind, nElems) = U.infoOf (env, ty)
163 :     val stateVar = stateVar spec
164 : jhr 3909 val (nAxes, domAxisKind) = (case nAxes
165 :     of NONE => (1, Nrrd.KindList)
166 :     | SOME n => (n, Nrrd.KindSpace)
167 :     (* end case *))
168 :     (* declarations *)
169 :     val sizesDecl = CL.mkDecl(CL.T_Array(sizeTy, SOME(nAxes+1)), "sizes", NONE)
170 :     (* count number of elements (and stable strands) *)
171 :     val countElems = let
172 :     val nElemsInit = CL.mkDeclInit(CL.uint32, "numElems", CL.mkInt 0)
173 :     val cntElems = CL.S_Exp(CL.mkAssignOp(numElemsV, CL.+=, seqLength(stateVar name)))
174 :     in
175 : jhr 3958 if #isGrid spec
176 : jhr 3909 then [
177 :     CL.mkComment["count number of elements"],
178 :     nElemsInit, forStrands cntElems
179 :     ]
180 :     else let
181 :     val cntBlk = CL.mkBlock[cntElems, CL.S_Exp(CL.mkPostOp(numStableV, CL.^++))]
182 :     val lpBody = if snapshot
183 :     then ifActive cntBlk
184 :     else ifStable cntBlk
185 :     in [
186 :     CL.mkComment["count number of output elements and stable strands"],
187 :     CL.mkDeclInit(CL.uint32, "numStable", CL.mkInt 0),
188 :     nElemsInit,
189 :     forStrands lpBody
190 :     ] end
191 :     end
192 :     (* code to check for zero outputs, which happens for collections with no active strands *)
193 : jhr 3958 val checkForNoStrands = if #isGrid spec
194 : jhr 3909 then []
195 :     else [
196 :     CL.mkComment["check for no output"],
197 :     CL.mkIfThen(
198 :     CL.mkBinOp(mkInt 0, CL.#==, numStableV),
199 :     CL.mkBlock[
200 :     CL.mkCall("nrrdEmpty", [nLengthsV]),
201 :     CL.mkCall("nrrdEmpty", [nDataV]),
202 :     CL.mkReturn(SOME(CL.mkVar "false"))
203 :     ])
204 :     ]
205 :     (* generate code to allocate the nLengths nrrd *)
206 :     val lengthsNrrd = let
207 :     val dimSizes = setSizes(0, CL.mkInt 2) (* nLengths is 2-element vector *)
208 :     in
209 :     CL.mkComment["allocate nLengths nrrd"] ::
210 : jhr 3958 (if #isGrid spec
211 : jhr 3909 then dimSizes ::
212 :     List.tabulate (nAxes, fn i =>
213 : jhr 3958 setSizes(i+1, CL.mkSubscript(CL.mkIndirect(wrldV, "_size"), mkInt(nAxes-i-1)))) @
214 : jhr 3927 [U.maybeAlloc (env, nLengthsV, Nrrd.tyToEnum Nrrd.TypeInt, nAxes+1)]
215 : jhr 3909 else [
216 :     dimSizes, setSizes(1, numStableV),
217 : jhr 3927 U.maybeAlloc (env, nLengthsV, Nrrd.tyToEnum Nrrd.TypeInt, 2)
218 : jhr 3909 ])
219 :     end
220 :     (* code to check for no data to output (i.e., all of the output sequences are empty) *)
221 :     val checkForEmpty = [
222 :     CL.mkComment["check for empty output"],
223 :     CL.mkIfThen(
224 :     CL.mkBinOp(mkInt 0, CL.#==, numElemsV),
225 :     CL.mkBlock[
226 :     CL.mkCall("nrrdEmpty", [nDataV]),
227 :     CL.mkReturn(SOME(CL.mkVar "false"))
228 :     ])
229 :     ]
230 :     (* generate code to allocate the data nrrd *)
231 :     val dataNrrd = if (axisKind = Nrrd.KindScalar)
232 :     then [ (* drop data axis for scalar data by convention *)
233 :     CL.mkComment["allocate nData nrrd"],
234 :     setSizes(0, numElemsV),
235 : jhr 3927 U.maybeAlloc (env, nDataV, Nrrd.tyToEnum nrrdType, 1)
236 : jhr 3909 ]
237 :     else [
238 :     CL.mkComment["allocate nData nrrd"],
239 :     setSizes(0, mkInt nElems),
240 :     setSizes(1, numElemsV),
241 : jhr 3927 U.maybeAlloc (env, nDataV, Nrrd.tyToEnum nrrdType, 2)
242 : jhr 3909 ]
243 :     (* generate the nLengths copy code *)
244 :     val copyLengths = let
245 :     val pInit = CL.mkDeclInit(CL.T_Ptr CL.uint32, "ip",
246 : jhr 3924 CL.mkReinterpretCast(CL.T_Ptr(CL.uint32), CL.mkIndirect(nLengthsV, "data")))
247 : jhr 3909 val offsetDecl = CL.mkDeclInit(CL.uint32, "offset", CL.mkInt 0)
248 :     val copyBlk = CL.mkBlock[
249 :     CL.mkDeclInit(CL.uint32, "n", seqLength(stateVar name)),
250 :     CL.mkAssign(CL.mkUnOp(CL.%*, CL.mkPostOp(ipV, CL.^++)), offsetV),
251 :     CL.mkAssign(CL.mkUnOp(CL.%*, CL.mkPostOp(ipV, CL.^++)), nV),
252 :     CL.S_Exp(CL.mkAssignOp(offsetV, CL.+=, nV))
253 :     ]
254 : jhr 3958 val copyStm = if #isGrid spec
255 : jhr 3909 then copyBlk
256 : jhr 3917 else if #snapshot spec
257 : jhr 3909 then ifActive copyBlk
258 :     else ifStable copyBlk
259 :     in
260 :     CL.mkComment["initialize nLengths nrrd"] ::
261 :     pInit ::
262 :     offsetDecl ::
263 :     forStrands copyStm ::
264 :     initAxisKinds (nLengthsV, Nrrd.Kind2Vector, nAxes, domAxisKind)
265 :     end
266 :     (* generate the nData copy code *)
267 :     val copyData = let
268 :     val pInit = CL.mkDeclInit(CL.charPtr, "cp",
269 : jhr 3924 CL.mkReinterpretCast(CL.charPtr, CL.mkIndirect(nDataV, "data")))
270 : jhr 3909 val copyStm = CL.mkAssign(cpV, seqCopy(
271 :     CL.mkBinOp(mkInt nElems, CL.#*, CL.mkSizeof(elemCTy)), cpV, stateVar name))
272 : jhr 3958 val copyStm = if #isGrid spec
273 : jhr 3909 then copyStm
274 : jhr 3917 else if #snapshot spec
275 : jhr 3909 then ifActive copyStm
276 :     else ifStable copyStm
277 :     in
278 :     CL.mkComment["initialize nLengths nrrd"] ::
279 :     pInit ::
280 :     forStrands copyStm ::
281 :     initAxisKinds (nDataV, axisKind, 1, Nrrd.KindList)
282 :     end
283 :     (* the function body *)
284 :     val stms =
285 :     sizesDecl ::
286 :     countElems @
287 :     checkForNoStrands @
288 :     lengthsNrrd @
289 :     checkForEmpty @
290 :     dataNrrd @
291 :     copyLengths @
292 :     copyData @
293 : jhr 3958 [CL.mkReturn(SOME(CL.mkVar "false"))]
294 : jhr 3909 in
295 :     ([CL.PARAM([], nrrdPtrTy, "nLengths"), CL.PARAM([], nrrdPtrTy, "nData")], CL.mkBlock stms)
296 :     end
297 :    
298 :     (* create the body of an output function for fixed-size outputs. The structure of the
299 :     * function body is:
300 :     *
301 :     * declare and compute sizes array
302 :     * allocate nrrd nData
303 :     * copy data from strands to nrrd
304 :     *)
305 : jhr 3917 fun genFixedOutput (env, snapshot, nAxes, ty, name) = let
306 :     val spec = Env.target env
307 :     val (elemCTy, nrrdType, axisKind, nElems) = U.infoOf (env, ty)
308 :     val stateVar = stateVar spec
309 : jhr 3909 val (nAxes, domAxisKind) = (case nAxes
310 :     of NONE => (1, Nrrd.KindList)
311 :     | SOME n => (n, Nrrd.KindSpace)
312 :     (* end case *))
313 :     val nDataAxes = if (axisKind = Nrrd.KindScalar) then 0 else 1
314 :     (* generate the sizes initialization code *)
315 :     val initSizes = let
316 :     val dimSizes = let
317 :     val dcl = CL.mkDecl(CL.T_Array(sizeTy, SOME(nAxes+nDataAxes)), "sizes", NONE)
318 :     in
319 :     if (axisKind = Nrrd.KindScalar)
320 :     then [dcl]
321 :     else [dcl, setSizes(0, mkInt nElems)]
322 :     end
323 :     in
324 : jhr 3958 if #isGrid spec
325 : jhr 3909 then dimSizes @
326 :     List.tabulate (nAxes, fn i =>
327 : jhr 3958 setSizes(i+nDataAxes, CL.mkSubscript(CL.mkIndirect(wrldV, "_size"), mkInt(nAxes-i-1))))
328 : jhr 3909 else let
329 :     val cntStm = CL.S_Exp(CL.mkPostOp(numStableV, CL.^++))
330 :     val lpBody = if snapshot
331 :     then ifActive cntStm
332 :     else ifStable cntStm
333 :     in
334 :     CL.mkDeclInit(sizeTy, "numStable", mkInt 0) ::
335 :     forStrands lpBody ::
336 :     dimSizes @ [setSizes(nDataAxes, numStableV)]
337 :     end
338 :     end
339 :     (* code to check for no data to output (i.e., no active strands) *)
340 : jhr 3958 val checkForEmpty = if (#isGrid spec)
341 : jhr 3909 then []
342 :     else [
343 :     CL.mkComment["check for empty output"],
344 :     CL.mkIfThen(
345 :     CL.mkBinOp(mkInt 0, CL.#==, numStableV),
346 :     CL.mkBlock[
347 :     CL.mkCall("nrrdEmpty", [nDataV]),
348 :     CL.mkReturn(SOME(CL.mkVar "false"))
349 :     ])
350 :     ]
351 :     (* generate the copy code *)
352 :     val copyCode = let
353 :     val pDecl = CL.mkDeclInit(CL.charPtr, "cp",
354 : jhr 3924 CL.mkReinterpretCast(CL.charPtr, CL.mkIndirect(nDataV, "data")))
355 : jhr 3909 val copyBlk = CL.mkBlock[
356 :     CL.mkCall("memcpy", [
357 :     cpV,
358 :     CL.mkUnOp(CL.%&, stateVar name),
359 :     CL.mkBinOp(mkInt nElems, CL.#*, CL.mkSizeof elemCTy)
360 :     ]),
361 :     CL.mkExpStm(CL.mkAssignOp(cpV, CL.+=,
362 :     CL.mkBinOp(mkInt nElems, CL.#*, CL.mkSizeof elemCTy)))
363 :     ]
364 : jhr 3958 val copyStm = if #isGrid spec
365 : jhr 3909 then copyBlk
366 :     else if snapshot
367 :     then ifActive copyBlk
368 :     else ifStable copyBlk
369 :     in
370 :     pDecl :: forStrands copyStm :: initAxisKinds (nDataV, axisKind, nAxes, domAxisKind)
371 :     end
372 :     (* the function body *)
373 :     val stms =
374 :     CL.mkComment["Compute sizes of nrrd file"] ::
375 :     initSizes @
376 :     checkForEmpty @
377 :     CL.mkComment["Allocate nData nrrd"] ::
378 : jhr 3927 U.maybeAlloc (env, nDataV, Nrrd.tyToEnum nrrdType, nAxes+nDataAxes) ::
379 : jhr 3909 CL.mkComment["copy data to output nrrd"] ::
380 :     copyCode @
381 :     [CL.mkReturn(SOME(CL.mkVar "false"))]
382 :     in
383 :     ([CL.PARAM([], nrrdPtrTy, "nData")], CL.mkBlock stms)
384 :     end
385 : jhr 3958
386 : jhr 3917 fun gen (env, nAxes, outputs) = let
387 :     val spec = Env.target env
388 :     fun getFn snapshot {name, ty, isGlobal} = let
389 : jhr 3909 val funcName = if snapshot
390 : jhr 3917 then GenLibraryInterface.snapshotGet(spec, name)
391 :     else GenLibraryInterface.outputGet(spec, name)
392 : jhr 3909 fun mkFunc (params, body) =
393 : jhr 3917 CL.D_Func(
394 :     [], CL.boolTy, funcName, CL.PARAM([], RN.worldPtrTy, "wrld")::params, body)
395 : jhr 3909 in
396 :     case ty
397 : jhr 3922 of Ty.SeqTy(ty', NONE) => mkFunc (genDynOutput(env, snapshot, nAxes, ty', name))
398 :     | _ => mkFunc (genFixedOutput(env, snapshot, nAxes, ty, name))
399 : jhr 3909 (* end case *)
400 :     end
401 : jhr 3917 val getFns = List.map (getFn false) outputs
402 :     in
403 :     if (#exec spec)
404 :     then getFns @ U.genOutput(env, outputs)
405 :     else if (#snapshot spec)
406 :     then List.map (getFn true) outputs @ getFns
407 :     else getFns
408 :     end
409 : jhr 3909
410 :     end

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0