PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/xf/nucleus/graph/KernelGraphOps.d

https://bitbucket.org/h3r3tic/boxen
D | 1887 lines | 1357 code | 282 blank | 248 comment | 159 complexity | 16557f717d13441de86ccca3eee2d0c7 MD5 | raw file
  1. module xf.nucleus.graph.KernelGraphOps;
  2. private {
  3. import xf.Common;
  4. import xf.nucleus.Function;
  5. import xf.nucleus.KernelImpl;
  6. import xf.nucleus.kernel.KernelDef;
  7. import xf.nucleus.graph.KernelGraph;
  8. // for the conversion
  9. import xf.nucleus.Param;
  10. import xf.nucleus.TypeSystem;
  11. import xf.nucleus.graph.Graph;
  12. import xf.nucleus.graph.GraphOps;
  13. import xf.nucleus.TypeConversion;
  14. import xf.mem.StackBuffer;
  15. import xf.mem.SmallTempArray;
  16. import xf.utils.LocalArray;
  17. import xf.utils.FormatTmp;
  18. import tango.text.convert.Format;
  19. // ----
  20. import xf.nucleus.Log : error = nucleusError, log = nucleusLog;
  21. }
  22. public {
  23. import xf.nucleus.graph.GraphDefs;
  24. }
  25. struct ConvCtx {
  26. SemanticConverterIter
  27. semanticConverters;
  28. bool delegate(cstring, KernelImpl*)
  29. getKernel;
  30. }
  31. /**
  32. * Whether to insert converters between regular graph nodes and an Output.
  33. * It's useful not to perform the conversion when fusing graphs together, so
  34. * that a conversion forth and back may be avoided without an opitimization step.
  35. * In such a case, Skip would be selected.
  36. *
  37. * Conversely, the data must be fully converted for nodes that escape the graph,
  38. * that is, Output (or Rasterize) nodes. In this case, go with Perform
  39. */
  40. enum OutputNodeConversion {
  41. Perform,
  42. Skip
  43. }
  44. /**
  45. * Finds the output parameter which is connected to a given /input/
  46. * parameter in the graph. Essentially - the source of data flow
  47. */
  48. bool findSrcParam(
  49. KernelGraph kg,
  50. GraphNodeId dstNid,
  51. cstring dstParam,
  52. GraphNodeId* srcNid,
  53. Param** srcParam
  54. ) {
  55. foreach (src; kg.flow.iterIncomingConnections(dstNid)) {
  56. foreach (fl; kg.flow.iterDataFlow(src, dstNid)) {
  57. if (fl.to == dstParam) {
  58. *srcNid = src;
  59. *srcParam = kg.getNode(src).getOutputParam(fl.from);
  60. return true;
  61. }
  62. }
  63. }
  64. return false;
  65. }
  66. /**
  67. * Acquires an output parameter from the node if it's a regular one,
  68. * or tracks it in the graph and returns the connected one if it's an
  69. * Output node.
  70. *
  71. * Use it to obtain an output param of a node to which you may want to
  72. * connect something. Or basically to get the 'real' output when
  73. * Output nodes are involved
  74. */
  75. bool getOutputParamIndirect(
  76. KernelGraph kg,
  77. GraphNodeId dstNid,
  78. cstring dstParam,
  79. GraphNodeId* srcNid,
  80. Param** srcParam
  81. ) {
  82. final node = kg.getNode(dstNid);
  83. /+if (KernelGraph.NodeType.Bridge == node.type && ) {
  84. if (
  85. } else +/if (KernelGraph.NodeType.Output == node.type) {
  86. return findSrcParam(kg, dstNid, dstParam, srcNid, srcParam);
  87. } else {
  88. if ((*srcParam = node.getOutputParam(dstParam)) !is null) {
  89. *srcNid = dstNid;
  90. return true;
  91. } else {
  92. return false;
  93. }
  94. }
  95. }
  96. /**
  97. * Connect two disjoint subgraphs by the means of an explicitly marked input node
  98. *
  99. * Auto flow is performed as if the two graphs were auto-connected in separation,
  100. * yet some conversions are potentially avoided.
  101. *
  102. * This operation removes the input node if it's of the Input type or leaves it be
  103. * if it's a different node. This makes it possible e.g. to connect a complete graph
  104. * with designated Input and Output nodes to an existing graph, or just tack a single
  105. * note into one.
  106. *
  107. * The _findSrcParam callback will have to provide the parameters on the input side
  108. * of the Output node in the existing (connected, converted) graph.
  109. *
  110. * Note: currently the callback is queried via the names of the /Input/ node
  111. * in the destination graph
  112. */
  113. void fuseGraph(
  114. KernelGraph graph,
  115. GraphNodeId input,
  116. ConvCtx ctx,
  117. GraphNodeId[] dstGraphTopological,
  118. bool delegate(
  119. Param* dstParam,
  120. GraphNodeId* srcNid,
  121. Param** srcParam
  122. ) _findSrcParam,
  123. OutputNodeConversion outNodeConversion
  124. ) {
  125. return fuseGraph(
  126. graph,
  127. input,
  128. ctx,
  129. (int delegate(ref GraphNodeId) sink) {
  130. foreach (nid; dstGraphTopological) {
  131. if (int r = sink(nid)) {
  132. return r;
  133. }
  134. }
  135. return 0;
  136. },
  137. _findSrcParam,
  138. outNodeConversion
  139. );
  140. }
  141. /// ditto
  142. void fuseGraph(
  143. KernelGraph graph,
  144. GraphNodeId input,
  145. ConvCtx ctx,
  146. int delegate(int delegate(ref GraphNodeId)) dstGraphTopological,
  147. bool delegate(
  148. Param* dstParam,
  149. GraphNodeId* srcNid,
  150. Param** srcParam
  151. ) _findSrcParam,
  152. OutputNodeConversion outNodeConversion
  153. ) {
  154. scope stack = new StackBuffer;
  155. alias KernelGraph.NodeType NT;
  156. foreach (dummy; graph.flow.iterIncomingConnections(input)) {
  157. error(
  158. "The 'input' node may not have any incoming connections"
  159. " prior to calling fuseGraph"
  160. );
  161. }
  162. bool inputNodeIsFunc = KernelGraph.NodeType.Input != graph.getNode(input).type;
  163. // A list of all output ports on the Input node, to be used in custom
  164. // auto flow port generation
  165. final outputPorts = LocalDynArray!(NodeParam)(stack);
  166. foreach (ref fromParam; *graph.getNode(input).getParamList()) {
  167. if (fromParam.isOutput) {
  168. outputPorts.pushBack(NodeParam(input, &fromParam));
  169. }
  170. }
  171. bool isOutputNode(GraphNodeId id) {
  172. return NT.Output == graph.getNode(id).type;
  173. }
  174. // Do auto flow for the second graph, extending the incoming flow of the
  175. // input node to the inputs of the output node from the first graph
  176. foreach (id; dstGraphTopological) {
  177. if (inputNodeIsFunc && input == id) {
  178. /*
  179. * This is a special case for when the destination graph's /input/
  180. * node it not of the Input type but e.g. a Func node. If this wasn't
  181. * the case, we'd be connecting the Input node's /successors/ to the
  182. * source graph in a more involved operation. This case is simpler
  183. * and only involves connecting the input node directly to what's
  184. * in the source graph.
  185. */
  186. doAutoFlow(
  187. graph,
  188. id,
  189. ctx,
  190. OutputNodeConversion.Skip == outNodeConversion && isOutputNode(id)
  191. ? FlowGenMode.DirectConnection
  192. : FlowGenMode.InsertConversionNodes,
  193. (Param* dstParam, void delegate(NodeParam) incomingSink) {
  194. GraphNodeId fromNode;
  195. Param* fromParam;
  196. if (!_findSrcParam(
  197. dstParam,
  198. &fromNode,
  199. &fromParam
  200. )) {
  201. assert (false,
  202. "Could not find a source parameter"
  203. " for the Output node twin to the"
  204. " Func node used in graph fusion."
  205. " This should have been triggered"
  206. " earlier, when resolving the Output."
  207. " The param was '" ~ dstParam.name ~ "'."
  208. );
  209. }
  210. assert (fromParam.isOutput);
  211. /*
  212. * Add the param returned by the user to the list of nodes
  213. * for which we're considering auto conversion. This will
  214. * usually be just one node that the user supplies, however
  215. * it could happen that there's e.g. a Data node connected
  216. * to the input on the destination side, so we cover that
  217. * case by using AutoFlow instead of a direct connection
  218. */
  219. incomingSink(NodeParam(fromNode, fromParam));
  220. }
  221. );
  222. // Convert SemanticExp to Semantic, nuff said
  223. simplifyParamSemantics(graph, id);
  224. } else if (input == id) {
  225. scope stack2 = new StackBuffer;
  226. final toRemove = LocalDynArray!(ConFlow)(stack2);
  227. foreach (outCon; graph.flow.iterOutgoingConnections(input)) {
  228. if (OutputNodeConversion.Perform == outNodeConversion || !isOutputNode(outCon)) {
  229. foreach (outFl; graph.flow.iterDataFlow(input, outCon)) {
  230. GraphNodeId fromNode;
  231. Param* fromParam;
  232. if (Param* fromTmp = graph.getNode(input).getOutputParam(outFl.from)) {
  233. if (!_findSrcParam(
  234. fromTmp,
  235. &fromNode,
  236. &fromParam
  237. )) {
  238. error(
  239. "_findSrcParam returned false for param '{}' and node {}.",
  240. fromTmp.name,
  241. input.id
  242. );
  243. }
  244. if (doManualFlow(
  245. graph,
  246. fromNode, outCon,
  247. DataFlow(fromParam.name, outFl.to),
  248. ctx
  249. )) {
  250. toRemove.pushBack(ConFlow(
  251. input, outFl.from,
  252. outCon, outFl.to
  253. ));
  254. }
  255. } else {
  256. error(
  257. "Src param '{}' not found in node {}.",
  258. outFl.from,
  259. input.id
  260. );
  261. }
  262. }
  263. }
  264. }
  265. foreach (rem; toRemove) {
  266. graph.flow.removeDataFlow(rem.fromNode, rem.fromParam, rem.toNode, rem.toParam);
  267. }
  268. } else {
  269. doAutoFlow(
  270. graph,
  271. id,
  272. ctx,
  273. OutputNodeConversion.Skip == outNodeConversion && isOutputNode(id)
  274. ? FlowGenMode.DirectConnection
  275. : FlowGenMode.InsertConversionNodes,
  276. /* Using custom enumeration of incoming nodes/ports
  277. * because we'll need to treat the Input node specially.
  278. *
  279. * Basically, we want the semantics of connecting the Output
  280. * and Input nodes together and resolving auto flow regularly.
  281. * This is not done in such a straightforward way, since
  282. * 1) Can't connect an Output node to an Input node, because
  283. * Output nodes only have _input_ ports and Input nodes
  284. * only have _output_ ports.
  285. * 2) There might be some additional conversions done in the
  286. * straightforward approach. They would subsequently require
  287. * an optimization pass.
  288. */
  289. (Param* toParam, void delegate(NodeParam) incomingSink) {
  290. foreach (fromId; graph.flow.iterIncomingConnections(id)) {
  291. /* Ordinary stuff so far */
  292. if (graph.flow.hasAutoFlow(fromId, id)) {
  293. /* Now, if this node has automatic flow from the
  294. * Input node, we will want to override it. Only in case
  295. * that the input node is of the Input type. The other
  296. * case is done for in the first clause of the top-most
  297. * conditional in this function.
  298. */
  299. if (!inputNodeIsFunc && input == fromId) {
  300. /* First, we figure out to which port auto
  301. * flow would connect this param to, but we
  302. * don't connect to it and instead dig deeper
  303. */
  304. findAutoFlow(
  305. graph,
  306. outputPorts.data,
  307. id,
  308. toParam,
  309. ctx,
  310. /* This dg will receive the port to which
  311. * auto flow would resolve to on the
  312. * side of the Input node
  313. */
  314. ( ConvSinkItem[] convChain,
  315. GraphNodeId intermediateId,
  316. Param* intermediateParam
  317. ) {
  318. if (input == intermediateId) {
  319. /* Now, the Input node is the graph2-side
  320. * connector, which is a twin to an
  321. * Output node in graph1 and which has
  322. * incoming params connected to. We now
  323. * find which param connects to the port
  324. * equivalent to what we just identified
  325. * for the Input node.
  326. */
  327. GraphNodeId fromNode;
  328. Param* fromParam;
  329. if (!_findSrcParam(
  330. toParam,
  331. &fromNode,
  332. &fromParam
  333. )) {
  334. assert (false,
  335. "Could not find a source parameter"
  336. " for the Output node twin to the"
  337. " Input node used in graph fusion."
  338. " This should have been triggered"
  339. " earlier, when resolving the Output."
  340. );
  341. }
  342. assert (fromParam.isOutput);
  343. /* Finally, the original port from graph1
  344. * is added to the list of all connections
  345. * to consider for the auto flow resolving
  346. * process for the particular param we're
  347. * evaluating a few scopes higher :P
  348. */
  349. incomingSink(NodeParam(fromNode, fromParam));
  350. } else {
  351. incomingSink(NodeParam(
  352. intermediateId,
  353. intermediateParam
  354. ));
  355. }
  356. },
  357. /*
  358. * This call to findAutoFlow doesn't need to find
  359. * sources for flow into all of the inputs, as
  360. * the outer doAutoFlow may decide to use other
  361. * nodes as well, say when using Data nodes
  362. * in addition to external input
  363. */
  364. ErrorHandlingMode.Ignore
  365. );
  366. } else {
  367. /* Data flow from a node different than the
  368. * Input node. Regular stuff
  369. */
  370. final fromNode = graph.getNode(fromId);
  371. ParamList* fromParams = fromNode.getParamList();
  372. foreach (ref fromParam; *fromParams) {
  373. if (fromParam.isOutput) {
  374. incomingSink(NodeParam(fromId, &fromParam));
  375. }
  376. }
  377. }
  378. }
  379. }
  380. }
  381. );
  382. // Convert SemanticExp to Semantic, nuff said
  383. simplifyParamSemantics(graph, id);
  384. {
  385. scope stack2 = new StackBuffer;
  386. final toRemove = LocalDynArray!(ConFlow)(stack2);
  387. foreach (con; graph.flow.iterOutgoingConnections(id)) {
  388. if (OutputNodeConversion.Perform == outNodeConversion || !isOutputNode(con)) {
  389. foreach (fl; graph.flow.iterDataFlow(id, con)) {
  390. if (doManualFlow(
  391. graph,
  392. id,
  393. con, fl,
  394. ctx
  395. )) {
  396. toRemove.pushBack(ConFlow(
  397. id, fl.from,
  398. con, fl.to
  399. ));
  400. }
  401. }
  402. }
  403. }
  404. foreach (rem; toRemove) {
  405. graph.flow.removeDataFlow(rem.fromNode, rem.fromParam, rem.toNode, rem.toParam);
  406. }
  407. }
  408. }
  409. }
  410. // The Input node is useless now, but don't remove any other node types
  411. if (KernelGraph.NodeType.Input == graph.getNode(input).type) {
  412. graph.removeNode(input);
  413. }
  414. }
  415. /**
  416. * Redirects the data flow from the 'output' node into the nodes connected
  417. * to the 'input' node, then removes both nodes, thus fusing two separate
  418. * sub-graphs.
  419. *
  420. * This should optimally be called before resolving auto flow, so that
  421. * some conversions may potentially be avoided.
  422. */
  423. void fuseGraph(
  424. KernelGraph graph,
  425. GraphNodeId output,
  426. int delegate(int delegate(ref GraphNodeId)) graph1NodeIter,
  427. GraphNodeId input,
  428. ConvCtx ctx,
  429. OutputNodeConversion outNodeConversion
  430. ) {
  431. scope stack = new StackBuffer;
  432. foreach (dummy; graph.flow.iterOutgoingConnections(output)) {
  433. error(
  434. "The 'output' node may not have any outgoing connections"
  435. " prior to calling fuseGraph"
  436. );
  437. }
  438. // Not disposed anywhere since its storage is on the StackBuffer
  439. DynamicBitSet graph1Nodes;
  440. graph1Nodes.alloc(graph.capacity, &stack.allocRaw);
  441. graph1Nodes.clearAll();
  442. //markPrecedingNodes(graph.backend_readOnly, &graph1Nodes, null, output);
  443. foreach (nid; graph1NodeIter) {
  444. graph1Nodes.set(nid.id);
  445. }
  446. final topological = stack.allocArray!(GraphNodeId)(graph.numNodes);
  447. findTopologicalOrder(graph.backend_readOnly, topological);
  448. int iterGraph1Nodes(int delegate(ref GraphNodeId) sink) {
  449. foreach (id; topological) {
  450. if (graph1Nodes.isSet(id.id)) {
  451. if (int r = sink(id)) {
  452. return r;
  453. }
  454. }
  455. }
  456. return 0;
  457. }
  458. int iterGraph2Nodes(int delegate(ref GraphNodeId) sink) {
  459. foreach (id; topological) {
  460. if (!graph1Nodes.isSet(id.id)) {
  461. if (int r = sink(id)) {
  462. return r;
  463. }
  464. }
  465. }
  466. return 0;
  467. }
  468. convertGraphDataFlowExceptOutput(
  469. graph,
  470. ctx,
  471. &iterGraph1Nodes
  472. );
  473. bool outputNodeIsFunc = KernelGraph.NodeType.Output != graph.getNode(output).type;
  474. fuseGraph(
  475. graph,
  476. input,
  477. ctx,
  478. &iterGraph2Nodes,
  479. (
  480. Param* dstParam,
  481. GraphNodeId* srcNid,
  482. Param** srcParam
  483. ) {
  484. /*
  485. * We perform the lookup by name only (TODO?).
  486. */
  487. return .getOutputParamIndirect(
  488. graph,
  489. output,
  490. dstParam.name,
  491. srcNid,
  492. srcParam
  493. );
  494. },
  495. outNodeConversion
  496. );
  497. // The Output node is useless now, but don't remove any other node types
  498. if (!outputNodeIsFunc) {
  499. graph.removeNode(output);
  500. }
  501. graph.flow.removeAllAutoFlow();
  502. }
  503. enum FlowGenMode {
  504. InsertConversionNodes,
  505. DirectConnection,
  506. NoAction
  507. }
  508. struct NodeParam {
  509. GraphNodeId node;
  510. Param* param;
  511. }
  512. private struct ConFlow {
  513. GraphNodeId fromNode;
  514. cstring fromParam;
  515. GraphNodeId toNode;
  516. cstring toParam;
  517. }
  518. void verifyDataFlowNames(KernelGraph graph) {
  519. final flow = graph.flow();
  520. foreach (fromId; graph.iterNodes) {
  521. final fromNode = graph.getNode(fromId);
  522. foreach (toId; flow.iterOutgoingConnections(fromId)) {
  523. final toNode = graph.getNode(toId);
  524. foreach (fl; flow.iterDataFlow(fromId, toId)) {
  525. final op = fromNode.getOutputParam(fl.from);
  526. if (op is null) {
  527. error(
  528. "verifyDataFlowNames: The source node for flow {}->{}"
  529. " doesn't have an output parameter called '{}'",
  530. fromId.id, toId.id, fl.from
  531. );
  532. }
  533. assert (op.isOutput);
  534. final ip = toNode.getInputParam(fl.to);
  535. if (ip is null) {
  536. error(
  537. "verifyDataFlowNames: The target node for flow {}->{}"
  538. " doesn't have an input parameter called '{}'",
  539. fromId.id, toId.id, fl.to
  540. );
  541. }
  542. assert (ip.isInput);
  543. assert (ip.hasPlainSemantic);
  544. }
  545. }
  546. }
  547. scope stack = new StackBuffer;
  548. foreach (toId; graph.iterNodes) {
  549. final toNode = graph.getNode(toId);
  550. ParamList* plist;
  551. if (KernelGraph.NodeType.Kernel == toNode.type) {
  552. plist = &toNode.kernel.kernel.func.params;
  553. } else {
  554. plist = toNode.getParamList();
  555. }
  556. DynamicBitSet paramHasInput;
  557. paramHasInput.alloc(plist.length, &stack.allocRaw);
  558. paramHasInput.clearAll();
  559. void onDuplicateFlow(cstring to) {
  560. cstring[] sources;
  561. foreach (fromId; flow.iterIncomingConnections(toId)) {
  562. final fromNode = graph.getNode(fromId);
  563. foreach (fl; flow.iterDataFlow(fromId, toId)) {
  564. if (fl.to == to) {
  565. sources ~= Format("{}.{}", fromId.id, fl.from);
  566. }
  567. }
  568. }
  569. error(
  570. "Duplicate flow to {}.{} from {}.",
  571. toId.id, to, sources
  572. );
  573. }
  574. foreach (fromId; flow.iterIncomingConnections(toId)) {
  575. final fromNode = graph.getNode(fromId);
  576. foreach (fl; flow.iterDataFlow(fromId, toId)) {
  577. Param* dst;
  578. if (plist.getInput(fl.to, &dst)) {
  579. final idx = plist.indexOf(dst);
  580. if (paramHasInput.isSet(idx)) {
  581. onDuplicateFlow(fl.to);
  582. } else {
  583. paramHasInput.set(idx);
  584. }
  585. } else {
  586. error(
  587. "verifyDataFlowNames: The target node for flow {}->{}"
  588. " doesn't have an input parameter called '{}'",
  589. fromId.id, toId.id, fl.to
  590. );
  591. }
  592. }
  593. }
  594. }
  595. }
  596. private cstring _fmtChain(ConvSinkItem[] chain) {
  597. cstring res;
  598. foreach (c; chain) {
  599. res ~= Format(
  600. " {} -> <{}>\n",
  601. c.converter.func.name,
  602. c.afterConversion.toString
  603. );
  604. }
  605. return res;
  606. }
  607. // returns true if any new connections were inserted
  608. private bool _insertConversionNodes(
  609. KernelGraph graph,
  610. ConvSinkItem[] chain,
  611. GraphNodeId fromId,
  612. Param* fromParam,
  613. GraphNodeId toId,
  614. Param* toParam
  615. ) {
  616. GraphNodeId srcId = fromId;
  617. Param* srcParam = fromParam;
  618. foreach (c; chain) {
  619. final cnodeId = graph.addNode(KernelGraph.NodeType.Func);
  620. final cnode = graph.getNode(cnodeId).func();
  621. cnode.func = c.converter.func;
  622. assert (2 == cnode.func.params.length);
  623. assert (cnode.func.params[0].isInput);
  624. assert (cnode.func.params[0].hasPlainSemantic);
  625. assert (cnode.func.params[1].isOutput);
  626. graph.flow.addDataFlow(
  627. srcId,
  628. srcParam.name,
  629. cnodeId,
  630. cnode.func.params[0].name
  631. );
  632. void* delegate(uword) mem = &graph._mem.pushBack;
  633. cnode.params._allocator = mem;
  634. cnode.params.add(cnode.func.params[0].dup(mem));
  635. auto p = cnode.params.add(ParamDirection.Out, cnode.func.params[1].name);
  636. p.hasPlainSemantic = true;
  637. *p.semantic() = c.afterConversion.dup(mem);
  638. // No need to care about the default value here.
  639. // No need to care about whether the param wants auto flow or not, either
  640. srcId = cnodeId;
  641. srcParam = cnode.params[1];
  642. }
  643. bool newFlow;
  644. graph.flow.addDataFlow(
  645. srcId,
  646. srcParam.name,
  647. toId,
  648. toParam.name,
  649. &newFlow
  650. );
  651. return newFlow || chain.length > 0;
  652. }
  653. private enum ErrorHandlingMode {
  654. Throw,
  655. Ignore
  656. }
  657. private void findAutoFlow(
  658. KernelGraph graph,
  659. NodeParam[] fromParams,
  660. GraphNodeId toId,
  661. Param* toParam,
  662. ConvCtx ctx,
  663. void delegate(
  664. ConvSinkItem[] convChain,
  665. GraphNodeId fromId,
  666. Param* fromParam
  667. ) result,
  668. ErrorHandlingMode errorHandlingMode = ErrorHandlingMode.Throw
  669. ) {
  670. scope stack = new StackBuffer;
  671. struct ConvInfo {
  672. ConvSinkItem[] chain;
  673. GraphNodeId fromId;
  674. Param* fromParam;
  675. ConvInfo* next;
  676. }
  677. int bestCost = int.max;
  678. ConvInfo* bestConvs;
  679. foreach (from_; fromParams) {
  680. GraphNodeId fromId = from_.node;
  681. Param* fromParam = from_.param;
  682. log.trace("findAutoFlow {} -> {}", fromId.id, toId.id);
  683. scope stack2 = new StackBuffer;
  684. assert (fromParam.isOutput);
  685. int convCost = 0;
  686. assert (toParam.isInput);
  687. assert (fromParam.hasPlainSemantic, fromParam.name);
  688. assert (toParam.hasPlainSemantic, toParam.name);
  689. findConversion(
  690. *fromParam.semantic,
  691. *toParam.semantic,
  692. ctx.semanticConverters,
  693. stack2,
  694. (ConvSinkItem[] convChain) {
  695. if (convCost > bestCost) {
  696. return;
  697. }
  698. stack2.mergeWith(stack);
  699. final info = stack._new!(ConvInfo)(
  700. convChain,
  701. fromId,
  702. fromParam,
  703. cast(ConvInfo*)null // stupid DMD
  704. );
  705. if (convCost < bestCost) {
  706. // replace
  707. bestConvs = info;
  708. bestCost = convCost;
  709. } else if (convCost == bestCost) {
  710. // add
  711. info.next = bestConvs;
  712. bestConvs = info;
  713. }
  714. },
  715. &convCost
  716. );
  717. }
  718. if (bestCost < int.max) {
  719. assert (bestConvs !is null);
  720. if (bestConvs.next !is null) {
  721. // Ambiguity error
  722. cstring errMsg = Format(
  723. "Auto flow ambiguity while trying to find flow to an input param"
  724. " '{}' in graph node {}.\n"
  725. "Found multiple conversion paths with the cost {}:\n",
  726. toParam.toString, toId.id, bestCost
  727. );
  728. for (auto it = bestConvs; it !is null; it = it.next) {
  729. errMsg ~= Format(
  730. " Path from node {}:\n",
  731. it.fromId.id
  732. );
  733. errMsg ~= _fmtChain(it.chain);
  734. }
  735. error("{}", errMsg);
  736. } else {
  737. // Found a unique conversion path
  738. result(
  739. bestConvs.chain,
  740. bestConvs.fromId, bestConvs.fromParam
  741. );
  742. }
  743. } else if (ErrorHandlingMode.Throw == errorHandlingMode) {
  744. // Conversion path not found
  745. uword numConv = 0;
  746. cstring suffix;
  747. foreach (from_; fromParams) {
  748. ++numConv;
  749. if (suffix is null) {
  750. suffix = Format("{}.{}", from_.node.id, from_.param.name);
  751. } else {
  752. suffix ~= Format(", {}.{}", from_.node.id, from_.param.name);
  753. }
  754. }
  755. assert (toParam.wantAutoFlow, toParam.toString);
  756. // This error probably shouldn't even be triggered, as the
  757. // node may be unused.
  758. /+error(
  759. "Auto flow not found for an input param"
  760. " '{}' in graph node {}.\n"
  761. "Considered {} converters from:\n[{}]",
  762. toParam.toString, toId.id, numConv, suffix
  763. );+/
  764. }
  765. }
  766. private void simplifyParamSemantics(KernelGraph graph, GraphNodeId id) {
  767. final node = graph.getNode(id);
  768. if (KernelGraph.NodeType.Kernel == node.type) {
  769. foreach (ref Param par; node.kernel.kernel.func.params) {
  770. if (!par.hasPlainSemantic) {
  771. error("Special Kernel-type nodes must only have plain semantics.");
  772. }
  773. }
  774. }
  775. else if (KernelGraph.NodeType.Func == node.type) {
  776. final params = &node.func.params;
  777. foreach (ref Param par; *params) {
  778. if (par.isInput || par.hasPlainSemantic) {
  779. continue;
  780. }
  781. Semantic plainSem = Semantic(&graph._mem.pushBack);
  782. findOutputSemantic(
  783. &par,
  784. // getFormalParamSemantic
  785. (cstring name) {
  786. foreach (ref Param p; *params) {
  787. if (p.isInput && p.name == name) {
  788. return *p.semantic();
  789. }
  790. }
  791. error(
  792. "simplifyParamSemantics: output param '{}' refers to a"
  793. " nonexistent formal parameter '{}'.",
  794. par.name,
  795. name
  796. );
  797. assert (false);
  798. },
  799. // getActualParamSemantic
  800. (cstring name) {
  801. GraphNodeId fromId;
  802. cstring fromName;
  803. // Note: O (cons * flow * inputPorts). Maybe too slow?
  804. foreach (fromId_; graph.flow.iterIncomingConnections(id)) {
  805. foreach (fl; graph.flow.iterDataFlow(fromId_, id)) {
  806. if (fl.to == name) {
  807. if (fromName !is null) {
  808. error(
  809. "The semantic conversion process introduced"
  810. " duplicate flow\n to {}.{}"
  811. " from {}.{} and {}.{}.",
  812. id.id, name,
  813. fromId.id, fromName,
  814. fromId_.id, fl.from
  815. );
  816. }
  817. fromId = fromId_;
  818. fromName = fl.from;
  819. }
  820. }
  821. }
  822. if (fromName !is null) {
  823. Param* dstParam;
  824. if (params.getInput(name, &dstParam)) {
  825. assert (dstParam !is null);
  826. if (!dstParam.wantAutoFlow) {
  827. error(
  828. "Output param {} of kernel {} used by node {}"
  829. " has an semantic expression using the actual"
  830. " parameter of the input {}, which has been"
  831. " marked as noauto and has no direct connection.",
  832. par.name, node.func.func.name, id.id,
  833. name
  834. );
  835. }
  836. } else {
  837. assert (false, name);
  838. }
  839. return *graph.getNode(fromId)
  840. .getOutputParam(fromName).semantic();
  841. } else {
  842. assert (false, "simplifyParamSemantics: No flow to " ~ name);
  843. }
  844. },
  845. &plainSem
  846. );
  847. par.hasPlainSemantic = true;
  848. *par.semantic() = plainSem;
  849. }
  850. foreach (ref Param par; *params) {
  851. if (par.isOutput) {
  852. continue;
  853. }
  854. GraphNodeId fromId;
  855. cstring fromName;
  856. // Note: O (cons * flow * inputPorts). Maybe too slow?
  857. foreach (fromId_; graph.flow.iterIncomingConnections(id)) {
  858. foreach (fl; graph.flow.iterDataFlow(fromId_, id)) {
  859. if (fl.to == par.name) {
  860. if (fromName !is null) {
  861. error(
  862. "The semantic conversion process introduced"
  863. " duplicate flow\n to {}.{}"
  864. " from {}.{} and {}.{}.",
  865. id.id, par.name,
  866. fromId.id, fromName,
  867. fromId_.id, fl.from
  868. );
  869. }
  870. fromId = fromId_;
  871. fromName = fl.from;
  872. }
  873. }
  874. }
  875. if (fromName is null) {
  876. if (par.wantAutoFlow) {
  877. // the loop is to test whether there are any outgoing
  878. // connections at all
  879. foreach (id; graph.flow.iterOutgoingConnections(id)) {
  880. error("No flow to '{}' D:", par.name);
  881. }
  882. }
  883. } else {
  884. final srcParam = graph.getNode(fromId)
  885. .getOutputParam(fromName);
  886. *par.semantic() = *srcParam.semantic();
  887. }
  888. }
  889. }
  890. }
  891. private void doAutoFlow(
  892. KernelGraph graph,
  893. GraphNodeId toId,
  894. ConvCtx ctx,
  895. FlowGenMode flowGenMode
  896. ) {
  897. return doAutoFlow(
  898. graph,
  899. toId,
  900. ctx,
  901. flowGenMode,
  902. (Param*, void delegate(NodeParam) incomingSink) {
  903. foreach (fromId; graph.flow.iterIncomingConnections(toId)) {
  904. if (graph.flow.hasAutoFlow(fromId, toId)) {
  905. final fromNode = graph.getNode(fromId);
  906. ParamList* fromParams = fromNode.getParamList();
  907. foreach (ref fromParam; *fromParams) {
  908. if (fromParam.isOutput) {
  909. incomingSink(NodeParam(fromId, &fromParam));
  910. }
  911. }
  912. }
  913. }
  914. }
  915. );
  916. }
  917. private void doAutoFlow(
  918. KernelGraph graph,
  919. GraphNodeId toId,
  920. ConvCtx ctx,
  921. FlowGenMode flowGenMode,
  922. void delegate(Param*, void delegate(NodeParam)) incomingGen
  923. ) {
  924. scope stack = new StackBuffer;
  925. final toNode = graph.getNode(toId);
  926. ParamList* plist = toNode.getParamList();
  927. switch (toNode.type) {
  928. case KernelGraph.NodeType.Func:
  929. case KernelGraph.NodeType.Output:
  930. case KernelGraph.NodeType.Kernel:
  931. case KernelGraph.NodeType.Bridge:
  932. break;
  933. default: return; // just outputs here
  934. }
  935. DynamicBitSet portHasDataFlow;
  936. portHasDataFlow.alloc(plist.length, (uword num) { return stack.allocRaw(num); });
  937. portHasDataFlow.clearAll();
  938. foreach (i, ref param; *plist) {
  939. if (!param.wantAutoFlow) {
  940. portHasDataFlow.set(i);
  941. }
  942. }
  943. // Note: O (cons * flow * inputPorts). Maybe too slow?
  944. foreach (fromId; graph.flow.iterIncomingConnections(toId)) {
  945. foreach (fl; graph.flow.iterDataFlow(fromId, toId)) {
  946. final p = plist.get(fl.to);
  947. assert (p !is null, "auto flow: fl.to missing: " ~ fl.to);
  948. portHasDataFlow.set(plist.indexOf(p));
  949. }
  950. }
  951. foreach (paramI, ref param; *plist) {
  952. if (param.isInput && !portHasDataFlow.isSet(paramI)) {
  953. scope stack2 = new StackBuffer;
  954. final fromIds = LocalDynArray!(NodeParam)(stack2);
  955. incomingGen(&param, (NodeParam np) {
  956. fromIds.pushBack(np);
  957. });
  958. findAutoFlow(graph, fromIds.data, toId, &param, ctx,
  959. ( ConvSinkItem[] convChain,
  960. GraphNodeId fromId,
  961. Param* fromParam
  962. ) {
  963. switch (flowGenMode) {
  964. case FlowGenMode.InsertConversionNodes: {
  965. log.info("Found a conversion path. Inserting nodes.");
  966. _insertConversionNodes(
  967. graph,
  968. convChain,
  969. fromId, fromParam,
  970. toId, &param
  971. );
  972. } break;
  973. case FlowGenMode.DirectConnection: {
  974. graph.flow.addDataFlow(
  975. fromId, fromParam.name,
  976. toId, param.name
  977. );
  978. } break;
  979. default: assert (false);
  980. }
  981. }
  982. );
  983. }
  984. }
  985. }
  986. private bool isTypeKernel(cstring type, KernelImpl* info, ConvCtx ctx) {
  987. return ctx.getKernel(type, info);
  988. }
  989. /*
  990. * Returns true if the node or its incoming subtree contains free params
  991. * not filtered out by the supplied delegate
  992. */
  993. private bool buildFunctionSubgraph(
  994. KernelGraph graph,
  995. GraphNodeId id,
  996. bool delegate(Param*) paramFilter,
  997. void delegate(Param*, GraphNodeId) freeParamSink,
  998. DynamicBitSet* clonedNodesSet,
  999. DynamicBitSet* visitedNodesSet,
  1000. GraphNodeId[] oldToNew,
  1001. KernelGraph newGraph,
  1002. GraphNodeId newGraphDataNode,
  1003. GraphNodeId oldGraphCompNode,
  1004. GraphNodeId* newNode
  1005. ) {
  1006. if (clonedNodesSet.isSet(id.id)) {
  1007. *newNode = oldToNew[id.id];
  1008. assert (newNode.valid);
  1009. return true;
  1010. } else if (visitedNodesSet.isSet(id.id)) {
  1011. return false;
  1012. }
  1013. visitedNodesSet.set(id.id);
  1014. //log.trace("buildFunctionSubgraph");
  1015. bool result = false;
  1016. scope stack = new StackBuffer;
  1017. ParamList* plist = graph.getNode(id).getParamList();
  1018. DynamicBitSet paramHasInput;
  1019. paramHasInput.alloc(plist.length, &stack.allocRaw);
  1020. paramHasInput.clearAll();
  1021. foreach (from; graph.flow.iterIncomingConnections(id)) {
  1022. foreach (fl; graph.flow.iterDataFlow(from, id)) {
  1023. Param* dst;
  1024. if (!plist.getInput(fl.to, &dst)) {
  1025. error(
  1026. "Shit happened. Destination flow invalid for {}.{}->{}.{}",
  1027. from.id, fl.from, id.id, fl.to
  1028. );
  1029. } else {
  1030. paramHasInput.set(plist.indexOf(dst));
  1031. }
  1032. }
  1033. GraphNodeId prevNode;
  1034. //log.trace("buildFunctionSubgraph -> buildFunctionSubgraph");
  1035. if (buildFunctionSubgraph(
  1036. graph,
  1037. from,
  1038. paramFilter,
  1039. freeParamSink,
  1040. clonedNodesSet,
  1041. visitedNodesSet,
  1042. oldToNew,
  1043. newGraph,
  1044. newGraphDataNode,
  1045. oldGraphCompNode,
  1046. &prevNode
  1047. )) {
  1048. if (!result) {
  1049. auto node = graph.getNode(id);
  1050. *newNode = newGraph.addNode(node.type);
  1051. oldToNew[id.id] = *newNode;
  1052. node.copyTo(newGraph.getNode(*newNode));
  1053. clonedNodesSet.set(id.id);
  1054. result = true;
  1055. }
  1056. foreach (fl; graph.flow.iterDataFlow(from, id)) {
  1057. newGraph.flow.addDataFlow(prevNode, fl.from, *newNode, fl.to);
  1058. }
  1059. }
  1060. //log.trace("buildFunctionSubgraph returned.");
  1061. }
  1062. foreach (i, ref param; *plist) {
  1063. if (!param.isInput) {
  1064. continue;
  1065. }
  1066. if (!paramHasInput.isSet(i)) {
  1067. if (paramFilter(&param)) {
  1068. if (!result) {
  1069. auto node = graph.getNode(id);
  1070. *newNode = newGraph.addNode(node.type);
  1071. oldToNew[id.id] = *newNode;
  1072. node.copyTo(newGraph.getNode(*newNode));
  1073. clonedNodesSet.set(id.id);
  1074. result = true;
  1075. }
  1076. freeParamSink(
  1077. newGraph.getNode(*newNode).getInputParam(param.name),
  1078. *newNode
  1079. );
  1080. }
  1081. }
  1082. }
  1083. if (result) {
  1084. auto dataNode = newGraph.getNode(newGraphDataNode).data();
  1085. auto oldCompNode = graph.getNode(oldGraphCompNode).composite();
  1086. foreach (i, ref param; *plist) {
  1087. if (!param.isInput) {
  1088. continue;
  1089. }
  1090. if (paramHasInput.isSet(i)) {
  1091. // This param will have to be bridged via the Data node
  1092. formatTmp((Fmt fmt) {
  1093. fmt.format("comp__p{}", dataNode.params.length);
  1094. },
  1095. (cstring pname) {
  1096. /*
  1097. * Find where from the flow comes into the param back in the
  1098. * original graph. We'll copy the flow into the function node
  1099. * which graph the subgraph we're constructing.
  1100. */
  1101. GraphNodeId oldSrcNid;
  1102. Param* oldSrcParam;
  1103. if (!findSrcParam(
  1104. graph,
  1105. id,
  1106. param.name,
  1107. &oldSrcNid,
  1108. &oldSrcParam
  1109. )) {
  1110. error("Huh. Flow not found to param {}. But... but... D:", param.name);
  1111. }
  1112. /*
  1113. * Only copy the flow if the source is not already in the cloned
  1114. * subgraph. Otherwise the flow would be duplicate
  1115. */
  1116. if (!clonedNodesSet.isSet(oldSrcNid.id)) {
  1117. /*
  1118. * Add a param to the data node in the new subgraph.
  1119. * Use a unique name for it.
  1120. */
  1121. auto dparam = dataNode.params.add(param, pname);
  1122. dparam.dir = ParamDirection.Out;
  1123. /*
  1124. * Now add flow from the newly added Data node param to the
  1125. * node we've just cloned as the main part of this function.
  1126. */
  1127. newGraph.flow.addDataFlow(
  1128. newGraphDataNode,
  1129. pname,
  1130. *newNode,
  1131. param.name
  1132. );
  1133. /*
  1134. * Add the same param as in the Data node to the function node
  1135. * we're building in the old graph
  1136. */
  1137. auto fparam = oldCompNode.params.add(param, pname);
  1138. fparam.dir = ParamDirection.In;
  1139. /*
  1140. * Finally, create flow to the function node
  1141. */
  1142. graph.flow.addDataFlow(
  1143. oldSrcNid,
  1144. oldSrcParam.name,
  1145. oldGraphCompNode,
  1146. pname
  1147. );
  1148. }
  1149. });
  1150. }
  1151. }
  1152. }
  1153. return result;
  1154. }
  1155. // returns true if any new connections were inserted
  1156. private bool doManualFlow(
  1157. KernelGraph graph,
  1158. GraphNodeId fromId,
  1159. GraphNodeId toId,
  1160. DataFlow fl,
  1161. ConvCtx ctx
  1162. ) {
  1163. scope stack = new StackBuffer;
  1164. final fromNode = graph.getNode(fromId);
  1165. assert (fromNode !is null, "doManualfrom: fromNode not found");
  1166. final fromParam = fromNode.getOutputParam(fl.from);
  1167. assert (fromParam !is null, "doManualfrom: fromParam not found: " ~ fl.from);
  1168. assert (fromParam.hasPlainSemantic);
  1169. final toNode = graph.getNode(toId);
  1170. assert (toNode !is null, "doManualfrom: toNode not found");
  1171. final toParam = toNode.getInputParam(fl.to);
  1172. assert (toParam !is null, "doManualfrom: toParam not found: " ~ fl.to);
  1173. assert (toParam.hasPlainSemantic);
  1174. {
  1175. KernelImpl dstKernelImpl;
  1176. final srcType = fromParam.type;
  1177. if ( toParam.hasTypeConstraint
  1178. && isTypeKernel(toParam.type, &dstKernelImpl, ctx)
  1179. && (!fromParam.hasTypeConstraint || srcType != toParam.type)
  1180. ) {
  1181. // The destination is a kernel, this is functional composition, not regular
  1182. // param flow.
  1183. if (dstKernelImpl.type != KernelImpl.Type.Kernel) {
  1184. error(
  1185. "Kernels used in functional composition may not be graph"
  1186. " kernels. There's flow to a graph kernel '{}': {}.{} -> {}.{}.",
  1187. toParam.type, fromId.id, fl.from, toId.id, fl.to
  1188. );
  1189. }
  1190. auto dstFunc = dstKernelImpl.kernel.func;
  1191. assert (dstFunc !is null);
  1192. Param* funcOutputParam = null;
  1193. uword numFuncOutputParams;
  1194. foreach (ref param; dstFunc.params) {
  1195. if (param.isOutput) {
  1196. ++numFuncOutputParams;
  1197. funcOutputParam = &param;
  1198. }
  1199. }
  1200. //assureNotCyclic(graph);
  1201. assert (
  1202. 1 == numFuncOutputParams,
  1203. "TODO: currently kernels used in functional composition may only"
  1204. " have one output param."
  1205. );
  1206. assert (funcOutputParam !is null);
  1207. KernelGraph subgraph = createKernelGraph();
  1208. //assureNotCyclic(graph);
  1209. /*
  1210. * There will be a subgraph of comprising the nodes being used in
  1211. * functional composition. Create an output node for it
  1212. */
  1213. final outNodeId = subgraph.addNode(KernelGraph.NodeType.Output);
  1214. final outNode = subgraph.getNode(outNodeId).output();
  1215. final outNodeParam = outNode.params.add(*funcOutputParam);
  1216. outNodeParam.dir = ParamDirection.In;
  1217. /*
  1218. * Similarly with the input node.
  1219. */
  1220. final inNodeId = subgraph.addNode(KernelGraph.NodeType.Input);
  1221. final inNode = subgraph.getNode(inNodeId).input();
  1222. foreach (ref param; dstFunc.params) {
  1223. inNode.params.add(param).dir = ParamDirection.Out;
  1224. }
  1225. const kernelOutputName = "kernel";
  1226. /*
  1227. * The functional composition will happen via a Composite node which
  1228. * will reside in the original graph and codegen into a struct in Cg
  1229. */
  1230. final compNodeId = graph.addNode(KernelGraph.NodeType.Composite);
  1231. final compNode = graph.getNode(compNodeId).composite();
  1232. auto compOutParam = compNode.params.add(ParamDirection.Out, kernelOutputName);
  1233. compOutParam.hasPlainSemantic = true;
  1234. compOutParam.type = toParam.type;
  1235. /*
  1236. * Params which go into the composition node on the side of the old
  1237. * graph, will appear in a data node on the side of the new graph.
  1238. */
  1239. final dataNodeId = subgraph.addNode(KernelGraph.NodeType.Data);
  1240. final dataNode = subgraph.getNode(dataNodeId).data();
  1241. dataNode.sourceKernelType = SourceKernelType.Composite;
  1242. compNode.targetFunc = dstFunc;
  1243. compNode.graph = subgraph;
  1244. compNode.dataNode = dataNodeId;
  1245. compNode.inNode = inNodeId;
  1246. compNode.outNode = outNodeId;
  1247. //assureNotCyclic(graph);
  1248. /*
  1249. * The input node's params must now be connected to the params in the
  1250. * source node's incoming tree which have no incoming flow.
  1251. * The matching is done by name, then semantic conversion is applied
  1252. * to each of them
  1253. */
  1254. GraphNodeId fromNodeInSubgraph;
  1255. //assureNotCyclic(graph);
  1256. DynamicBitSet clonedNodesSet;
  1257. clonedNodesSet.alloc(graph.capacity, &stack.allocRaw);
  1258. clonedNodesSet.clearAll();
  1259. DynamicBitSet visitedNodesSet;
  1260. visitedNodesSet.alloc(graph.capacity, &stack.allocRaw);
  1261. visitedNodesSet.clearAll();
  1262. final oldToNew = stack.allocArray!(GraphNodeId)(graph.capacity);
  1263. //log.trace("doManualFlow -> buildFunctionSubgraph");
  1264. buildFunctionSubgraph(
  1265. graph,
  1266. fromId,
  1267. /* paramFilter */
  1268. (Param* param) {
  1269. assert (param.isInput);
  1270. foreach (fp; dstFunc.params) {
  1271. if (fp.isInput && fp.name == param.name) {
  1272. return true;
  1273. }
  1274. }
  1275. return false;
  1276. },
  1277. /* freeParamSink */
  1278. (Param* param, GraphNodeId node) {
  1279. Param* inParam = null;
  1280. inNode.params.getOutput(param.name, &inParam);
  1281. assert (inParam !is null);
  1282. //assureNotCyclic(graph);
  1283. if (!findConversion(
  1284. *inParam.semantic,
  1285. *param.semantic,
  1286. ctx.semanticConverters,
  1287. stack,
  1288. (ConvSinkItem[] convChain) {
  1289. _insertConversionNodes(
  1290. subgraph,
  1291. convChain,
  1292. inNodeId,
  1293. inParam,
  1294. node,
  1295. param
  1296. );
  1297. }
  1298. )) {
  1299. error(
  1300. "Could not find a conversion for direct flow:"
  1301. " {}:{} -> {}:{} when performing functional composition"
  1302. " using kernel {}. The input param in the kernel was {}.",
  1303. inNodeId.id, inParam.toString,
  1304. node.id, param.toString,
  1305. toParam.type, funcOutputParam.toString
  1306. );
  1307. }
  1308. //assureNotCyclic(graph);
  1309. },
  1310. &clonedNodesSet,
  1311. &visitedNodesSet,
  1312. oldToNew,
  1313. subgraph,
  1314. dataNodeId,
  1315. compNodeId,
  1316. &fromNodeInSubgraph
  1317. );
  1318. //assureNotCyclic(graph);
  1319. Semantic funcOutputPlainSem = Semantic(&subgraph._mem.pushBack);
  1320. findOutputSemantic(
  1321. funcOutputParam,
  1322. // getFormalParamSemantic
  1323. (cstring name) {
  1324. foreach (ref Param p; dstFunc.params) {
  1325. if (p.isInput && p.name == name) {
  1326. return *p.semantic();
  1327. }
  1328. }
  1329. error(
  1330. "simplifyParamSemantics: output param '{}' refers to a"
  1331. " nonexistent formal parameter '{}'.",
  1332. funcOutputParam.name,
  1333. name
  1334. );
  1335. assert (false);
  1336. },
  1337. // getActualParamSemantic
  1338. (cstring name) {
  1339. error(
  1340. "Kernels used for functional composition must not use"
  1341. " actual input param semantics."
  1342. );
  1343. return Semantic.init;
  1344. },
  1345. &funcOutputPlainSem
  1346. );
  1347. compNode.returnType = funcOutputPlainSem.getTrait("type");
  1348. //assureNotCyclic(graph);
  1349. /*
  1350. * The function's output param must be convertible to the destination
  1351. * param's semantic. The destination will sample the output param.
  1352. * Because the sampling may be performed directly by a kernel func,
  1353. * any conversion nodes must be inserted into the subgraph generated
  1354. * for the function.
  1355. */
  1356. if (!findConversion(
  1357. *fromParam.semantic,
  1358. funcOutputPlainSem,
  1359. ctx.semanticConverters,
  1360. stack,
  1361. (ConvSinkItem[] convChain) {
  1362. _insertConversionNodes(
  1363. subgraph,
  1364. convChain,
  1365. fromNodeInSubgraph,
  1366. fromParam,
  1367. outNodeId,
  1368. outNodeParam
  1369. );
  1370. }
  1371. )) {
  1372. error(
  1373. "Could not find a conversion for direct flow:"
  1374. " {}:{} -> {}:{} when performing functional composition"
  1375. " using kernel {}. The output param in the kernel was {}.",
  1376. fromId.id, fromParam.toString,
  1377. outNodeId.id, outNodeParam.toString,
  1378. toParam.type, funcOutputParam.toString
  1379. );
  1380. }
  1381. /*
  1382. * Finally, connect the output of the Composite node with the
  1383. * destination param from which we started the operation
  1384. */
  1385. graph.flow.addDataFlow(
  1386. compNodeId,
  1387. kernelOutputName,
  1388. toId,
  1389. toParam.name
  1390. );
  1391. //assureNotCyclic(graph);
  1392. /*
  1393. * New connections were added, remove the original one which caused
  1394. * the manual data flow func to be invoked.
  1395. */
  1396. return true;
  1397. }
  1398. }
  1399. bool anyNewCons = false;
  1400. if (!findConversion(
  1401. *fromParam.semantic,
  1402. *toParam.semantic,
  1403. ctx.semanticConverters,
  1404. stack,
  1405. (ConvSinkItem[] convChain) {
  1406. anyNewCons = _insertConversionNodes(
  1407. graph,
  1408. convChain,
  1409. fromId,
  1410. fromParam,
  1411. toId,
  1412. toParam
  1413. );
  1414. }
  1415. )) {
  1416. error(
  1417. "Could not find a conversion for direct flow:"
  1418. " {}:{} -> {}:{}",
  1419. fromId.id, fromParam.toString,
  1420. toId.id, toParam.toString
  1421. );
  1422. }
  1423. return anyNewCons;
  1424. }
  1425. // Assumes the consists of param/calc nodes only
  1426. void convertGraphDataFlow(
  1427. KernelGraph graph,
  1428. ConvCtx ctx
  1429. ) {
  1430. scope stack = new StackBuffer;
  1431. final topological = stack.allocArray!(GraphNodeId)(graph.numNodes);
  1432. findTopologicalOrder(graph.backend_readOnly, topological);
  1433. return convertGraphDataFlow(graph, ctx, topological);
  1434. }
  1435. void convertGraphDataFlow(
  1436. KernelGraph graph,
  1437. ConvCtx ctx,
  1438. GraphNodeId[] topological
  1439. ) {
  1440. foreach (id; topological) {
  1441. doAutoFlow(
  1442. graph,
  1443. id,
  1444. ctx,
  1445. FlowGenMode.InsertConversionNodes
  1446. );
  1447. simplifyParamSemantics(graph, id);
  1448. scope stack2 = new StackBuffer;
  1449. final toRemove = LocalDynArray!(ConFlow)(stack2);
  1450. foreach (con; graph.flow.iterOutgoingConnections(id)) {
  1451. foreach (fl; graph.flow.iterDataFlow(id, con)) {
  1452. if (doManualFlow(
  1453. graph,
  1454. id,
  1455. con, fl,
  1456. ctx
  1457. )) {
  1458. toRemove.pushBack(ConFlow(
  1459. id, fl.from,
  1460. con, fl.to
  1461. ));
  1462. }
  1463. }
  1464. }
  1465. foreach (rem; toRemove) {
  1466. graph.flow.removeDataFlow(rem.fromNode, rem.fromParam, rem.toNode, rem.toParam);
  1467. }
  1468. }
  1469. // NOTE: this was removed, m'kay?
  1470. //graph.flow.removeAllAutoFlow();
  1471. }
  1472. void convertGraphDataFlowExceptOutput(
  1473. KernelGraph graph,
  1474. ConvCtx ctx
  1475. ) {
  1476. scope stack = new StackBuffer;
  1477. final topological = stack.allocArray!(GraphNodeId)(graph.numNodes);
  1478. findTopologicalOrder(graph.backend_readOnly, topological);
  1479. return convertGraphDataFlowExceptOutput(graph, ctx, topological);
  1480. }
  1481. void convertGraphDataFlowExceptOutput(
  1482. KernelGraph graph,
  1483. ConvCtx ctx,
  1484. GraphNodeId[] topological
  1485. ) {
  1486. return convertGraphDataFlowExceptOutput(
  1487. graph,
  1488. ctx,
  1489. (int delegate(ref GraphNodeId) sink) {
  1490. foreach (id; topological) {
  1491. if (int r = sink(id)) {
  1492. return r;
  1493. }
  1494. }
  1495. return 0;
  1496. }
  1497. );
  1498. }
  1499. void convertGraphDataFlowExceptOutput(
  1500. KernelGraph graph,
  1501. ConvCtx ctx,
  1502. int delegate(int delegate(ref GraphNodeId)) topological
  1503. ) {
  1504. bool isOutputNode(GraphNodeId id) {
  1505. return KernelGraph.NodeType.Output == graph.getNode(id).type;
  1506. }
  1507. // Do auto flow for the first graph, but not generating conversions
  1508. // for the output node
  1509. foreach (id; topological) {
  1510. doAutoFlow(
  1511. graph,
  1512. id,
  1513. ctx,
  1514. isOutputNode(id)
  1515. ? FlowGenMode.DirectConnection
  1516. : FlowGenMode.InsertConversionNodes
  1517. );
  1518. simplifyParamSemantics(graph, id);
  1519. scope stack2 = new StackBuffer;
  1520. final toRemove = LocalDynArray!(ConFlow)(stack2);
  1521. foreach (con; graph.flow.iterOutgoingConnections(id)) {
  1522. bool markedForRemoval = false;
  1523. if (!isOutputNode(con)) {
  1524. foreach (fl; graph.flow.iterDataFlow(id, con)) {
  1525. if (doManualFlow(
  1526. graph,
  1527. id,
  1528. con, fl,
  1529. ctx
  1530. )) {
  1531. toRemove.pushBack(ConFlow(
  1532. id, fl.from,
  1533. con, fl.to
  1534. ));
  1535. }
  1536. }
  1537. }
  1538. }
  1539. foreach (rem; toRemove) {
  1540. graph.flow.removeDataFlow(rem.fromNode, rem.fromParam, rem.toNode, rem.toParam);
  1541. }
  1542. }
  1543. // NOTE: this was removed, m'kay?
  1544. //graph.flow.removeAllAutoFlow();
  1545. }
  1546. void reduceGraphData(
  1547. KernelGraph kg,
  1548. void delegate(void delegate(
  1549. GraphNodeId nid,
  1550. cstring pname
  1551. )) iterNodes,
  1552. Function reductionFunc,
  1553. GraphNodeId* outputNid,
  1554. cstring* outputPName
  1555. ) {
  1556. GraphNodeId prevNid;
  1557. cstring prevPName;
  1558. bool gotAny = false;
  1559. cstring reductionPName;
  1560. foreach (i, p; reductionFunc.params) {
  1561. if (i > 3 || (i <= 1 && p.isOutput) || (2 == i && p.isInput)) {
  1562. error(
  1563. "reduceGraphData: '{}' is not a valid reduction func.",
  1564. reductionFunc.name
  1565. );
  1566. }
  1567. if (p.isOutput) {
  1568. if (reductionPName is null) {
  1569. reductionPName = p.name;
  1570. } else {
  1571. error(
  1572. "reduceGraphData: The reduction func must only have"
  1573. " one output param. The passed '{}' func has more.",
  1574. reductionFunc.name
  1575. );
  1576. }
  1577. }
  1578. }
  1579. iterNodes((
  1580. GraphNodeId nid,
  1581. cstring pname
  1582. ) {
  1583. if (gotAny) {
  1584. final rnid = kg.addFuncNode(reductionFunc);
  1585. final rnode = kg.getNode(rnid);
  1586. kg.flow.addDataFlow(
  1587. prevNid, prevPName,
  1588. rnid, reductionFunc.params[0].name
  1589. );
  1590. kg.flow.addDataFlow(
  1591. nid, pname,
  1592. rnid, reductionFunc.params[1].name
  1593. );
  1594. prevNid = rnid;
  1595. prevPName = reductionPName;
  1596. } else {
  1597. prevNid = nid;
  1598. prevPName = pname;
  1599. gotAny = true;
  1600. }
  1601. }
  1602. );
  1603. *outputNid = prevNid;
  1604. *outputPName = prevPName;
  1605. }