cprover
java_local_variable_table.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Java local variable table processing
4 
5 Author: Chris Smowton, chris.smowton@diffblue.com
6 
7 \*******************************************************************/
8 
11 
13 
14 #include "java_types.h"
15 
16 #include <util/arith_tools.h>
17 #include <util/invariant.h>
18 #include <util/string2int.h>
19 
20 #include <climits>
21 #include <iostream>
22 
23 // Specialise the CFG representation to work over Java instead of GOTO programs.
24 // This must be done at global scope due to template resolution rules.
25 
26 template<class T>
28  T,
30  unsigned> :
31  public grapht<cfg_base_nodet<T, unsigned> >
32 {
34  typedef std::map<unsigned, unsigned> entry_mapt;
35  entry_mapt entry_map;
36 
38 
39  void operator()(const method_with_amapt &args)
40  {
41  const auto &method=args.first;
42  const auto &amap=args.second;
43  for(const auto &inst : amap)
44  {
45  // Map instruction PCs onto node indices:
46  entry_map[inst.first]=this->add_node();
47  // Map back:
48  (*this)[entry_map[inst.first]].PC=inst.first;
49  }
50  // Add edges declared in the address map:
51  for(const auto &inst : amap)
52  {
53  for(auto succ : inst.second.successors)
54  this->add_edge(entry_map.at(inst.first), entry_map.at(succ));
55  }
56  // Next, add edges declared in the exception table, which
57  // don't figure in the address map successors/predecessors as yet:
58  for(const auto &table_entry : method.exception_table)
59  {
60  auto findit=amap.find(table_entry.start_pc);
61  assert(findit!=amap.end() &&
62  "Exception table entry doesn't point to an instruction?");
63  for(; findit->first<table_entry.end_pc; ++findit)
64  {
65  // For now just assume any non-branch
66  // instruction could potentially throw.
67  auto succit=findit;
68  ++succit;
69  if(succit==amap.end())
70  continue;
71  const auto &thisinst=findit->second;
72  if(thisinst.successors.size()==1 &&
73  thisinst.successors.back()==succit->first)
74  {
75  this->add_edge(
76  entry_map.at(findit->first),
77  entry_map.at(table_entry.handler_pc));
78  }
79  }
80  }
81  }
82 
83  unsigned get_first_node(const method_with_amapt &args) const
84  {
85  return args.second.begin()->first;
86  }
87  unsigned get_last_node(const method_with_amapt &args) const
88  {
89  return (--args.second.end())->first;
90  }
91  unsigned nodes_empty(const method_with_amapt &args) const
92  {
93  return args.second.empty();
94  }
95 };
96 
97 // Grab some class typedefs for brevity:
108 
109 // Comparators for local variables:
110 
111 static bool lt_index(
114 {
115  return a.var.index<b.var.index;
116 }
117 static bool lt_startpc(
120 {
121  return a->var.start_pc<b->var.start_pc;
122 }
123 
124 // The predecessor map, and a top-sorting comparator:
125 
126 typedef std::map<
128  std::set<local_variable_with_holest *> >
130 
132 {
134 
135  explicit is_predecessor_oft(const predecessor_mapt &_order) : order(_order) {}
136 
140  {
141  auto findit=order.find(a);
142  if(findit==order.end())
143  return false;
144  return findit->second.count(b)>0;
145  }
146 };
147 
148 // Helper routines for the find-initialisers code below:
149 
157  const predecessor_mapt &predecessor_map,
158  std::set<local_variable_with_holest*> &result)
159 {
160  if(!result.insert(start).second)
161  return;
162  auto findit=predecessor_map.find(start);
163  if(findit==predecessor_map.end())
164  return;
165  for(const auto pred : findit->second)
166  gather_transitive_predecessors(pred, predecessor_map, result);
167 }
168 
174 static bool is_store_to_slot(
176  unsigned slotidx)
177 {
178  const std::string prevstatement=id2string(inst.statement);
179  if(!(prevstatement.size()>=1 && prevstatement.substr(1, 5)=="store"))
180  return false;
181 
182  unsigned storeslotidx;
183  if(inst.args.size()==1)
184  {
185  // Store with an argument:
186  const auto &arg=inst.args[0];
187  bool ret=to_unsigned_integer(to_constant_expr(arg), storeslotidx);
188  CHECK_RETURN(!ret);
189  }
190  else
191  {
192  // Store shorthands, like "store_0", "store_1"
193  assert(prevstatement[6]=='_' && prevstatement.size()==8);
194  std::string storeslot(1, prevstatement[7]);
195  assert(isdigit(storeslot[0]));
196  storeslotidx=safe_string2unsigned(storeslot);
197  }
198  return storeslotidx==slotidx;
199 }
200 
205 static void maybe_add_hole(
207  unsigned from,
208  unsigned to)
209 {
210  assert(to>=from);
211  if(to!=from)
212  var.holes.push_back({from, to-from});
213 }
214 
223  local_variable_table_with_holest::iterator firstvar,
224  local_variable_table_with_holest::iterator varlimit,
225  std::vector<local_variable_with_holest *> &live_variable_at_address)
226 {
227  for(auto it=firstvar, itend=varlimit; it!=itend; ++it)
228  {
229  if(it->var.start_pc+it->var.length>live_variable_at_address.size())
230  live_variable_at_address.resize(it->var.start_pc+it->var.length);
231 
232  for(unsigned idx=it->var.start_pc,
233  idxlim=it->var.start_pc+it->var.length;
234  idx!=idxlim;
235  ++idx)
236  {
237  assert((!live_variable_at_address[idx]) && "Local variable table clash?");
238  live_variable_at_address[idx]=&*it;
239  }
240  }
241 }
242 
258  local_variable_table_with_holest::iterator firstvar,
259  local_variable_table_with_holest::iterator varlimit,
260  const std::vector<local_variable_with_holest *> &live_variable_at_address,
261  const address_mapt &amap,
262  predecessor_mapt &predecessor_map,
263  message_handlert &msg_handler)
264 {
265  messaget msg(msg_handler);
266  for(auto it=firstvar, itend=varlimit; it!=itend; ++it)
267  {
268  // Parameters are irrelevant to us and shouldn't be changed:
269  if(it->var.start_pc==0)
270  continue;
271 
272  // Find the last instruction within the live range:
273  unsigned end_pc=it->var.start_pc+it->var.length;
274  auto amapit=amap.find(end_pc);
275  assert(amapit!=amap.begin());
276  auto old_amapit=amapit;
277  --amapit;
278  if(old_amapit==amap.end())
279  {
280  assert(
281  end_pc>amapit->first &&
282  "Instruction live range doesn't align to instruction boundary?");
283  }
284 
285  // Find vartable entries that flow into this one:
286  unsigned new_start_pc=it->var.start_pc;
287  for(; amapit->first>=it->var.start_pc; --amapit)
288  {
289  for(auto pred : amapit->second.predecessors)
290  {
291  auto pred_var=
292  (pred<live_variable_at_address.size() ?
293  live_variable_at_address[pred] :
294  nullptr);
295  if(pred_var==&*it)
296  {
297  // Flow from within same live range?
298  continue;
299  }
300  else if(!pred_var)
301  {
302  // Flow from out of range?
303  // Check if this is an initialiser, and if so expand the live range
304  // to include it, but don't check its predecessors:
305  auto inst_before_this=amapit;
306  assert(inst_before_this!=amap.begin());
307  --inst_before_this;
308  if(amapit->first!=it->var.start_pc || inst_before_this->first!=pred)
309  {
310  // These sorts of infeasible edges can occur because jsr
311  // handling is presently vague (any subroutine is assumed to
312  // be able to return to any callsite)
313  msg.warning() << "Local variable table: ignoring flow from "
314  << "out of range for " << it->var.name << ' '
315  << pred << " -> " << amapit->first
316  << messaget::eom;
317  continue;
318  }
319  if(!is_store_to_slot(
320  *(inst_before_this->second.source),
321  it->var.index))
322  {
323  throw "local variable table: didn't find initialising store";
324  }
325  new_start_pc=pred;
326  }
327  else
328  {
329  if(pred_var->var.name!=it->var.name ||
330  pred_var->var.signature!=it->var.signature)
331  {
332  // These sorts of infeasible edges can occur because
333  // jsr handling is presently vague (any subroutine is
334  // assumed to be able to return to any callsite)
335  msg.warning() << "Local variable table: ignoring flow from "
336  << "clashing variable for "
337  << it->var.name << ' ' << pred << " -> "
338  << amapit->first << messaget::eom;
339  continue;
340  }
341  // OK, this is a flow from a similar but
342  // distinct entry in the local var table.
343  predecessor_map[&*it].insert(pred_var);
344  }
345  }
346  }
347 
348  // If a simple pre-block initialiser was found,
349  // add it to the live range now:
350  it->var.length+=(it->var.start_pc-new_start_pc);
351  it->var.start_pc=new_start_pc;
352  }
353 }
354 
363 static unsigned get_common_dominator(
364  const std::set<local_variable_with_holest*> &merge_vars,
365  const java_cfg_dominatorst &dominator_analysis)
366 {
367  assert(!merge_vars.empty());
368 
369  unsigned first_pc=UINT_MAX;
370  for(auto v : merge_vars)
371  {
372  if(v->var.start_pc<first_pc)
373  first_pc=v->var.start_pc;
374  }
375 
376  std::vector<unsigned> candidate_dominators;
377  for(auto v : merge_vars)
378  {
379  const auto &dominator_nodeidx=
380  dominator_analysis.cfg.entry_map.at(v->var.start_pc);
381  const auto &this_var_doms=
382  dominator_analysis.cfg[dominator_nodeidx].dominators;
383  for(const auto this_var_dom : this_var_doms)
384  if(this_var_dom<=first_pc)
385  candidate_dominators.push_back(this_var_dom);
386  }
387  std::sort(candidate_dominators.begin(), candidate_dominators.end());
388 
389  // Working from the back, simply find the first PC
390  // that occurs merge_vars.size() times and therefore
391  // dominates all vars we seek to merge:
392  for(auto domit=candidate_dominators.rbegin(),
393  domitend=candidate_dominators.rend();
394  domit!=domitend;
395  /* Don't increment here */)
396  {
397  unsigned repeats=0;
398  auto dom=*domit;
399  while(domit!=domitend && *domit==dom)
400  {
401  ++domit;
402  ++repeats;
403  }
404  assert(repeats<=merge_vars.size());
405  if(repeats==merge_vars.size())
406  return dom;
407  }
408 
409  throw "variable live ranges with no common dominator?";
410 }
411 
421  local_variable_with_holest &merge_into,
422  const std::set<local_variable_with_holest *> &merge_vars,
423  unsigned expanded_live_range_start)
424 {
425  std::vector<local_variable_with_holest *> sorted_by_startpc(
426  merge_vars.begin(), merge_vars.end());
427  std::sort(sorted_by_startpc.begin(), sorted_by_startpc.end(), lt_startpc);
428 
430  merge_into,
431  expanded_live_range_start,
432  sorted_by_startpc[0]->var.start_pc);
433  for(unsigned idx=0; idx<sorted_by_startpc.size()-1; ++idx)
434  {
436  merge_into,
437  sorted_by_startpc[idx]->var.start_pc+sorted_by_startpc[idx]->var.length,
438  sorted_by_startpc[idx+1]->var.start_pc);
439  }
440 }
441 
450  local_variable_with_holest &merge_into,
451  const std::set<local_variable_with_holest *> &merge_vars,
452  const java_cfg_dominatorst &dominator_analysis,
453  std::ostream &debug_out)
454 {
455  // Because we need a lexically-scoped declaration,
456  // we must have the merged variable
457  // enter scope both in a block that dominates all entries, and which
458  // precedes them in program order.
459  unsigned found_dominator=
460  get_common_dominator(merge_vars, dominator_analysis);
461 
462  // Populate the holes in the live range
463  // (addresses where the new variable will be in scope,
464  // but references to this stack slot should not resolve to it
465  // as it was not visible in the original local variable table)
466  populate_live_range_holes(merge_into, merge_vars, found_dominator);
467 
468  unsigned last_pc=0;
469  for(auto v : merge_vars)
470  {
471  if(v->var.start_pc+v->var.length>last_pc)
472  last_pc=v->var.start_pc+v->var.length;
473  }
474 
475  // Apply the changes:
476  merge_into.var.start_pc=found_dominator;
477  merge_into.var.length=last_pc-found_dominator;
478 
479 #ifdef DEBUG
480  debug_out << "Merged " << merge_vars.size() << " variables named "
481  << merge_into.var.name << "; new live range "
482  << merge_into.var.start_pc << '-'
483  << merge_into.var.start_pc + merge_into.var.length << '\n';
484 #endif
485 
486  // Nuke the now-subsumed var-table entries:
487  for(auto &v : merge_vars)
488  if(v!=&merge_into)
489  v->var.length=0;
490 }
491 
504  local_variable_table_with_holest::iterator firstvar,
505  local_variable_table_with_holest::iterator varlimit,
506  const address_mapt &amap,
507  const java_cfg_dominatorst &dominator_analysis)
508 {
509  // Build a simple map from instruction PC to the variable
510  // live in this slot at that PC, and a map from each variable
511  // to variables that flow into it:
512  std::vector<local_variable_with_holest *> live_variable_at_address;
513  populate_variable_address_map(firstvar, varlimit, live_variable_at_address);
514 
515  // Now find variables that flow together by
516  // walking backwards to find initialisers
517  // or branches from other live ranges:
518  predecessor_mapt predecessor_map;
520  firstvar,
521  varlimit,
522  live_variable_at_address,
523  amap,
524  predecessor_map,
525  get_message_handler());
526 
527  // OK, we've established the flows all seem sensible.
528  // Now merge vartable entries according to the predecessor_map:
529 
530  // Take the transitive closure of the predecessor map:
531  for(auto &kv : predecessor_map)
532  {
533  std::set<local_variable_with_holest *> closed_preds;
534  gather_transitive_predecessors(kv.first, predecessor_map, closed_preds);
535  kv.second=std::move(closed_preds);
536  }
537 
538  // Top-sort so that we get the bottom variables first:
539  is_predecessor_oft comp(predecessor_map);
540  std::vector<local_variable_with_holest *> topsorted_vars;
541  for(auto it=firstvar, itend=varlimit; it!=itend; ++it)
542  topsorted_vars.push_back(&*it);
543 
544  std::sort(topsorted_vars.begin(), topsorted_vars.end(), comp);
545 
546  // Now merge the entries:
547  for(auto merge_into : topsorted_vars)
548  {
549  // Already merged into another variable?
550  if(merge_into->var.length==0)
551  continue;
552 
553  auto findit=predecessor_map.find(merge_into);
554  // Nothing to do?
555  if(findit==predecessor_map.end())
556  continue;
557 
558  const auto &merge_vars=findit->second;
559  assert(merge_vars.size()>=2);
560 
562  *merge_into,
563  merge_vars,
564  dominator_analysis,
565  status());
566  }
567 }
568 
576 static void walk_to_next_index(
577  local_variable_table_with_holest::iterator &it1,
578  local_variable_table_with_holest::iterator &it2,
579  local_variable_table_with_holest::iterator itend)
580 {
581  if(it2==itend)
582  {
583  it1=itend;
584  return;
585  }
586 
587  auto old_it2=it2;
588  auto index=it2->var.index;
589  while(it2!=itend && it2->var.index==index)
590  ++it2;
591  it1=old_it2;
592 }
593 
602  const address_mapt &amap,
603  const java_cfg_dominatorst &dominator_analysis)
604 {
605  // Sort table entries by local slot index:
606  std::sort(vars.begin(), vars.end(), lt_index);
607 
608  // For each block of entries using a particular index,
609  // try to combine them:
610  auto it1=vars.begin();
611  auto it2=it1;
612  auto itend=vars.end();
613  walk_to_next_index(it1, it2, itend);
614  for(; it1!=itend; walk_to_next_index(it1, it2, itend))
615  find_initialisers_for_slot(it1, it2, amap, dominator_analysis);
616 }
617 
621 static void cleanup_var_table(
622  std::vector<local_variable_with_holest> &vars_with_holes)
623 {
624  size_t toremove=0;
625  for(size_t i=0; i<(vars_with_holes.size()-toremove); ++i)
626  {
627  auto &v=vars_with_holes[i];
628  if(v.var.length==0)
629  {
630  // Move to end; consider the new element we've swapped in:
631  ++toremove;
632  if(i!=vars_with_holes.size()-toremove) // Already where it needs to be?
633  std::swap(v, vars_with_holes[vars_with_holes.size()-toremove]);
634  --i; // May make i (size_t)-1, but it will immediately be
635  // re-incremented as the loop iterates.
636  }
637  }
638 
639  // Remove un-needed entries.
640  vars_with_holes.resize(vars_with_holes.size()-toremove);
641 }
642 
650  const methodt &m,
651  const address_mapt &amap)
652 {
653  // Compute CFG dominator tree
654  java_cfg_dominatorst dominator_analysis;
655  method_with_amapt dominator_args(m, amap);
656  dominator_analysis(dominator_args);
657 
658  // Find out which local variable table entries should be merged:
659  // Wrap each entry so we have somewhere to record live ranges with holes:
660  std::vector<local_variable_with_holest> vars_with_holes;
661  for(const auto &v : m.local_variable_table)
662  vars_with_holes.push_back({v, {}});
663 
664  // Merge variable records:
665  find_initialisers(vars_with_holes, amap, dominator_analysis);
666 
667  // Clean up removed records from the variable table:
668  cleanup_var_table(vars_with_holes);
669 
670  // Do the locals and parameters in the variable table, which is available when
671  // compiled with -g or for methods with many local variables in the latter
672  // case, different variables can have the same index, depending on the
673  // context.
674  //
675  // to calculate which variable to use, one uses the address of the instruction
676  // that uses the variable, the size of the instruction and the start_pc /
677  // length values in the local variable table
678  for(const auto &v : vars_with_holes)
679  {
680  if(v.var.start_pc==0) // Parameter?
681  continue;
682 
683  typet t=java_type_from_string(v.var.signature);
684  std::ostringstream id_oss;
685  id_oss << method_id << "::" << v.var.start_pc << "::" << v.var.name;
686  irep_idt identifier(id_oss.str());
687  symbol_exprt result(identifier, t);
688  result.set(ID_C_base_name, v.var.name);
689 
690  variables[v.var.index].push_back(variablet());
691  auto &newv=variables[v.var.index].back();
692  newv.symbol_expr=result;
693  newv.start_pc=v.var.start_pc;
694  newv.length=v.var.length;
695  newv.holes=std::move(v.holes);
696 
697  symbolt new_symbol;
698  new_symbol.name=identifier;
699  new_symbol.type=t;
700  new_symbol.base_name=v.var.name;
701  new_symbol.pretty_name=id2string(identifier).substr(6, std::string::npos);
702  new_symbol.mode=ID_java;
703  new_symbol.is_type=false;
704  new_symbol.is_file_local=true;
705  new_symbol.is_thread_local=true;
706  new_symbol.is_lvalue=true;
707  symbol_table.add(new_symbol);
708  }
709 }
710 
719  size_t address,
720  variablest &var_list)
721 {
722  for(const variablet &var : var_list)
723  {
724  size_t start_pc=var.start_pc;
725  size_t length=var.length;
726  if(address>=start_pc && address<(start_pc+length))
727  {
728  bool found_hole=false;
729  for(auto &hole : var.holes)
730  if(address>=hole.start_pc && address<(hole.start_pc+hole.length))
731  {
732  found_hole=true;
733  break;
734  }
735  if(found_hole)
736  continue;
737  return var;
738  }
739  }
740  // add unnamed local variable to end of list at this index
741  // with scope from 0 to SIZE_T_MAX
742  // as it is at the end of the vector, it will only be taken into account
743  // if no other variable is valid
744  size_t list_length=var_list.size();
745  var_list.resize(list_length+1);
746  var_list[list_length].start_pc=0;
747  var_list[list_length].length=std::numeric_limits<size_t>::max();
748  return var_list[list_length];
749 }
void find_initialisers_for_slot(local_variable_table_with_holest::iterator firstvar, local_variable_table_with_holest::iterator varlimit, const address_mapt &amap, const java_cfg_dominatorst &doms)
Given a sequence of users of the same local variable slot, this figures out which ones are related by...
The type of an expression.
Definition: type.h:20
mstreamt & warning()
Definition: message.h:228
irep_idt name
The unique identifier.
Definition: symbol.h:46
static void merge_variable_table_entries(local_variable_with_holest &merge_into, const std::set< local_variable_with_holest *> &merge_vars, const java_cfg_dominatorst &dominator_analysis, std::ostream &debug_out)
See above.
java_bytecode_convert_methodt::address_mapt address_mapt
A generic directed graph with a parametric node type.
Definition: graph.h:132
JAVA Bytecode Language Conversion.
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
bool is_thread_local
Definition: symbol.h:70
std::pair< const methodt &, const address_mapt & > method_with_amapt
irep_idt mode
Language mode.
Definition: symbol.h:55
entry_mapt entry_map
Definition: cfg.h:87
static void maybe_add_hole(local_variable_with_holest &var, unsigned from, unsigned to)
See above.
static unsigned get_common_dominator(const std::set< local_variable_with_holest *> &merge_vars, const java_cfg_dominatorst &dominator_analysis)
Used to find out where to put a variable declaration that subsumes several variable live ranges...
void setup_local_variables(const methodt &m, const address_mapt &amap)
See find_initialisers_for_slot above for more detail.
irep_idt pretty_name
Language-specific display name.
Definition: symbol.h:58
Symbol table entry.This is a symbol in the symbol table, stored in an object of type symbol_tablet...
Definition: symbol.h:33
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:240
static mstreamt & eom(mstreamt &m)
Definition: message.h:193
std::map< unsigned, converted_instructiont > address_mapt
static bool is_store_to_slot(const java_bytecode_convert_methodt::instructiont &inst, unsigned slotidx)
See above.
bool to_unsigned_integer(const constant_exprt &expr, unsigned &uint_value)
convert a positive integer expression to an unsigned int
Definition: arith_tools.cpp:95
std::vector< local_variable_with_holest > local_variable_table_with_holest
typet java_type_from_string(const std::string &src)
Definition: java_types.cpp:152
static void walk_to_next_index(local_variable_table_with_holest::iterator &it1, local_variable_table_with_holest::iterator &it2, local_variable_table_with_holest::iterator itend)
Walk a vector, a contiguous block of entries with equal slot index at a time.
java_bytecode_convert_methodt::holet holet
static void gather_transitive_predecessors(local_variable_with_holest *start, const predecessor_mapt &predecessor_map, std::set< local_variable_with_holest *> &result)
See above start: Variable to find the predecessors of predecessor_map: Map from local variables to se...
static void populate_live_range_holes(local_variable_with_holest &merge_into, const std::set< local_variable_with_holest *> &merge_vars, unsigned expanded_live_range_start)
See above.
std::map< local_variable_with_holest *, std::set< local_variable_with_holest * > > predecessor_mapt
static bool lt_startpc(const local_variable_with_holest *a, const local_variable_with_holest *b)
void find_initialisers(local_variable_table_with_holest &vars, const address_mapt &amap, const java_cfg_dominatorst &doms)
See find_initialisers_for_slot above for more detail.
static void populate_variable_address_map(local_variable_table_with_holest::iterator firstvar, local_variable_table_with_holest::iterator varlimit, std::vector< local_variable_with_holest *> &live_variable_at_address)
See above.
java_bytecode_convert_methodt::local_variable_with_holest local_variable_with_holest
unsigned safe_string2unsigned(const std::string &str, int base)
Definition: string2int.cpp:51
java_bytecode_convert_methodt::java_cfg_dominatorst java_cfg_dominatorst
typet type
Type of symbol.
Definition: symbol.h:37
static void cleanup_var_table(std::vector< local_variable_with_holest > &vars_with_holes)
See above.
bool operator()(local_variable_with_holest *a, local_variable_with_holest *b) const
irep_idt base_name
Base (non-scoped) name.
Definition: symbol.h:52
const variablet & find_variable_for_slot(size_t address, variablest &var_list)
See above.
void add_edge(node_indext a, node_indext b)
Definition: graph.h:197
bool is_file_local
Definition: symbol.h:71
Expression to hold a symbol (variable)
Definition: std_expr.h:82
static bool lt_index(const local_variable_with_holest &a, const local_variable_with_holest &b)
is_predecessor_oft(const predecessor_mapt &_order)
const constant_exprt & to_constant_expr(const exprt &expr)
Cast a generic exprt to a constant_exprt.
Definition: std_expr.h:3725
bool is_type
Definition: symbol.h:66
static void populate_predecessor_map(local_variable_table_with_holest::iterator firstvar, local_variable_table_with_holest::iterator varlimit, const std::vector< local_variable_with_holest *> &live_variable_at_address, const address_mapt &amap, predecessor_mapt &predecessor_map, message_handlert &msg_handler)
Usually a live variable range begins with a store instruction initialising the relevant local variabl...
java_bytecode_convert_methodt::local_variable_table_with_holest local_variable_table_with_holest
bool is_lvalue
Definition: symbol.h:71
const predecessor_mapt & order