]> git.vpit.fr Git - perl/modules/autovivification.git/blob - autovivification.xs
Make sure MULTIPLICITY builds set PERL_IMPLICIT_CONTEXT
[perl/modules/autovivification.git] / autovivification.xs
1 /* This file is part of the autovivification Perl module.
2  * See http://search.cpan.org/dist/autovivification/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "autovivification"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #ifndef HvNAME_get
15 # define HvNAME_get(H) HvNAME(H)
16 #endif
17
18 #ifndef HvNAMELEN_get
19 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
20 #endif
21
22 #define A_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
23
24 #ifndef A_WORKAROUND_REQUIRE_PROPAGATION
25 # define A_WORKAROUND_REQUIRE_PROPAGATION !A_HAS_PERL(5, 10, 1)
26 #endif
27
28 #ifndef A_HAS_RPEEP
29 # define A_HAS_RPEEP A_HAS_PERL(5, 13, 5)
30 #endif
31
32 #ifndef OpSIBLING
33 # ifdef OP_SIBLING
34 #  define OpSIBLING(O) OP_SIBLING(O)
35 # else
36 #  define OpSIBLING(O) ((O)->op_sibling)
37 # endif
38 #endif
39
40 /* ... Thread safety and multiplicity ...................................... */
41
42 /* Always safe when the workaround isn't needed */
43 #if !A_WORKAROUND_REQUIRE_PROPAGATION
44 # undef A_FORKSAFE
45 # define A_FORKSAFE 1
46 /* Otherwise, safe unless Makefile.PL says it's Win32 */
47 #elif !defined(A_FORKSAFE)
48 # define A_FORKSAFE 1
49 #endif
50
51 #ifndef A_MULTIPLICITY
52 # if defined(MULTIPLICITY)
53 #  define A_MULTIPLICITY 1
54 # else
55 #  define A_MULTIPLICITY 0
56 # endif
57 #endif
58 #if A_MULTIPLICITY
59 # ifndef PERL_IMPLICIT_CONTEXT
60 #  error MULTIPLICITY builds must set PERL_IMPLICIT_CONTEXT
61 # endif
62 #endif
63
64 #ifndef tTHX
65 # define tTHX PerlInterpreter*
66 #endif
67
68 #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))
69 # define A_THREADSAFE 1
70 # ifndef MY_CXT_CLONE
71 #  define MY_CXT_CLONE \
72     dMY_CXT_SV;                                                      \
73     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
74     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
75     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
76 # endif
77 #else
78 # define A_THREADSAFE 0
79 # undef  dMY_CXT
80 # define dMY_CXT      dNOOP
81 # undef  MY_CXT
82 # define MY_CXT       a_globaldata
83 # undef  START_MY_CXT
84 # define START_MY_CXT static my_cxt_t MY_CXT;
85 # undef  MY_CXT_INIT
86 # define MY_CXT_INIT  NOOP
87 # undef  MY_CXT_CLONE
88 # define MY_CXT_CLONE NOOP
89 #endif
90
91 #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
92 # define A_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
93 # define A_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
94 #else
95 # define A_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
96 # define A_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
97 #endif
98
99 typedef OP *(*a_ck_t)(pTHX_ OP *);
100
101 #ifdef wrap_op_checker
102
103 # define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
104
105 #else
106
107 static void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) {
108 #define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP))
109  A_CHECK_MUTEX_LOCK;
110  if (!*old_ck_p) {
111   *old_ck_p      = PL_check[type];
112   PL_check[type] = new_ck;
113  }
114  A_CHECK_MUTEX_UNLOCK;
115 }
116
117 #endif
118
119 static void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) {
120 #define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP))
121  A_CHECK_MUTEX_LOCK;
122  if (*old_ck_p) {
123   PL_check[type] = *old_ck_p;
124   *old_ck_p      = 0;
125  }
126  A_CHECK_MUTEX_UNLOCK;
127 }
128
129 /* --- Helpers ------------------------------------------------------------- */
130
131 /* ... Thread-safe hints ................................................... */
132
133 #if A_WORKAROUND_REQUIRE_PROPAGATION
134
135 typedef struct {
136  U32 bits;
137  IV  require_tag;
138 } a_hint_t;
139
140 #define A_HINT_FREE(H) PerlMemShared_free(H)
141
142 #if A_THREADSAFE
143
144 #define PTABLE_NAME        ptable_hints
145 #define PTABLE_VAL_FREE(V) A_HINT_FREE(V)
146
147 #define pPTBL  pTHX
148 #define pPTBL_ pTHX_
149 #define aPTBL  aTHX
150 #define aPTBL_ aTHX_
151
152 #include "ptable.h"
153
154 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
155 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
156
157 #endif /* A_THREADSAFE */
158
159 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
160
161 #define PTABLE_NAME        ptable_seen
162 #define PTABLE_VAL_FREE(V) NOOP
163
164 #include "ptable.h"
165
166 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
167 #define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V))
168 #define ptable_seen_clear(T)       ptable_seen_clear(aPTBLMS_ (T))
169 #define ptable_seen_free(T)        ptable_seen_free(aPTBLMS_ (T))
170
171 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
172
173 typedef struct {
174 #if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
175  ptable *tbl;   /* It really is a ptable_hints */
176  tTHX    owner;
177 #endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
178  ptable *seen;  /* It really is a ptable_seen */
179 } my_cxt_t;
180
181 START_MY_CXT
182
183 #if A_THREADSAFE
184
185 #if A_WORKAROUND_REQUIRE_PROPAGATION
186
187 typedef struct {
188  ptable       *tbl;
189 #if A_HAS_PERL(5, 13, 2)
190  CLONE_PARAMS *params;
191 #else
192  CLONE_PARAMS  params;
193 #endif
194 } a_ptable_clone_ud;
195
196 #if A_HAS_PERL(5, 13, 2)
197 # define a_ptable_clone_ud_init(U, T, O) \
198    (U).tbl    = (T); \
199    (U).params = Perl_clone_params_new((O), aTHX)
200 # define a_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
201 # define a_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), (U)->params))
202 #else
203 # define a_ptable_clone_ud_init(U, T, O) \
204    (U).tbl               = (T);     \
205    (U).params.stashes    = newAV(); \
206    (U).params.flags      = 0;       \
207    (U).params.proto_perl = (O)
208 # define a_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
209 # define a_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), &((U)->params)))
210 #endif
211
212 static void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
213  a_ptable_clone_ud *ud = ud_;
214  a_hint_t *h1 = ent->val;
215  a_hint_t *h2;
216
217  h2              = PerlMemShared_malloc(sizeof *h2);
218  h2->bits        = h1->bits;
219  h2->require_tag = PTR2IV(a_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
220
221  ptable_hints_store(ud->tbl, ent->key, h2);
222 }
223
224 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
225
226 static void a_thread_cleanup(pTHX_ void *ud) {
227  dMY_CXT;
228
229 #if A_WORKAROUND_REQUIRE_PROPAGATION
230  ptable_hints_free(MY_CXT.tbl);
231  MY_CXT.tbl  = NULL;
232 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
233  ptable_seen_free(MY_CXT.seen);
234  MY_CXT.seen = NULL;
235 }
236
237 static int a_endav_free(pTHX_ SV *sv, MAGIC *mg) {
238  SAVEDESTRUCTOR_X(a_thread_cleanup, NULL);
239
240  return 0;
241 }
242
243 static MGVTBL a_endav_vtbl = {
244  0,
245  0,
246  0,
247  0,
248  a_endav_free
249 #if MGf_COPY
250  , 0
251 #endif
252 #if MGf_DUP
253  , 0
254 #endif
255 #if MGf_LOCAL
256  , 0
257 #endif
258 };
259
260 #endif /* A_THREADSAFE */
261
262 #if A_WORKAROUND_REQUIRE_PROPAGATION
263
264 static IV a_require_tag(pTHX) {
265 #define a_require_tag() a_require_tag(aTHX)
266  const CV *cv, *outside;
267
268  cv = PL_compcv;
269
270  if (!cv) {
271   /* If for some reason the pragma is operational at run-time, try to discover
272    * the current cv in use. */
273   const PERL_SI *si;
274
275   for (si = PL_curstackinfo; si; si = si->si_prev) {
276    I32 cxix;
277
278    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
279     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
280
281     switch (CxTYPE(cx)) {
282      case CXt_SUB:
283      case CXt_FORMAT:
284       /* The propagation workaround is only needed up to 5.10.0 and at that
285        * time format and sub contexts were still identical. And even later the
286        * cv members offsets should have been kept the same. */
287       cv = cx->blk_sub.cv;
288       goto get_enclosing_cv;
289      case CXt_EVAL:
290       cv = cx->blk_eval.cv;
291       goto get_enclosing_cv;
292      default:
293       break;
294     }
295    }
296   }
297
298   cv = PL_main_cv;
299  }
300
301 get_enclosing_cv:
302  for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
303   cv = outside;
304
305  return PTR2IV(cv);
306 }
307
308 static SV *a_tag(pTHX_ UV bits) {
309 #define a_tag(B) a_tag(aTHX_ (B))
310  a_hint_t *h;
311 #if A_THREADSAFE
312  dMY_CXT;
313
314  if (!MY_CXT.tbl)
315   return newSViv(0);
316 #endif /* A_THREADSAFE */
317
318  h              = PerlMemShared_malloc(sizeof *h);
319  h->bits        = bits;
320  h->require_tag = a_require_tag();
321
322 #if A_THREADSAFE
323  /* We only need for the key to be an unique tag for looking up the value later
324   * Allocated memory provides convenient unique identifiers, so that's why we
325   * use the hint as the key itself. */
326  ptable_hints_store(MY_CXT.tbl, h, h);
327 #endif /* A_THREADSAFE */
328
329  return newSViv(PTR2IV(h));
330 }
331
332 static UV a_detag(pTHX_ const SV *hint) {
333 #define a_detag(H) a_detag(aTHX_ (H))
334  a_hint_t *h;
335 #if A_THREADSAFE
336  dMY_CXT;
337
338  if (!MY_CXT.tbl)
339   return 0;
340 #endif /* A_THREADSAFE */
341
342  if (!(hint && SvIOK(hint)))
343   return 0;
344
345  h = INT2PTR(a_hint_t *, SvIVX(hint));
346 #if A_THREADSAFE
347  h = ptable_fetch(MY_CXT.tbl, h);
348 #endif /* A_THREADSAFE */
349
350  if (a_require_tag() != h->require_tag)
351   return 0;
352
353  return h->bits;
354 }
355
356 #else /* A_WORKAROUND_REQUIRE_PROPAGATION */
357
358 #define a_tag(B)   newSVuv(B)
359 /* PVs fetched from the hints chain have their SvLEN set to zero, so get the UV
360  * from a copy. */
361 #define a_detag(H) \
362  ((H)              \
363   ? (SvIOK(H)      \
364      ? SvUVX(H)    \
365      : (SvPOK(H)   \
366         ? sv_2uv(SvLEN(H) ? (H) : sv_mortalcopy(H)) \
367         : 0        \
368        )           \
369      )             \
370   : 0)
371
372 #endif /* !A_WORKAROUND_REQUIRE_PROPAGATION */
373
374 /* Used both for hints and op flags */
375 #define A_HINT_STRICT 1
376 #define A_HINT_WARN   2
377 #define A_HINT_FETCH  4
378 #define A_HINT_STORE  8
379 #define A_HINT_EXISTS 16
380 #define A_HINT_DELETE 32
381 #define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN)
382 #define A_HINT_DO     (A_HINT_FETCH|A_HINT_STORE|A_HINT_EXISTS|A_HINT_DELETE)
383 #define A_HINT_MASK   (A_HINT_NOTIFY|A_HINT_DO)
384
385 /* Only used in op flags */
386 #define A_HINT_ROOT   64
387 #define A_HINT_DEREF  128
388
389 static U32 a_hash = 0;
390
391 static UV a_hint(pTHX) {
392 #define a_hint() a_hint(aTHX)
393  SV *hint;
394 #ifdef cop_hints_fetch_pvn
395  hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__, a_hash, 0);
396 #elif A_HAS_PERL(5, 9, 5)
397  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
398                                        NULL,
399                                        __PACKAGE__, __PACKAGE_LEN__,
400                                        0,
401                                        a_hash);
402 #else
403  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
404  if (!val)
405   return 0;
406  hint = *val;
407 #endif
408  return a_detag(hint);
409 }
410
411 /* ... op => info map ...................................................... */
412
413 typedef struct {
414  OP   *(*old_pp)(pTHX);
415  void   *next;
416  UV      flags;
417 } a_op_info;
418
419 #define PTABLE_NAME        ptable_map
420 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
421
422 #include "ptable.h"
423
424 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
425 #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
426 #define ptable_map_delete(T, K)   ptable_map_delete(aPTBLMS_ (T), (K))
427
428 static ptable *a_op_map = NULL;
429
430 #ifdef USE_ITHREADS
431
432 #define dA_MAP_THX a_op_info a_op_map_tmp_oi
433
434 static perl_mutex a_op_map_mutex;
435
436 #define A_LOCK(M)   MUTEX_LOCK(M)
437 #define A_UNLOCK(M) MUTEX_UNLOCK(M)
438
439 static const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) {
440  const a_op_info *val;
441
442  A_LOCK(&a_op_map_mutex);
443
444  val = ptable_fetch(a_op_map, o);
445  if (val) {
446   *oi = *val;
447   val = oi;
448  }
449
450  A_UNLOCK(&a_op_map_mutex);
451
452  return val;
453 }
454
455 #define a_map_fetch(O) a_map_fetch((O), &a_op_map_tmp_oi)
456
457 #else /* USE_ITHREADS */
458
459 #define dA_MAP_THX dNOOP
460
461 #define A_LOCK(M)   NOOP
462 #define A_UNLOCK(M) NOOP
463
464 #define a_map_fetch(O) ptable_fetch(a_op_map, (O))
465
466 #endif /* !USE_ITHREADS */
467
468 static const a_op_info *a_map_store_locked(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
469 #define a_map_store_locked(O, PP, N, F) a_map_store_locked(aPTBLMS_ (O), (PP), (N), (F))
470  a_op_info *oi;
471
472  if (!(oi = ptable_fetch(a_op_map, o))) {
473   oi = PerlMemShared_malloc(sizeof *oi);
474   ptable_map_store(a_op_map, o, oi);
475  }
476
477  oi->old_pp = old_pp;
478  oi->next   = next;
479  oi->flags  = flags;
480
481  return oi;
482 }
483
484 static void a_map_store(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
485 #define a_map_store(O, PP, N, F) a_map_store(aPTBLMS_ (O), (PP), (N), (F))
486  A_LOCK(&a_op_map_mutex);
487
488  a_map_store_locked(o, old_pp, next, flags);
489
490  A_UNLOCK(&a_op_map_mutex);
491 }
492
493 static void a_map_delete(pTHX_ const OP *o) {
494 #define a_map_delete(O) a_map_delete(aTHX_ (O))
495  A_LOCK(&a_op_map_mutex);
496
497  ptable_map_delete(a_op_map, o);
498
499  A_UNLOCK(&a_op_map_mutex);
500 }
501
502 static const OP *a_map_descend(const OP *o) {
503  switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
504   case OA_BASEOP:
505   case OA_UNOP:
506   case OA_BINOP:
507   case OA_BASEOP_OR_UNOP:
508    return cUNOPo->op_first;
509   case OA_LIST:
510   case OA_LISTOP:
511    return cLISTOPo->op_last;
512  }
513
514  return NULL;
515 }
516
517 static void a_map_store_root(pPTBLMS_ const OP *root, OP *(*old_pp)(pTHX), UV flags) {
518 #define a_map_store_root(R, PP, F) a_map_store_root(aPTBLMS_ (R), (PP), (F))
519  const a_op_info *roi;
520  a_op_info *oi;
521  const OP *o = root;
522
523  A_LOCK(&a_op_map_mutex);
524
525  roi = a_map_store_locked(o, old_pp, (OP *) root, flags | A_HINT_ROOT);
526
527  while (o->op_flags & OPf_KIDS) {
528   o = a_map_descend(o);
529   if (!o)
530    break;
531   if ((oi = ptable_fetch(a_op_map, o))) {
532    oi->flags &= ~A_HINT_ROOT;
533    oi->next   = (a_op_info *) roi;
534    break;
535   }
536  }
537
538  A_UNLOCK(&a_op_map_mutex);
539
540  return;
541 }
542
543 static void a_map_update_flags_topdown(const OP *root, UV flags) {
544  a_op_info *oi;
545  const OP *o = root;
546
547  A_LOCK(&a_op_map_mutex);
548
549  flags &= ~A_HINT_ROOT;
550
551  do {
552   if ((oi = ptable_fetch(a_op_map, o)))
553    oi->flags = (oi->flags & A_HINT_ROOT) | flags;
554   if (!(o->op_flags & OPf_KIDS))
555    break;
556   o = a_map_descend(o);
557  } while (o);
558
559  A_UNLOCK(&a_op_map_mutex);
560
561  return;
562 }
563
564 #define a_map_cancel(R) a_map_update_flags_topdown((R), 0)
565
566 static void a_map_update_flags_bottomup(const OP *o, UV flags, UV rflags) {
567  a_op_info *oi;
568
569  A_LOCK(&a_op_map_mutex);
570
571  flags  &= ~A_HINT_ROOT;
572  rflags |=  A_HINT_ROOT;
573
574  oi = ptable_fetch(a_op_map, o);
575  while (!(oi->flags & A_HINT_ROOT)) {
576   oi->flags = flags;
577   oi        = oi->next;
578  }
579  oi->flags = rflags;
580
581  A_UNLOCK(&a_op_map_mutex);
582
583  return;
584 }
585
586 /* ... Decide whether this expression should be autovivified or not ........ */
587
588 static UV a_map_resolve(const OP *o, const a_op_info *oi) {
589  UV flags = 0, rflags;
590  const OP *root;
591  const a_op_info *roi = oi;
592
593  while (!(roi->flags & A_HINT_ROOT))
594   roi = roi->next;
595  if (!roi)
596   goto cancel;
597
598  rflags = roi->flags & ~A_HINT_ROOT;
599  if (!rflags)
600   goto cancel;
601
602  root = roi->next;
603  if (root->op_flags & OPf_MOD) {
604   if (rflags & A_HINT_STORE)
605    flags = (A_HINT_STORE|A_HINT_DEREF);
606  } else if (rflags & A_HINT_FETCH)
607    flags = (A_HINT_FETCH|A_HINT_DEREF);
608
609  if (!flags) {
610 cancel:
611   a_map_update_flags_bottomup(o, 0, 0);
612   return 0;
613  }
614
615  flags |= (rflags & A_HINT_NOTIFY);
616  a_map_update_flags_bottomup(o, flags, 0);
617
618  return oi->flags & A_HINT_ROOT ? 0 : flags;
619 }
620
621 /* ... Inspired from pp_defined() .......................................... */
622
623 static int a_undef(pTHX_ SV *sv) {
624 #define a_undef(S) a_undef(aTHX_ (S))
625  switch (SvTYPE(sv)) {
626   case SVt_NULL:
627    return 1;
628   case SVt_PVAV:
629    if (AvMAX(sv) >= 0 || SvGMAGICAL(sv)
630                       || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
631     return 0;
632    break;
633   case SVt_PVHV:
634    if (HvARRAY(sv) || SvGMAGICAL(sv)
635                    || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
636     return 0;
637    break;
638   default:
639    SvGETMAGIC(sv);
640    if (SvOK(sv))
641     return 0;
642  }
643
644  return 1;
645 }
646
647 /* --- PP functions -------------------------------------------------------- */
648
649 /* Be aware that we restore PL_op->op_ppaddr from the pointer table old_pp
650  * value, another extension might have saved our pp replacement as the ppaddr
651  * for this op, so this doesn't ensure that our function will never be called
652  * again. That's why we don't remove the op info from our map, so that it can
653  * still run correctly if required. */
654
655 /* ... pp_rv2av ............................................................ */
656
657 static OP *a_pp_rv2av(pTHX) {
658  dA_MAP_THX;
659  const a_op_info *oi;
660  dSP;
661
662  oi = a_map_fetch(PL_op);
663
664  if (oi->flags & A_HINT_DEREF) {
665   if (a_undef(TOPs)) {
666    /* We always need to push an empty array to fool the pp_aelem() that comes
667     * later. */
668    SV *av;
669    (void) POPs;
670    av = sv_2mortal((SV *) newAV());
671    PUSHs(av);
672    RETURN;
673   }
674  }
675
676  return oi->old_pp(aTHX);
677 }
678
679 /* ... pp_rv2hv ............................................................ */
680
681 static OP *a_pp_rv2hv_simple(pTHX) {
682  dA_MAP_THX;
683  const a_op_info *oi;
684  dSP;
685
686  oi = a_map_fetch(PL_op);
687
688  if (oi->flags & A_HINT_DEREF) {
689   if (a_undef(TOPs))
690    RETURN;
691  }
692
693  return oi->old_pp(aTHX);
694 }
695
696 static OP *a_pp_rv2hv(pTHX) {
697  dA_MAP_THX;
698  const a_op_info *oi;
699  dSP;
700
701  oi = a_map_fetch(PL_op);
702
703  if (oi->flags & A_HINT_DEREF) {
704   if (a_undef(TOPs)) {
705    SV *hv;
706    (void) POPs;
707    hv = sv_2mortal((SV *) newHV());
708    PUSHs(hv);
709    RETURN;
710   }
711  }
712
713  return oi->old_pp(aTHX);
714 }
715
716 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
717
718 static void a_cannot_vivify(pTHX_ UV flags) {
719 #define a_cannot_vivify(F) a_cannot_vivify(aTHX_ (F))
720  if (flags & A_HINT_STRICT)
721   croak("Reference vivification forbidden");
722  else if (flags & A_HINT_WARN)
723   warn("Reference was vivified");
724  else /* A_HINT_STORE */
725   croak("Can't vivify reference");
726 }
727
728 static OP *a_pp_deref(pTHX) {
729  dA_MAP_THX;
730  const a_op_info *oi;
731  UV flags;
732  dSP;
733
734  oi = a_map_fetch(PL_op);
735
736  flags = oi->flags;
737  if (flags & A_HINT_DEREF) {
738   OP *o;
739
740   o = oi->old_pp(aTHX);
741
742   if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) {
743    SPAGAIN;
744    if (a_undef(TOPs))
745     a_cannot_vivify(flags);
746   }
747
748   return o;
749  }
750
751  return oi->old_pp(aTHX);
752 }
753
754 /* ... pp_root (exists,delete,keys,values) ................................. */
755
756 static OP *a_pp_root_unop(pTHX) {
757  dSP;
758
759  if (a_undef(TOPs)) {
760   (void) POPs;
761   /* Can only be reached by keys or values */
762   if (GIMME_V == G_SCALAR) {
763    dTARGET;
764    PUSHi(0);
765   }
766   RETURN;
767  }
768
769  {
770   dA_MAP_THX;
771   const a_op_info *oi = a_map_fetch(PL_op);
772   return oi->old_pp(aTHX);
773  }
774 }
775
776 static OP *a_pp_root_binop(pTHX) {
777  dSP;
778
779  if (a_undef(TOPm1s)) {
780   (void) POPs;
781   (void) POPs;
782   if (PL_op->op_type == OP_EXISTS)
783    RETPUSHNO;
784   else
785    RETPUSHUNDEF;
786  }
787
788  {
789   dA_MAP_THX;
790   const a_op_info *oi = a_map_fetch(PL_op);
791   return oi->old_pp(aTHX);
792  }
793 }
794
795 /* --- Check functions ----------------------------------------------------- */
796
797 static void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) {
798 #define a_recheck_rv2xv(O, T, PP) a_recheck_rv2xv(aTHX_ (O), (T), (PP))
799
800  if (o->op_type == type && o->op_ppaddr != new_pp
801                         && cUNOPo->op_first->op_type != OP_GV) {
802   dA_MAP_THX;
803   const a_op_info *oi = a_map_fetch(o);
804   if (oi) {
805    a_map_store(o, o->op_ppaddr, oi->next, oi->flags);
806    o->op_ppaddr = new_pp;
807   }
808  }
809
810  return;
811 }
812
813 /* ... ck_pad{any,sv} ...................................................... */
814
815 /* Sadly, the padsv OPs we are interested in don't trigger the padsv check
816  * function, but are instead manually mutated from a padany. So we store
817  * the op entry in the op map in the padany check function, and we set their
818  * op_ppaddr member in our peephole optimizer replacement below. */
819
820 static OP *(*a_old_ck_padany)(pTHX_ OP *) = 0;
821
822 static OP *a_ck_padany(pTHX_ OP *o) {
823  UV hint;
824
825  o = a_old_ck_padany(aTHX_ o);
826
827  hint = a_hint();
828  if (hint & A_HINT_DO)
829   a_map_store_root(o, o->op_ppaddr, hint);
830  else
831   a_map_delete(o);
832
833  return o;
834 }
835
836 static OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0;
837
838 static OP *a_ck_padsv(pTHX_ OP *o) {
839  UV hint;
840
841  o = a_old_ck_padsv(aTHX_ o);
842
843  hint = a_hint();
844  if (hint & A_HINT_DO) {
845   a_map_store_root(o, o->op_ppaddr, hint);
846   o->op_ppaddr = a_pp_deref;
847  } else
848   a_map_delete(o);
849
850  return o;
851 }
852
853 /* ... ck_deref (aelem,helem,rv2sv) ........................................ */
854
855 /* Those ops appear both at the root and inside an expression but there's no
856  * way to distinguish both situations. Worse, we can't even know if we are in a
857  * modifying context, so the expression can't be resolved yet. It will be at the
858  * first invocation of a_pp_deref() for this expression. */
859
860 static OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0;
861 static OP *(*a_old_ck_helem)(pTHX_ OP *) = 0;
862 static OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0;
863
864 static OP *a_ck_deref(pTHX_ OP *o) {
865  OP * (*old_ck)(pTHX_ OP *o) = 0;
866  UV hint = a_hint();
867
868  switch (o->op_type) {
869   case OP_AELEM:
870    old_ck = a_old_ck_aelem;
871    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
872     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2AV, a_pp_rv2av);
873    break;
874   case OP_HELEM:
875    old_ck = a_old_ck_helem;
876    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
877     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv_simple);
878    break;
879   case OP_RV2SV:
880    old_ck = a_old_ck_rv2sv;
881    break;
882  }
883  o = old_ck(aTHX_ o);
884
885  if (hint & A_HINT_DO) {
886   a_map_store_root(o, o->op_ppaddr, hint);
887   o->op_ppaddr = a_pp_deref;
888  } else
889   a_map_delete(o);
890
891  return o;
892 }
893
894 /* ... ck_rv2xv (rv2av,rv2hv) .............................................. */
895
896 /* Those ops also appear both inisde and at the root, hence the caveats for
897  * a_ck_deref() still apply here. Since a padsv/rv2sv must appear before a
898  * rv2[ah]v, resolution is handled by the first call to a_pp_deref() in the
899  * expression. */
900
901 static OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0;
902 static OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0;
903
904 static OP *a_ck_rv2xv(pTHX_ OP *o) {
905  OP * (*old_ck)(pTHX_ OP *o) = 0;
906  OP * (*new_pp)(pTHX)        = 0;
907  UV hint;
908
909  switch (o->op_type) {
910   case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
911   case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv_simple; break;
912  }
913  o = old_ck(aTHX_ o);
914
915  if (cUNOPo->op_first->op_type == OP_GV)
916   return o;
917
918  hint = a_hint();
919  if (hint & A_HINT_DO && !(hint & A_HINT_STRICT)) {
920   a_map_store_root(o, o->op_ppaddr, hint);
921   o->op_ppaddr = new_pp;
922  } else
923   a_map_delete(o);
924
925  return o;
926 }
927
928 /* ... ck_xslice (aslice,hslice) ........................................... */
929
930 /* I think those are only found at the root, but there's nothing that really
931  * prevent them to be inside the expression too. We only need to update the
932  * root so that the rest of the expression will see the right context when
933  * resolving. That's why we don't replace the ppaddr. */
934
935 static OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0;
936 static OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0;
937
938 static OP *a_ck_xslice(pTHX_ OP *o) {
939  OP * (*old_ck)(pTHX_ OP *o) = 0;
940  UV hint = a_hint();
941
942  switch (o->op_type) {
943   case OP_ASLICE:
944    old_ck = a_old_ck_aslice;
945    break;
946   case OP_HSLICE:
947    old_ck = a_old_ck_hslice;
948    if (hint & A_HINT_DO)
949     a_recheck_rv2xv(OpSIBLING(cUNOPo->op_first), OP_RV2HV, a_pp_rv2hv);
950    break;
951  }
952  o = old_ck(aTHX_ o);
953
954  if (hint & A_HINT_DO) {
955   a_map_store_root(o, 0, hint);
956  } else
957   a_map_delete(o);
958
959  return o;
960 }
961
962 /* ... ck_root (exists,delete,keys,values) ................................. */
963
964 /* Those ops are only found at the root of a dereferencing expression. We can
965  * then resolve at compile time if vivification must take place or not. */
966
967 static OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
968 static OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
969 static OP *(*a_old_ck_keys)  (pTHX_ OP *) = 0;
970 static OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
971
972 static OP *a_ck_root(pTHX_ OP *o) {
973  OP * (*old_ck)(pTHX_ OP *o) = 0;
974  OP * (*new_pp)(pTHX)        = 0;
975  bool enabled = FALSE;
976  UV hint = a_hint();
977
978  switch (o->op_type) {
979   case OP_EXISTS:
980    old_ck  = a_old_ck_exists;
981    new_pp  = a_pp_root_binop;
982    enabled = hint & A_HINT_EXISTS;
983    break;
984   case OP_DELETE:
985    old_ck  = a_old_ck_delete;
986    new_pp  = a_pp_root_binop;
987    enabled = hint & A_HINT_DELETE;
988    break;
989   case OP_KEYS:
990    old_ck  = a_old_ck_keys;
991    new_pp  = a_pp_root_unop;
992    enabled = hint & A_HINT_FETCH;
993    break;
994   case OP_VALUES:
995    old_ck  = a_old_ck_values;
996    new_pp  = a_pp_root_unop;
997    enabled = hint & A_HINT_FETCH;
998    break;
999  }
1000  o = old_ck(aTHX_ o);
1001
1002  if (hint & A_HINT_DO) {
1003   if (enabled) {
1004    a_map_update_flags_topdown(o, hint | A_HINT_DEREF);
1005    a_map_store_root(o, o->op_ppaddr, hint);
1006    o->op_ppaddr = new_pp;
1007   } else {
1008    a_map_cancel(o);
1009   }
1010  } else
1011   a_map_delete(o);
1012
1013  return o;
1014 }
1015
1016 /* ... Our peephole optimizer .............................................. */
1017
1018 static peep_t a_old_peep = 0; /* This is actually the rpeep past 5.13.5 */
1019
1020 static void a_peep_rec(pTHX_ OP *o, ptable *seen);
1021
1022 static void a_peep_rec(pTHX_ OP *o, ptable *seen) {
1023 #define a_peep_rec(O) a_peep_rec(aTHX_ (O), seen)
1024  for (; o; o = o->op_next) {
1025   dA_MAP_THX;
1026   const a_op_info *oi = NULL;
1027   UV flags = 0;
1028
1029 #if !A_HAS_RPEEP
1030   if (ptable_fetch(seen, o))
1031    break;
1032   ptable_seen_store(seen, o, o);
1033 #endif
1034
1035   switch (o->op_type) {
1036 #if A_HAS_RPEEP
1037    case OP_NEXTSTATE:
1038    case OP_DBSTATE:
1039    case OP_STUB:
1040    case OP_UNSTACK:
1041     if (ptable_fetch(seen, o))
1042      return;
1043     ptable_seen_store(seen, o, o);
1044     break;
1045 #endif
1046    case OP_PADSV:
1047     if (o->op_ppaddr != a_pp_deref) {
1048      oi = a_map_fetch(o);
1049      if (oi && (oi->flags & A_HINT_DO)) {
1050       a_map_store(o, o->op_ppaddr, oi->next, oi->flags);
1051       o->op_ppaddr = a_pp_deref;
1052      }
1053     }
1054     /* FALLTHROUGH */
1055    case OP_AELEM:
1056    case OP_AELEMFAST:
1057    case OP_HELEM:
1058    case OP_RV2SV:
1059     if (o->op_ppaddr != a_pp_deref)
1060      break;
1061     oi = a_map_fetch(o);
1062     if (!oi)
1063      break;
1064     flags = oi->flags;
1065     if (!(flags & A_HINT_DEREF)
1066         && (flags & A_HINT_DO)
1067         && (o->op_private & OPpDEREF || flags & A_HINT_ROOT)) {
1068      /* Decide if the expression must autovivify or not. */
1069      flags = a_map_resolve(o, oi);
1070     }
1071     if (flags & A_HINT_DEREF)
1072      o->op_private = ((o->op_private & ~OPpDEREF) | OPpLVAL_DEFER);
1073     else
1074      o->op_ppaddr  = oi->old_pp;
1075     break;
1076    case OP_RV2AV:
1077    case OP_RV2HV:
1078     if (   o->op_ppaddr != a_pp_rv2av
1079         && o->op_ppaddr != a_pp_rv2hv
1080         && o->op_ppaddr != a_pp_rv2hv_simple)
1081      break;
1082     oi = a_map_fetch(o);
1083     if (!oi)
1084      break;
1085     if (!(oi->flags & A_HINT_DEREF))
1086      o->op_ppaddr  = oi->old_pp;
1087     break;
1088 #if !A_HAS_RPEEP
1089    case OP_MAPWHILE:
1090    case OP_GREPWHILE:
1091    case OP_AND:
1092    case OP_OR:
1093    case OP_ANDASSIGN:
1094    case OP_ORASSIGN:
1095    case OP_COND_EXPR:
1096    case OP_RANGE:
1097 # if A_HAS_PERL(5, 10, 0)
1098    case OP_ONCE:
1099    case OP_DOR:
1100    case OP_DORASSIGN:
1101 # endif
1102     a_peep_rec(cLOGOPo->op_other);
1103     break;
1104    case OP_ENTERLOOP:
1105    case OP_ENTERITER:
1106     a_peep_rec(cLOOPo->op_redoop);
1107     a_peep_rec(cLOOPo->op_nextop);
1108     a_peep_rec(cLOOPo->op_lastop);
1109     break;
1110 # if A_HAS_PERL(5, 9, 5)
1111    case OP_SUBST:
1112     a_peep_rec(cPMOPo->op_pmstashstartu.op_pmreplstart);
1113     break;
1114 # else
1115    case OP_QR:
1116    case OP_MATCH:
1117    case OP_SUBST:
1118     a_peep_rec(cPMOPo->op_pmreplstart);
1119     break;
1120 # endif
1121 #endif /* !A_HAS_RPEEP */
1122    default:
1123     break;
1124   }
1125  }
1126 }
1127
1128 static void a_peep(pTHX_ OP *o) {
1129  dMY_CXT;
1130  ptable *seen = MY_CXT.seen;
1131
1132  a_old_peep(aTHX_ o);
1133
1134  if (seen) {
1135   ptable_seen_clear(seen);
1136   a_peep_rec(o);
1137   ptable_seen_clear(seen);
1138  }
1139 }
1140
1141 /* --- Interpreter setup/teardown ------------------------------------------ */
1142
1143 static U32 a_initialized = 0;
1144
1145 static void a_teardown(pTHX_ void *root) {
1146
1147  if (!a_initialized)
1148   return;
1149
1150 #if A_MULTIPLICITY
1151  if (aTHX != root)
1152   return;
1153 #endif
1154
1155  {
1156   dMY_CXT;
1157 # if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
1158   ptable_hints_free(MY_CXT.tbl);
1159   MY_CXT.tbl  = NULL;
1160 # endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
1161   ptable_seen_free(MY_CXT.seen);
1162   MY_CXT.seen = NULL;
1163  }
1164
1165  a_ck_restore(OP_PADANY, &a_old_ck_padany);
1166  a_ck_restore(OP_PADSV,  &a_old_ck_padsv);
1167
1168  a_ck_restore(OP_AELEM,  &a_old_ck_aelem);
1169  a_ck_restore(OP_HELEM,  &a_old_ck_helem);
1170  a_ck_restore(OP_RV2SV,  &a_old_ck_rv2sv);
1171
1172  a_ck_restore(OP_RV2AV,  &a_old_ck_rv2av);
1173  a_ck_restore(OP_RV2HV,  &a_old_ck_rv2hv);
1174
1175  a_ck_restore(OP_ASLICE, &a_old_ck_aslice);
1176  a_ck_restore(OP_HSLICE, &a_old_ck_hslice);
1177
1178  a_ck_restore(OP_EXISTS, &a_old_ck_exists);
1179  a_ck_restore(OP_DELETE, &a_old_ck_delete);
1180  a_ck_restore(OP_KEYS,   &a_old_ck_keys);
1181  a_ck_restore(OP_VALUES, &a_old_ck_values);
1182
1183 #if A_HAS_RPEEP
1184  PL_rpeepp  = a_old_peep;
1185 #else
1186  PL_peepp   = a_old_peep;
1187 #endif
1188  a_old_peep = 0;
1189
1190  a_initialized = 0;
1191 }
1192
1193 static void a_setup(pTHX) {
1194 #define a_setup() a_setup(aTHX)
1195  if (a_initialized)
1196   return;
1197
1198  {
1199   MY_CXT_INIT;
1200 # if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
1201   MY_CXT.tbl   = ptable_new();
1202   MY_CXT.owner = aTHX;
1203 # endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
1204   MY_CXT.seen  = ptable_new();
1205  }
1206
1207  a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany);
1208  a_ck_replace(OP_PADSV,  a_ck_padsv,  &a_old_ck_padsv);
1209
1210  a_ck_replace(OP_AELEM,  a_ck_deref,  &a_old_ck_aelem);
1211  a_ck_replace(OP_HELEM,  a_ck_deref,  &a_old_ck_helem);
1212  a_ck_replace(OP_RV2SV,  a_ck_deref,  &a_old_ck_rv2sv);
1213
1214  a_ck_replace(OP_RV2AV,  a_ck_rv2xv,  &a_old_ck_rv2av);
1215  a_ck_replace(OP_RV2HV,  a_ck_rv2xv,  &a_old_ck_rv2hv);
1216
1217  a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice);
1218  a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice);
1219
1220  a_ck_replace(OP_EXISTS, a_ck_root,   &a_old_ck_exists);
1221  a_ck_replace(OP_DELETE, a_ck_root,   &a_old_ck_delete);
1222  a_ck_replace(OP_KEYS,   a_ck_root,   &a_old_ck_keys);
1223  a_ck_replace(OP_VALUES, a_ck_root,   &a_old_ck_values);
1224
1225 #if A_HAS_RPEEP
1226  a_old_peep = PL_rpeepp;
1227  PL_rpeepp  = a_peep;
1228 #else
1229  a_old_peep = PL_peepp;
1230  PL_peepp   = a_peep;
1231 #endif
1232
1233 #if A_MULTIPLICITY
1234  call_atexit(a_teardown, aTHX);
1235 #else
1236  call_atexit(a_teardown, NULL);
1237 #endif
1238
1239  a_initialized = 1;
1240 }
1241
1242 static U32 a_booted = 0;
1243
1244 /* --- XS ------------------------------------------------------------------ */
1245
1246 MODULE = autovivification      PACKAGE = autovivification
1247
1248 PROTOTYPES: ENABLE
1249
1250 BOOT:
1251 {
1252  if (!a_booted++) {
1253   HV *stash;
1254
1255   a_op_map = ptable_new();
1256 #ifdef USE_ITHREADS
1257   MUTEX_INIT(&a_op_map_mutex);
1258 #endif
1259
1260   PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__);
1261
1262   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
1263   newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));
1264   newCONSTSUB(stash, "A_HINT_WARN",   newSVuv(A_HINT_WARN));
1265   newCONSTSUB(stash, "A_HINT_FETCH",  newSVuv(A_HINT_FETCH));
1266   newCONSTSUB(stash, "A_HINT_STORE",  newSVuv(A_HINT_STORE));
1267   newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS));
1268   newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE));
1269   newCONSTSUB(stash, "A_HINT_MASK",   newSVuv(A_HINT_MASK));
1270   newCONSTSUB(stash, "A_THREADSAFE",  newSVuv(A_THREADSAFE));
1271   newCONSTSUB(stash, "A_FORKSAFE",    newSVuv(A_FORKSAFE));
1272  }
1273
1274  a_setup();
1275 }
1276
1277 #if A_THREADSAFE
1278
1279 void
1280 CLONE(...)
1281 PROTOTYPE: DISABLE
1282 PREINIT:
1283 #if A_WORKAROUND_REQUIRE_PROPAGATION
1284  ptable *t;
1285 #endif
1286  ptable *s;
1287  GV     *gv;
1288 PPCODE:
1289  {
1290 #if A_WORKAROUND_REQUIRE_PROPAGATION
1291   dMY_CXT;
1292   {
1293    a_ptable_clone_ud ud;
1294
1295    t = ptable_new();
1296    a_ptable_clone_ud_init(ud, t, MY_CXT.owner);
1297    ptable_walk(MY_CXT.tbl, a_ptable_clone, &ud);
1298    a_ptable_clone_ud_deinit(ud);
1299   }
1300 #endif
1301   s = ptable_new();
1302  }
1303  {
1304   MY_CXT_CLONE;
1305 #if A_WORKAROUND_REQUIRE_PROPAGATION
1306   MY_CXT.tbl   = t;
1307   MY_CXT.owner = aTHX;
1308 #endif
1309   MY_CXT.seen  = s;
1310  }
1311  gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
1312  if (gv) {
1313   CV *cv = GvCV(gv);
1314   if (!PL_endav)
1315    PL_endav = newAV();
1316   SvREFCNT_inc(cv);
1317   if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
1318    SvREFCNT_dec(cv);
1319   sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &a_endav_vtbl, NULL, 0);
1320  }
1321  XSRETURN(0);
1322
1323 void
1324 _THREAD_CLEANUP(...)
1325 PROTOTYPE: DISABLE
1326 PPCODE:
1327  a_thread_cleanup(aTHX_ NULL);
1328  XSRETURN(0);
1329
1330 #endif /* A_THREADSAFE */
1331
1332 SV *
1333 _tag(SV *hint)
1334 PROTOTYPE: $
1335 CODE:
1336  RETVAL = a_tag(SvOK(hint) ? SvUV(hint) : 0);
1337 OUTPUT:
1338  RETVAL
1339
1340 SV *
1341 _detag(SV *tag)
1342 PROTOTYPE: $
1343 CODE:
1344  if (!SvOK(tag))
1345   XSRETURN_UNDEF;
1346  RETVAL = newSVuv(a_detag(tag));
1347 OUTPUT:
1348  RETVAL