Revert "[mlir][Affine] Add support for multi-store producer fusion"
[lldb.git] / mlir / include / mlir / Transforms / Passes.td
1 //===-- Passes.td - Transforms pass definition file --------*- tablegen -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains definitions for passes within the Transforms/ directory.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef MLIR_TRANSFORMS_PASSES
14 #define MLIR_TRANSFORMS_PASSES
15
16 include "mlir/Pass/PassBase.td"
17
18 def AffineLoopFusion : FunctionPass<"affine-loop-fusion"> {
19   let summary = "Fuse affine loop nests";
20   let constructor = "mlir::createLoopFusionPass()";
21   let options = [
22     Option<"computeToleranceThreshold", "fusion-compute-tolerance", "double",
23            /*default=*/"0.30f", "Fractional increase in additional computation "
24                                 "tolerated while fusing">,
25     Option<"fastMemorySpace", "fusion-fast-mem-space", "unsigned",
26            /*default=*/"0",
27            "Faster memory space number to promote fusion buffers to">,
28     Option<"localBufSizeThreshold", "fusion-local-buf-threshold", "uint64_t",
29            /*default=*/"0", "Threshold size (KiB) for promoting local buffers "
30                             "to fast memory space">,
31     Option<"maximalFusion", "fusion-maximal", "bool", /*default=*/"false",
32            "Enables maximal loop fusion">,
33   ];
34 }
35
36 def AffinePipelineDataTransfer
37     : FunctionPass<"affine-pipeline-data-transfer"> {
38   let summary = "Pipeline non-blocking data transfers between explicitly "
39                 "managed levels of the memory hierarchy";
40   let description = [{
41     This pass performs a transformation to overlap non-blocking DMA operations
42     in a loop with computations through double buffering. This is achieved by
43     advancing dma_start operations with respect to other operations.
44
45     Input
46
47     ```mlir
48     func @pipelinedatatransfer() {
49       %0 = alloc() : memref<256xf32>
50       %1 = alloc() : memref<32xf32, 1>
51       %2 = alloc() : memref<1xf32>
52       %c0 = constant 0 : index
53       %c128 = constant 128 : index
54       affine.for %i0 = 0 to 8 {
55         affine.dma_start %0[%i0], %1[%i0], %2[%c0], %c128 : memref<256xf32>, memref<32xf32, 1>, memref<1xf32>
56         affine.dma_wait %2[%c0], %c128 : memref<1xf32>
57         %3 = affine.load %1[%i0] : memref<32xf32, 1>
58         %4 = "compute"(%3) : (f32) -> f32
59         affine.store %4, %1[%i0] : memref<32xf32, 1>
60       }
61       return
62     }
63     ```
64
65     Output
66
67     ```mlir
68     module {
69       func @pipelinedatatransfer() {
70         %c8 = constant 8 : index
71         %c0 = constant 0 : index
72         %0 = alloc() : memref<256xf32>
73         %c0_0 = constant 0 : index
74         %c128 = constant 128 : index
75         %1 = alloc() : memref<2x32xf32, 1>
76         %2 = alloc() : memref<2x1xf32>
77         affine.dma_start %0[%c0], %1[%c0 mod 2, %c0], %2[%c0 mod 2, symbol(%c0_0)], %c128 : memref<256xf32>, memref<2x32xf32, 1>, memref<2x1xf32>
78         affine.for %arg0 = 1 to 8 {
79           affine.dma_start %0[%arg0], %1[%arg0 mod 2, %arg0], %2[%arg0 mod 2, symbol(%c0_0)], %c128 : memref<256xf32>, memref<2x32xf32, 1>, memref<2x1xf32>
80           %8 = affine.apply #map3(%arg0)
81           %9 = affine.apply #map4(%8)
82           %10 = affine.apply #map4(%8)
83           affine.dma_wait %2[%8 mod 2, symbol(%c0_0)], %c128 : memref<2x1xf32>
84           %11 = affine.load %1[%8 mod 2, %8] : memref<2x32xf32, 1>
85           %12 = "compute"(%11) : (f32) -> f32
86           affine.store %12, %1[%8 mod 2, %8] : memref<2x32xf32, 1>
87         }
88         %3 = affine.apply #map3(%c8)
89         %4 = affine.apply #map4(%3)
90         %5 = affine.apply #map4(%3)
91         affine.dma_wait %2[%3 mod 2, symbol(%c0_0)], %c128 : memref<2x1xf32>
92         %6 = affine.load %1[%3 mod 2, %3] : memref<2x32xf32, 1>
93         %7 = "compute"(%6) : (f32) -> f32
94         affine.store %7, %1[%3 mod 2, %3] : memref<2x32xf32, 1>
95         dealloc %2 : memref<2x1xf32>
96         dealloc %1 : memref<2x32xf32, 1>
97         return
98       }
99     }
100     ```
101   }];
102   let constructor = "mlir::createPipelineDataTransferPass()";
103 }
104
105 def BufferDeallocation : FunctionPass<"buffer-deallocation"> {
106   let summary = "Adds all required dealloc operations for all allocations in the "
107                 "input program";
108   let description = [{
109     This pass implements an algorithm to automatically introduce all required
110     deallocation operations for all buffers in the input program. This ensures that
111     the resulting program does not have any memory leaks.
112
113
114     Input
115
116     ```mlir
117     #map0 = affine_map<(d0) -> (d0)>
118     module {
119       func @condBranch(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
120         cond_br %arg0, ^bb1, ^bb2
121       ^bb1:
122         br ^bb3(%arg1 : memref<2xf32>)
123       ^bb2:
124         %0 = alloc() : memref<2xf32>
125         linalg.generic {
126           args_in = 1 : i64,
127           args_out = 1 : i64,
128           indexing_maps = [#map0, #map0],
129           iterator_types = ["parallel"]} %arg1, %0 {
130         ^bb0(%gen1_arg0: f32, %gen1_arg1: f32):
131           %tmp1 = exp %gen1_arg0 : f32
132           linalg.yield %tmp1 : f32
133         }: memref<2xf32>, memref<2xf32>
134         br ^bb3(%0 : memref<2xf32>)
135       ^bb3(%1: memref<2xf32>):
136         "linalg.copy"(%1, %arg2) : (memref<2xf32>, memref<2xf32>) -> ()
137         return
138       }
139     }
140
141     ```
142
143     Output
144
145     ```mlir
146     #map0 = affine_map<(d0) -> (d0)>
147     module {
148       func @condBranch(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
149         cond_br %arg0, ^bb1, ^bb2
150       ^bb1:  // pred: ^bb0
151         %0 = alloc() : memref<2xf32>
152         linalg.copy(%arg1, %0) : memref<2xf32>, memref<2xf32>
153         br ^bb3(%0 : memref<2xf32>)
154       ^bb2:  // pred: ^bb0
155         %1 = alloc() : memref<2xf32>
156         linalg.generic {
157           args_in = 1 : i64,
158           args_out = 1 : i64,
159           indexing_maps = [#map0, #map0],
160           iterator_types = ["parallel"]} %arg1, %1 {
161         ^bb0(%arg3: f32, %arg4: f32):  // no predecessors
162           %4 = exp %arg3 : f32
163           linalg.yield %4 : f32
164         }: memref<2xf32>, memref<2xf32>
165         %2 = alloc() : memref<2xf32>
166         linalg.copy(%1, %2) : memref<2xf32>, memref<2xf32>
167         dealloc %1 : memref<2xf32>
168         br ^bb3(%2 : memref<2xf32>)
169       ^bb3(%3: memref<2xf32>):  // 2 preds: ^bb1, ^bb2
170         linalg.copy(%3, %arg2) : memref<2xf32>, memref<2xf32>
171         dealloc %3 : memref<2xf32>
172         return
173       }
174
175     }
176     ```
177
178   }];
179   let constructor = "mlir::createBufferDeallocationPass()";
180   // TODO: this pass likely shouldn't depend on Linalg?
181   let dependentDialects = ["linalg::LinalgDialect"];
182 }
183
184 def BufferHoisting : FunctionPass<"buffer-hoisting"> {
185   let summary = "Optimizes placement of allocation operations by moving them "
186                 "into common dominators and out of nested regions";
187   let description = [{
188     This pass implements an approach to aggressively move allocations upwards
189     into common dominators and out of nested regions.
190   }];
191   let constructor = "mlir::createBufferHoistingPass()";
192 }
193
194 def BufferLoopHoisting : FunctionPass<"buffer-loop-hoisting"> {
195   let summary = "Optimizes placement of allocation operations by moving them "
196                 "out of loop nests";
197   let description = [{
198     This pass implements an approach to aggressively move allocations upwards
199     out of loop nests. It does not move allocations into common dominators.
200   }];
201   let constructor = "mlir::createBufferLoopHoistingPass()";
202 }
203
204 def PromoteBuffersToStack : FunctionPass<"promote-buffers-to-stack"> {
205   let summary = "Promotes heap-based allocations to automatically managed "
206                 "stack-based allocations";
207   let description = [{
208     This pass implements a simple algorithm to convert heap-based memory
209     allocations to stack-based ones. It uses a built-in heuristic to decide
210     whether it makes sense to convert an allocation. Furthermore, dynamic
211     shaped buffers that are limited by the rank of the tensor can be
212     converted. They are only transformed if they are considered to be small.
213   }];
214   let constructor = "mlir::createPromoteBuffersToStackPass()";
215   let options = [
216     Option<"maxAllocSizeInBytes", "max-alloc-size-in-bytes", "unsigned",
217            /*default=*/"1024",
218            "Maximal size in bytes to promote allocations to stack.">,
219     Option<"bitwidthOfIndexType", "bitwidth-of-index-type", "unsigned",
220            /*default=*/"64",
221            "Bitwidth of the index type. Used for size estimation.">,
222     Option<"maxRankOfAllocatedMemRef", "max-rank-of-allocated-memref", "unsigned",
223            /*default=*/"1",
224            "Maximal memref rank to promote dynamic buffers.">,
225   ];
226 }
227
228 def BufferResultsToOutParams : Pass<"buffer-results-to-out-params", "ModuleOp">  {
229   let summary = "Converts memref-typed function results to out-params";
230   let description = [{
231     Some calling conventions prefer to pass output memrefs as "out params". The
232     conversion to this calling convention must be done as an atomic
233     transformation of the entire program (hence this is a module pass).
234
235     For example, if a call is rewritten, the callee needs to be rewritten
236     otherwise the IR will end up invalid. Thus, this transformation
237     require an atomic change to the entire program (e.g. the whole module).
238
239     This pass is expected to run immediately after bufferization is finished.
240     At that point, tensor-typed results will have been converted to memref-typed
241     results, and can be consistently converted to out params.
242
243     All memref-typed results are appended to the function argument list.
244
245     The main issue with this pass (and the out-param calling convention) is that
246     buffers for results need to be allocated in the caller. This currently only
247     works for static shaped memrefs.
248   }];
249   let constructor = "mlir::createBufferResultsToOutParamsPass()";
250   let dependentDialects = ["linalg::LinalgDialect"];
251 }
252
253 def Canonicalizer : Pass<"canonicalize"> {
254   let summary = "Canonicalize operations";
255   let description = [{
256     This pass performs various types of canonicalizations over a set of
257     operations. See [Operation Canonicalization](Canonicalization.md) for more
258     details.
259   }];
260   let constructor = "mlir::createCanonicalizerPass()";
261 }
262
263 def CopyRemoval : FunctionPass<"copy-removal"> {
264   let summary = "Remove the redundant copies from input IR";
265   let constructor = "mlir::createCopyRemovalPass()";
266 }
267
268 def CSE : Pass<"cse"> {
269   let summary = "Eliminate common sub-expressions";
270   let description = [{
271     This pass implements a generalized algorithm for common sub-expression
272     elimination. This pass relies on information provided by the
273     `Memory SideEffect` interface to identify when it is safe to eliminate
274     operations. See [Common subexpression elimination](https://en.wikipedia.org/wiki/Common_subexpression_elimination)
275     for more general details on this optimization.
276   }];
277   let constructor = "mlir::createCSEPass()";
278   let statistics = [
279     Statistic<"numCSE", "num-cse'd", "Number of operations CSE'd">,
280     Statistic<"numDCE", "num-dce'd", "Number of operations DCE'd">
281   ];
282 }
283
284 def Inliner : Pass<"inline"> {
285   let summary = "Inline function calls";
286   let constructor = "mlir::createInlinerPass()";
287   let options = [
288     Option<"defaultPipelineStr", "default-pipeline", "std::string",
289            /*default=*/"", "The default optimizer pipeline used for callables">,
290     ListOption<"opPipelineStrs", "op-pipelines", "std::string",
291                "Callable operation specific optimizer pipelines (in the form "
292                "of `dialect.op(pipeline)`)",
293                "llvm::cl::MiscFlags::CommaSeparated">,
294     Option<"maxInliningIterations", "max-iterations", "unsigned",
295            /*default=*/"4",
296            "Maximum number of iterations when inlining within an SCC">,
297   ];
298 }
299
300 def FinalizingBufferize : FunctionPass<"finalizing-bufferize"> {
301   let summary = "Finalize a partial bufferization";
302   let description = [{
303     A bufferize pass that finalizes a partial bufferization by removing
304     remaining `tensor_load` and `tensor_to_memref` operations.
305
306     The removal of those operations is only possible if the operations only
307     exist in pairs, i.e., all uses of `tensor_load` operations are
308     `tensor_to_memref` operations.
309
310     This pass will fail if not all operations can be removed or if any operation
311     with tensor typed operands remains.
312   }];
313   let constructor = "mlir::createFinalizingBufferizePass()";
314 }
315
316 def LocationSnapshot : Pass<"snapshot-op-locations"> {
317   let summary = "Generate new locations from the current IR";
318   let description = [{
319     This pass allows for generating new locations from the IR during any stage
320     of compilation, by snapshotting the IR to a file and using that file to
321     generate new locations for the operations.
322
323     Depending on the value of the `tag` option, different resulting locations
324     may be generated:
325
326     * If unset, the original location of the operation is replaced.
327
328     Example:
329
330     ```mlir
331     // old:
332     ... loc("original_source.cpp":1:1)
333
334     // new:
335     ... loc("snapshot_source.mlir":10:10)
336     ```
337
338     * If set, the new location is fused with the original location in the form
339     of a [`Name Location`](Diagnostics.md#name-location) with the specified tag.
340
341     Example:
342
343     ```mlir
344     // old:
345     ... loc("original_source.cpp":1:1)
346
347     // new:
348     ... loc(fused["original_source.cpp":1:1, "snapshot"("snapshot_source.mlir":10:10)])
349     ```
350   }];
351   let constructor = "mlir::createLocationSnapshotPass()";
352   let options = [
353     Option<"fileName", "filename", "std::string", /*default=*/"",
354            "The filename to print the generated IR">,
355     Option<"tag", "tag", "std::string", /*default=*/"",
356            "A tag to use when fusing the new locations with the "
357            "original. If unset, the locations are replaced.">,
358   ];
359 }
360
361 def LoopCoalescing : FunctionPass<"loop-coalescing"> {
362   let summary = "Coalesce nested loops with independent bounds into a single "
363                 "loop";
364   let constructor = "mlir::createLoopCoalescingPass()";
365 }
366
367 def LoopInvariantCodeMotion : Pass<"loop-invariant-code-motion"> {
368   let summary = "Hoist loop invariant instructions outside of the loop";
369   let constructor = "mlir::createLoopInvariantCodeMotionPass()";
370 }
371
372 def MemRefDataFlowOpt : FunctionPass<"memref-dataflow-opt"> {
373   let summary = "Perform store/load forwarding for memrefs";
374   let description = [{
375     This pass performs store to load forwarding for memref's to eliminate memory
376     accesses and potentially the entire memref if all its accesses are
377     forwarded.
378
379     Input
380
381     ```mlir
382     func @store_load_affine_apply() -> memref<10x10xf32> {
383       %cf7 = constant 7.0 : f32
384       %m = alloc() : memref<10x10xf32>
385       affine.for %i0 = 0 to 10 {
386         affine.for %i1 = 0 to 10 {
387           affine.store %cf7, %m[%i0, %i1] : memref<10x10xf32>
388           %v0 = affine.load %m[%i0, %i1] : memref<10x10xf32>
389           %v1 = addf %v0, %v0 : f32
390         }
391       }
392       return %m : memref<10x10xf32>
393     }
394     ```
395
396     Output
397
398     ```mlir
399     module {
400       func @store_load_affine_apply() -> memref<10x10xf32> {
401         %cst = constant 7.000000e+00 : f32
402         %0 = alloc() : memref<10x10xf32>
403         affine.for %arg0 = 0 to 10 {
404           affine.for %arg1 = 0 to 10 {
405             affine.store %cst, %0[%arg0, %arg1] : memref<10x10xf32>
406             %1 = addf %cst, %cst : f32
407           }
408         }
409         return %0 : memref<10x10xf32>
410       }
411     }
412     ```
413   }];
414   let constructor = "mlir::createMemRefDataFlowOptPass()";
415 }
416
417 def NormalizeMemRefs : Pass<"normalize-memrefs", "ModuleOp"> {
418   let summary = "Normalize memrefs";
419    let description = [{
420     This pass transforms memref types with a non-trivial
421     [layout map](https://mlir.llvm.org/docs/LangRef/#layout-map) into
422     memref types with an identity layout map, e.g. (i, j) -> (i, j). This
423     pass is inter-procedural, in the sense that it can modify function
424     interfaces and call sites that pass memref types. In order to modify
425     memref types while preserving the original behavior, users of those
426     memref types are also modified to incorporate the resulting layout map.
427     For instance, an [AffineLoadOp]
428     (https://mlir.llvm.org/docs/Dialects/Affine/#affineload-affineloadop)
429     will be updated to compose the layout map with with the affine expression
430     contained in the op. Operations marked with the [MemRefsNormalizable]
431     (https://mlir.llvm.org/docs/Traits/#memrefsnormalizable) trait are
432     expected to be normalizable. Supported operations include affine
433     operations, std.alloc, std.dealloc, and std.return.
434
435     Given an appropriate layout map specified in the code, this transformation
436     can express tiled or linearized access to multi-dimensional data
437     structures, but will not modify memref types without an explicit layout
438     map.
439
440     Currently this pass is limited to only modify
441     functions where all memref types can be normalized. If a function
442     contains any operations that are not MemRefNormalizable, then the function
443     and any functions that call or call it will not be modified.
444
445     Input
446
447     ```mlir
448     #tile = affine_map<(i) -> (i floordiv 4, i mod 4)>
449     func @matmul(%A: memref<16xf64, #tile>,
450                  %B: index, %C: memref<16xf64>) -> (memref<16xf64, #tile>) {
451       affine.for %arg3 = 0 to 16 {
452             %a = affine.load %A[%arg3] : memref<16xf64, #tile>
453             %p = mulf %a, %a : f64
454             affine.store %p, %A[%arg3] : memref<16xf64, #tile>
455       }
456       %c = alloc() : memref<16xf64, #tile>
457       %d = affine.load %c[0] : memref<16xf64, #tile>
458       return %A: memref<16xf64, #tile>
459     }
460     ```
461
462     Output
463
464     ```mlir
465     func @matmul(%arg0: memref<4x4xf64>, %arg1: index, %arg2: memref<16xf64>)
466       -> memref<4x4xf64> {
467       affine.for %arg3 = 0 to 16 {
468         %3 = affine.load %arg0[%arg3 floordiv 4, %arg3 mod 4]: memref<4x4xf64>
469         %4 = mulf %3, %3 : f64
470         affine.store %4, %arg0[%arg3 floordiv 4, %arg3 mod 4]: memref<4x4xf64>
471       }
472       %0 = alloc() : memref<4x4xf64>
473       %1 = affine.apply #map1()
474       %2 = affine.load %0[0, 0] : memref<4x4xf64>
475       return %arg0 : memref<4x4xf64>
476     }
477     ```
478
479     Input
480
481     ```
482     #linear8 = affine_map<(i, j) -> (i * 8 + j)>
483     func @linearize(%arg0: memref<8x8xi32, #linear8>,
484                     %arg1: memref<8x8xi32, #linear8>,
485                     %arg2: memref<8x8xi32, #linear8>) {
486       %c8 = constant 8 : index
487       %c0 = constant 0 : index
488       %c1 = constant 1 : index
489       affine.for %arg3 = %c0 to %c8  {
490       affine.for %arg4 = %c0 to %c8  {
491         affine.for %arg5 = %c0 to %c8 {
492           %0 = affine.load %arg0[%arg3, %arg5] : memref<8x8xi32, #linear8>
493           %1 = affine.load %arg1[%arg5, %arg4] : memref<8x8xi32, #linear8>
494           %2 = affine.load %arg2[%arg3, %arg4] : memref<8x8xi32, #linear8>
495           %3 = muli %0, %1 : i32
496           %4 = addi %2, %3 : i32
497           affine.store %4, %arg2[%arg3, %arg4] : memref<8x8xi32, #linear8>
498         }
499       }
500       }
501       return
502     }
503     ```
504
505     Output
506
507     ```mlir
508     func @linearize(%arg0: memref<64xi32>,
509                     %arg1: memref<64xi32>,
510                     %arg2: memref<64xi32>) {
511     %c8 = constant 8 : index
512     %c0 = constant 0 : index
513     affine.for %arg3 = %c0 to %c8 {
514       affine.for %arg4 = %c0 to %c8 {
515         affine.for %arg5 = %c0 to %c8 {
516           %0 = affine.load %arg0[%arg3 * 8 + %arg5] : memref<64xi32>
517           %1 = affine.load %arg1[%arg5 * 8 + %arg4] : memref<64xi32>
518           %2 = affine.load %arg2[%arg3 * 8 + %arg4] : memref<64xi32>
519           %3 = muli %0, %1 : i32
520           %4 = addi %2, %3 : i32
521           affine.store %4, %arg2[%arg3 * 8 + %arg4] : memref<64xi32>
522         }
523       }
524     }
525     return
526   }
527   ```
528   }];
529   let constructor = "mlir::createNormalizeMemRefsPass()";
530 }
531
532 def ParallelLoopCollapsing : Pass<"parallel-loop-collapsing"> {
533   let summary = "Collapse parallel loops to use less induction variables";
534   let constructor = "mlir::createParallelLoopCollapsingPass()";
535   let options = [
536     ListOption<"clCollapsedIndices0", "collapsed-indices-0", "unsigned",
537                "Which loop indices to combine 0th loop index",
538                "llvm::cl::MiscFlags::CommaSeparated">,
539     ListOption<"clCollapsedIndices1", "collapsed-indices-1", "unsigned",
540                "Which loop indices to combine into the position 1 loop index",
541                "llvm::cl::MiscFlags::CommaSeparated">,
542     ListOption<"clCollapsedIndices2", "collapsed-indices-2", "unsigned",
543                "Which loop indices to combine into the position 2 loop index",
544                "llvm::cl::MiscFlags::CommaSeparated">,
545   ];
546 }
547
548 def PrintCFG : FunctionPass<"print-cfg-graph"> {
549   let summary = "Print CFG graph per-Region";
550   let constructor = "mlir::createPrintCFGGraphPass()";
551 }
552
553 def PrintOpStats : Pass<"print-op-stats"> {
554   let summary = "Print statistics of operations";
555   let constructor = "mlir::createPrintOpStatsPass()";
556 }
557
558 def PrintOp : Pass<"print-op-graph", "ModuleOp"> {
559   let summary = "Print op graph per-Region";
560   let constructor = "mlir::createPrintOpGraphPass()";
561 }
562
563 def SCCP : Pass<"sccp"> {
564   let summary = "Sparse Conditional Constant Propagation";
565   let description = [{
566     This pass implements a general algorithm for sparse conditional constant
567     propagation. This algorithm detects values that are known to be constant and
568     optimistically propagates this throughout the IR. Any values proven to be
569     constant are replaced, and removed if possible.
570
571     This implementation is based on the algorithm described by Wegman and Zadeck
572     in [“Constant Propagation with Conditional Branches”](https://dl.acm.org/doi/10.1145/103135.103136) (1991).
573   }];
574   let constructor = "mlir::createSCCPPass()";
575 }
576
577 def StripDebugInfo : Pass<"strip-debuginfo"> {
578   let summary = "Strip debug info from all operations";
579   let description = [{
580     This pass strips the IR of any location information, by replacing all
581     operation locations with [`unknown`](Diagnostics.md#unknown-location).
582   }];
583   let constructor = "mlir::createStripDebugInfoPass()";
584 }
585
586 def SymbolDCE : Pass<"symbol-dce"> {
587   let summary = "Eliminate dead symbols";
588   let description = [{
589     This pass deletes all symbols that are found to be unreachable. This is done
590     by computing the set of operations that are known to be live, propagating
591     that liveness to other symbols, and then deleting all symbols that are not
592     within this live set. Live symbols are those that have a
593     [visibility](SymbolsAndSymbolTables.md#symbol-visibility) that extends
594     beyond the IR, e.g. `public`, or those that are referenced by live symbols
595     or other non-Symbol operations.
596
597     For example, consider the following input:
598
599     ```mlir
600     func private @dead_private_function()
601     func private @live_private_function()
602
603     // Note: The `public` isn't necessary here, as this is the default.
604     func public @public_function() {
605       "foo.return"() {uses = [@live_private_function]} : () -> ()
606     }
607     ```
608
609     A known live function, `public_function`, contains a reference to an
610     otherwise non-live function `live_private_function`. After running
611     `symbol-dce`, only these two symbols should remain, as the final symbol
612     `dead_private_function` is not visible outside of the current IR and there
613     are no links to known-live operations. After running, we get the expected:
614
615     ```mlir
616     func private @live_private_function()
617
618     func public @public_function() {
619       "foo.return"() {uses = [@live_private_function]} : () -> ()
620     }
621     ```
622
623     See [Symbols and SymbolTables](SymbolsAndSymbolTables.md) for more
624     information on `Symbols`.
625   }];
626   let constructor = "mlir::createSymbolDCEPass()";
627 }
628 #endif // MLIR_TRANSFORMS_PASSES