Files
mercury/runtime/mercury_term_size.c
Zoltan Somogyi 86f563a94d Pack subword-sized arguments next to a remote sectag.
compiler/du_type_layout.m:
    If the --allow-packing-remote-sectag option is set, then try to pack
    an initial subsequence of subword-sized arguments next to remote sectags.

    To allow the polymorphism transformation to put the type_infos and/or
    typeclass_infos it adds to a function symbol's argument list at the
    *front* of that argument list, pack arguments next to remote sectags
    only in function symbols that won't have any such extra arguments
    added to them.

    Do not write all new code for the new optimization; instead, generalize
    the code that already does a very similar job for packing args next to
    local sectags.

    Delete the code we used to have that picked the packed representation
    over the base unpacked representation only if it reduced the
    "rounded-to-even" number of words. A case could be made for its usefulness,
    but in the presence of the new optimization the extra code complexity
    it requires is not worth it (in my opinion).

    Extend the code that informs users about possible argument order
    rearrangements that yield better packing to take packing next to sectags
    into account.

compiler/hlds_data.m:
    Provide a representation for cons_tags that use the new optimization.
    Instead of adding a new cons_tag, we do this by replacing several old
    cons_tags that all represent pointers to memory cells with a single
    cons_tag named remote_args_tag with an argument that selects among
    the old cons_tags being replaced, and adding a new alternative inside
    this new type. The new alternative is remote_args_shared with a
    remote_sectag whose size is rsectag_subword(...).

    Instead of representing the value of the "data" field in classes
    on the Java and C# backends as a strange kind of secondary tag
    that is added to a memory cell by a class constructor instead of
    having to be explicitly added to the front of the argument vector
    by the code of a unification, represent it more directly as separate
    kind of remote_args_tag. Continuing to treat it as a sectag would have
    been very confusing to readers of the code of ml_unify_gen_*.m in the
    presence of the new optimization.

    Replacing several cons_tags that were usually treated similarly with
    one cons_tag simplifies many switches. Instead of an switch with that
    branches to the same switch arm for single_functor_tag, unshared_tag
    and shared_remote_tag, and then switches on these three tags again
    to get e.g. the primary tag of each, the new code of the switch arm
    is executed for just cons_tag value (remote_args_tag), and switches
    on the various kinds of remote args tags only when it needs to.
    In is also more natural to pass around the argument of remote_args_tag
    than to pass around a variable of type cons_tag that can be bound to only
    single_functor_tag, unshared_tag or shared_remote_tag.

    Add an XXX about possible further steps along these lines, such as
    making a new cons_tag named something like "user_const_tag" represent
    all user-visible constants.

compiler/unify_gen_construct.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_gen_test.m:
compiler/unify_gen_util.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
compiler/ml_unify_gen_test.m:
compiler/ml_unify_gen_util.m:
    Implement X = f(Yi) unifications where f uses the new representation,
    i.e. some of its arguments are stored next to a remote sectag.

    Some of the Yi are stored in a tagword (a word that also contains a tag,
    in this case the remote secondary tag), while some are stored in other
    words in a memory cell. This means that such unifications have similarities
    both to unifications involving arguments being packed next to local
    sectags, and to unifications involving ordinary arguments in memory cells.
    Therefore wherever possible, their implemenation uses suitably generalized
    versions of existing code that did those two jobs for two separate kinds of
    cons_tags.

    Making such generalizations possible in some cases required shifting the
    boundary between predicates, moving work from a caller to a callee
    or vice versa.

    In unify_gen_deconstruct.m, stop using uni_vals to represent *either* a var
    *or* a word in a memory cell. While this enabled us to factor out some
    common code, the predicate boundaries it lead to are unsuitable for the
    generalizations we now need.

    Consistently use unsigned ints to represent both the whole and the parts
    of words containing packed arguments (and maybe sectags), except when
    comparing ptag constants with the result of applying the "tag" unop
    to a word, (since that unop returns an int, at least for now).

    In a few cases, avoid the recomputation of some information that we
    already know. The motivation is not efficiency, since the recomputation
    we avoid is usually cheap, but the simplification of the code's correctness
    argument.

    Use more consistent terminology in things such as variable names.

    Note the possibility of further future improvements in several places.

