mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-19 03:13:40 +00:00
Discussion of these changes can be found on the Mercury developers
mailing list archives from June 2018.
COPYING.LIB:
Add a special linking exception to the LGPL.
*:
Update references to COPYING.LIB.
Clean up some minor errors that have accumulated in copyright
messages.
779 lines
39 KiB
C
779 lines
39 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 1995-2006 The University of Melbourne.
|
|
// Copyright (C) 2014, 2016, 2018 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// mercury_stacks.h - definitions for manipulating the det and nondet stacks.
|
|
//
|
|
// See compiler/notes/failure.html for documentation on the nondet stack
|
|
// frame handling.
|
|
|
|
#ifndef MERCURY_STACKS_H
|
|
#define MERCURY_STACKS_H
|
|
|
|
#include "mercury_regs.h"
|
|
#include "mercury_types.h"
|
|
#include "mercury_overflow.h"
|
|
#include "mercury_debug.h"
|
|
#include "mercury_overflow.h"
|
|
#include "mercury_goto.h"
|
|
#include "mercury_tabling.h"
|
|
#include "mercury_engine.h"
|
|
|
|
#ifdef MR_STACK_FRAME_STATS
|
|
#include "mercury_dword.h"
|
|
|
|
extern MR_Dword MR_det_frame_count;
|
|
extern MR_Dword MR_det_frame_total_size;
|
|
extern MR_Word *MR_det_frame_max;
|
|
extern MR_Dword MR_non_frame_count;
|
|
extern MR_Dword MR_non_frame_total_size;
|
|
extern MR_Word *MR_non_frame_max;
|
|
|
|
// This temporary is for use in the MR_increment_dword_tmp macro only.
|
|
// Making the temporary variable global (nonlocal to the macro) allows
|
|
// the macro have the form of an expression, instead of a statement,
|
|
// without relying on GNU extensions to C.
|
|
|
|
extern MR_uint_least32_t MR_old_low_tmp;
|
|
|
|
extern void MR_init_stack_frame_stats(void);
|
|
extern void MR_print_stack_frame_stats(void);
|
|
|
|
#define MR_collect_det_frame_stats(size) \
|
|
( \
|
|
MR_increment_dword_tmp(MR_det_frame_count, 1, MR_old_low_tmp), \
|
|
MR_increment_dword_tmp(MR_det_frame_total_size, \
|
|
(size), MR_old_low_tmp), \
|
|
((MR_sp > MR_det_frame_max) ? \
|
|
(MR_det_frame_max = MR_sp) : (void) 0) \
|
|
)
|
|
#define MR_collect_non_frame_stats(slots) \
|
|
( \
|
|
MR_increment_dword_tmp(MR_non_frame_count, 1, MR_old_low_tmp), \
|
|
MR_increment_dword_tmp(MR_non_frame_total_size, \
|
|
(slots) + MR_NONDET_FIXED_SIZE, MR_old_low_tmp), \
|
|
((MR_maxfr > MR_non_frame_max) ? \
|
|
(MR_non_frame_max = MR_maxfr) : (void) 0) \
|
|
)
|
|
|
|
#else // !MR_STACK_FRAME_STATS
|
|
|
|
#define MR_collect_det_frame_stats(size) ((void) 0)
|
|
#define MR_collect_non_frame_stats(slots) ((void) 0)
|
|
|
|
#endif // !MR_STACK_FRAME_STATS
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef MR_STACK_SEGMENTS
|
|
|
|
#define MR_detstack_extend_and_check(incr) \
|
|
do { \
|
|
MR_Word *new_sp; \
|
|
MR_Word *threshold; \
|
|
\
|
|
threshold = (MR_Word *) MR_CONTEXT(MR_ctxt_detstack_zone)-> \
|
|
MR_zone_extend_threshold; \
|
|
new_sp = MR_sp + (incr); \
|
|
if (new_sp > threshold) { \
|
|
MR_save_registers(); \
|
|
new_sp = MR_new_detstack_segment(MR_sp, (incr)); \
|
|
MR_restore_registers(); \
|
|
MR_succip_word = (MR_Word) MR_ENTRY(MR_pop_detstack_segment); \
|
|
} \
|
|
MR_sp_word = (MR_Word) new_sp; \
|
|
} while (0)
|
|
|
|
#define MR_detstack_extend_and_no_check(incr) \
|
|
do { \
|
|
MR_sp_word = (MR_Word) (MR_sp + (incr)); \
|
|
} while (0)
|
|
|
|
// Check whether there is room for a new frame whose size is incr words
|
|
// in the current segment of the nondet stack, and if not, allocate
|
|
// a new segment to hold it, and a sentinel frame at the bottom of that
|
|
// new segment.
|
|
//
|
|
// The prevfr argument should be a variable that holds the value that
|
|
// the caller proposes to assign to the prevfr field of the new stack frame.
|
|
// If this macro finds it necessary to create a new segment, it will update
|
|
// this variable to point to the sentinel frame, since *that* will be
|
|
// the stack frame immediately before the new frame.
|
|
//
|
|
// The reason why we check for MR_maxfr < zone_min is that MR_maxfr may be
|
|
// in a different stack segment than MR_CONTEXT(MR_ctxt_nondetstack_zone).
|
|
// See the comment for MR_new_nondetstack_segment in mercury_stacks.c.
|
|
|
|
#define MR_nondetstack_extend_and_check(prevfr, incr) \
|
|
do { \
|
|
MR_Word *new_maxfr; \
|
|
MR_Word *threshold; \
|
|
\
|
|
threshold = (MR_Word *) MR_CONTEXT(MR_ctxt_nondetstack_zone)-> \
|
|
MR_zone_extend_threshold; \
|
|
new_maxfr = MR_maxfr + incr; \
|
|
if (MR_maxfr < MR_CONTEXT(MR_ctxt_nondetstack_zone->MR_zone_min) \
|
|
|| new_maxfr > threshold) \
|
|
{ \
|
|
MR_save_registers(); \
|
|
new_maxfr = MR_new_nondetstack_segment(MR_maxfr, (incr)); \
|
|
MR_restore_registers(); \
|
|
prevfr = new_maxfr - (incr); \
|
|
} \
|
|
MR_maxfr_word = (MR_Word) new_maxfr; \
|
|
} while (0)
|
|
|
|
extern MR_Word *MR_new_detstack_segment(MR_Word *sp, int n);
|
|
|
|
// This function reserves a stack frame of n words on the nondet stack,
|
|
// after conceptually (a) freeing any nondet stack segments that are wholly
|
|
// above old_maxfr, and (b) creating a new nondet stack segment.
|
|
// In practice, it will reuse an existing memory zone, if one is available.
|
|
// It returns the address that should be the new value of maxfr.
|
|
|
|
extern MR_Word *MR_new_nondetstack_segment(MR_Word *old_maxfr, int n);
|
|
|
|
#else // !MR_STACK_SEGMENTS
|
|
|
|
#define MR_detstack_extend_and_check(incr) \
|
|
do { \
|
|
MR_sp_word = (MR_Word) (MR_sp + (incr)); \
|
|
} while (0)
|
|
|
|
#define MR_detstack_extend_and_no_check(incr) \
|
|
do { \
|
|
MR_sp_word = (MR_Word) (MR_sp + (incr)); \
|
|
} while (0)
|
|
|
|
#define MR_nondetstack_extend_and_check(prevfr, incr) \
|
|
do { \
|
|
MR_maxfr_word = (MR_Word) (MR_maxfr + (incr)); \
|
|
} while (0)
|
|
|
|
#endif // MR_STACK_SEGMENTS
|
|
|
|
MR_declare_entry(MR_pop_detstack_segment);
|
|
MR_declare_entry(MR_pop_nondetstack_segment);
|
|
|
|
#ifdef MR_EXTEND_STACKS_WHEN_NEEDED
|
|
|
|
#define MR_detstack_post_extend_check() \
|
|
MR_IF (MR_sp >= MR_CONTEXT(MR_ctxt_detstack_zone)->MR_zone_end, ( \
|
|
MR_extend_detstack() \
|
|
))
|
|
#define MR_nondetstack_post_extend_check() \
|
|
MR_IF (MR_maxfr >= MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_end, ( \
|
|
MR_extend_nondetstack() \
|
|
))
|
|
|
|
extern void MR_extend_detstack(void);
|
|
extern void MR_extend_nondetstack(void);
|
|
|
|
#else // !MR_EXTEND_STACKS_WHEN_NEEDED
|
|
|
|
#define MR_detstack_post_extend_check() ((void) 0)
|
|
#define MR_nondetstack_post_extend_check() ((void) 0)
|
|
|
|
#endif // MR_EXTEND_STACKS_WHEN_NEEDED
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEFINITIONS FOR MANIPULATING THE DET STACK
|
|
|
|
// The first stack slot in each stack frame is MR_stackvar(1) while MR_sp
|
|
// points to the topmost used word on the stack: this is why we need to add 1.
|
|
// In compiler-generated code, the value of n is always known, so the C
|
|
// compiler will be able to compute 1-n at compile time. The debugger will
|
|
// need to perform the addition at runtime after it gets the value of n from
|
|
// a layout structure, but this is not performance critical. It is useful
|
|
// to have MR_sp and MR_maxfr both point to the topmost used words on their
|
|
// stacks when moving stack segments around in minimal model tabling.
|
|
|
|
#define MR_based_stackvar(base_sp, n) ((base_sp)[1 - (n)])
|
|
#define MR_stackvar(n) MR_based_stackvar(MR_sp, (n))
|
|
#define MR_sv(n) MR_stackvar(n)
|
|
#define MR_parent_sv(n) MR_based_stackvar(MR_parent_sp, (n))
|
|
|
|
#define MR_incr_sp(n) \
|
|
do { \
|
|
MR_debugincrsp(n, MR_sp); \
|
|
MR_detstack_extend_and_check(n); \
|
|
MR_detstack_post_extend_check(); \
|
|
MR_detstack_overflow_check_msg("MR_incr_sp"); \
|
|
MR_collect_det_frame_stats(n); \
|
|
} while (0)
|
|
|
|
// We reserve MR_stack_margin_size_words words at the end of every det stack
|
|
// segment for leaf procedures whose stack frames fit into that many words.
|
|
// The check is done in compiler/llds_out_instr; you can find it if you
|
|
// search for uses of max_leaf_stack_frame_size.
|
|
|
|
#define MR_incr_sp_leaf(n) \
|
|
do { \
|
|
MR_debugincrsp(n, MR_sp); \
|
|
MR_detstack_extend_and_no_check(n); \
|
|
MR_detstack_post_extend_check(); \
|
|
MR_detstack_overflow_check_msg("MR_incr_sp_leaf"); \
|
|
MR_collect_det_frame_stats(n); \
|
|
} while (0)
|
|
|
|
#define MR_decr_sp(n) \
|
|
do { \
|
|
MR_debugdecrsp(n, MR_sp); \
|
|
MR_sp_word = (MR_Word) (MR_sp - (n)); \
|
|
MR_detstack_underflow_check_msg("MR_decr_sp"); \
|
|
} while (0)
|
|
|
|
#define MR_decr_sp_and_return(n) \
|
|
do { \
|
|
MR_Code *return_addr; \
|
|
\
|
|
return_addr = (MR_Word *) MR_stackvar(n); \
|
|
MR_debugdecrsp(n, MR_sp); \
|
|
MR_sp_word = (MR_Word) (MR_sp - (n)); \
|
|
MR_detstack_underflow_check_msg("MR_decr_sp_and_return"); \
|
|
MR_debugproceed(); \
|
|
MR_GOTO(return_addr); \
|
|
} while (0)
|
|
|
|
// The msg argument of MR_incr_sp_push_msg is not used at runtime. It is
|
|
// intended for use by tools/frame_sizes, which scans compiler-generated C
|
|
// source files.
|
|
|
|
#define MR_incr_sp_push_msg(n, msg) MR_incr_sp(n)
|
|
|
|
#define MR_decr_sp_pop_msg(n) MR_decr_sp(n)
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEFINITIONS FOR NONDET STACK FRAMES
|
|
|
|
#define MR_PREVFR (-0) // prev frame on stack, set up at call
|
|
#define MR_REDOIP (-1) // in this proc, set up at clause entry
|
|
#define MR_REDOFR (-2) // value for curfr on backtracking
|
|
#define MR_SUCCIP (-3) // in caller proc, set up at call
|
|
#define MR_SUCCFR (-4) // frame of caller proc, set up at call
|
|
#define MR_TMP_DETFR (-3) // sp, in model_det temp frames only
|
|
#define MR_TABLE_DETFR (-5) // sp, in minimal model main frames only
|
|
|
|
// This setup allows MR_USE_MINIMAL_MODEL_STACK_COPY_EXTRA_SLOT to be defined
|
|
// even if MR_USE_MINIMAL_MODEL_STACK_COPY isn't, which can be useful for
|
|
// performance testing.
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
#define MR_USE_MINIMAL_MODEL_STACK_COPY_EXTRA_SLOT
|
|
#endif
|
|
|
|
// MR_Code that traverses the nondet stack depends on the relationship
|
|
// MR_NONDET_TEMP_SIZE < MR_DET_TEMP_SIZE < MR_NONDET_FIXED_SIZE.
|
|
// All three sizes are measured in words.
|
|
|
|
// prevfr, redoip, redofr
|
|
#define MR_NONDET_TEMP_SIZE 3
|
|
|
|
// prevfr, redoip, redofr, detfr
|
|
#define MR_DET_TEMP_SIZE 4
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY_EXTRA_SLOT
|
|
// prevfr, redoip, redofr, succip, succfr, sp
|
|
#define MR_NONDET_FIXED_SIZE 6
|
|
#else
|
|
// prevfr, redoip, redofr, succip, succfr
|
|
#define MR_NONDET_FIXED_SIZE 5
|
|
#endif
|
|
|
|
// In grades that have stack segments, each segment has a sentinel frame
|
|
// at the bottom. These look like ordinary frames whose succfr and succip
|
|
// fields are not used for their usual purpose. For further documentation
|
|
// on these fields, see the comment on MR_new_nondetstack_segment in
|
|
// mercury_stacks.c.
|
|
|
|
#define MR_SAVEVAL (-MR_NONDET_FIXED_SIZE)
|
|
// Saved values start at this offset.
|
|
|
|
#define MR_prevfr_addr(fr) (&((MR_Word *) (fr))[MR_PREVFR])
|
|
#define MR_redoip_addr(fr) (&((MR_Word *) (fr))[MR_REDOIP])
|
|
#define MR_redofr_addr(fr) (&((MR_Word *) (fr))[MR_REDOFR])
|
|
#define MR_succip_addr(fr) (&((MR_Word *) (fr))[MR_SUCCIP])
|
|
#define MR_succfr_addr(fr) (&((MR_Word *) (fr))[MR_SUCCFR])
|
|
#define MR_tmp_detfr_addr(fr) (&((MR_Word *) (fr))[MR_TMP_DETFR])
|
|
#define MR_table_detfr_addr(fr) (&((MR_Word *) (fr))[MR_TABLE_DETFR])
|
|
|
|
#define MR_succip_slot_addr(fr) (&(((MR_Code **) (fr))[MR_SUCCIP]))
|
|
|
|
#define MR_prevfr_slot_word(fr) (((MR_Word *) (fr))[MR_PREVFR])
|
|
#define MR_redoip_slot_word(fr) (((MR_Word *) (fr))[MR_REDOIP])
|
|
#define MR_redofr_slot_word(fr) (((MR_Word *) (fr))[MR_REDOFR])
|
|
#define MR_succip_slot_word(fr) (((MR_Word *) (fr))[MR_SUCCIP])
|
|
#define MR_succfr_slot_word(fr) (((MR_Word *) (fr))[MR_SUCCFR])
|
|
#define MR_tmp_detfr_slot_word(fr) (((MR_Word *) (fr))[MR_TMP_DETFR])
|
|
#define MR_table_detfr_slot_word(fr) (((MR_Word *) (fr))[MR_TABLE_DETFR])
|
|
|
|
#define MR_prevfr_slot(fr) ((MR_Word *) MR_prevfr_slot_word(fr))
|
|
#define MR_redoip_slot(fr) ((MR_Code *) MR_redoip_slot_word(fr))
|
|
#define MR_redofr_slot(fr) ((MR_Word *) MR_redofr_slot_word(fr))
|
|
#define MR_succip_slot(fr) ((MR_Code *) MR_succip_slot_word(fr))
|
|
#define MR_succfr_slot(fr) ((MR_Word *) MR_succfr_slot_word(fr))
|
|
#define MR_tmp_detfr_slot(fr) ((MR_Word *) MR_tmp_detfr_slot_word(fr))
|
|
#define MR_table_detfr_slot(fr) ((MR_Word *) MR_table_detfr_slot_word(fr))
|
|
|
|
#define MR_based_framevar_addr(fr, n) \
|
|
(&(((MR_Word *) (fr))[MR_SAVEVAL + 1 - (n)]))
|
|
|
|
#define MR_based_framevar(fr, n) (((MR_Word *) (fr))[MR_SAVEVAL + 1 - (n)])
|
|
#define MR_framevar(n) MR_based_framevar(MR_curfr, n)
|
|
#define MR_fv(n) MR_framevar(n)
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEFINITIONS FOR MANIPULATING THE NONDET STACK
|
|
|
|
#define MR_nondet_zone_min (MR_CONTEXT(MR_ctxt_nondetstack_zone)->MR_zone_min)
|
|
|
|
#ifdef MR_EXTEND_STACKS_WHEN_NEEDED
|
|
|
|
// Note: these macros don't work in the presence of stack segments,
|
|
// which is why automatic stack extension and stack segments cannot
|
|
// both enabled at the same time.
|
|
|
|
#define MR_save_maxfr(lval) \
|
|
do { \
|
|
lval = (MR_Word) (MR_maxfr - MR_nondet_zone_min); \
|
|
} while (0)
|
|
|
|
#define MR_restore_maxfr(lval) \
|
|
do { \
|
|
MR_maxfr_word = (MR_Word) \
|
|
(((MR_Word *) lval) + MR_nondet_zone_min); \
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define MR_save_maxfr(lval) \
|
|
do { \
|
|
lval = (MR_Word) MR_maxfr; \
|
|
} while (0)
|
|
#define MR_restore_maxfr(lval) \
|
|
do { \
|
|
MR_maxfr_word = (MR_Word) lval; \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY_EXTRA_SLOT
|
|
#define MR_maybe_fill_table_detfr_slot() \
|
|
do { \
|
|
MR_table_detfr_slot_word(MR_curfr) = MR_sp_word; \
|
|
} while (0)
|
|
#else
|
|
#define MR_maybe_fill_table_detfr_slot() \
|
|
((void) 0)
|
|
#endif
|
|
|
|
#define MR_mkframe_basic(predname, numslots) \
|
|
do { \
|
|
MR_Word *prevfr; \
|
|
MR_Word *succfr; \
|
|
\
|
|
prevfr = MR_maxfr; \
|
|
succfr = MR_curfr; \
|
|
MR_nondetstack_extend_and_check(prevfr, \
|
|
MR_NONDET_FIXED_SIZE + (numslots)); \
|
|
MR_nondetstack_post_extend_check(); \
|
|
MR_curfr_word = MR_maxfr_word; \
|
|
MR_prevfr_slot_word(MR_curfr) = (MR_Word) prevfr; \
|
|
MR_succip_slot_word(MR_curfr) = (MR_Word) MR_succip; \
|
|
MR_succfr_slot_word(MR_curfr) = (MR_Word) succfr; \
|
|
MR_redofr_slot_word(MR_curfr) = MR_curfr_word; \
|
|
MR_maybe_fill_table_detfr_slot(); \
|
|
MR_debugmkframe(predname); \
|
|
MR_nondetstack_overflow_check_msg("MR_mkframe_basic"); \
|
|
MR_collect_non_frame_stats(numslots); \
|
|
} while (0)
|
|
|
|
#define MR_mkframe(predname, numslots, redoip) \
|
|
do { \
|
|
MR_mkframe_basic(predname, numslots); \
|
|
MR_redoip_slot_word(MR_curfr) = (MR_Word) redoip; \
|
|
} while (0)
|
|
|
|
#define MR_mkframe_no_redoip(predname, numslots) \
|
|
do { \
|
|
MR_mkframe_basic(predname, numslots); \
|
|
} while (0)
|
|
|
|
// Just like mkframe, but also reserves space for a struct
|
|
// with the given tag at the bottom of the nondet stack frame.
|
|
#define MR_mkpragmaframe(predname, numslots, structname, redoip) \
|
|
do { \
|
|
MR_mkframe_basic(predname, numslots + \
|
|
MR_bytes_to_words(sizeof(struct structname))); \
|
|
MR_redoip_slot_word(MR_curfr) = (MR_Word) redoip; \
|
|
} while (0)
|
|
|
|
#define MR_mkpragmaframe_no_redoip(predname, numslots, structname) \
|
|
do { \
|
|
MR_mkframe_basic(predname, numslots + \
|
|
MR_bytes_to_words(sizeof(struct structname))); \
|
|
} while (0)
|
|
|
|
#define MR_mktempframe(redoip) \
|
|
do { \
|
|
MR_Word *prevfr; \
|
|
\
|
|
prevfr = MR_maxfr; \
|
|
MR_nondetstack_extend_and_check(prevfr, MR_NONDET_TEMP_SIZE); \
|
|
MR_prevfr_slot_word(MR_maxfr) = (MR_Word) prevfr; \
|
|
MR_redoip_slot_word(MR_maxfr) = (MR_Word) redoip; \
|
|
MR_redofr_slot_word(MR_maxfr) = MR_curfr_word; \
|
|
MR_debugmktempframe(); \
|
|
MR_nondetstack_overflow_check_msg("MR_mktempframe"); \
|
|
} while (0)
|
|
|
|
#define MR_mkdettempframe(redoip) \
|
|
do { \
|
|
MR_Word *prevfr; \
|
|
\
|
|
prevfr = MR_maxfr; \
|
|
MR_nondetstack_extend_and_check(prevfr, MR_DET_TEMP_SIZE); \
|
|
MR_prevfr_slot_word(MR_maxfr) = (MR_Word) prevfr; \
|
|
MR_redoip_slot_word(MR_maxfr) = (MR_Word) redoip; \
|
|
MR_redofr_slot_word(MR_maxfr) = MR_curfr_word; \
|
|
MR_tmp_detfr_slot_word(MR_maxfr) = MR_sp_word; \
|
|
MR_debugmkdettempframe(); \
|
|
MR_nondetstack_overflow_check_msg("MR_mkdettempframe"); \
|
|
} while (0)
|
|
|
|
#define MR_succeed() \
|
|
do { \
|
|
MR_Word *childfr; \
|
|
\
|
|
MR_debugsucceed(); \
|
|
childfr = MR_curfr; \
|
|
MR_curfr_word = MR_succfr_slot_word(childfr); \
|
|
MR_GOTO(MR_succip_slot(childfr)); \
|
|
} while (0)
|
|
|
|
#define MR_succeed_discard() \
|
|
do { \
|
|
MR_Word *childfr; \
|
|
\
|
|
MR_debugsucceeddiscard(); \
|
|
childfr = MR_curfr; \
|
|
MR_maxfr_word = MR_prevfr_slot_word(childfr); \
|
|
MR_curfr_word = MR_succfr_slot_word(childfr); \
|
|
MR_GOTO(MR_succip_slot(childfr)); \
|
|
} while (0)
|
|
|
|
#define MR_fail() \
|
|
do { \
|
|
MR_debugfail(); \
|
|
MR_maxfr_word = MR_prevfr_slot_word(MR_maxfr); \
|
|
MR_curfr_word = MR_redofr_slot_word(MR_maxfr); \
|
|
MR_nondetstack_underflow_check_msg("MR_fail"); \
|
|
MR_GOTO(MR_redoip_slot(MR_maxfr)); \
|
|
} while (0)
|
|
|
|
#define MR_redo() \
|
|
do { \
|
|
MR_debugredo(); \
|
|
MR_curfr_word = MR_redofr_slot_word(MR_maxfr); \
|
|
MR_GOTO(MR_redoip_slot(MR_maxfr)); \
|
|
} while (0)
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// DEFINITIONS FOR EXCEPTION HANDLING
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
#define MR_IF_NOT_CONSERVATIVE_GC(x)
|
|
#else
|
|
#define MR_IF_NOT_CONSERVATIVE_GC(x) x
|
|
#endif
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
#define MR_IF_USE_TRAIL(x) x
|
|
#else
|
|
#define MR_IF_USE_TRAIL(x)
|
|
#endif
|
|
|
|
// This enum specifies the kind of handler in an exception handler
|
|
// nondet stack frame.
|
|
|
|
enum MR_HandlerCodeModel {
|
|
// For these three values, the exception handler is a Mercury closure with
|
|
// the specified determinism. If an exception occurs, then after the
|
|
// Mercury stacks have been unwound, the closure will be called.
|
|
|
|
MR_MODEL_DET_HANDLER,
|
|
MR_MODEL_SEMI_HANDLER,
|
|
MR_MODEL_NON_HANDLER,
|
|
// For this value, the exception will be handled by C code using
|
|
// setjmp/longjmp. If an exception occurs, then after the Mercury stacks
|
|
// have been unwound, `MR_longjmp(MR_ENGINE(MR_eng_jmp_buf))' will be
|
|
// called.
|
|
|
|
MR_C_LONGJMP_HANDLER
|
|
};
|
|
|
|
// Define a struct for the framevars that we use in an exception handler
|
|
// nondet stack frame. This struct gets allocated on the nondet stack
|
|
// using MR_mkpragmaframe(), with a special redoip of
|
|
// `MR_exception_handler_do_fail'.
|
|
|
|
typedef struct MR_Exception_Handler_Frame_struct {
|
|
// The `code_model' field is used to identify what kind of handler it is.
|
|
// It holds values of type MR_HandlerCodeModel (see above), but it is
|
|
// declared to have type `MR_Word' to ensure that everything remains
|
|
// word-aligned.
|
|
|
|
MR_Word MR_excp_code_model;
|
|
|
|
// If code_model is MR_MODEL_*_HANDLER, then the `handler' field holds the
|
|
// Mercury closure for the handler, which will be a closure of the
|
|
// specified determinism. If code_model is MR_C_LONGJMP, then this field
|
|
// is unused.
|
|
|
|
MR_Word MR_excp_handler;
|
|
|
|
// The value of MR_trace_from_full, saved at the time the frame was
|
|
// created. This holds a value of type MR_bool, but it is declared
|
|
// to have type MR_Word to ensure that everything remains word-aligned.
|
|
|
|
MR_Word MR_excp_full_trace;
|
|
|
|
// The remaining fields hold stuff that must be saved in order
|
|
// to unwind the Mercury stacks.
|
|
|
|
// The det stack pointer.
|
|
MR_Word *MR_excp_stack_ptr;
|
|
|
|
// The trail state.
|
|
MR_IF_USE_TRAIL(
|
|
MR_Word MR_excp_trail_ptr;
|
|
MR_Word MR_excp_ticket_counter;
|
|
)
|
|
|
|
// The heap state.
|
|
MR_IF_NOT_CONSERVATIVE_GC(
|
|
MR_Word *MR_excp_heap_ptr;
|
|
MR_Word *MR_excp_solns_heap_ptr;
|
|
MR_MemoryZone *MR_excp_heap_zone;
|
|
)
|
|
} MR_Exception_Handler_Frame;
|
|
|
|
// In deep profiling grades, we need (a) 1 stack slot (framevar 1)
|
|
// to save the input closure across the call port code; (b) up to 2 stack slots
|
|
// (framevars 1 and 2) to store the result and maybe the success indicator,
|
|
// and (c) 4 stack slots (frame vars 3 to 6) for use by the profiling
|
|
// routines themselves. We can reuse framevar 1 for two purposes because their
|
|
// live ranges do not overlap.
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
#define MR_EXCEPTION_FRAMEVARS 6
|
|
#define MR_EXCEPTION_FIRST_DEEP_SLOT 3
|
|
#else
|
|
#define MR_EXCEPTION_FRAMEVARS 2
|
|
#endif
|
|
|
|
#define MR_EXCEPTION_STRUCT \
|
|
(((MR_Exception_Handler_Frame *) \
|
|
(MR_curfr + 1 - MR_EXCEPTION_FRAMEVARS - MR_NONDET_FIXED_SIZE)) - 1)
|
|
|
|
#define MR_create_exception_handler(name, \
|
|
handler_code_model, handler_closure, redoip) \
|
|
do { \
|
|
/* \
|
|
** Create a handler on the stack with the special redoip of \
|
|
** `MR_exception_handler_do_fail' (we'll look for this redoip when \
|
|
** unwinding the nondet stack in builtin_throw/1), and save the stuff \
|
|
** we will need if an exception is thrown. \
|
|
*/ \
|
|
MR_mkpragmaframe((name), MR_EXCEPTION_FRAMEVARS, \
|
|
MR_Exception_Handler_Frame_struct, \
|
|
MR_ENTRY(MR_exception_handler_do_fail)); \
|
|
/* Record the handler's code model. */ \
|
|
MR_EXCEPTION_STRUCT->MR_excp_code_model = (handler_code_model); \
|
|
/* Save the handler's closure. */ \
|
|
MR_EXCEPTION_STRUCT->MR_excp_handler = (handler_closure); \
|
|
/* Save the full tracing flag. */ \
|
|
MR_EXCEPTION_STRUCT->MR_excp_full_trace = (MR_Word) MR_trace_from_full;\
|
|
/* Save the det stack pointer. */ \
|
|
MR_EXCEPTION_STRUCT->MR_excp_stack_ptr = MR_sp; \
|
|
MR_IF_NOT_CONSERVATIVE_GC( \
|
|
/* Save the heap and solutions heap pointers. */ \
|
|
MR_EXCEPTION_STRUCT->MR_excp_heap_ptr = MR_hp; \
|
|
MR_EXCEPTION_STRUCT->MR_excp_solns_heap_ptr = MR_sol_hp; \
|
|
MR_EXCEPTION_STRUCT->MR_excp_heap_zone = \
|
|
MR_ENGINE(MR_eng_heap_zone); \
|
|
) \
|
|
MR_IF_USE_TRAIL( \
|
|
/* Save the trail state. */ \
|
|
MR_mark_ticket_stack(MR_EXCEPTION_STRUCT->MR_excp_ticket_counter); \
|
|
MR_store_ticket(MR_EXCEPTION_STRUCT->MR_excp_trail_ptr); \
|
|
) \
|
|
\
|
|
/* \
|
|
** Now we need to create another frame. This is so that we can be sure \
|
|
** that no-one will hijack the redoip of the special frame we created \
|
|
** above. (The compiler sometimes generates ``hijacking'' code that \
|
|
** saves the topmost redoip on the stack, and temporarily replaces it \
|
|
** with a new redoip that will do some processing on failure before \
|
|
** restoring the original redoip. This would cause problems when doing \
|
|
** stack unwinding in builtin_throw/1, because we wouldn't be able to \
|
|
** find the special redoip. But code will only ever hijack the topmost \
|
|
** frame, so we can avoid this by creating a second frame above the \
|
|
** special frame.) \
|
|
*/ \
|
|
MR_mktempframe(redoip); \
|
|
} while (0)
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
|
|
// DEFINITIONS FOR GENERATOR STACK FRAMES
|
|
|
|
// The generator stack has one entry for each call to a minimal model tabled
|
|
// procedure that is (a) acting as the generator for its subgoal and (b) is
|
|
// in the active state. The MR_gen_frame field points to the home nondet stack
|
|
// frame of the generator, and the MR_gen_subgoal field points to the
|
|
// generator's data structure.
|
|
//
|
|
// In systems such as XSB, each choice point has a flag saying whether it is
|
|
// an active generator or not, and if yes, where its subgoal's tabling
|
|
// information is stored. In Mercury, the equivalent test checks whether
|
|
// the generator stack has an entry whose MR_gen_frame field matches the
|
|
// address of the nondet stack frame. This approach that minimizes the
|
|
// performance impact of minimal model evaluation on non-tabled procedures.
|
|
|
|
struct MR_GenStackFrameStruct {
|
|
MR_Word *MR_gen_frame;
|
|
MR_SubgoalPtr MR_gen_subgoal;
|
|
};
|
|
|
|
extern MR_Integer MR_gen_next_var;
|
|
extern MR_GenStackFrame *MR_gen_stack_var;
|
|
|
|
extern void MR_push_generator(MR_Word *frame_addr,
|
|
MR_SubgoalPtr subgoal);
|
|
extern MR_Subgoal *MR_top_generator_table(void);
|
|
extern void MR_pop_generator(void);
|
|
extern void MR_print_gen_stack(FILE *fp);
|
|
extern void MR_print_any_gen_stack(FILE *fp,
|
|
MR_Integer gen_next,
|
|
MR_GenStackFrame *gen_block);
|
|
|
|
// DEFINITIONS FOR CUT STACK FRAMES
|
|
|
|
// The cut stack has one entry for each commit goal currently active.
|
|
// (A commit goal is an existential quantification that has a different
|
|
// determinism than the goal being quantified over.) We use the cut stack
|
|
// to prevent generators in the quantified being left active but incomplete
|
|
// when the commit goal succeeds. We need to clean up any such generators
|
|
// because otherwise, consumers will be depend on the generator to find all
|
|
// the answers to the generator's subgoal, but the generator will never
|
|
// compute any more answers, since it will never be backtracked into.
|
|
// The MR_cut_generators field of a cut stack entry contains the list of
|
|
// generators that are inside the corresponding commit and not inside
|
|
// some nested commit.
|
|
//
|
|
// The MR_cut_frame field specifies the address of the nondet stack frame
|
|
// (it should be a temp frame) used by the commit to get control on the
|
|
// failure of the quantified goal. The MR_cut_gen_next field records
|
|
// the value of MR_gen_next (the size of the generator stack) when the
|
|
// commit goal started. When the quantified goal succeeds, the commit cuts
|
|
// away the nondet stack frame at and above MR_cut_frame; we must also throw
|
|
// away the generator stack entries that act as markers on the discarded nondet
|
|
// stack frames. This means the generator stack entries at index
|
|
// MR_cut_gen_next and above.
|
|
//
|
|
// The MR_cut_depth field records the depth of the cut stack entry in
|
|
// the interleaving of the cut stack and pneg stack entries dictated by
|
|
// the nesting of committed choice and possibly negated contexts currently
|
|
// active.
|
|
|
|
typedef struct MR_CutGeneratorListNode *MR_CutGeneratorList;
|
|
struct MR_CutGeneratorListNode {
|
|
MR_SubgoalPtr MR_cut_generator_ptr;
|
|
MR_CutGeneratorList MR_cut_next_generator;
|
|
};
|
|
|
|
struct MR_CutStackFrameStruct {
|
|
MR_Word *MR_cut_frame;
|
|
MR_CutGeneratorList MR_cut_generators;
|
|
MR_Integer MR_cut_gen_next;
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
int MR_cut_depth;
|
|
#endif
|
|
};
|
|
|
|
extern MR_Integer MR_cut_next_var;
|
|
extern MR_CutStackFrame *MR_cut_stack_var;
|
|
|
|
extern void MR_commit_mark(void);
|
|
extern void MR_commit_cut(void);
|
|
|
|
extern void MR_register_generator_ptr(MR_SubgoalPtr);
|
|
extern void MR_print_cut_stack(FILE *fp);
|
|
extern void MR_print_any_cut_stack(FILE *fp,
|
|
MR_Integer cut_next,
|
|
MR_CutStackFrame *cut_block);
|
|
|
|
// DEFINITIONS FOR PNEG STACK FRAMES
|
|
|
|
// The pneg stack has one entry for each possibly negated context currently
|
|
// active. (Possibly negated contexts include the conditions of if-then-elses
|
|
// as well negated goals.) The MR_pneg_consumers field of a pneg stack entry
|
|
// records all the consumers that are inside the corresponding possibly negated
|
|
// context and not inside any nested possibly negated context. When the goal
|
|
// in the possibly negated context fails, we check whether any of these
|
|
// consumers are waiting for more answers from their generator. If yes,
|
|
// then the failure is an artifact of the tabling implementation, and
|
|
// committing to the else branch of the if-then-else or continuing after the
|
|
// negated goal would be incorrect, so we abort the program.
|
|
//
|
|
// The MR_pneg_frame field specifies the address of the nondet stack frame
|
|
// (it should be a temp frame) used by the negation or if-then-else to get
|
|
// control on the failure of the possibly negated goal. The MR_pneg_gen_next
|
|
// field records the value of MR_gen_next (the size of the generator stack)
|
|
// when the possibly negated goal started. Currently, neither field is used.
|
|
//
|
|
// The MR_pneg_depth field records the depth of the pneg stack entry in
|
|
// the interleaving of the cut stack and pneg stack entries dictated by
|
|
// the nesting of committed choice and possibly negated contexts currently
|
|
// active.
|
|
|
|
struct MR_PNegConsumerListNodeStruct {
|
|
MR_Consumer *MR_pneg_consumer_ptr;
|
|
MR_PNegConsumerList MR_pneg_next_consumer;
|
|
};
|
|
|
|
struct MR_PNegStackFrameStruct {
|
|
MR_PNegConsumerList MR_pneg_consumers;
|
|
MR_Word *MR_pneg_frame;
|
|
#ifdef MR_MINIMAL_MODEL_DEBUG
|
|
MR_Integer MR_pneg_gen_next;
|
|
int MR_pneg_depth;
|
|
#endif
|
|
};
|
|
|
|
extern MR_Integer MR_pneg_next_var;
|
|
extern MR_PNegStackFrame *MR_pneg_stack_var;
|
|
|
|
extern void MR_register_suspension(MR_Consumer *consumer);
|
|
extern void MR_pneg_enter_cond(void);
|
|
extern void MR_pneg_enter_then(void);
|
|
extern void MR_pneg_enter_else(const char *context);
|
|
|
|
extern void MR_print_pneg_stack(FILE *fp);
|
|
extern void MR_print_any_pneg_stack(FILE *fp,
|
|
MR_Integer pneg_next,
|
|
MR_PNegStackFrame *pneg_block);
|
|
|
|
#endif // MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
|
|
#endif // not MERCURY_STACKS_H
|