+# if XSH_HAS_PERL(5, 23, 8)
+static const char *su_block_type[] = {
+ "NULL",
+ "WHEN",
+ "BLOCK",
+ "GIVEN",
+ "LOOP_ARY",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV",
+ "LOOP_LIST",
+ "LOOP_PLAIN",
+ "SUB",
+ "FORMAT",
+ "EVAL",
+ "SUBST"
+};
+# elif XSH_HAS_PERL(5, 11, 0)
+static const char *su_block_type[] = {
+ "NULL",
+ "WHEN",
+ "BLOCK",
+ "GIVEN",
+ "LOOP_FOR",
+ "LOOP_PLAIN",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV",
+ "SUB",
+ "FORMAT",
+ "EVAL",
+ "SUBST"
+};
+# elif XSH_HAS_PERL(5, 10, 0)
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "LOOP",
+ "SUBST",
+ "BLOCK",
+ "FORMAT"
+ "WHEN",
+ "GIVEN"
+};
+# else
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "LOOP",
+ "SUBST",
+ "BLOCK",
+ "FORMAT"
+};
+# endif
+# define SU_CXNAME(C) su_block_type[CxTYPE(C)]
+#endif
+
+/* for debugging. These indicate how many ENTERs each context type
+ * does before the PUSHBLOCK */
+
+static const int su_cxt_enter_count[] = {
+# if XSH_HAS_PERL(5, 23, 8)
+ 0 /* context pushes no longer do ENTERs */
+# elif XSH_HAS_PERL(5, 11, 0)
+ /* NULL WHEN BLOCK GIVEN LOOP_FOR LOOP_PLAIN LOOP_LAZYSV
+ * LOOP_LAZYIV SUB FORMAT EVAL SUBST */
+ 0, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 0
+# elif XSH_HAS_PERL(5, 10, 0)
+ /* NULL SUB EVAL LOOP SUBST BLOCK FORMAT WHEN GIVEN */
+ 0, 1, 1, 2, 0, 1, 1, 1, 1
+# else
+ /* NULL SUB EVAL LOOP SUBST BLOCK FORMAT */
+ 0, 1, 1, 2, 0, 1, 1
+# endif
+};
+
+
+
+/* push at least 'size' slots worth of padding onto the savestack */
+
+static void su_ss_push_padding(pTHX_ void *ud, I32 size) {
+ if (size <= 0)
+ return;
+ if (size < SU_SAVE_ALLOC_SIZE + 1) /* minimum possible SAVEt_ALLOC */
+ size = SU_SAVE_ALLOC_SIZE + 1;
+ XSH_D(su_debug_log(
+ "%p: push %2d padding at save_ix=%d\n",
+ ud, size, PL_savestack_ix));
+ save_alloc((size - SU_SAVE_ALLOC_SIZE)*sizeof(*PL_savestack), 0);
+}
+
+
+static void su_pop(pTHX_ void *ud);
+
+
+
+/* push an su_pop destructor onto the savestack with suitable padding.
+ * first indicates that this is the first push of a destructor */
+
+static void su_ss_push_destructor(pTHX_ void *ud, I32 depth, bool first) {
+ su_ud_origin_elem *origin = SU_UD_ORIGIN(ud);
+ I32 pad;
+
+ assert(first || origin[depth+1].orig_ix == PL_savestack_ix);
+ su_ss_push_padding(aTHX_ ud,
+ (origin[depth].orig_ix + origin[depth].offset) - PL_savestack_ix);
+ XSH_D(su_debug_log(
+ "%p: push destructor at save_ix=%d depth=%d scope_ix=%d\n",
+ ud, PL_savestack_ix, depth, PL_scopestack_ix));
+ SAVEDESTRUCTOR_X(su_pop, ud);
+ assert(first ||
+ PL_savestack_ix <= origin[depth+1].orig_ix + origin[depth+1].offset);
+}
+
+
+/* this is called during each leave_scope() via SAVEDESTRUCTOR_X */