X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11tester.git;a=blobdiff_plain;f=action.cc;h=14160052d24cac65f75161f5a152c6b1f3f4200d;hp=13da2b96312f0ee9c524b04fb13f0fcc997697b5;hb=0229244d21ca78bf781e41684810e9fb6d5ca56c;hpb=7f0963612d33cd3c1e1330bb42c9962259a03da3 diff --git a/action.cc b/action.cc index 13da2b96..14160052 100644 --- a/action.cc +++ b/action.cc @@ -1,7 +1,7 @@ #include #define __STDC_FORMAT_MACROS #include -#include +#include #include "model.h" #include "action.h" @@ -9,9 +9,16 @@ #include "common.h" #include "threads-model.h" #include "nodestack.h" +#include "wildcard.h" #define ACTION_INITIAL_CLOCK 0 +/** @brief A special value to represent a successful trylock */ +#define VALUE_TRYSUCCESS 1 + +/** @brief A special value to represent a failed trylock */ +#define VALUE_TRYFAILED 0 + /** * @brief Construct a new ModelAction * @@ -28,9 +35,11 @@ ModelAction::ModelAction(action_type_t type, memory_order order, void *loc, uint64_t value, Thread *thread) : type(type), order(order), + original_order(order), location(loc), value(value), reads_from(NULL), + reads_from_promise(NULL), last_fence_release(NULL), node(NULL), seq_number(ACTION_INITIAL_CLOCK), @@ -77,6 +86,11 @@ bool ModelAction::is_thread_start() const return type == THREAD_START; } +bool ModelAction::is_thread_join() const +{ + return type == THREAD_JOIN || type == PTHREAD_JOIN; +} + bool ModelAction::is_relseq_fixup() const { return type == MODEL_FIXUP_RELSEQ; @@ -150,6 +164,11 @@ bool ModelAction::could_be_write() const return is_write() || is_rmwr(); } +bool ModelAction::is_yield() const +{ + return type == THREAD_YIELD; +} + bool ModelAction::is_rmwr() const { return type == ATOMIC_RMWR; @@ -175,6 +194,11 @@ bool ModelAction::is_initialization() const return type == ATOMIC_INIT; } +bool ModelAction::is_annotation() const +{ + return type == ATOMIC_ANNOTATION; +} + bool ModelAction::is_relaxed() const { return order == std::memory_order_relaxed; @@ -252,21 +276,33 @@ void ModelAction::copy_typeandorder(ModelAction * act) */ Thread * ModelAction::get_thread_operand() const { - if (type == THREAD_CREATE) - /* THREAD_CREATE uses (Thread *) for location */ - return (Thread *)get_location(); - else if (type == THREAD_JOIN) + if (type == THREAD_CREATE) { + /* thread_operand is set in execution.cc */ + return thread_operand; + } else if (type == PTHREAD_CREATE) { + return thread_operand; + } else if (type == THREAD_JOIN) { /* THREAD_JOIN uses (Thread *) for location */ return (Thread *)get_location(); - else + } else if (type == PTHREAD_JOIN) { + // return NULL; + // thread_operand is stored in execution::pthread_map; + return (Thread *)get_location(); + } else return NULL; } -/** This method changes an existing read part of an RMW action into either: - * (1) a full RMW action in case of the completed write or - * (2) a READ action in case a failed action. +/** + * @brief Convert the read portion of an RMW + * + * Changes an existing read part of an RMW action into either: + * -# a full RMW action in case of the completed write or + * -# a READ action in case a failed action. + * * @todo If the memory_order changes, we may potentially need to update our * clock vector. + * + * @param act The second half of the RMW (either RMWC or RMW) */ void ModelAction::process_rmw(ModelAction *act) { @@ -279,14 +315,18 @@ void ModelAction::process_rmw(ModelAction *act) } } -/** The is_synchronizing method should only explore interleavings if: - * (1) the operations are seq_cst and don't commute or - * (2) the reordering may establish or break a synchronization relation. - * Other memory operations will be dealt with by using the reads_from - * relation. +/** + * @brief Check if this action should be backtracked with another, due to + * potential synchronization + * + * The is_synchronizing method should only explore interleavings if: + * -# the operations are seq_cst and don't commute or + * -# the reordering may establish or break a synchronization relation. * - * @param act is the action to consider exploring a reordering. - * @return tells whether we have to explore a reordering. + * Other memory operations will be dealt with by using the reads_from relation. + * + * @param act The action to consider exploring a reordering + * @return True, if we have to explore a reordering; otherwise false */ bool ModelAction::could_synchronize_with(const ModelAction *act) const { @@ -295,18 +335,16 @@ bool ModelAction::could_synchronize_with(const ModelAction *act) const return false; // Different locations commute - if (!same_var(act)) + if (!same_var(act) && !is_fence() && !act->is_fence()) return false; // Explore interleavings of seqcst writes/fences to guarantee total // order of seq_cst operations that don't commute - if ((could_be_write() || act->could_be_write() || is_fence() || act->is_fence()) - && is_seqcst() && act->is_seqcst()) + if ((could_be_write() || act->could_be_write() || is_fence() || act->is_fence()) && is_seqcst() && act->is_seqcst()) return true; - // Explore synchronizing read/write/fence pairs - if (is_acquire() && act->is_release() && (is_read() || is_fence()) && - (act->could_be_write() || act->is_fence())) + // Explore synchronizing read/write pairs + if (is_acquire() && act->is_release() && is_read() && act->could_be_write()) return true; // lock just released...we can grab lock @@ -371,11 +409,67 @@ void ModelAction::create_cv(const ModelAction *parent) cv = new ClockVector(NULL, this); } -void ModelAction::set_try_lock(bool obtainedlock) { - if (obtainedlock) - value = VALUE_TRYSUCCESS; +void ModelAction::set_try_lock(bool obtainedlock) +{ + value = obtainedlock ? VALUE_TRYSUCCESS : VALUE_TRYFAILED; +} + +/** + * @brief Get the value read by this load + * + * We differentiate this function from ModelAction::get_write_value and + * ModelAction::get_value for the purpose of RMW's, which may have both a + * 'read' and a 'write' value. + * + * Note: 'this' must be a load. + * + * @return The value read by this load + */ +uint64_t ModelAction::get_reads_from_value() const +{ + ASSERT(is_read()); + if (reads_from) + return reads_from->get_write_value(); + else if (reads_from_promise) + return reads_from_promise->get_value(); + return VALUE_NONE; /* Only for new actions with no reads-from */ +} + +/** + * @brief Get the value written by this store + * + * We differentiate this function from ModelAction::get_reads_from_value and + * ModelAction::get_value for the purpose of RMW's, which may have both a + * 'read' and a 'write' value. + * + * Note: 'this' must be a store. + * + * @return The value written by this store + */ +uint64_t ModelAction::get_write_value() const +{ + ASSERT(is_write()); + return value; +} + +/** + * @brief Get the value returned by this action + * + * For atomic reads (including RMW), an operation returns the value it read. + * For atomic writes, an operation returns the value it wrote. For other + * operations, the return value varies (sometimes is a "don't care"), but the + * value is simply stored in the "value" field. + * + * @return This action's return value + */ +uint64_t ModelAction::get_return_value() const +{ + if (is_read()) + return get_reads_from_value(); + else if (is_write()) + return get_write_value(); else - value = VALUE_TRYFAILED; + return value; } /** @return The Node associated with this ModelAction */ @@ -392,17 +486,31 @@ Node * ModelAction::get_node() const */ void ModelAction::set_read_from(const ModelAction *act) { + ASSERT(act); + reads_from = act; reads_from_promise = NULL; - if (act && act->is_uninitialized()) - model->assert_bug("May read from uninitialized atomic\n"); + + if (act->is_uninitialized()) { // WL + uint64_t val = *((uint64_t *) location); + ModelAction * act_initialized = (ModelAction *)act; + act_initialized->set_value(val); + reads_from = (const ModelAction *)act_initialized; + +// disabled by WL, because LLVM IR is unable to detect atomic init +/* model->assert_bug("May read from uninitialized atomic:\n" + " action %d, thread %d, location %p (%s, %s)", + seq_number, id_to_int(tid), location, + get_type_str(), get_mo_str()); +*/ + } } /** * Set this action's read-from promise * @param promise The promise to read from */ -void ModelAction::set_read_from_promise(const Promise *promise) +void ModelAction::set_read_from_promise(Promise *promise) { ASSERT(is_read()); reads_from_promise = promise; @@ -417,9 +525,8 @@ void ModelAction::set_read_from_promise(const Promise *promise) */ bool ModelAction::synchronize_with(const ModelAction *act) { - if (*this < *act && type != THREAD_JOIN && type != ATOMIC_LOCK) + if (*this < *act) return false; - model->check_promises(act->get_tid(), cv, act->cv); cv->merge(act->cv); return true; } @@ -440,115 +547,74 @@ bool ModelAction::happens_before(const ModelAction *act) const return act->cv->synchronized_since(this); } -/** @brief Print nicely-formatted info about this ModelAction */ -void ModelAction::print() const +const char * ModelAction::get_type_str() const { - const char *type_str, *mo_str; switch (this->type) { - case MODEL_FIXUP_RELSEQ: - type_str = "relseq fixup"; - break; - case THREAD_CREATE: - type_str = "thread create"; - break; - case THREAD_START: - type_str = "thread start"; - break; - case THREAD_YIELD: - type_str = "thread yield"; - break; - case THREAD_JOIN: - type_str = "thread join"; - break; - case THREAD_FINISH: - type_str = "thread finish"; - break; - case ATOMIC_UNINIT: - type_str = "uninitialized"; - break; - case ATOMIC_READ: - type_str = "atomic read"; - break; - case ATOMIC_WRITE: - type_str = "atomic write"; - break; - case ATOMIC_RMW: - type_str = "atomic rmw"; - break; - case ATOMIC_FENCE: - type_str = "fence"; - break; - case ATOMIC_RMWR: - type_str = "atomic rmwr"; - break; - case ATOMIC_RMWC: - type_str = "atomic rmwc"; - break; - case ATOMIC_INIT: - type_str = "init atomic"; - break; - case ATOMIC_LOCK: - type_str = "lock"; - break; - case ATOMIC_UNLOCK: - type_str = "unlock"; - break; - case ATOMIC_TRYLOCK: - type_str = "trylock"; - break; - case ATOMIC_WAIT: - type_str = "wait"; - break; - case ATOMIC_NOTIFY_ONE: - type_str = "notify one"; - break; - case ATOMIC_NOTIFY_ALL: - type_str = "notify all"; - break; - default: - type_str = "unknown type"; - } - - uint64_t valuetoprint; - if (type == ATOMIC_READ && reads_from != NULL) - valuetoprint = reads_from->value; - else - valuetoprint = value; - + case MODEL_FIXUP_RELSEQ: return "relseq fixup"; + case THREAD_CREATE: return "thread create"; + case THREAD_START: return "thread start"; + case THREAD_YIELD: return "thread yield"; + case THREAD_JOIN: return "thread join"; + case THREAD_FINISH: return "thread finish"; + + case PTHREAD_CREATE: return "pthread create"; + case PTHREAD_JOIN: return "pthread join"; + + case ATOMIC_UNINIT: return "uninitialized"; + case ATOMIC_READ: return "atomic read"; + case ATOMIC_WRITE: return "atomic write"; + case ATOMIC_RMW: return "atomic rmw"; + case ATOMIC_FENCE: return "fence"; + case ATOMIC_RMWR: return "atomic rmwr"; + case ATOMIC_RMWC: return "atomic rmwc"; + case ATOMIC_INIT: return "init atomic"; + case ATOMIC_LOCK: return "lock"; + case ATOMIC_UNLOCK: return "unlock"; + case ATOMIC_TRYLOCK: return "trylock"; + case ATOMIC_WAIT: return "wait"; + case ATOMIC_NOTIFY_ONE: return "notify one"; + case ATOMIC_NOTIFY_ALL: return "notify all"; + case ATOMIC_ANNOTATION: return "annotation"; + default: return "unknown type"; + }; +} + +const char * ModelAction::get_mo_str() const +{ switch (this->order) { - case std::memory_order_relaxed: - mo_str = "relaxed"; - break; - case std::memory_order_acquire: - mo_str = "acquire"; - break; - case std::memory_order_release: - mo_str = "release"; - break; - case std::memory_order_acq_rel: - mo_str = "acq_rel"; - break; - case std::memory_order_seq_cst: - mo_str = "seq_cst"; - break; - default: - mo_str = "unknown"; - break; + case std::memory_order_relaxed: return "relaxed"; + case std::memory_order_acquire: return "acquire"; + case std::memory_order_release: return "release"; + case std::memory_order_acq_rel: return "acq_rel"; + case std::memory_order_seq_cst: return "seq_cst"; + default: return "unknown"; } +} + +/** @brief Print nicely-formatted info about this ModelAction */ +void ModelAction::print() const +{ + const char *type_str = get_type_str(), *mo_str = get_mo_str(); - model_print("(%4d) Thread: %-2d Action: %-13s MO: %7s Loc: %14p Value: %-#18" PRIx64, - seq_number, id_to_int(tid), type_str, mo_str, location, valuetoprint); + model_print("%-4d %-2d %-14s %7s %14p %-#18" PRIx64, + seq_number, id_to_int(tid), type_str, mo_str, location, get_return_value()); if (is_read()) { if (reads_from) - model_print(" Rf: %-3d", reads_from->get_seq_number()); - else - model_print(" Rf: ? "); + model_print(" %-3d", reads_from->get_seq_number()); + else if (reads_from_promise) { + int idx = reads_from_promise->get_index(); + if (idx >= 0) + model_print(" P%-2d", idx); + else + model_print(" P? "); + } else + model_print(" ? "); } if (cv) { if (is_read()) model_print(" "); else - model_print(" "); + model_print(" "); cv->print(); } else model_print("\n"); @@ -562,7 +628,52 @@ unsigned int ModelAction::hash() const hash ^= seq_number << 5; hash ^= id_to_int(tid) << 6; - if (is_read() && reads_from) - hash ^= reads_from->get_seq_number(); + if (is_read()) { + if (reads_from) + hash ^= reads_from->get_seq_number(); + else if (reads_from_promise) + hash ^= reads_from_promise->get_index(); + hash ^= get_reads_from_value(); + } return hash; } + +/** + * @brief Checks the NodeStack to see if a ModelAction is in our may-read-from set + * @param write The ModelAction to check for + * @return True if the ModelAction is found; false otherwise + */ +bool ModelAction::may_read_from(const ModelAction *write) const +{ + for (int i = 0; i < node->get_read_from_past_size(); i++) + if (node->get_read_from_past(i) == write) + return true; + return false; +} + +/** + * @brief Checks the NodeStack to see if a Promise is in our may-read-from set + * @param promise The Promise to check for + * @return True if the Promise is found; false otherwise + */ +bool ModelAction::may_read_from(const Promise *promise) const +{ + for (int i = 0; i < node->get_read_from_promise_size(); i++) + if (node->get_read_from_promise(i) == promise) + return true; + return false; +} + +/** + * Only valid for LOCK, TRY_LOCK, UNLOCK, and WAIT operations. + * @return The mutex operated on by this action, if any; otherwise NULL + */ +cdsc::mutex * ModelAction::get_mutex() const +{ + if (is_trylock() || is_lock() || is_unlock()) + return (cdsc::mutex *)get_location(); + else if (is_wait()) + return (cdsc::mutex *)get_value(); + else + return NULL; +}