compiler/ml_foreign_proc_gen.m:
    Delete a long unused predicate.

compiler/mlds.m:
    Add an XXX documenting a possible improvement.

compiler/rtti.m:
    Update the compiler's internal representation of RTTI data structures
    to make them able to describe secondary tags that are smaller than
    a full word.

compiler/rtti_out.m:
    Conform to the changes above, and delete a long-unused predicate.

compiler/type_ctor_info.m:
    Use the RTTI's du_hl_rep to represent cons_tags that distinguish
    between function symbols using a field in a class.

compiler/ml_type_gen.m:
    Provide a specialized form of a function for code in ml_unify_gen_*.m.
    Conform to the changes above.

compiler/add_special_pred.m:
compiler/bytecode_gen.m:
compiler/export.m:
compiler/hlds_code_util.m:
compiler/lco.m:
compiler/ml_closure_gen.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/rtti_to_mlds.m:
compiler/switch_util.m:
compiler/tag_switch.m:
    Conform to the changes above.

runtime/mercury_type_info.h:
    Update the runtime's representation of RTTI data structures to make them
    able to describe remote secondary tags that are smaller than a full word.

runtime/mercury_deconstruct.[ch]:
runtime/mercury_deconstruct.h:
runtime/mercury_deconstruct_macros.h:
runtime/mercury_ml_expand_body.h:
runtime/mercury_ml_arg_body.h:
runtime/mercury_ml_deconstruct_body.h:
runtime/mercury_ml_functor_body.h:
    These modules collectively implement the predicates in deconstruct.m
    in the library, and provide access to its functionality to other C code,
    e.g. in the debugger. Update these to be able to handle terms with the
    new data representation optimization.

    This update requires a significant change in the distribution of work
    between these files for the predicates deconstruct.deconstruct and
    deconstruct.limited_deconstruct. We used to have mercury_ml_expand_body.h
    fill in the fields of their expand_info structures (whose types are
    defined in mercury_deconstruct.h) with pointers to three vectors:
    (a) a vector of arg_locns with one element per argument, with a NULL
    pointer being equivalent to a vector with a given element in every slot;
    (b) a vector of type_infos with one element per argument, constructed
    dynamically (and later freed) if necessary; and (c) a vector of argument
    words. Once upon a time, before double-word and sub-word arguments,
    vector (c) also had one word per argument, but that hasn't been true
    for a while; we added vector (a) help the consumers of the expand_info
    decode the difference. The consumers of this info  always used these
    vectors to build up a Mercury term containing a list of univs,
    with one univ for each argument.

    This structure could be stretched to handle function symbols that store
    *all* their arguments in a tagword next to a local sectag, but I found
    that stretching it to cover function symbols that have *some* of their
    arguments packed next to a remote sectag and *some other* of their
    arguments in a memory cell as usual would have required a well-nigh
    incomprehensibly complex, and therefore almost undebuggable, interface
    between mercury_ml_expand_body.h and the other files above. This diff
    therefore changes the interface to have mercury_ml_expand_body.h
    build the list of univs directly. This make its code relatively simple
    and self-contained, and it should be somewhat faster then the old code
    as well, since it never needs to allocate, fill in and then free
    vectors of type_infos (each such typeinfo now gets put into a univ
    as soon as it is constructed). The downside is that if we ever wanted
    to get all the arguments at once for a purpose other than constructing
    a list of univs from them, it would nevertheless require constructing
    that list of univs anyway as an intermediate data structure. I don't see
    this downside is significant, because (a) I don't think such a use case
    is very likely, and (b) even if one arises, debuggable but a bit slow
    is probably preferable to faster but very hard to debug.

    Reduce the level of indentation of some of these files to make the code
    easier to edit. Do this by

    - not adding an indent level from switch statements to their cases; and
    - not adding an indent level when a case in a switch has a local block.

    Move the break or return ending a case inside that case's block,
    if it has one.

