# HG changeset patch # User Max Brister # Date 1338595723 18000 # Node ID 39d52aa37a087e525a19739b93d4c5c64891317f # Parent 3b40dbc145722de93c348cd8e050dff6ea4b42e5 Use standard SSA construction algorithm, and support break/continue diff -r 3b40dbc14572 -r 39d52aa37a08 build-aux/mkinstalldirs --- a/build-aux/mkinstalldirs Wed May 30 09:36:38 2012 -0500 +++ b/build-aux/mkinstalldirs Fri Jun 01 19:08:43 2012 -0500 @@ -1,7 +1,7 @@ #! /bin/sh # mkinstalldirs --- make directory hierarchy -scriptversion=2012-05-25.20; # UTC +scriptversion=2009-04-28.21; # UTC # Original author: Noah Friedman # Created: 1993-05-16 @@ -81,9 +81,9 @@ echo "mkdir -p -- $*" exec mkdir -p -- "$@" else - # On NextStep and OpenStep, the `mkdir' command does not + # On NextStep and OpenStep, the 'mkdir' command does not # recognize any option. It will interpret all options as - # directories to create, and then abort because `.' already + # directories to create, and then abort because '.' already # exists. test -d ./-p && rmdir ./-p test -d ./--version && rmdir ./--version diff -r 3b40dbc14572 -r 39d52aa37a08 src/pt-jit.cc --- a/src/pt-jit.cc Wed May 30 09:36:38 2012 -0500 +++ b/src/pt-jit.cc Fri Jun 01 19:08:43 2012 -0500 @@ -90,6 +90,13 @@ throw jit_fail_exception (reason); } +std::ostream& jit_print (std::ostream& os, jit_type *atype) +{ + if (! atype) + return os << "null"; + return os << atype->name (); +} + // function that jit code calls extern "C" void octave_jit_print_any (const char *name, octave_base_value *obv) @@ -618,11 +625,20 @@ // -------------------- jit_value -------------------- jit_value::~jit_value (void) { + replace_with (0); +} + +void +jit_value::replace_with (jit_value *value) +{ while (use_head) { jit_instruction *user = use_head->user (); size_t idx = use_head->index (); - user->stash_argument (idx, 0); + if (idx < user->argument_count ()) + user->stash_argument (idx, value); + else + user->stash_tag (0); } } @@ -636,13 +652,58 @@ JIT_VISIT_IR_NOTEMPLATE #undef JIT_METH +std::ostream& +operator<< (std::ostream& os, const jit_value& value) +{ + return value.short_print (os); +} + // -------------------- jit_instruction -------------------- +void +jit_instruction::push_variable (void) +{ + if (tag ()) + tag ()->push (this); +} + +void +jit_instruction::pop_variable (void) +{ + if (tag ()) + tag ()->pop (); +} + llvm::BasicBlock * jit_instruction::parent_llvm (void) const { return mparent->to_llvm (); } +std::ostream& +jit_instruction::short_print (std::ostream& os) const +{ + if (type ()) + jit_print (os, type ()) << ": "; + + if (tag ()) + os << tag ()->name () << "." << id; + else + os << "#" << id; + return os; +} + +jit_variable * +jit_instruction::tag (void) const +{ + return reinterpret_cast (mtag.value ()); +} + +void +jit_instruction::stash_tag (jit_variable *atag) +{ + mtag.stash_value (atag, this); +} + // -------------------- jit_block -------------------- jit_instruction * jit_block::prepend (jit_instruction *instr) @@ -660,6 +721,23 @@ return instr; } +jit_instruction * +jit_block::insert_before (iterator loc, jit_instruction *instr) +{ + instructions.insert (loc, instr); + instr->stash_parent (this); + return instr; +} + +jit_instruction * +jit_block::insert_after (iterator loc, jit_instruction *instr) +{ + ++loc; + instructions.insert (loc, instr); + instr->stash_parent (this); + return instr; +} + jit_terminator * jit_block::terminator (void) const { @@ -687,13 +765,6 @@ return use->user_parent (); } -llvm::Value * -jit_block::pred_terminator_llvm (size_t idx) const -{ - jit_terminator *term = pred_terminator (idx); - return term ? term->to_llvm () : 0; -} - size_t jit_block::pred_index (jit_block *apred) const { @@ -718,9 +789,10 @@ to_llvm ()); // fix the predecessor jump if it has been created - llvm::Value *term = pred_terminator_llvm (pred_idx); - if (term) + jit_terminator *jterm = pred_terminator (pred_idx); + if (jterm->has_llvm ()) { + llvm::Value *term = jterm->to_llvm (); llvm::TerminatorInst *branch = llvm::cast (term); for (size_t i = 0; i < branch->getNumSuccessors (); ++i) { @@ -735,6 +807,13 @@ } } +jit_block * +jit_block::succ (size_t i) const +{ + jit_terminator *term = terminator (); + return term->sucessor (i); +} + size_t jit_block::succ_count (void) const { @@ -742,24 +821,198 @@ return term ? term->sucessor_count () : 0; } -jit_phi * -jit_block::search_phi (const std::string& tag_name) -{ - jit_phi *ret = 0; - for (iterator iter = begin (); iter != end () - && (ret = dynamic_cast (*iter)); ++iter) - if (ret->tag () == tag_name) - return ret; - - return 0; -} - llvm::BasicBlock * jit_block::to_llvm (void) const { return llvm::cast (llvm_value); } +std::ostream& +jit_block::print_dom (std::ostream& os) const +{ + short_print (os); + os << ":\n"; + os << " mid: " << mid << std::endl; + os << " pred: "; + for (size_t i = 0; i < pred_count (); ++i) + os << *pred (i) << " "; + os << std::endl; + + os << " succ: "; + for (size_t i = 0; i < succ_count (); ++i) + os << *succ (i) << " "; + os << std::endl; + + os << " idom: "; + if (idom) + os << *idom; + else + os << "NULL"; + os << std::endl; + os << " df: "; + for (df_iterator iter = df_begin (); iter != df_end (); ++iter) + os << **iter << " "; + os << std::endl; + + os << " dom_succ: "; + for (size_t i = 0; i < dom_succ.size (); ++i) + os << *dom_succ[i] << " "; + + return os << std::endl; +} + +void +jit_block::compute_df (size_t visit_count) +{ + if (mvisit_count > visit_count) + return; + ++mvisit_count; + + if (pred_count () >= 2) + { + for (size_t i = 0; i < pred_count (); ++i) + { + jit_block *runner = pred (i); + while (runner != idom) + { + runner->mdf.insert (this); + runner = runner->idom; + } + } + } + + for (size_t i = 0; i < succ_count (); ++i) + succ (i)->compute_df (visit_count); +} + +bool +jit_block::update_idom (size_t visit_count) +{ + if (mvisit_count > visit_count) + return false; + ++mvisit_count; + + if (! pred_count ()) + return false; + + bool changed = false; + for (size_t i = 0; i < pred_count (); ++i) + changed = pred (i)->update_idom (visit_count) || changed; + + jit_block *new_idom = pred (0); + for (size_t i = 1; i < pred_count (); ++i) + { + jit_block *pidom = pred (i)->idom; + if (! new_idom) + new_idom = pidom; + else if (pidom) + new_idom = pidom->idom_intersect (new_idom); + } + + if (idom != new_idom) + { + idom = new_idom; + return true; + } + + return changed; +} + +void +jit_block::finish_phi (jit_block *apred) +{ + size_t pred_idx = pred_index (apred); + for (iterator iter = begin (); iter != end () + && dynamic_cast (*iter); ++iter) + { + jit_instruction *phi = *iter; + jit_variable *var = phi->tag (); + phi->stash_argument (pred_idx, var->top ()); + } +} + +void +jit_block::do_construct_ssa (jit_convert& convert, size_t visit_count) +{ + if (mvisit_count > visit_count) + return; + ++mvisit_count; + + for (iterator iter = begin (); iter != end (); ++iter) + { + jit_instruction *instr = *iter; + bool isphi = dynamic_cast (instr); + + if (! isphi) + { + for (size_t i = 0; i < instr->argument_count (); ++i) + { + jit_variable *var; + var = dynamic_cast (instr->argument (i)); + if (var) + instr->stash_argument (i, var->top ()); + } + + // FIXME: Remove need for jit_store_argument dynamic cast + jit_variable *tag = instr->tag (); + if (tag && tag->has_top () + && ! dynamic_cast (instr)) + { + jit_call *rel = convert.create (jit_typeinfo::release, + tag->top ()); + insert_after (iter, rel); + ++iter; + } + } + + instr->push_variable (); + } + + for (size_t i = 0; i < succ_count (); ++i) + succ (i)->finish_phi (this); + + for (size_t i = 0; i < dom_succ.size (); ++i) + dom_succ[i]->do_construct_ssa (convert, visit_count); + + for (iterator iter = begin (); iter != end (); ++iter) + { + jit_instruction *instr = *iter; + instr->pop_variable (); + } +} + +void +jit_block::create_dom_tree (size_t visit_count) +{ + if (mvisit_count > visit_count) + return; + ++mvisit_count; + + if (idom != this) + idom->dom_succ.push_back (this); + + for (size_t i = 0; i < succ_count (); ++i) + succ (i)->create_dom_tree (visit_count); +} + +jit_block * +jit_block::idom_intersect (jit_block *b) +{ + jit_block *i = this; + jit_block *j = b; + + while (i != j) + { + while (i->id () > j->id ()) + i = i->idom; + + while (j->id () > i->id ()) + j = j->idom; + } + + return i; +} + // -------------------- jit_call -------------------- bool jit_call::infer (void) @@ -792,45 +1045,39 @@ // -------------------- jit_convert -------------------- jit_convert::jit_convert (llvm::Module *module, tree &tee) + : iterator_count (0), breaking (false) { jit_instruction::reset_ids (); - jit_block *entry_block = create ("body"); + entry_block = create ("body"); + blocks.push_back (entry_block); block = entry_block; - blocks.push_back (block); - - toplevel_map tlevel (*this, block); - variables = &tlevel; - final_block = create ("final"); visit (tee); - blocks.push_back (final_block); - block->append (create (final_block)); + // FIXME: Remove if we no longer only compile loops + assert (! breaking); + assert (breaks.empty ()); + assert (continues.empty ()); - for (variable_map::iterator iter = variables->begin (); - iter != variables->end (); ++iter) - final_block->append (create (iter->first, iter->second)); + jit_block *final_block = block; + for (vmap_t::iterator iter = vmap.begin (); iter != vmap.end (); ++iter) + + { + jit_variable *var = iter->second; + const std::string& name = var->name (); + if (name.size () && name[0] != '#') + final_block->append (create (var)); + } - // FIXME: Maybe we should remove dead code here? + construct_ssa (final_block); // initialize the worklist to instructions derived from constants for (std::list::iterator iter = constants.begin (); iter != constants.end (); ++iter) append_users (*iter); - // also get anything from jit_extract_argument, as these have constant types - for (jit_block::iterator iter = entry_block->begin (); - iter != entry_block->end (); ++iter) - { - jit_instruction *instr = *iter; - if (jit_extract_argument *extract = dynamic_cast(instr)) - { - if (! extract->type ()) - // we depend on an unknown type - fail ("Unknown initial type: \"" + extract->tag () + "\""); - append_users (extract); - } - } + if (debug_print) + print_blocks ("octave jit ir"); // FIXME: Describe algorithm here while (worklist.size ()) @@ -846,11 +1093,7 @@ { std::cout << "-------------------- Compiling tree --------------------\n"; std::cout << tee.str_print_code () << std::endl; - std::cout << "-------------------- octave jit ir --------------------\n"; - for (std::list::iterator iter = blocks.begin (); - iter != blocks.end (); ++iter) - (*iter)->print (std::cout, 0); - std::cout << std::endl; + print_blocks ("octave jit ir"); } // for now just init arguments from entry, later we will have to do something @@ -859,11 +1102,11 @@ iter != entry_block->end (); ++iter) { if (jit_extract_argument *extract = dynamic_cast (*iter)) - arguments.push_back (std::make_pair (extract->tag (), true)); + arguments.push_back (std::make_pair (extract->name (), true)); } convert_llvm to_llvm; - function = to_llvm.convert (module, arguments, blocks, constants); + function = to_llvm.convert (module, arguments, blocks); if (debug_print) { @@ -914,7 +1157,8 @@ void jit_convert::visit_break_command (tree_break_command&) { - fail (); + breaks.push_back (block); + breaking = true; } void @@ -926,7 +1170,8 @@ void jit_convert::visit_continue_command (tree_continue_command&) { - fail (); + continues.push_back (block); + breaking = true; } void @@ -961,29 +1206,13 @@ // better type inference bounds on variables defined and used only // inside the for loop (e.g. the index variable) - // prev_block: % pred = ? - // #control.0 = % compute_control (note this will just be a temp) - // #iter.0 = call for_init (#control.0) % Let type of control decide iter - // % initial value and type - // #temp.0 = call for_check (control.0, #iter.0) - // cond_break #temp.0, for_body, for_tail - // for_body: % pred = for_init, for_cond - // idxvar.2 = phi | for_init -> idxvar.1 - // | for_body -> idxvar.3 - // #iter.1 = phi | for_init -> #iter.0 - // | for_body -> #iter.2 - // idxvar.3 = call for_index (#control.0, #iter.1) - // % do loop body - // #iter.2 = #iter.1 + 1 % release is implicit in iter reuse - // #check = call for_check (#control.0, iter.2) - // cond_break #check for_body, for_tail - // for_tail: % pred = prev_block, for_body - // #iter.3 = phi | prev_block -> #iter.0 - // | for_body -> #iter.2 - // idxvar.4 = phi | prev_block -> idxvar.0 - // | for_body -> idxvar.3 - // call release (#iter.3) - // % rest of code + // If we are a nested for loop we need to store the previous breaks + assert (! breaking); + unwind_protect prot; + prot.protect_var (breaks); + prot.protect_var (continues); + prot.protect_var (breaking); + breaks.clear (); // FIXME: one of these days we will introduce proper lvalues... tree_identifier *lhs = dynamic_cast(cmd.left_hand_side ()); @@ -991,6 +1220,13 @@ fail (); std::string lhs_name = lhs->name (); + // we need a variable for our iterator, because it is used in multiple blocks + std::stringstream ss; + ss << "#iter" << iterator_count++; + std::string iter_name = ss.str (); + jit_variable *iterator = create (iter_name); + vmap[iter_name] = iterator; + jit_block *body = create ("for_body"); blocks.push_back (body); @@ -999,55 +1235,57 @@ // do control expression, iter init, and condition check in prev_block (block) jit_value *control = visit (cmd.control_expr ()); jit_call *init_iter = create (jit_typeinfo::for_init, control); - init_iter->stash_tag ("#iter"); + init_iter->stash_tag (iterator); block->append (init_iter); + jit_value *check = block->append (create (jit_typeinfo::for_check, - control, init_iter)); + control, iterator)); block->append (create (check, body, tail)); - - // we need to do iter phi manually, for_map handles the rest - jit_phi *iter_phi = create (2); - iter_phi->stash_tag ("#iter"); - iter_phi->stash_argument (0, init_iter); - body->append (iter_phi); - - variable_map *merge_vars = variables; - for_map body_vars (variables, body); - variables = &body_vars; block = body; - // first thing we do in the for loop is bind our index from our itertor - jit_call *idx_rhs = create (jit_typeinfo::for_index, control, iter_phi); + // compute the syntactical iterator + jit_call *idx_rhs = create (jit_typeinfo::for_index, control, iterator); block->append (idx_rhs); - idx_rhs->stash_tag (lhs_name); do_assign (lhs_name, idx_rhs, false); + // do loop tree_statement_list *pt_body = cmd.body (); pt_body->accept (*this); - // increment iterator, check conditional, and repeat + if (breaking && continues.empty ()) + { + // WTF are you doing user? Every branch was a continue, why did you have + // a loop??? Users are silly people... + finish_breaks (tail, breaks); + blocks.push_back (tail); + block = tail; + return; + } + + // check our condition, continues jump to this block + jit_block *check_block = create ("for_check"); + blocks.push_back (check_block); + + if (! breaking) + block->append (create (check_block)); + finish_breaks (check_block, continues); + + block = check_block; const jit_function& add_fn = jit_typeinfo::binary_op (octave_value::op_add); - jit_call *iter_inc = create (add_fn, iter_phi, - create (1)); - iter_inc->stash_tag ("#iter"); + jit_instruction *one = create (1); + block->append (one); + + jit_call *iter_inc = create (add_fn, iterator, one); + iter_inc->stash_tag (iterator); block->append (iter_inc); check = block->append (create (jit_typeinfo::for_check, control, - iter_inc)); + iterator)); block->append (create (check, body, tail)); - iter_phi->stash_argument (1, iter_inc); - body_vars.finish_phi (*variables); - merge (tail, *merge_vars, block, body_vars); + // breaks will go to our tail blocks.push_back (tail); + finish_breaks (tail, breaks); block = tail; - variables = merge_vars; - - iter_phi = create (2); - iter_phi->stash_tag ("#iter"); - iter_phi->stash_argument (0, init_iter); - iter_phi->stash_argument (1, iter_inc); - block->append (iter_phi); - block->append (create (jit_typeinfo::release, iter_phi)); } void @@ -1090,8 +1328,8 @@ jit_convert::visit_identifier (tree_identifier& ti) { const jit_function& fn = jit_typeinfo::grab (); - jit_value *var = variables->get (ti.name ()); - result = block->append (create (fn, var)); + jit_value *decl = get_variable (ti.name ()); + result = block->append (create (fn, decl)); } void @@ -1120,6 +1358,10 @@ // c = c + 3; // endif + // ******************** + // FIXME: Documentation no longer reflects current version + // ******************** + // Generates: // prev_block0: % pred - ? // #temp.0 = call binary== (a.0, 1) @@ -1149,7 +1391,6 @@ // the condition check for the ith clause. For the else, it is simple the // else body. If there is no else body, then it is padded with the tail std::vector entry_blocks (lst.size () + 1 - last_else); - std::vector branch_variables (lst.size (), 0); std::vector branch_blocks (lst.size (), 0); // final blocks entry_blocks[0] = block; @@ -1169,16 +1410,13 @@ if (! last_else) entry_blocks[entry_blocks.size () - 1] = tail; - // actually fill out the contents of our blocks. We store the variable maps - // at the end of each branch, this allows us to merge them in the tail - variable_map *prev_map = variables; + size_t num_incomming = 0; // number of incomming blocks to our tail iter = lst.begin (); for (size_t i = 0; iter != lst.end (); ++iter, ++i) { tree_if_clause *tic = *iter; block = entry_blocks[i]; assert (block); - variables = prev_map; if (i) // the first block is prev_block, so it has already been added blocks.push_back (entry_blocks[i]); @@ -1195,31 +1433,29 @@ entry_blocks[i + 1]); block->append (br); block = body; - - variables = new compound_map (variables); - branch_variables[i] = variables; } tree_statement_list *stmt_lst = tic->commands (); assert (stmt_lst); // jwe: Can this be null? stmt_lst->accept (*this); - branch_variables[i] = variables; - branch_blocks[i] = block; - block->append (create (tail)); + if (breaking) + breaking = false; + else + { + ++num_incomming; + block->append (create (tail)); + } } - blocks.push_back (tail); - - // We create phi nodes in the tail to merge blocks - for (size_t i = 0; i < branch_variables.size () - last_else; ++i) + if (num_incomming || ! last_else) { - merge (tail, *prev_map, branch_blocks[i], *branch_variables[i]); - delete branch_variables[i]; + blocks.push_back (tail); + block = tail; } - - variables = prev_map; - block = tail; + else + // every branch broke, so we don't have a tail + breaking = true; } void @@ -1267,7 +1503,9 @@ result = create (rv); } else - fail (); + fail ("Unknown constant"); + + block->append (result); } void @@ -1311,7 +1549,7 @@ { // resolve rhs tree_expression *rhs = tsa.right_hand_side (); - jit_value *rhsv = visit (rhs); + jit_instruction *rhsv = visit (rhs); // resolve lhs tree_expression *lhs = tsa.left_hand_side (); @@ -1319,11 +1557,7 @@ fail (); std::string lhs_name = lhs->name (); - do_assign (lhs_name, rhsv, tsa.print_result ()); - result = rhsv; - - if (jit_instruction *instr = dynamic_cast(rhsv)) - instr->stash_tag (lhs_name); + result = do_assign (lhs_name, rhsv, tsa.print_result ()); } void @@ -1348,7 +1582,7 @@ else do_bind_ans = (! expr->is_assignment_expression ()); - jit_value *expr_result = visit (expr); + jit_instruction *expr_result = visit (expr); if (do_bind_ans) do_assign ("ans", expr_result, expr->print_result ()); @@ -1373,6 +1607,9 @@ // jwe: Can this ever be null? assert (elt); elt->accept (*this); + + if (breaking) + break; } } @@ -1418,86 +1655,113 @@ fail (); } -void -jit_convert::do_assign (const std::string& lhs, jit_value *rhs, bool print) +jit_variable * +jit_convert::get_variable (const std::string& vname) { - const jit_function& release = jit_typeinfo::release (); - jit_value *current = variables->get (lhs); - block->append (create (release, current)); - variables->set (lhs, rhs); + vmap_t::iterator iter; + iter = vmap.find (vname); + if (iter != vmap.end ()) + return iter->second; + + jit_variable *var = create (vname); + octave_value val = symbol_table::find (vname); + jit_type *type = jit_typeinfo::type_of (val); + jit_extract_argument *extract; + extract = create (type, var); + entry_block->prepend (extract); + + return vmap[vname] = var; +} + +jit_instruction * +jit_convert::do_assign (const std::string& lhs, jit_instruction *rhs, + bool print) +{ + jit_variable *var = get_variable (lhs); + rhs->stash_tag (var); if (print) { const jit_function& print_fn = jit_typeinfo::print_value (); jit_const_string *name = create (lhs); - block->append (create (print_fn, name, rhs)); + block->append (create (print_fn, name, var)); } + + return rhs; } -jit_value * +jit_instruction * jit_convert::visit (tree& tee) { result = 0; tee.accept (*this); - jit_value *ret = result; + jit_instruction *ret = result; result = 0; return ret; } void -jit_convert::merge (jit_block *merge_block, variable_map& merge_vars, - jit_block *incomming_block, - const variable_map& incomming_vars) +jit_convert::construct_ssa (jit_block *final_block) { - size_t pred_count = merge_block->pred_count (); - size_t merge_idx = merge_block->pred_index (incomming_block); - for (variable_map::const_iterator iter = incomming_vars.begin (); - iter != incomming_vars.end (); ++iter) + final_block->label (); + entry_block->compute_idom (final_block); + entry_block->compute_df (); + entry_block->create_dom_tree (); + + // insert phi nodes where needed + for (vmap_t::iterator iter = vmap.begin (); iter != vmap.end (); ++iter) { - const std::string& vname = iter->first; - jit_value *merge_val = merge_vars.get (vname); - jit_value *inc_val = iter->second; + jit_block::df_set visited, added_phi; + std::list ssa_worklist; + iter->second->use_blocks (visited); + ssa_worklist.insert (ssa_worklist.begin (), visited.begin (), visited.end ()); - if (merge_val != inc_val) + while (ssa_worklist.size ()) { - jit_phi *phi = dynamic_cast (merge_val); - if (! (phi && phi->parent () == merge_block)) + jit_block *b = ssa_worklist.front (); + ssa_worklist.pop_front (); + + for (jit_block::df_iterator diter = b->df_begin (); + diter != b->df_end (); ++diter) { - phi = merge_block->search_phi (vname); - if (! phi) + jit_block *dblock = *diter; + if (! added_phi.count (dblock)) { - phi = create (pred_count, merge_val); - merge_block->prepend (phi); + jit_phi *phi = create (iter->second, + dblock->pred_count ()); + dblock->prepend (phi); + added_phi.insert (dblock); } - merge_vars.set (vname, phi); + if (! visited.count (dblock)) + { + ssa_worklist.push_back (dblock); + visited.insert (dblock); + } } - - phi->stash_argument (merge_idx, inc_val); } } + + entry_block->construct_ssa (*this); } -// -------------------- jit_convert::toplevel_map -------------------- -jit_value * -jit_convert::toplevel_map::insert (const std::string& name, jit_value *pval) +void +jit_convert::finish_breaks (jit_block *dest, const break_list& lst) { - assert (pval == 0); // we have no parent - - jit_block *entry = block (); - octave_value val = symbol_table::find (name); - jit_type *type = jit_typeinfo::type_of (val); - jit_instruction *ret = convert.create (type, name); - return vars[name] = entry->prepend (ret); + for (break_list::const_iterator iter = lst.begin (); iter != lst.end (); + ++iter) + { + jit_block *b = *iter; + b->append (create (dest)); + } } // -------------------- jit_convert::convert_llvm -------------------- llvm::Function * jit_convert::convert_llvm::convert (llvm::Module *module, const std::vector >& args, - const std::list& blocks, - const std::list& constants) + const std::list& blocks) { jit_type *any = jit_typeinfo::get_any (); @@ -1522,12 +1786,6 @@ arguments[args[i].first] = loaded_arg; } - // we need to generate llvm values for constants, as these don't appear in - // a block - for (std::list::const_iterator iter = constants.begin (); - iter != constants.end (); ++iter) - visit (*iter); - std::list::const_iterator biter; for (biter = blocks.begin (); biter != blocks.end (); ++biter) { @@ -1667,12 +1925,12 @@ const jit_function::overload& ol = call.overload (); if (! ol.function) fail ("No overload for: " + call.print_string ()); - + std::vector args (call.argument_count ()); for (size_t i = 0; i < call.argument_count (); ++i) args[i] = call.argument_llvm (i); - call.stash_llvm (builder.CreateCall (ol.function, args, call.tag ())); + call.stash_llvm (builder.CreateCall (ol.function, args)); } void @@ -1682,10 +1940,10 @@ if (! ol.function) fail (); - llvm::Value *arg = arguments[extract.tag ()]; + llvm::Value *arg = arguments[extract.name ()]; assert (arg); arg = builder.CreateLoad (arg); - extract.stash_llvm (builder.CreateCall (ol.function, arg, extract.tag ())); + extract.stash_llvm (builder.CreateCall (ol.function, arg, extract.name ())); } void @@ -1698,7 +1956,7 @@ arg_value = builder.CreateCall (ol.function, arg_value); - llvm::Value *arg = arguments[store.tag ()]; + llvm::Value *arg = arguments[store.name ()]; store.stash_llvm (builder.CreateStore (arg_value, arg)); } @@ -1708,8 +1966,7 @@ // we might not have converted all incoming branches, so we don't // set incomming branches now llvm::PHINode *node = llvm::PHINode::Create (phi.type_llvm (), - phi.argument_count (), - phi.tag ()); + phi.argument_count ()); builder.Insert (node); phi.stash_llvm (node); @@ -1719,6 +1976,12 @@ parent->create_merge (function, i); } +void +jit_convert::convert_llvm::visit (jit_variable&) +{ + fail ("ERROR: SSA construction should remove all variables"); +} + // -------------------- tree_jit -------------------- tree_jit::tree_jit (void) : module (0), engine (0) diff -r 3b40dbc14572 -r 39d52aa37a08 src/pt-jit.h --- a/src/pt-jit.h Wed May 30 09:36:38 2012 -0500 +++ b/src/pt-jit.h Fri Jun 01 19:08:43 2012 -0500 @@ -28,6 +28,7 @@ #include #include #include +#include #include "Array.h" #include "Range.h" @@ -48,6 +49,7 @@ // // For loops are compiled again! // if, elseif, and else statements compile again! +// break and continue now work! // Additionally, make check passes using jit. // // The octave low level IR is a linear IR, it works by converting everything to @@ -58,11 +60,12 @@ // // // TODO: -// 1. Support error cases -// 2. Support break/continue -// 3. Fix memory leaks in JIT -// 4. Cleanup/documentation -// 5. ... +// 1. Rename symbol_table::symbol_record_ref -> symbol_table::symbol_reference +// 2. Support some simple matrix case (and cleanup Octave low level IR) +// 3. Support error cases +// 4. Fix memory leaks in JIT +// 5. Cleanup/documentation +// 6. ... // --------------------------------------------------------- @@ -150,12 +153,7 @@ }; // seperate print function to allow easy printing if type is null -static std::ostream& jit_print (std::ostream& os, jit_type *atype) -{ - if (! atype) - return os << "null"; - return os << atype->name (); -} +std::ostream& jit_print (std::ostream& os, jit_type *atype); // Keeps track of overloads for a builtin function. Used for both type inference // and code generation. @@ -496,7 +494,8 @@ JIT_METH(call); \ JIT_METH(extract_argument); \ JIT_METH(store_argument); \ - JIT_METH(phi) + JIT_METH(phi); \ + JIT_METH(variable) #define JIT_VISIT_IR_CLASSES \ JIT_VISIT_IR_NOTEMPLATE; \ @@ -515,6 +514,9 @@ virtual ~jit_value (void); + // replace all uses with + void replace_with (jit_value *value); + jit_type *type (void) const { return ty; } llvm::Type *type_llvm (void) const @@ -540,15 +542,21 @@ return ss.str (); } - virtual std::ostream& print (std::ostream& os, size_t indent = 0) = 0; + virtual std::ostream& print (std::ostream& os, size_t indent = 0) const = 0; - virtual std::ostream& short_print (std::ostream& os) + virtual std::ostream& short_print (std::ostream& os) const { return print (os); } virtual void accept (jit_ir_walker& walker) = 0; + bool has_llvm (void) const + { + return llvm_value; + } + llvm::Value *to_llvm (void) const { + assert (llvm_value); return llvm_value; } @@ -557,10 +565,10 @@ llvm_value = compiled; } protected: - std::ostream& print_indent (std::ostream& os, size_t indent) + std::ostream& print_indent (std::ostream& os, size_t indent) const { - for (size_t i = 0; i < indent; ++i) - os << "\t"; + for (size_t i = 0; i < indent * 8; ++i) + os << " "; return os; } @@ -571,54 +579,7 @@ size_t myuse_count; }; -// defnie accept methods for subclasses -#define JIT_VALUE_ACCEPT(clname) \ - virtual void accept (jit_ir_walker& walker); - -template -class -jit_const : public jit_value -{ -public: - typedef PASS_T pass_t; - - jit_const (PASS_T avalue) : mvalue (avalue) - { - stash_type (EXTRACT_T ()); - } - - PASS_T value (void) const { return mvalue; } - - virtual std::ostream& print (std::ostream& os, size_t indent) - { - print_indent (os, indent) << type_name () << ": "; - if (QUOTE) - os << "\""; - os << mvalue; - if (QUOTE) - os << "\""; - return os; - } - - JIT_VALUE_ACCEPT (jit_const); -private: - T mvalue; -}; - -typedef jit_const jit_const_scalar; -typedef jit_const jit_const_index; - -typedef jit_const -jit_const_string; -typedef jit_const -jit_const_range; - -#define JIT_VISIT_IR_CONST \ - JIT_METH(const_scalar); \ - JIT_METH(const_index); \ - JIT_METH(const_string); \ - JIT_METH(const_range) +std::ostream& operator<< (std::ostream& os, const jit_value& value); class jit_instruction; class jit_block; @@ -705,6 +666,8 @@ size_t mindex; }; +class jit_variable; + class jit_instruction : public jit_value { @@ -764,7 +727,6 @@ jit_type *argument_type (size_t i) const { - assert (argument (i)); return argument (i)->type (); } @@ -808,19 +770,11 @@ virtual bool infer (void) { return false; } - virtual std::ostream& short_print (std::ostream& os) - { - if (mtag.empty ()) - jit_print (os, type ()) << ": #" << id; - else - jit_print (os, type ()) << ": " << mtag << "." << id; + void push_variable (void); - return os; - } + void pop_variable (void); - const std::string& tag (void) const { return mtag; } - - void stash_tag (const std::string& atag) { mtag = atag; } + virtual std::ostream& short_print (std::ostream& os) const; jit_block *parent (void) const { return mparent; } @@ -831,6 +785,10 @@ assert (! mparent); mparent = aparent; } + + jit_variable *tag (void) const; + + void stash_tag (jit_variable *atag); protected: std::vector already_infered; private: @@ -845,13 +803,65 @@ std::vector arguments; - std::string mtag; + jit_use mtag; + size_t id; jit_block *mparent; }; +// defnie accept methods for subclasses +#define JIT_VALUE_ACCEPT(clname) \ + virtual void accept (jit_ir_walker& walker); + +template +class +jit_const : public jit_instruction +{ +public: + typedef PASS_T pass_t; + + jit_const (PASS_T avalue) : mvalue (avalue) + { + stash_type (EXTRACT_T ()); + } + + PASS_T value (void) const { return mvalue; } + + virtual std::ostream& print (std::ostream& os, size_t indent) const + { + print_indent (os, indent); + short_print (os) << " = "; + if (QUOTE) + os << "\""; + os << mvalue; + if (QUOTE) + os << "\""; + return os; + } + + JIT_VALUE_ACCEPT (jit_const); +private: + T mvalue; +}; + +typedef jit_const jit_const_scalar; +typedef jit_const jit_const_index; + +typedef jit_const +jit_const_string; +typedef jit_const +jit_const_range; + +#define JIT_VISIT_IR_CONST \ + JIT_METH(const_scalar); \ + JIT_METH(const_index); \ + JIT_METH(const_string); \ + JIT_METH(const_range) + class jit_terminator; class jit_phi; +class jit_convert; class jit_block : public jit_value @@ -861,7 +871,11 @@ typedef instruction_list::iterator iterator; typedef instruction_list::const_iterator const_iterator; - jit_block (const std::string& aname) : mname (aname) + typedef std::set df_set; + typedef df_set::const_iterator df_iterator; + + jit_block (const std::string& aname) : mvisit_count (0), mid (NO_ID), idom (0), + mname (aname) {} const std::string& name (void) const { return mname; } @@ -870,6 +884,15 @@ jit_instruction *append (jit_instruction *instr); + jit_instruction *insert_before (iterator loc, jit_instruction *instr); + + jit_instruction *insert_after (iterator loc, jit_instruction *instr); + + void remove (jit_block::iterator iter) + { + instructions.erase (iter); + } + jit_terminator *terminator (void) const; jit_block *pred (size_t idx) const; @@ -879,9 +902,7 @@ return pred (idx)->terminator (); } - llvm::Value *pred_terminator_llvm (size_t idx) const; - - std::ostream& print_pred (std::ostream& os, size_t idx) + std::ostream& print_pred (std::ostream& os, size_t idx) const { return pred (idx)->short_print (os); } @@ -907,6 +928,8 @@ size_t pred_count (void) const { return use_count (); } + jit_block *succ (size_t i) const; + size_t succ_count (void) const; iterator begin (void) { return instructions.begin (); } @@ -915,15 +938,75 @@ iterator end (void) { return instructions.end (); } - const_iterator end (void) const { return instructions.begin (); } + const_iterator end (void) const { return instructions.end (); } + + iterator phi_begin (void); + + iterator phi_end (void); + + iterator nonphi_begin (void); + + // must label before id is valid + size_t id (void) const { return mid; } + + // dominance frontier + const df_set& df (void) const { return mdf; } + + df_iterator df_begin (void) const { return mdf.begin (); } + + df_iterator df_end (void) const { return mdf.end (); } + + // label with a RPO walk + void label (void) + { + size_t number = 0; + label (mvisit_count, number); + } + + void label (size_t visit_count, size_t& number) + { + if (mvisit_count > visit_count) + return; + ++mvisit_count; + + for (size_t i = 0; i < pred_count (); ++i) + pred (i)->label (visit_count, number); - // search for the phi function with the given tag_name, if no function - // exists then null is returned - jit_phi *search_phi (const std::string& tag_name); + mid = number; + ++number; + } + + // See for idom computation algorithm + // Cooper, Keith D.; Harvey, Timothy J; and Kennedy, Ken (2001). + // "A Simple, Fast Dominance Algorithm" + void compute_idom (jit_block *final) + { + bool changed; + idom = this; + do + changed = final->update_idom (mvisit_count); + while (changed); + } - virtual std::ostream& print (std::ostream& os, size_t indent) + // compute dominance frontier + void compute_df (void) + { + compute_df (mvisit_count); + } + + void create_dom_tree (void) { - print_indent (os, indent) << mname << ":\tpred = "; + create_dom_tree (mvisit_count); + } + + void construct_ssa (jit_convert& convert) + { + do_construct_ssa (convert, mvisit_count); + } + + virtual std::ostream& print (std::ostream& os, size_t indent) const + { + print_indent (os, indent) << mname << ": %pred = "; for (size_t i = 0; i < pred_count (); ++i) { print_pred (os, i); @@ -932,7 +1015,7 @@ } os << std::endl; - for (iterator iter = begin (); iter != end (); ++iter) + for (const_iterator iter = begin (); iter != end (); ++iter) { jit_instruction *instr = *iter; instr->print (os, indent + 1) << std::endl; @@ -940,7 +1023,10 @@ return os; } - virtual std::ostream& short_print (std::ostream& os) + // print dominator infomration + std::ostream& print_dom (std::ostream& os) const; + + virtual std::ostream& short_print (std::ostream& os) const { return os << mname; } @@ -949,18 +1035,93 @@ JIT_VALUE_ACCEPT (block) private: + void compute_df (size_t visit_count); + + bool update_idom (size_t visit_count); + + void finish_phi (jit_block *pred); + + void do_construct_ssa (jit_convert& convert, size_t visit_count); + + void create_dom_tree (size_t visit_count); + + jit_block *idom_intersect (jit_block *b); + + static const size_t NO_ID = static_cast (-1); + size_t mvisit_count; + size_t mid; + jit_block *idom; + df_set mdf; + std::vector dom_succ; std::string mname; instruction_list instructions; mutable std::vector mpred_llvm; }; + + +// A non-ssa variable +class +jit_variable : public jit_value +{ +public: + jit_variable (const std::string& aname) : mname (aname) {} + + const std::string &name (void) const { return mname; } + + // manipulate the value_stack, for use during SSA construction. The top of the + // value stack represents the current value for this variable + bool has_top (void) const + { + return ! value_stack.empty (); + } + + jit_value *top (void) const + { + return value_stack.top (); + } + + void push (jit_value *v) + { + value_stack.push (v); + } + + void pop (void) + { + value_stack.pop (); + } + + // blocks in which we are used + void use_blocks (jit_block::df_set& result) + { + jit_use *use = first_use (); + while (use) + { + result.insert (use->user_parent ()); + use = use->next (); + } + } + + virtual std::ostream& print (std::ostream& os, size_t indent) const + { + return print_indent (os, indent) << mname; + } + + JIT_VALUE_ACCEPT (variable) +private: + std::string mname; + std::stack value_stack; +}; + class jit_phi : public jit_instruction { public: - jit_phi (size_t npred, jit_value *adefault = 0) - : jit_instruction (npred, adefault) - {} + jit_phi (jit_variable *avariable, size_t npred) + : jit_instruction (npred) + { + stash_tag (avariable); + } virtual bool infer (void) { @@ -977,13 +1138,13 @@ return false; } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { std::stringstream ss; print_indent (ss, indent); short_print (ss) << " phi "; std::string ss_str = ss.str (); - std::string indent_str (ss_str.size () + 7, ' '); + std::string indent_str (ss_str.size (), ' '); os << ss_str; jit_block *pblock = parent (); @@ -1028,7 +1189,7 @@ return pllvm == spred_llvm ? succ_llvm : spred_llvm; } - std::ostream& print_sucessor (std::ostream& os, size_t idx = 0) + std::ostream& print_sucessor (std::ostream& os, size_t idx = 0) const { return sucessor (idx)->short_print (os); } @@ -1050,7 +1211,7 @@ size_t sucessor_count (void) const { return 1; } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { print_indent (os, indent) << "break: "; return print_sucessor (os); @@ -1068,7 +1229,7 @@ jit_value *cond (void) const { return argument (0); } - std::ostream& print_cond (std::ostream& os) + std::ostream& print_cond (std::ostream& os) const { return cond ()->short_print (os); } @@ -1086,7 +1247,7 @@ size_t sucessor_count (void) const { return 2; } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { print_indent (os, indent) << "cond_break: "; print_cond (os) << ", "; @@ -1122,11 +1283,11 @@ return mfunction.get_overload (argument_types ()); } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { print_indent (os, indent); - if (use_count ()) + if (use_count () || tag ()) short_print (os) << " = "; os << "call " << mfunction.name () << " ("; @@ -1150,11 +1311,16 @@ jit_extract_argument : public jit_instruction { public: - jit_extract_argument (jit_type *atype, const std::string& aname) + jit_extract_argument (jit_type *atype, jit_variable *var) : jit_instruction () { stash_type (atype); - stash_tag (aname); + stash_tag (var); + } + + const std::string& name (void) const + { + return tag ()->name (); } const jit_function::overload& overload (void) const @@ -1162,10 +1328,12 @@ return jit_typeinfo::cast (type (), jit_typeinfo::get_any ()); } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { print_indent (os, indent); - return short_print (os) << " = extract: " << tag (); + os << "exract "; + short_print (os); + return os; } JIT_VALUE_ACCEPT (extract_argument) @@ -1175,10 +1343,15 @@ jit_store_argument : public jit_instruction { public: - jit_store_argument (const std::string& aname, jit_value *aresult) - : jit_instruction (aresult) + jit_store_argument (jit_variable *var) + : jit_instruction (var) { - stash_tag (aname); + stash_tag (var); + } + + const std::string& name (void) const + { + return tag ()->name (); } const jit_function::overload& overload (void) const @@ -1201,10 +1374,11 @@ return result ()->to_llvm (); } - virtual std::ostream& print (std::ostream& os, size_t indent) + virtual std::ostream& print (std::ostream& os, size_t indent) const { jit_value *res = result (); - print_indent (os, indent) << tag () << " <- "; + print_indent (os, indent) << "store "; + short_print (os) << " = "; return res->short_print (os); } @@ -1338,158 +1512,6 @@ void visit_while_command (tree_while_command&); void visit_do_until_command (tree_do_until_command&); -private: - std::vector > arguments; - type_bound_vector bounds; - - class - variable_map - { - // internal variable map - typedef std::map ivar_map; - public: - typedef ivar_map::iterator iterator; - typedef ivar_map::const_iterator const_iterator; - - variable_map (variable_map *aparent, jit_block *ablock) : mparent (aparent), - mblock (ablock) - {} - - virtual ~variable_map () {} - - variable_map *parent (void) const { return mparent; } - - jit_block *block (void) const { return mblock; } - - jit_value *get (const std::string& name) - { - ivar_map::iterator iter = vars.find (name); - if (iter != vars.end ()) - return iter->second; - - if (mparent) - { - jit_value *pval = mparent->get (name); - return insert (name, pval); - } - - return insert (name, 0); - } - - jit_value *set (const std::string& name, jit_value *val) - { - get (name); // force insertion - return vars[name] = val; - } - - iterator begin (void) { return vars.begin (); } - const_iterator begin (void) const { return vars.begin (); } - - iterator end (void) { return vars.end (); } - const_iterator end (void) const { return vars.end (); } - - size_t size (void) const { return vars.size (); } - protected: - virtual jit_value *insert (const std::string& name, jit_value *pval) = 0; - - ivar_map vars; - private: - variable_map *mparent; - jit_block *mblock; - }; - - class - toplevel_map : public variable_map - { - public: - toplevel_map (jit_convert& aconvert, jit_block *aentry) - : variable_map (0, aentry), convert (aconvert) {} - protected: - virtual jit_value *insert (const std::string& name, jit_value *pval); - private: - jit_convert& convert; - }; - - class - for_map : public variable_map - { - public: - typedef variable_map::iterator iterator; - typedef variable_map::const_iterator const_iterator; - - for_map (variable_map *aparent, jit_block *ablock) - : variable_map (aparent, ablock) - { - // force insertion of all phi nodes - for (iterator iter = aparent->begin (); iter != aparent->end (); ++iter) - get (iter->first); - } - - void finish_phi (variable_map& from) - { - jit_block *for_body = block (); - for (jit_block::iterator iter = for_body->begin (); - iter != for_body->end () && dynamic_cast (*iter); ++iter) - { - jit_instruction *node = *iter; - if (! node->argument (1)) - node->stash_argument (1, from.get (node->tag ())); - } - } - protected: - virtual jit_value *insert (const std::string& name, jit_value *pval) - { - jit_phi *ret = new jit_phi (2); - ret->stash_tag (name); - block ()->prepend (ret); - ret->stash_argument (0, pval); - return vars[name] = ret; - } - }; - - class - compound_map : public variable_map - { - public: - compound_map (variable_map *aparent) : variable_map (aparent, 0) - {} - protected: - virtual jit_value *insert (const std::string&, jit_value *pval) - { - return pval; - } - }; - - - variable_map *variables; - - // used instead of return values from visit_* functions - jit_value *result; - - jit_block *block; - jit_block *final_block; - - llvm::Function *function; - - std::list blocks; - - std::list worklist; - - std::list constants; - - std::list all_values; - - void do_assign (const std::string& lhs, jit_value *rhs, bool print); - - jit_value *visit (tree *tee) { return visit (*tee); } - - jit_value *visit (tree& tee); - - void append_users (jit_value *v) - { - for (jit_use *use = v->first_use (); use; use = use->next ()) - worklist.push_back (use->user ()); - } // this would be easier with variadic templates template @@ -1523,19 +1545,87 @@ track_value (ret); return ret; } +private: + std::vector > arguments; + type_bound_vector bounds; + + // used instead of return values from visit_* functions + jit_instruction *result; + + jit_block *entry_block; + + jit_block *block; + + llvm::Function *function; + + std::list blocks; + + std::list worklist; + + std::list constants; + + std::list all_values; + + size_t iterator_count; + + typedef std::map vmap_t; + vmap_t vmap; + + jit_variable *get_variable (const std::string& vname); + + jit_instruction *do_assign (const std::string& lhs, jit_instruction *rhs, + bool print); + + jit_instruction *visit (tree *tee) { return visit (*tee); } + + jit_instruction *visit (tree& tee); + + void append_users (jit_value *v) + { + for (jit_use *use = v->first_use (); use; use = use->next ()) + worklist.push_back (use->user ()); + } void track_value (jit_value *value) { - if (value->type () && ! dynamic_cast(value)) + if (value->type ()) constants.push_back (value); all_values.push_back (value); } - // place phi nodes in the current block to merge ref with variables - // we assume the same number of deffinitions - void merge (jit_block *merge_block, variable_map& merge_vars, - jit_block *incomming_block, - const variable_map& incomming_vars); + void construct_ssa (jit_block *final_block); + + void print_blocks (const std::string& header) + { + std::cout << "-------------------- " << header << " --------------------\n"; + for (std::list::iterator iter = blocks.begin (); + iter != blocks.end (); ++iter) + { + assert (*iter); + (*iter)->print (std::cout, 0); + } + std::cout << std::endl; + } + + void print_dom (void) + { + std::cout << "-------------------- dom info --------------------\n"; + for (std::list::iterator iter = blocks.begin (); + iter != blocks.end (); ++iter) + { + assert (*iter); + (*iter)->print_dom (std::cout); + } + std::cout << std::endl; + } + + typedef std::list break_list; + + bool breaking; // true if we are breaking OR continuing + break_list breaks; + break_list continues; + + void finish_breaks (jit_block *dest, const break_list& lst); // this case is much simpler, just convert from the jit ir to llvm class @@ -1544,8 +1634,7 @@ public: llvm::Function *convert (llvm::Module *module, const std::vector >& args, - const std::list& blocks, - const std::list& constants); + const std::list& blocks); #define JIT_METH(clname) \ virtual void visit (jit_ ## clname&);