• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Connor Abbott (cwabbott0@gmail.com)
25  *
26  */
27 
28 #include "nir.h"
29 
30 /* SSA-based mark-and-sweep dead code elimination */
31 
32 typedef struct {
33    struct exec_node node;
34    nir_instr *instr;
35 } worklist_elem;
36 
37 static void
worklist_push(struct exec_list * worklist,nir_instr * instr)38 worklist_push(struct exec_list *worklist, nir_instr *instr)
39 {
40    worklist_elem *elem = ralloc(worklist, worklist_elem);
41    elem->instr = instr;
42    instr->pass_flags = 1;
43    exec_list_push_tail(worklist, &elem->node);
44 }
45 
46 static nir_instr *
worklist_pop(struct exec_list * worklist)47 worklist_pop(struct exec_list *worklist)
48 {
49    struct exec_node *node = exec_list_pop_head(worklist);
50    worklist_elem *elem = exec_node_data(worklist_elem, node, node);
51    return elem->instr;
52 }
53 
54 static bool
mark_live_cb(nir_src * src,void * _state)55 mark_live_cb(nir_src *src, void *_state)
56 {
57    struct exec_list *worklist = (struct exec_list *) _state;
58 
59    if (src->is_ssa && !src->ssa->parent_instr->pass_flags) {
60       worklist_push(worklist, src->ssa->parent_instr);
61    }
62 
63    return true;
64 }
65 
66 static void
init_instr(nir_instr * instr,struct exec_list * worklist)67 init_instr(nir_instr *instr, struct exec_list *worklist)
68 {
69    nir_alu_instr *alu_instr;
70    nir_intrinsic_instr *intrin_instr;
71    nir_tex_instr *tex_instr;
72 
73    /* We use the pass_flags to store the live/dead information.  In DCE, we
74     * just treat it as a zero/non-zero boolean for whether or not the
75     * instruction is live.
76     */
77    instr->pass_flags = 0;
78 
79    switch (instr->type) {
80    case nir_instr_type_call:
81    case nir_instr_type_jump:
82       worklist_push(worklist, instr);
83       break;
84 
85    case nir_instr_type_alu:
86       alu_instr = nir_instr_as_alu(instr);
87       if (!alu_instr->dest.dest.is_ssa)
88          worklist_push(worklist, instr);
89       break;
90 
91    case nir_instr_type_intrinsic:
92       intrin_instr = nir_instr_as_intrinsic(instr);
93       if (nir_intrinsic_infos[intrin_instr->intrinsic].flags &
94           NIR_INTRINSIC_CAN_ELIMINATE) {
95          if (nir_intrinsic_infos[intrin_instr->intrinsic].has_dest &&
96              !intrin_instr->dest.is_ssa) {
97             worklist_push(worklist, instr);
98          }
99       } else {
100          worklist_push(worklist, instr);
101       }
102       break;
103 
104    case nir_instr_type_tex:
105       tex_instr = nir_instr_as_tex(instr);
106       if (!tex_instr->dest.is_ssa)
107          worklist_push(worklist, instr);
108       break;
109 
110    default:
111       break;
112    }
113 }
114 
115 static bool
init_block(nir_block * block,struct exec_list * worklist)116 init_block(nir_block *block, struct exec_list *worklist)
117 {
118    nir_foreach_instr(instr, block)
119       init_instr(instr, worklist);
120 
121    nir_if *following_if = nir_block_get_following_if(block);
122    if (following_if) {
123       if (following_if->condition.is_ssa &&
124           !following_if->condition.ssa->parent_instr->pass_flags)
125          worklist_push(worklist, following_if->condition.ssa->parent_instr);
126    }
127 
128    return true;
129 }
130 
131 static bool
nir_opt_dce_impl(nir_function_impl * impl)132 nir_opt_dce_impl(nir_function_impl *impl)
133 {
134    struct exec_list *worklist = rzalloc(NULL, struct exec_list);
135    exec_list_make_empty(worklist);
136 
137    nir_foreach_block(block, impl) {
138       init_block(block, worklist);
139    }
140 
141    while (!exec_list_is_empty(worklist)) {
142       nir_instr *instr = worklist_pop(worklist);
143       nir_foreach_src(instr, mark_live_cb, worklist);
144    }
145 
146    ralloc_free(worklist);
147 
148    bool progress = false;
149 
150    nir_foreach_block(block, impl) {
151       nir_foreach_instr_safe(instr, block) {
152          if (!instr->pass_flags) {
153             nir_instr_remove(instr);
154             progress = true;
155          }
156       }
157    }
158 
159    if (progress)
160       nir_metadata_preserve(impl, nir_metadata_block_index |
161                                   nir_metadata_dominance);
162 
163    return progress;
164 }
165 
166 bool
nir_opt_dce(nir_shader * shader)167 nir_opt_dce(nir_shader *shader)
168 {
169    bool progress = false;
170    nir_foreach_function(function, shader) {
171       if (function->impl && nir_opt_dce_impl(function->impl))
172          progress = true;
173    }
174 
175    return progress;
176 }
177