runtime/mercury_deep_copy_body.h:
runtime/mercury_table_type_body.h:
    Update these to enable the copying or tabling of terms whose
    representations uses the new optimization.

    Use the techniques listed above to reduce the level of indentation
    make the code easier to edit.

runtime/mercury_tabling.c:
runtime/mercury_term_size.c:
    Conform to the changes above.

runtime/mercury_unify_compare_body.h:
    Make this code compile after the changes above. It does need to work
    correctly, since we only ever used this code to compare the speed
    of unify-by-rtti with the speed of unify-by-compiler-generated-code,
    and in real life, we always use the latter. (It hasn't been updated
    to work right with previous arg packing changes either.)

library/construct.m:
    Update to enable the code to construct terms whose representations
    uses the new optimization.

    Add some sanity checks.

library/private_builtin.m:
runtime/mercury_dotnet.cs.in:
java/runtime/Sectag_Locn.java:
    Update the list of possible sectag kinds.

library/store.m:
    Conform to the changes above.

trace/mercury_trace_vars.c:
    Conform to the changes above.

tests/hard_coded/deconstruct_arg.{m,exp,exp2}:
    Extend this test to test the deconstruction of terms whose
    representations uses the new optimization.

    Modify some of the existing terms being tested to make them more diverse,
    in order to make the output easier to navigate.

tests/hard_coded/construct_packed.{m,exp}:
    A new test case to test the construction of terms whose
    representations uses the new optimization.

tests/debugger/browse_packed.{m,exp}:
    A new test case to test access to the fields of terms whose
    representations uses the new optimization.

tests/tabling/test_packed.{m,exp}:
    A new test case to test the tabling of terms whose
    representations uses the new optimization.

tests/debugger/Mmakefile:
tests/hard_coded/Mmakefile:
tests/tabling/Mmakefile:
    Enable the new test cases.
2018-08-30 05:14:38 +10:00

