void AspEventHandler::dfs(DFSStackElement start_elt) { DFSStack stack; push(stack, start_elt); while(stack.size() > 0) { DFSStackElement current_elt = peek(stack); Transaction* current_tx = current_elt.tx_; if(current_elt.begin_ == current_elt.end_) { // no out-going edges; done with current transaction; pop it current_tx->set_dfs_state(BLACK); pop(stack); // add tx to the topologically sorted txlist txlist_sorted_.push_front(current_tx); if(stack.size() > 0) { // iterate the new top to next edge current_elt = peek(stack); current_elt.begin_++; pop_push(stack, current_elt); } continue; } Edge e = *(current_elt.begin_); assert(current_tx == e.first->tx()); Transaction* next_tx = e.second->tx(); assert(current_tx != next_tx); dfs_state_t next_state = next_tx->dfs_state(); if(next_state == WHITE) { next_tx->set_dfs_state(GRAY); EdgeList* edges = next_tx->conflict_edges(); push(stack, DFSStackElement(next_tx, edges->begin(), edges->end())); } else { assert(next_state == BLACK || next_state == GRAY); // check cycle if(next_state == GRAY) { // Cycle; extract it from the current dfs stack and save it Cycle* cycle = extract_cycle(stack, next_tx); // extract_cycle may skip reporting a cycle, so check if cycle is non-null if(cycle != NULL) { cycles_.push_back(cycle); } } // iterate top to next edge current_elt.begin_++; pop_push(stack, current_elt); } } }
bool event_grapht::graph_explorert::backtrack( std::set<critical_cyclet> &set_of_cycles, unsigned source, unsigned vertex, bool unsafe_met, /* unsafe pair for the model met in the visited path */ unsigned po_trans, /* po-transition skips still allowed */ bool same_var_pair, /* in a thread, tells if we already met one rfi wsi fri */ bool lwfence_met, /* if we try to skip a lwsync (only valid for lwsyncWR) */ bool has_to_be_unsafe, irep_idt var_to_avoid, memory_modelt model) { #ifdef DEBUG for(unsigned i=0; i<80; egraph.message.debug() << "-", ++i); egraph.message.debug() << messaget::eom; egraph.message.debug() << "marked size:" << marked_stack.size() << messaget::eom; std::stack<unsigned> tmp; while(!point_stack.empty()) { egraph.message.debug() << point_stack.top() << " | "; tmp.push(point_stack.top()); point_stack.pop(); } egraph.message.debug() << messaget::eom; while(!tmp.empty()) { point_stack.push(tmp.top()); tmp.pop(); } while(!marked_stack.empty()) { egraph.message.debug() << marked_stack.top() << " | "; tmp.push(marked_stack.top()); marked_stack.pop(); } egraph.message.debug() << messaget::eom; while(!tmp.empty()) { marked_stack.push(tmp.top()); tmp.pop(); } #endif // TO DISCUSS: shouldn't we still allow po-transition through it instead? if(filtering(vertex)) return false; egraph.message.debug() << "bcktck "<<egraph[vertex].id<<"#"<<vertex<<", " <<egraph[source].id<<"#"<<source<<" lw:"<<lwfence_met<<" unsafe:" <<unsafe_met << messaget::eom; bool f=false; bool get_com_only=false; bool unsafe_met_updated=unsafe_met; bool same_var_pair_updated=same_var_pair; bool not_thin_air=true; const abstract_eventt& this_vertex=egraph[vertex]; /* if a thread starts with variable x, the last event of this thread in the cycle cannot be with x */ irep_idt avoid_at_the_end=var_to_avoid; bool no_comm=false; if(avoid_at_the_end!="" && avoid_at_the_end==this_vertex.variable) no_comm=true; /* if specified, maximum number of events reached */ if(max_var!=0 && point_stack.size()>max_var*3) return false; /* we only explore shared variables */ if(!this_vertex.local) { /* only the lwsyncWR can be interpreted as poWR (i.e., skip of the fence) */ if(lwfence_met && this_vertex.operation!=abstract_eventt::Read) return false; //{no_comm=true;get_com_only=false;}//return false; bool has_to_be_unsafe_updated=false; // TODO: propagate this constraint within the optimisation // -- no optimisation can strongly affect performances /* tab[] can appear several times */ if(egraph.ignore_arrays || id2string(this_vertex.variable).find("[]")==std::string::npos) { /* no more than 4 events per thread */ if(this_vertex.operation!=abstract_eventt::Fence && this_vertex.operation!=abstract_eventt::Lwfence && this_vertex.operation!=abstract_eventt::ASMfence) { if(events_per_thread[this_vertex.thread]==4) return false; else events_per_thread[this_vertex.thread]++; } /* Multiple re-orderings constraint: if the thread on this cycles contains more than one, ensure that an unsafe pair is not protected by another relation in the thread. E.g., in Wx Rx Wy, under TSO, the rfi cannot be delayed, since the only way to make this transformation matter is to re-order also the two writes, which is not permitted on TSO. */ if(has_to_be_unsafe && point_stack.size() >= 2) { const unsigned previous = point_stack.top(); point_stack.pop(); const unsigned preprevious = point_stack.top(); point_stack.push(previous); if(!egraph[preprevious].unsafe_pair(this_vertex,model) && !(this_vertex.operation==abstract_eventt::Fence || egraph[preprevious].operation==abstract_eventt::Fence || this_vertex.operation==abstract_eventt::Lwfence || egraph[preprevious].operation==abstract_eventt::Lwfence || this_vertex.operation==abstract_eventt::ASMfence || egraph[preprevious].operation==abstract_eventt::ASMfence)) return false; } } has_to_be_unsafe_updated = has_to_be_unsafe; /* constraint 1.a: there is at most one pair of events per thread with different variables. Given that we cannot have more than three events on the same variable, with two in the same thread, this means that we can have at most 2 consecutive events by po with the same variable, and two variables per thread (fences are not taken into account) */ if(!point_stack.empty() && egraph.are_po_ordered(point_stack.top(),vertex) && this_vertex.operation!=abstract_eventt::Fence && this_vertex.operation!=abstract_eventt::Lwfence && this_vertex.operation!=abstract_eventt::ASMfence && this_vertex.variable==egraph[point_stack.top()].variable) { if(same_var_pair || (this_vertex.operation==abstract_eventt::Read && egraph[point_stack.top()].operation==abstract_eventt::Read)) { events_per_thread[this_vertex.thread]--; return false; //{no_comm=true;get_com_only=false;} //return false; } else { same_var_pair_updated = true; if(events_per_thread[this_vertex.thread]>=3) get_com_only = true; } } /* constraint 1.b */ if(!point_stack.empty() && egraph.are_po_ordered(point_stack.top(),vertex) && this_vertex.operation!=abstract_eventt::Fence && this_vertex.operation!=abstract_eventt::Lwfence && this_vertex.operation!=abstract_eventt::ASMfence && this_vertex.variable!=egraph[point_stack.top()].variable) { same_var_pair_updated = false; } /* constraint 2: per variable, either W W, R W, W R, or R W R */ if(this_vertex.operation!=abstract_eventt::Fence && this_vertex.operation!=abstract_eventt::Lwfence && this_vertex.operation!=abstract_eventt::ASMfence) { const unsigned char nb_writes = writes_per_variable[this_vertex.variable]; const unsigned char nb_reads = reads_per_variable[this_vertex.variable]; if(nb_writes+nb_reads==3) { events_per_thread[this_vertex.thread]--; return false; //{no_comm=true;get_com_only=false;} //return false; } else if(this_vertex.operation==abstract_eventt::Write) { if(nb_writes==2) { events_per_thread[this_vertex.thread]--; return false; //{no_comm=true;get_com_only=false;} //return false; } else writes_per_variable[this_vertex.variable]++; } else if(this_vertex.operation==abstract_eventt::Read) { if(nb_reads==2) { events_per_thread[this_vertex.thread]--; return false; //{no_comm=true;get_com_only=false;} //return false; } else reads_per_variable[this_vertex.variable]++; } } if(!point_stack.empty()) { const abstract_eventt& prev_vertex = egraph[point_stack.top()]; unsafe_met_updated |= (prev_vertex.unsafe_pair(this_vertex,model) && !(prev_vertex.thread==this_vertex.thread && egraph.map_data_dp[this_vertex.thread].dp(prev_vertex,this_vertex))); if (unsafe_met_updated && !unsafe_met && egraph.are_po_ordered(point_stack.top(), vertex)) has_to_be_unsafe_updated=true; } point_stack.push(vertex); mark[vertex]=true; marked_stack.push(vertex); if(!get_com_only) { /* we first visit via po transition, if existing */ for(graph<abstract_eventt>::edgest::const_iterator w_it=egraph.po_out(vertex).begin(); w_it!=egraph.po_out(vertex).end(); w_it++) { const unsigned w = w_it->first; if(w == source && point_stack.size()>=4 && (unsafe_met_updated || this_vertex.unsafe_pair(egraph[source],model)) ) { critical_cyclet new_cycle = extract_cycle(vertex, source, cycle_nb++); not_thin_air = !egraph.filter_thin_air || new_cycle.is_not_thin_air(); if(!not_thin_air) { for(critical_cyclet::const_iterator e_it=new_cycle.begin(); e_it!=new_cycle.end(); ++e_it) thin_air_events.insert(*e_it); } if((!egraph.filter_uniproc || new_cycle.is_not_uniproc(model)) && not_thin_air && new_cycle.is_cycle() && new_cycle.is_unsafe(model) /*&& new_cycle.is_unsafe_asm(model)*/) { egraph.message.debug() << new_cycle.print_name(model,false) << messaget::eom; set_of_cycles.insert(new_cycle); #if 0 const critical_cyclet* reduced=new_cycle.hide_internals(); set_of_cycles.insert(*reduced); delete(reduced); #endif } f = true; } else if(!mark[w]) f |= backtrack(set_of_cycles, source, w, unsafe_met_updated, po_trans, same_var_pair_updated, false, has_to_be_unsafe_updated, avoid_at_the_end, model); } } if(!no_comm) /* we then visit via com transitions, if existing */ for(graph<abstract_eventt>::edgest::const_iterator w_it=egraph.com_out(vertex).begin(); w_it!=egraph.com_out(vertex).end(); w_it++) { const unsigned w = w_it->first; if(w < source) egraph.remove_com_edge(vertex,w); else if(w == source && point_stack.size()>=4 && (unsafe_met_updated || this_vertex.unsafe_pair(egraph[source],model)) ) { critical_cyclet new_cycle = extract_cycle(vertex, source, cycle_nb++); not_thin_air = !egraph.filter_thin_air || new_cycle.is_not_thin_air(); if(!not_thin_air) { for(critical_cyclet::const_iterator e_it=new_cycle.begin(); e_it!=new_cycle.end(); ++e_it) thin_air_events.insert(*e_it); } if((!egraph.filter_uniproc || new_cycle.is_not_uniproc(model)) && not_thin_air && new_cycle.is_cycle() && new_cycle.is_unsafe(model) /*&& new_cycle.is_unsafe_asm(model)*/) { egraph.message.debug() << new_cycle.print_name(model,false) << messaget::eom; set_of_cycles.insert(new_cycle); #if 0 const critical_cyclet* reduced=new_cycle.hide_internals(); set_of_cycles.insert(*reduced); delete(reduced); #endif } f = true; } else if(!mark[w]) f |= backtrack(set_of_cycles, source, w, unsafe_met_updated, po_trans, false, false, false, "", model); } if(f) { while(!marked_stack.empty() && marked_stack.top()!=vertex) { unsigned up = marked_stack.top(); marked_stack.pop(); mark[up] = false; } if(!marked_stack.empty()) marked_stack.pop(); mark[vertex] = false; } assert(!point_stack.empty()); point_stack.pop(); /* removes variable access */ if(this_vertex.operation!=abstract_eventt::Fence && this_vertex.operation!=abstract_eventt::Lwfence && this_vertex.operation!=abstract_eventt::ASMfence) { if(this_vertex.operation==abstract_eventt::Write) writes_per_variable[this_vertex.variable]--; else reads_per_variable[this_vertex.variable]--; events_per_thread[this_vertex.thread]--; } } /* transitivity of po: do the same, but skip this event (except if it is a fence or no more po-transition skips allowed); if the cycle explored so far has a thin-air subcycle, this cycle is not valid: stop this exploration here */ if( skip_tracked.find(vertex)==skip_tracked.end() ) // 25 oct if( not_thin_air && !get_com_only && (po_trans > 1 || po_trans==0) && !point_stack.empty() && egraph.are_po_ordered(point_stack.top(),vertex) && this_vertex.operation!=abstract_eventt::Fence && ( this_vertex.operation!=abstract_eventt::Lwfence || egraph[point_stack.top()].operation==abstract_eventt::Write) && ( this_vertex.operation!=abstract_eventt::ASMfence || !this_vertex.WRfence || egraph[point_stack.top()].operation==abstract_eventt::Write) ) { skip_tracked.insert(vertex); std::stack<unsigned> tmp; while(marked_stack.size()>0 && marked_stack.top()!=vertex) { tmp.push(marked_stack.top()); mark[marked_stack.top()]=false; marked_stack.pop(); } if(marked_stack.size()>0) { assert(marked_stack.top()==vertex); mark[vertex]=true; } else { while(tmp.size()>0) { marked_stack.push(tmp.top()); mark[tmp.top()]=true; tmp.pop(); } mark[vertex]=true; marked_stack.push(vertex); } if(!egraph[point_stack.top()].unsafe_pair(this_vertex, model)) { /* tab[] should never be avoided */ if(egraph.ignore_arrays || id2string(this_vertex.variable).find("[]")==std::string::npos) avoid_at_the_end = this_vertex.variable; } /* skip lwfence by po-transition only if we consider a WR */ // TO CHECK const bool is_lwfence = (this_vertex.operation==abstract_eventt::Lwfence && egraph[point_stack.top()].operation==abstract_eventt::Write) || (this_vertex.operation==abstract_eventt::ASMfence && (!this_vertex.WRfence && egraph[point_stack.top()].operation==abstract_eventt::Write)); for(graph<abstract_eventt>::edgest::const_iterator w_it= egraph.po_out(vertex).begin(); w_it!=egraph.po_out(vertex).end(); w_it++) { const unsigned w = w_it->first; f |= backtrack(set_of_cycles, source, w, unsafe_met/*_updated*/, (po_trans==0?0:po_trans-1), same_var_pair/*_updated*/, is_lwfence, has_to_be_unsafe, avoid_at_the_end, model); } if(f) { while(!marked_stack.empty() && marked_stack.top()!=vertex) { unsigned up = marked_stack.top(); marked_stack.pop(); mark[up] = false; } if(!marked_stack.empty()) marked_stack.pop(); mark[vertex] = false; } skip_tracked.erase(vertex); } return f; }