CACAO
NullCheckEliminationPass.cpp
Go to the documentation of this file.
1 /* src/vm/jit/compiler2/NullCheckEliminationPass.cpp - NullCheckEliminationPass
2 
3  Copyright (C) 2013
4  CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5 
6  This file is part of CACAO.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2, or (at
11  your option) any later version.
12 
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  02110-1301, USA.
22 
23 */
24 
25 #include <algorithm>
26 #include <cassert>
27 
40 
41 #include "toolbox/logging.hpp"
42 
43 // define name for debugging (see logging.hpp)
44 #define DEBUG_NAME "compiler2/NullCheckEliminationPass"
45 
46 namespace cacao {
47 namespace jit {
48 namespace compiler2 {
49 
51  Instruction *I = objectref->to_Instruction();
52 
53  if (I->to_LOADInst()) {
54  LOADInst *load = I->to_LOADInst();
55  // A LOADInst is known to be non-null if it is a reference
56  // to the this-pointer within an instance-method.
57  return !M->is_static() && load->get_index() == 0;
58  }
59 
60  // Instructions that create new objects are trivially non-null.
61  return I->to_NEWInst()
62  || I->to_NEWARRAYInst()
63  || I->to_ANEWARRAYInst()
64  || I->to_MULTIANEWARRAYInst();
65 }
66 
68  int num_of_bits = 0;
69  for (auto it = M->begin(); it != M->end(); it++) {
70  Instruction *I = *it;
71  if (I->get_type() == Type::ReferenceTypeID) {
72  bitpositions[I] = num_of_bits++;
73  LOG2("Map " << I << " to bitposition " << bitpositions[I] << nl);
74  }
75  }
76 }
77 
79  // Prepare the initial bitevector for each block,
80  for (auto bb_it = M->bb_begin(); bb_it != M->bb_end(); bb_it++) {
81  BeginInst *begin = *bb_it;
82  non_null_references_at_entry[begin].resize(bitpositions.size());
83  non_null_references_at_entry[begin].set();
84  non_null_references_at_exit[begin].resize(bitpositions.size());
85  non_null_references_at_exit[begin].set();
86  }
87 
88  // At method entry we assume that each object reference may be null.
90 }
91 
93  ListSchedulingPass *schedule = get_Pass<ListSchedulingPass>();
94 
97 
98  worklist.push(M->get_init_bb());
99  in_worklist[M->get_init_bb()] = true;
100 
101  while (!worklist.empty()) {
102  BeginInst *begin = worklist.front();
103  worklist.pop();
104  in_worklist[begin] = false;
105 
106  // The local analysis state.
107  auto &non_null_references = non_null_references_at_exit[begin];
108 
109  // Initialize the local state to be equal to the entry state.
110  non_null_references = non_null_references_at_entry[begin];
111 
112  LOG2("Process " << begin << nl);
113 
114  // Compute data-flow information for each instruction within the current block.
115  for (auto inst_it = schedule->inst_begin(begin);
116  inst_it != schedule->inst_end(begin); inst_it++) {
117  Instruction *I = *inst_it;
118 
119  if (I->get_type() == Type::ReferenceTypeID) {
120  if (is_trivially_non_null(I)) {
121  LOG2(I << " is trivially non-null" << nl);
122  non_null_references[bitpositions[I]] = true;
123  } else if (I->to_PHIInst()) {
124  bool meet = true;
125  for (auto op_it = I->op_begin(); op_it != I->op_end(); op_it++) {
126  Instruction *op = (*op_it)->to_Instruction();
127  meet &= non_null_references[bitpositions[op]];
128  }
129  non_null_references[bitpositions[I]] = meet;
130  }
131  }
132 
133  if (I->to_DereferenceInst() != nullptr) {
134  DereferenceInst *dereference = I->to_DereferenceInst();
135  Instruction *objectref = dereference->get_objectref();
136 
137  assert(objectref);
138  assert(objectref->get_type() == Type::ReferenceTypeID);
139 
140  if (non_null_references[bitpositions[objectref]]) {
141  // The objectref that is dereferenced by the current
142  // instruction is known to be non-null, therefore no
143  // null-check has to be performed at this point.
144  dereference->set_needs_null_check(false);
145  LOG2("Remove null-check on " << objectref << " by " << I << nl);
146  } else {
147  // The objectref that is dereferenced by the current
148  // instruction may be null, therefore a null-check has to
149  // be performed at this point.
150  dereference->set_needs_null_check(true);
151  non_null_references[bitpositions[objectref]] = true;
152  LOG2("Introduce null-check on " << objectref << " by " << I << nl);
153  }
154  }
155  }
156 
157  // Process successors of the current block.
158  EndInst *end = begin->get_EndInst();
159  for (auto succ_it = end->succ_begin(); succ_it != end->succ_end(); succ_it++) {
160  BeginInst *succ = (*succ_it).get();
161 
162  auto num_of_set_bits = non_null_references_at_entry[succ].count();
163 
164  // Compute the analysis state for the entry of the successor block.
165  non_null_references_at_entry[succ] &= non_null_references;
166 
167  auto new_num_of_set_bits = non_null_references_at_entry[succ].count();
168 
169  // In case the analysis state at the successor changed, the
170  // successor has to be reanalyzed as well.
171  bool reanalyze_successor = num_of_set_bits != new_num_of_set_bits;
172  if (reanalyze_successor && !in_worklist[succ]) {
173  worklist.push(succ);
174  in_worklist[succ] = true;
175  }
176  }
177  }
178 }
179 
180 #ifdef ENABLE_LOGGING
181 void NullCheckEliminationPass::print_final_results() {
182  for (auto inst_it = M->begin(); inst_it != M->end(); inst_it++) {
183  Instruction *I = *inst_it;
184  DereferenceInst *dereference = I->to_DereferenceInst();
185 
186  if (dereference) {
187  Instruction *objectref = dereference->get_objectref();
188  if (!dereference->get_needs_null_check()) {
189  LOG("Null-check on " << objectref << " by " << I << " is redundant" << nl);
190  } else {
191  LOG("Null-check on " << objectref << " by " << I << " is not redundant" << nl);
192  }
193  }
194  }
195 }
196 #endif
197 
199  M = JD.get_Method();
200 
204 #ifdef ENABLE_LOGGING
205  print_final_results();
206 #endif
207 
208  return true;
209 }
210 
211 // pass usage
216  return PU;
217 }
218 
219 // register pass
220 static PassRegistry<NullCheckEliminationPass> X("NullCheckEliminationPass");
221 
222 } // end namespace compiler2
223 } // end namespace jit
224 } // end namespace cacao
225 
226 
227 /*
228  * These are local overrides for various environment variables in Emacs.
229  * Please do not remove this and leave it at the end of the file, where
230  * Emacs will automagically detect them.
231  * ---------------------------------------------------------------------
232  * Local variables:
233  * mode: c++
234  * indent-tabs-mode: t
235  * c-basic-offset: 4
236  * tab-width: 4
237  * End:
238  * vim:noexpandtab:sw=4:ts=4:
239  */
virtual Instruction * to_Instruction()
Definition: Value.hpp:88
Type::TypeID get_type() const
get the value type of the instruction
Definition: Value.hpp:68
void set_needs_null_check(bool needs_null_check)
Control whether a null-check is needed to safeguard the dereference.
virtual PassUsage & get_PassUsage(PassUsage &PU) const
Set the requirements for the pass.
const_bb_iterator bb_end() const
Definition: MethodC2.hpp:159
This Instruction marks the start of a basic block.
u2 op
Definition: disass.cpp:129
This Instruction marks the end of a basic block.
alloc::unordered_map< Instruction *, int >::type bitpositions
Maps each object reference to a unique bitvector position.
virtual Instruction * get_objectref()=0
Get the corresponding object reference.
virtual LOADInst * to_LOADInst()
Definition: Instruction.hpp:60
alloc::unordered_map< BeginInst *, boost::dynamic_bitset<> >::type non_null_references_at_entry
Holds for each BeginInst the local analysis state in the form of a bitvector.
void add_schedule_before()
Schedule before PassName.
Definition: PassUsage.hpp:127
const_inst_iterator inst_begin(const BeginInst *BI) const
SuccessorListTy::const_iterator succ_end() const
Stores the interdependencies of a pass.
Definition: PassUsage.hpp:55
Instruction super class.
Definition: Instruction.hpp:75
#define LOG2(STMT)
Definition: logging.hpp:93
SuccessorListTy::const_iterator succ_begin() const
const_iterator end() const
Definition: MethodC2.hpp:147
const_op_iterator op_begin() const
virtual MULTIANEWARRAYInst * to_MULTIANEWARRAYInst()
Definition: Instruction.hpp:64
#define LOG(STMT)
Analogous to DEBUG.
Definition: logging.hpp:91
#define I(value)
Definition: codegen.c:279
const_iterator begin() const
Definition: MethodC2.hpp:143
bool get_needs_null_check() const
Whether a null-check is needed to safeguard the dereference.
std::queue< T, Container > type
Definition: queue.hpp:38
unsigned get_index() const
The index of the argument is represented by this LOADInst.
Base type of instructions that dereference an object reference.
const_inst_iterator inst_end(const BeginInst *BI) const
virtual DereferenceInst * to_DereferenceInst()
static PassRegistry< BasicBlockSchedulingPass > X("BasicBlockSchedulingPass")
Nl nl
Definition: OStream.cpp:56
virtual ANEWARRAYInst * to_ANEWARRAYInst()
Definition: Instruction.hpp:63
virtual NEWARRAYInst * to_NEWARRAYInst()
Definition: Instruction.hpp:62
alloc::unordered_map< BeginInst *, boost::dynamic_bitset<> >::type non_null_references_at_exit
const_op_iterator op_end() const
BeginInst * get_init_bb() const
Definition: MethodC2.hpp:139
void add_requires()
PassName is required.
Definition: PassUsage.hpp:78
A LOADInst represents an argument that is passed to the current method.
const_bb_iterator bb_begin() const
Definition: MethodC2.hpp:155
std::unordered_map< Key, T, Hash, KeyEqual, Allocator< std::pair< const Key, T > > > type