831 lines
27 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 2003-2005, 2007, 2009, 2011 The University of Melbourne.
// Copyright (C) 2014, 2016-2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// mercury_term_size.c
//
// This module defines a function for measuring the sizes of terms.
#include "mercury_imp.h"
#include "mercury_runtime_util.h" // For MR_STRERROR_BUF_SIZE.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef MR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#ifdef MR_RECORD_TERM_SIZES
MR_ComplexityCounter MR_complexity_word_counter = 0;
MR_ComplexityCounter MR_complexity_cell_counter = 0;
MR_ComplexityCounter MR_complexity_tick_counter = 0;
static void MR_write_complexity_proc(MR_ComplexityProc *proc);
static void MR_complexity_output_args_desc(FILE *fp,
MR_ComplexityProc *proc);
MR_Unsigned
MR_term_size(MR_TypeInfo type_info, MR_Word term)
{
MR_TypeCtorInfo type_ctor_info;
MR_DuTypeLayout du_type_layout;
const MR_DuPtagLayout *ptag_layout;
int ptag;
int sectag;
int arity;
int size;
try_again:
type_ctor_info = MR_TYPEINFO_GET_TYPE_CTOR_INFO(type_info);
if (! MR_type_ctor_has_valid_rep(type_ctor_info)) {
MR_fatal_error("MR_term_size: term of unknown representation");
}
switch (MR_type_ctor_rep(type_ctor_info)) {
case MR_TYPECTOR_REP_RESERVED_ADDR:
case MR_TYPECTOR_REP_RESERVED_ADDR_USEREQ:
// XXX The code to handle these cases hasn't been written yet.
MR_fatal_error("MR_term_size: RESERVED_ADDR");
case MR_TYPECTOR_REP_DU:
case MR_TYPECTOR_REP_DU_USEREQ:
du_type_layout = MR_type_ctor_layout(type_ctor_info).MR_layout_du;
ptag = MR_tag(term);
ptag_layout = &du_type_layout[ptag];
switch (ptag_layout->MR_sectag_locn) {
case MR_SECTAG_NONE:
#ifdef MR_DEBUG_TERM_SIZES
if (ptag_layout->MR_sectag_alternatives[0]->
MR_du_functor_orig_arity <= 0)
{
MR_fatal_error("MR_term_size: zero arity ptag none");
}
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: du sectag none %p -> %d\n",
(void *) term,
(int) MR_field(MR_mktag(ptag), term, -1));
printf("type %s.%s/%d, functor %s\n",
type_ctor_info->MR_type_ctor_module_name,
type_ctor_info->MR_type_ctor_name,
type_ctor_info->MR_type_ctor_arity,
ptag_layout->MR_sectag_alternatives[0]->
MR_du_functor_name);
}
#endif
return MR_field(MR_mktag(ptag), term, -1);
case MR_SECTAG_NONE_DIRECT_ARG:
// The compiler should not generate direct arg tags
// in term size recording grades.
MR_fatal_error("MR_term_size: DIRECT_ARG");
case MR_SECTAG_LOCAL_REST_OF_WORD: // fall-through
case MR_SECTAG_LOCAL_BITS:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: du sectag local %p\n",
(void *) term);
}
#endif
return 0;
case MR_SECTAG_REMOTE_WORD: // fall-through
case MR_SECTAG_REMOTE_BITS:
#ifdef MR_DEBUG_TERM_SIZES
sectag = MR_field(MR_mktag(ptag), term, 0);
if (ptag_layout->MR_sectag_alternatives[sectag]->
MR_du_functor_orig_arity <= 0)
{
MR_fatal_error("MR_term_size: zero arity ptag remote");
}
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: du sectag remote %p -> %d\n",
(void *) term,
(int) MR_field(MR_mktag(ptag), term, -1));
printf("type %s.%s/%d, functor %s\n",
type_ctor_info->MR_type_ctor_module_name,
type_ctor_info->MR_type_ctor_name,
type_ctor_info->MR_type_ctor_arity,
ptag_layout->MR_sectag_alternatives[sectag]->
MR_du_functor_name);
}
#endif
return MR_field(MR_mktag(ptag), term, -1);
case MR_SECTAG_VARIABLE:
MR_fatal_error("MR_term_size: VARIABLE");
default:
fprintf(stderr, "sectag_locn: %d\n",
(int) ptag_layout->MR_sectag_locn);
MR_fatal_error("MR_term_size: sectag_locn");
}
case MR_TYPECTOR_REP_EQUIV:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: equiv %p\n", (void *) term);
}
#endif
type_info = MR_create_type_info(
MR_TYPEINFO_GET_FIXED_ARITY_ARG_VECTOR(type_info),
MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
goto try_again;
case MR_TYPECTOR_REP_EQUIV_GROUND:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: equiv ground %p\n", (void *) term);
}
#endif
type_info = MR_pseudo_type_info_is_ground(
MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
goto try_again;
case MR_TYPECTOR_REP_NOTAG:
case MR_TYPECTOR_REP_NOTAG_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: notag (usereq) %p\n", (void *) term);
}
#endif
MR_save_transient_hp();
type_info = MR_create_type_info(
MR_TYPEINFO_GET_FIXED_ARITY_ARG_VECTOR(type_info),
MR_type_ctor_layout(type_ctor_info).MR_layout_notag->
MR_notag_functor_arg_type);
MR_restore_transient_hp();
goto try_again;
case MR_TYPECTOR_REP_NOTAG_GROUND:
case MR_TYPECTOR_REP_NOTAG_GROUND_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: notag ground (usereq) %p\n",
(void *) term);
}
#endif
type_info = MR_pseudo_type_info_is_ground(
MR_type_ctor_layout(type_ctor_info).MR_layout_notag
->MR_notag_functor_arg_type);
goto try_again;
case MR_TYPECTOR_REP_TUPLE:
arity = MR_TYPEINFO_GET_VAR_ARITY_ARITY(type_info);
if (arity == 0) {
// Term may be a NULL pointer, so don't follow it.
size = 0;
} else {
size = MR_field(MR_mktag(0), term, -1);
}
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: tuple %p -> %d\n",
(void *) term, size);
}
#endif
return size;
case MR_TYPECTOR_REP_PRED:
case MR_TYPECTOR_REP_FUNC:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: pred/func %p\n", (void *) term);
}
#endif
// Currently we don't collect stats on closure sizes.
return 0;
case MR_TYPECTOR_REP_ARRAY:
// Currently we don't collect stats on array sizes.
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: array %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_BITMAP:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: bitmap %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_ENUM:
case MR_TYPECTOR_REP_ENUM_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: enum (usereq) %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_FOREIGN_ENUM:
case MR_TYPECTOR_REP_FOREIGN_ENUM_USEREQ:
#ifdef MR_DEBUG_TERMSIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: foreign enum (usereq) %p\n",
(void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_INT:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: int %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
(void *) term, (MR_Integer) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_UINT:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: uint %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
(void *) term, (MR_Unsigned) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_INT8:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: int8 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
(void *) term, (MR_Integer) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_UINT8:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: uint8 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
(void *) term, (MR_Unsigned) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_INT16:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: int16 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
(void *) term, (MR_Integer) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_UINT16:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: uint16 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
(void *) term, (MR_Unsigned) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_INT32:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: int32 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
(void *) term, (MR_Integer) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_UINT32:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: uint32 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
(void *) term, (MR_Unsigned) term);
}
#endif
return 0;
// XXX Maybe we should also print the value of the term in the int64,
// uint64 and float cases?
case MR_TYPECTOR_REP_INT64:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: int64 %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_UINT64:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf(
"MR_term_size: uint64 %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_CHAR:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: char %p %c\n",
(void *) term, (char) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_FLOAT:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: float %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_STRING:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: string %p '%s'\n",
(void *) term, (char *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_SUCCIP:
case MR_TYPECTOR_REP_HP:
case MR_TYPECTOR_REP_CURFR:
case MR_TYPECTOR_REP_MAXFR:
case MR_TYPECTOR_REP_REDOFR:
case MR_TYPECTOR_REP_REDOIP:
case MR_TYPECTOR_REP_TRAIL_PTR:
case MR_TYPECTOR_REP_TICKET:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: impl artifact type %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_TYPEINFO:
case MR_TYPECTOR_REP_TYPECLASSINFO:
case MR_TYPECTOR_REP_TYPECTORINFO:
case MR_TYPECTOR_REP_BASETYPECLASSINFO:
case MR_TYPECTOR_REP_TYPEDESC:
case MR_TYPECTOR_REP_TYPECTORDESC:
case MR_TYPECTOR_REP_PSEUDOTYPEDESC:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: type_info etc %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_SUBGOAL:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: subgoal %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_C_POINTER:
case MR_TYPECTOR_REP_STABLE_C_POINTER:
case MR_TYPECTOR_REP_FOREIGN:
case MR_TYPECTOR_REP_STABLE_FOREIGN:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: c_pointer/foreign %p\n", (void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_REFERENCE:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: reference %p\n", (void *) term);
}
#endif
return 1;
case MR_TYPECTOR_REP_DUMMY:
#ifdef MR_DEBUG_TERM_SIZES
if (MR_heapdebug && MR_lld_print_enabled) {
printf("MR_term_size: dummy %p\n",
(void *) term);
}
#endif
return 0;
case MR_TYPECTOR_REP_VOID:
MR_fatal_error("MR_term_size: VOID");
case MR_TYPECTOR_REP_UNKNOWN:
MR_fatal_error("MR_term_size: UNKNOWN");
default:
fprintf(stderr, "default rep: %d\n",
(int) MR_type_ctor_rep(type_ctor_info));
MR_fatal_error("MR_term_size: default");
}
MR_fatal_error("MR_term_size: unexpected fallthrough");
}
void
MR_write_complexity_procs(void)
{
int proc_num;
MR_ComplexityProc *proc;
for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
proc = &MR_complexity_procs[proc_num];
if (proc->MR_clp_num_profiled_args >= 0) {
MR_write_complexity_proc(proc);
}
}
}
#define MR_COMPLEXITY_ARGS_DIR "ComplexityArgs"
#define MR_COMPLEXITY_DATA_DIR "ComplexityData"
static MR_bool MR_have_created_complexity_dirs = MR_FALSE;
static MR_bool MR_have_printed_complexity_dirs_error = MR_FALSE;
static void
MR_write_complexity_proc(MR_ComplexityProc *proc)
{
char *full_proc_name;
int full_proc_name_len;
FILE *fp;
char *args_filename;
char *data_filename;
const char *data_filemode;
struct stat statbuf;
char *slash;
MR_ComplexityMetrics *metrics;
int num_profiled_args;
int *sizes;
int num_slots;
MR_ComplexityPastSlots *past_slots;
char *cmd_buf;
char errbuf[MR_STRERROR_BUF_SIZE];
full_proc_name_len = strlen(proc->MR_clp_full_proc_name);
full_proc_name = MR_malloc(100 + full_proc_name_len);
strcpy(full_proc_name, proc->MR_clp_full_proc_name);
// We can't have slash characters in the filenames we construct from
// full_proc_name.
while ((slash = strchr(full_proc_name, '/')) != NULL) {
*slash = ':';
}
cmd_buf = MR_malloc(100 + 2 * full_proc_name_len);
// Will be big enough.
if (! MR_have_created_complexity_dirs) {
sprintf(cmd_buf, "mkdir -p %s %s",
MR_COMPLEXITY_ARGS_DIR, MR_COMPLEXITY_DATA_DIR);
if (system(cmd_buf) != 0) {
if (! MR_have_printed_complexity_dirs_error) {
fprintf(stderr, "%s: cannot create %s and %s: %s\n",
MR_progname, MR_COMPLEXITY_ARGS_DIR,
MR_COMPLEXITY_DATA_DIR,
MR_strerror(errno, errbuf, sizeof(errbuf)));
// there is no point in aborting
MR_have_printed_complexity_dirs_error = MR_TRUE;
return;
}
}
MR_have_created_complexity_dirs = MR_TRUE;
}
args_filename = MR_malloc(100 + full_proc_name_len);
// Will be big enough.
sprintf(args_filename, "%s/%s", MR_COMPLEXITY_ARGS_DIR, full_proc_name);
if (stat(args_filename, &statbuf) != 0) {
// args_filename does not exist.
fp = fopen(args_filename, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, args_filename,
MR_strerror(errno, errbuf, sizeof(errbuf)));
// There is no point in aborting.
return;
}
MR_complexity_output_args_desc(fp, proc);
fclose(fp);
data_filemode = "w";
} else {
// args_filename does exist.
char *tmp_filename;
tmp_filename = MR_malloc(100 + full_proc_name_len);
sprintf(tmp_filename, "%s/%s.tmp",
MR_COMPLEXITY_ARGS_DIR, full_proc_name);
fp = fopen(tmp_filename, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, tmp_filename,
MR_strerror(errno, errbuf, sizeof(errbuf)));
// There is no point in aborting.
return;
}
MR_complexity_output_args_desc(fp, proc);
fclose(fp);
sprintf(cmd_buf, "cmp -s %s %s", args_filename, tmp_filename);
if (system(cmd_buf) == 0) {
// The files are identical.
(void) unlink(tmp_filename);
data_filemode = "a";
} else {
// The files are different.
rename(tmp_filename, args_filename);
data_filemode = "w";
}
}
data_filename = MR_malloc(100 + full_proc_name_len);
sprintf(data_filename, "%s/%s", MR_COMPLEXITY_DATA_DIR, full_proc_name);
fp = fopen(data_filename, data_filemode);
if (fp == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
MR_progname, data_filename,
MR_strerror(errno, errbuf, sizeof(errbuf)));
// There is no point in aborting.
return;
}
num_profiled_args = proc->MR_clp_num_profiled_args;
metrics = proc->MR_clp_metrics;
sizes = proc->MR_clp_sizes;
num_slots = proc->MR_clp_next_slot_num;
past_slots = proc->MR_clp_past_slots;
do {
int slot, arg;
for (slot = num_slots - 1; slot >= 0; slot--) {
fprintf(fp, "%d %d %d",
metrics[slot].MR_clpm_num_words,
metrics[slot].MR_clpm_num_cells,
metrics[slot].MR_clpm_num_ticks);
for (arg = 0; arg < num_profiled_args; arg++) {
fprintf(fp, " %d", sizes[slot * num_profiled_args + arg]);
}
fprintf(fp, "\n");
}
if (past_slots == NULL) {
break;
}
metrics = past_slots->MR_clpps_metrics;
sizes = past_slots->MR_clpps_sizes;
num_slots = MR_COMPLEXITY_SLOTS_PER_CHUNK;
past_slots = past_slots->MR_clpps_previous;
} while (MR_TRUE);
(void) fclose(fp);
}
static void
MR_complexity_output_args_desc(FILE *fp, MR_ComplexityProc *proc)
{
int arg;
int num_args;
MR_ComplexityArgInfo *arg_infos;
arg_infos = proc->MR_clp_arg_infos;
num_args = proc->MR_clp_num_args;
for (arg = 0; arg < num_args; arg++) {
if (arg_infos[arg].MR_clpai_maybe_name != NULL) {
fprintf(fp, "%s ", arg_infos[arg].MR_clpai_maybe_name);
} else {
fprintf(fp, "_ ");
}
switch (arg_infos[arg].MR_clpai_kind) {
case MR_COMPLEXITY_INPUT_VAR_SIZE:
fprintf(fp, "profiled_input\n");
break;
case MR_COMPLEXITY_INPUT_FIX_SIZE:
fprintf(fp, "unprofiled_input\n");
break;
case MR_COMPLEXITY_OUTPUT:
fprintf(fp, "output\n");
break;
default:
fprintf(fp, "unknown\n");
break;
}
}
}
void
MR_init_complexity_proc(int proc_num, const char *fullname,
int num_profiled_args, int num_args, MR_ComplexityArgInfo *arg_infos)
{
MR_ComplexityProc *proc;
if (MR_complexity_procs == NULL) {
fprintf(stderr, "%s: executable wasn't fully prepared "
"for complexity experiment\n", MR_progname);
exit(1);
}
proc = &MR_complexity_procs[proc_num];
if (! MR_streq(fullname, proc->MR_clp_full_proc_name)) {
fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
MR_progname, proc_num, proc->MR_clp_full_proc_name, fullname);
exit(1);
}
if (proc->MR_clp_num_profiled_args >= 0) {
fprintf(stderr, "%s: proc_num %d: duplicate initialization\n",
MR_progname, proc_num);
exit(1);
}
if (num_profiled_args < 0) {
fprintf(stderr, "%s: proc_num %d: bad num_profiled_args\n",
MR_progname, proc_num);
exit(1);
}
proc->MR_clp_num_profiled_args = num_profiled_args;
proc->MR_clp_num_args = num_args;
proc->MR_clp_arg_infos = arg_infos;
proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
MR_COMPLEXITY_SLOTS_PER_CHUNK);
proc->MR_clp_sizes = MR_NEW_ARRAY(int,
MR_COMPLEXITY_SLOTS_PER_CHUNK * num_profiled_args);
}
void
MR_check_complexity_init(void)
{
int proc_num;
MR_bool printed_heading;
MR_ComplexityProc *proc;
printed_heading = MR_FALSE;
for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
proc = &MR_complexity_procs[proc_num];
if (proc->MR_clp_num_profiled_args < 0) {
if (! printed_heading) {
fprintf(stderr, "%s: the following procedures are "
"not available for complexity experiment:\n",
MR_progname);
printed_heading = MR_TRUE;
}
fprintf(stderr, "%s\n", proc->MR_clp_full_proc_name);
}
}
if (printed_heading) {
exit(1);
}
}
MR_ComplexityIsActive
MR_complexity_is_active_func(int num_procs, int proc_num, const char *name,
int num_profiled_inputs)
{
MR_ComplexityProc *proc;
if (num_procs != MR_num_complexity_procs || MR_complexity_procs == NULL) {
fprintf(stderr, "%s: executable wasn't fully prepared "
"for complexity experiment\n", MR_progname);
exit(1);
}
if (proc_num >= num_procs) {
fprintf(stderr, "%s: proc_num %d >= num_procs %d\n",
MR_progname, proc_num, num_procs);
exit(1);
}
proc = &MR_complexity_procs[proc_num];
if (! MR_streq(name, proc->MR_clp_full_proc_name)) {
fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
MR_progname, proc_num, proc->MR_clp_full_proc_name, name);
exit(1);
}
if (proc->MR_clp_num_profiled_args != num_profiled_inputs) {
fprintf(stderr, "%s: proc_num %d: bad num_profiled_inputs\n",
MR_progname, proc_num);
exit(1);
}
return proc->MR_clp_is_active;
}
int
MR_complexity_call_func(int procnum)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
int slot;
proc = &MR_complexity_procs[procnum];
slot = proc->MR_clp_next_slot_num;
if (slot < MR_COMPLEXITY_SLOTS_PER_CHUNK) {
proc->MR_clp_next_slot_num++;
} else {
MR_ComplexityPastSlots *past_slots;
past_slots = MR_NEW(MR_ComplexityPastSlots);
past_slots->MR_clpps_metrics = proc->MR_clp_metrics;
past_slots->MR_clpps_sizes = proc->MR_clp_sizes;
past_slots->MR_clpps_previous = proc->MR_clp_past_slots;
proc->MR_clp_past_slots = past_slots;
proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
MR_COMPLEXITY_SLOTS_PER_CHUNK);
proc->MR_clp_sizes = MR_NEW_ARRAY(int,
MR_COMPLEXITY_SLOTS_PER_CHUNK * proc->MR_clp_num_profiled_args);
proc->MR_clp_next_slot_num = 1;
slot = 0;
}
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words -= MR_complexity_word_counter;
metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;
return slot;
}
void
MR_complexity_fill_size_slot(MR_ComplexityProc *proc, int slot,
int num_profiled_args, int argnum, int size)
{
MR_ComplexityCounter *sizes;
sizes = proc->MR_clp_sizes;
sizes[(slot * proc->MR_clp_num_profiled_args) + argnum] = size;
}
void
MR_complexity_leave_func(int procnum, int slot)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
proc = &MR_complexity_procs[procnum];
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words += MR_complexity_word_counter;
metrics->MR_clpm_num_cells += MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks += MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_INACTIVE;
}
void
MR_complexity_redo_func(int procnum, int slot)
{
MR_ComplexityProc *proc;
MR_ComplexityMetrics *metrics;
proc = &MR_complexity_procs[procnum];
metrics = &proc->MR_clp_metrics[slot];
metrics->MR_clpm_num_words -= MR_complexity_word_counter;
metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;
}
#endif // MR_RECORD_TERM_SIZES