// vim: ts=4 sw=4 expandtab ft=c

// Copyright (C) 1997-2000, 2002-2003, 2007 The University of Melbourne.
// Copyright (C) 2016, 2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.

// deepcopy.h - declares the MR_deep_copy() function.

#ifndef MERCURY_DEEP_COPY_H
#define MERCURY_DEEP_COPY_H

#include "mercury_types.h"          // for `MR_Word'
#include "mercury_bitmap.h"
#include "mercury_type_info.h"      // for `MR_TypeInfo'
#include "mercury_conf.h"           // for `MR_MIGHT_RECLAIM_HP_ON_FAILURE'

// MR_deep_copy:
//
// Copy a data item, completely.
//
// The copying is done depth first. Any part of the data structure that is
// outside the given upper and lower bounds not be copied, instead a reference
// to the original data will be used. For conservative gc grades, the entire
// data structure will be copied, as there is no heap.
//
// The caller must provide the type_info describing the type of this
// data structure. It must also provide the upper and lower limits;
// if no limits are desired, pass NULL as the lower_limit.
//
// Deep copy returns the actual data that it copied, which may need to be
// stored on the heap, or put in a register or stack slot (depending on
// what you are using deep copy for). This may be a tagged pointer, or,
// if the data is just a simple type like a constant or integer,
// it will be the constant or integer itself.
//
// Please note - MR_deep_copy increments the heap pointer, however on
// some platforms (notably, SPARCs) the register-windows mean the transient
// Mercury registers may be lost. So before calling MR_deep_copy, call
// MR_save_transient_hp();
//
// MR_deep_copy will use MR_restore_transient_hp() to restore and modify
// the heap pointer, and then call MR_save_transient_hp() to save it again.
// (This may also restore/save other registers in the process.)
//
// After calling MR_deep_copy, be sure to do a MR_restore_transient_hp();
// so that the registers are restored.
//
// If writing a C function that calls MR_deep_copy, make sure you document that
// around your function, MR_save_transient_hp()/MR_restore_transient_hp()
// need to be used.
//
// Deep copy does not preserve sharing of subterms. Each subterm is copied
// in full, except for data items that are stored outside the heap limits.
//  XXX For some applications, sharing is useful. For others we want a copy
// that is completely unique. We should modify MR_deep_copy to do both.

extern MR_Word  MR_deep_copy(MR_Word data, MR_TypeInfo type_info,
                    const MR_Word *lower_limit, const MR_Word *upper_limit);

// MR_agc_deep_copy:
//
// Just like MR_deep_copy(), but it will leave forwarding pointers
// in the old data (destructively). lower_limit and upper_limit give
// the boundaries for copying data, and the boundaries for leaving
// forwarding pointers.
//
// Data will be copied to wherever the heap pointer is pointing.
//
// A forwarding pointer will be left simply by copying the new value
// of the data into the old location. If the data was a tagged pointer,
// the pointer will now refer to the rest of the data on the new heap.
// (If the data wasn't a tagged pointer, it will be a constant anyway).
//
// The upper and lower limits allow forwarding pointers to be detected
// and treated just as if they were pointers off the heap (say to a constant
// data structure in the data segment of the program).
//
// Note: You cannot pass NULL as the lower_limit to MR_agc_deep_copy
// (which is possible with normal MR_deep_copy).

extern MR_Word  MR_agc_deep_copy(MR_Word data, MR_TypeInfo type_info,
                    const MR_Word *lower_limit, const MR_Word *upper_limit);

// This holds a bitmap used by MR_agc_deep_copy() to record which objects
// have already been copied and hence contain forwarding pointers.
// It gets initialized by MR_garbage_collect().

extern MR_Word *MR_has_forwarding_pointer;

// MR_make_permanent:
//
// Returns a copy of term that can be accessed safely even after Mercury
// execution has backtracked past the point at which the term was allocated.
//
// Note that if we are never going to reclaim heap on failure (e.g. in
// conservative GC grades) then nothing needs to be done, and hence the term
// is just returned.
//
// When not using a conservative GC grade, MR_save_transient_hp() and
// MR_restore_transient_hp() need to be used around this function.
// (When using a conservative GC grade, these macros are harmless,
// so they can be used then too.)

#define MR_make_permanent(term, type_info)                              \
    MR_make_long_lived((term), (type_info), NULL)

// MR_make_long_lived:
//
// This is the same as MR_make_permanent, except that if limit is an
// address on the heap, parts of term that are "older" than limit will
// not be copied. This is useful when you know that the permanent copy
// of term will not be accessed after the heap pointer has backtracked
// beyond limit. Naturally, this always occurs when the permanent term
// is to be stored in *limit.
//
// I'd like to describe the limit argument without referring to the "heap,"
// but don't see how to.

#ifndef MR_MIGHT_RECLAIM_HP_ON_FAILURE
  #define MR_make_long_lived(term, type_info, lower_limit) (term)
#else
  extern MR_Word    MR_make_long_lived(MR_Word term, MR_TypeInfo type_info,
                        MR_Word *lower_limit);
#endif

#endif // not MERCURY_DEEP_COPY_H
