+#if A_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV))
+# define A_THREADSAFE 1
+# ifndef MY_CXT_CLONE
+# define MY_CXT_CLONE \
+ dMY_CXT_SV; \
+ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
+ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
+ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+# endif
+#else
+# define A_THREADSAFE 0
+# undef dMY_CXT
+# define dMY_CXT dNOOP
+# undef MY_CXT
+# define MY_CXT a_globaldata
+# undef START_MY_CXT
+# define START_MY_CXT static my_cxt_t MY_CXT;
+# undef MY_CXT_INIT
+# define MY_CXT_INIT NOOP
+# undef MY_CXT_CLONE
+# define MY_CXT_CLONE NOOP
+#endif
+
+#if A_THREADSAFE
+/* We must use preexistent global mutexes or we will never be able to destroy
+ * them. */
+# if A_HAS_PERL(5, 9, 3)
+# define A_LOADED_LOCK MUTEX_LOCK(&PL_my_ctx_mutex)
+# define A_LOADED_UNLOCK MUTEX_UNLOCK(&PL_my_ctx_mutex)
+# else
+# define A_LOADED_LOCK OP_REFCNT_LOCK
+# define A_LOADED_UNLOCK OP_REFCNT_UNLOCK
+# endif
+#else
+# define A_LOADED_LOCK NOOP
+# define A_LOADED_UNLOCK NOOP
+#endif
+
+#if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
+# define A_CHECK_LOCK OP_CHECK_MUTEX_LOCK
+# define A_CHECK_UNLOCK OP_CHECK_MUTEX_UNLOCK
+#elif A_HAS_PERL(5, 9, 3)
+# define A_CHECK_LOCK OP_REFCNT_LOCK
+# define A_CHECK_UNLOCK OP_REFCNT_UNLOCK
+#else
+/* Before perl 5.9.3, indirect_ck_*() calls are already protected by the
+ * A_LOADED mutex, which falls back to the OP_REFCNT mutex. Make sure we don't
+ * lock it twice. */
+# define A_CHECK_LOCK NOOP
+# define A_CHECK_UNLOCK NOOP
+#endif
+
+typedef OP *(*a_ck_t)(pTHX_ OP *);
+
+#ifdef wrap_op_checker
+
+# define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
+
+#else
+
+static void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) {
+#define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP))
+ A_CHECK_LOCK;
+ if (!*old_ck_p) {
+ *old_ck_p = PL_check[type];
+ PL_check[type] = new_ck;
+ }
+ A_CHECK_UNLOCK;
+}
+
+#endif
+
+static void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) {
+#define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP))
+ A_CHECK_LOCK;
+ if (*old_ck_p) {
+ PL_check[type] = *old_ck_p;
+ *old_ck_p = 0;
+ }
+ A_CHECK_UNLOCK;
+}
+
+/* --- Helpers ------------------------------------------------------------- */
+
+/* ... Check if the module is loaded ....................................... */
+
+static I32 a_loaded = 0;
+
+#if A_THREADSAFE
+
+#define PTABLE_NAME ptable_loaded
+#define PTABLE_NEED_DELETE 1
+#define PTABLE_NEED_WALK 0
+
+#include "ptable.h"
+
+#define ptable_loaded_store(T, K, V) ptable_loaded_store(aPTBLMS_ (T), (K), (V))
+#define ptable_loaded_delete(T, K) ptable_loaded_delete(aPTBLMS_ (T), (K))
+#define ptable_loaded_free(T) ptable_loaded_free(aPTBLMS_ (T))
+
+static ptable *a_loaded_cxts = NULL;
+
+static int a_is_loaded(pTHX_ void *cxt) {
+#define a_is_loaded(C) a_is_loaded(aTHX_ (C))
+ int res = 0;
+
+ A_LOADED_LOCK;
+ if (a_loaded_cxts && ptable_fetch(a_loaded_cxts, cxt))
+ res = 1;
+ A_LOADED_UNLOCK;
+
+ return res;
+}
+
+static int a_set_loaded_locked(pTHX_ void *cxt) {
+#define a_set_loaded_locked(C) a_set_loaded_locked(aTHX_ (C))
+ int global_setup = 0;
+
+ if (a_loaded <= 0) {
+ assert(a_loaded == 0);
+ assert(!a_loaded_cxts);
+ a_loaded_cxts = ptable_new();
+ global_setup = 1;
+ }
+ ++a_loaded;
+ assert(a_loaded_cxts);
+ ptable_loaded_store(a_loaded_cxts, cxt, cxt);
+
+ return global_setup;
+}
+
+static int a_clear_loaded_locked(pTHX_ void *cxt) {
+#define a_clear_loaded_locked(C) a_clear_loaded_locked(aTHX_ (C))
+ int global_teardown = 0;
+
+ if (a_loaded > 1) {
+ assert(a_loaded_cxts);
+ ptable_loaded_delete(a_loaded_cxts, cxt);
+ --a_loaded;
+ } else if (a_loaded_cxts) {
+ assert(a_loaded == 1);
+ ptable_loaded_free(a_loaded_cxts);
+ a_loaded_cxts = NULL;
+ a_loaded = 0;
+ global_teardown = 1;
+ }
+
+ return global_teardown;
+}
+
+#else
+
+#define a_is_loaded(C) (a_loaded > 0)
+#define a_set_loaded_locked(C) ((a_loaded++ <= 0) ? 1 : 0)
+#define a_clear_loaded_locked(C) ((--a_loaded <= 0) ? 1 : 0)
+
+#endif
+
+/* ... Thread-safe hints ................................................... */
+
+#if A_WORKAROUND_REQUIRE_PROPAGATION
+
+typedef struct {
+ U32 bits;
+ IV require_tag;
+} a_hint_t;
+
+#define A_HINT_FREE(H) PerlMemShared_free(H)
+
+#if A_THREADSAFE
+
+#define PTABLE_NAME ptable_hints
+#define PTABLE_VAL_FREE(V) A_HINT_FREE(V)
+#define PTABLE_NEED_DELETE 0
+#define PTABLE_NEED_WALK 1
+
+#define pPTBL pTHX
+#define pPTBL_ pTHX_
+#define aPTBL aTHX
+#define aPTBL_ aTHX_
+
+#include "ptable.h"
+
+#define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
+#define ptable_hints_free(T) ptable_hints_free(aTHX_ (T))
+
+#endif /* A_THREADSAFE */
+
+#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
+
+#define PTABLE_NAME ptable_seen
+#define PTABLE_NEED_DELETE 0
+#define PTABLE_NEED_WALK 0
+
+#include "ptable.h"
+
+/* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
+#define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V))
+#define ptable_seen_clear(T) ptable_seen_clear(aPTBLMS_ (T))
+#define ptable_seen_free(T) ptable_seen_free(aPTBLMS_ (T))
+
+#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
+
+typedef struct {
+ peep_t old_peep; /* This is actually the rpeep past 5.13.5 */
+ ptable *seen; /* It really is a ptable_seen */
+#if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
+ ptable *tbl; /* It really is a ptable_hints */
+ tTHX owner;
+#endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
+} my_cxt_t;
+
+START_MY_CXT
+
+#if A_WORKAROUND_REQUIRE_PROPAGATION
+
+#if A_THREADSAFE
+
+typedef struct {
+ ptable *tbl;
+#if A_HAS_PERL(5, 13, 2)
+ CLONE_PARAMS *params;
+#else
+ CLONE_PARAMS params;
+#endif
+} a_ptable_clone_ud;
+
+#if A_HAS_PERL(5, 13, 2)
+# define a_ptable_clone_ud_init(U, T, O) \
+ (U).tbl = (T); \
+ (U).params = Perl_clone_params_new((O), aTHX)
+# define a_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
+# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), (U)->params))
+#else
+# define a_ptable_clone_ud_init(U, T, O) \
+ (U).tbl = (T); \
+ (U).params.stashes = newAV(); \
+ (U).params.flags = 0; \
+ (U).params.proto_perl = (O)
+# define a_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
+# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), &((U)->params)))
+#endif
+
+static void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
+ a_ptable_clone_ud *ud = ud_;
+ a_hint_t *h1 = ent->val;
+ a_hint_t *h2;
+
+ h2 = PerlMemShared_malloc(sizeof *h2);
+ h2->bits = h1->bits;
+ h2->require_tag = PTR2IV(a_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
+
+ ptable_hints_store(ud->tbl, ent->key, h2);
+}
+
+#endif /* A_THREADSAFE */
+
+static IV a_require_tag(pTHX) {
+#define a_require_tag() a_require_tag(aTHX)
+ const CV *cv, *outside;
+
+ cv = PL_compcv;
+
+ if (!cv) {
+ /* If for some reason the pragma is operational at run-time, try to discover
+ * the current cv in use. */
+ const PERL_SI *si;
+
+ for (si = PL_curstackinfo; si; si = si->si_prev) {
+ I32 cxix;
+
+ for (cxix = si->si_cxix; cxix >= 0; --cxix) {
+ const PERL_CONTEXT *cx = si->si_cxstack + cxix;
+
+ switch (CxTYPE(cx)) {
+ case CXt_SUB:
+ case CXt_FORMAT:
+ /* The propagation workaround is only needed up to 5.10.0 and at that
+ * time format and sub contexts were still identical. And even later the
+ * cv members offsets should have been kept the same. */
+ cv = cx->blk_sub.cv;
+ goto get_enclosing_cv;
+ case CXt_EVAL:
+ cv = cx->blk_eval.cv;
+ goto get_enclosing_cv;
+ default:
+ break;
+ }
+ }