/*
 * %CopyrightBegin%

 *
 * Copyright Ericsson AB 2001-2012. All Rights Reserved.
 *
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * %CopyrightEnd%
 */
/*
 * hipe_bif1.c
 *
 * Performance analysis support.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sys.h"
#include "global.h"
#include "bif.h"
#include "big.h"
#include "error.h"
#include "beam_load.h"
#include "hipe_bif0.h"
#include "hipe_bif1.h"

#define BeamOpCode(Op)	((Uint)BeamOp(Op))

BIF_RETTYPE hipe_bifs_call_count_on_1(BIF_ALIST_1)
{
    Eterm *pc;
    struct hipe_call_count *hcc;

    pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
    if (!pc)
	BIF_ERROR(BIF_P, BADARG);
    ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI));
    if (pc[0] == BeamOpCode(op_hipe_trap_call))
	BIF_ERROR(BIF_P, BADARG);
    if (pc[0] == BeamOpCode(op_hipe_call_count))
	BIF_RET(NIL);
    hcc = erts_alloc(ERTS_ALC_T_HIPE, sizeof(*hcc));
    hcc->count = 0;
    hcc->opcode = pc[0];
    pc[-4] = (Eterm)hcc;
    pc[0] = BeamOpCode(op_hipe_call_count);
    BIF_RET(am_true);
}

BIF_RETTYPE hipe_bifs_call_count_off_1(BIF_ALIST_1)
{
    Eterm *pc;
    struct hipe_call_count *hcc;
    unsigned count;

    pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
    if (!pc)
	BIF_ERROR(BIF_P, BADARG);
    ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI));
    if (pc[0] != BeamOpCode(op_hipe_call_count))
	BIF_RET(am_false);
    hcc = (struct hipe_call_count*)pc[-4];
    count = hcc->count;
    pc[0] = hcc->opcode;
    pc[-4] = (Eterm)NULL;
    erts_free(ERTS_ALC_T_HIPE, hcc);
    BIF_RET(make_small(count));
}

BIF_RETTYPE hipe_bifs_call_count_get_1(BIF_ALIST_1)
{
    Eterm *pc;
    struct hipe_call_count *hcc;

    pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
    if (!pc)
	BIF_ERROR(BIF_P, BADARG);
    ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI));
    if (pc[0] != BeamOpCode(op_hipe_call_count))
	BIF_RET(am_false);
    hcc = (struct hipe_call_count*)pc[-4];
    BIF_RET(make_small(hcc->count));
}

BIF_RETTYPE hipe_bifs_call_count_clear_1(BIF_ALIST_1)
{
    Eterm *pc;
    struct hipe_call_count *hcc;
    unsigned count;

    pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1);
    if (!pc)
	BIF_ERROR(BIF_P, BADARG);
    ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI));
    if (pc[0] != BeamOpCode(op_hipe_call_count))
	BIF_RET(am_false);
    hcc = (struct hipe_call_count*)pc[-4];
    count = hcc->count;
    hcc->count = 0;
    BIF_RET(make_small(count));
}

unsigned int hipe_trap_count;

BIF_RETTYPE hipe_bifs_trap_count_get_0(BIF_ALIST_0)
{
    BIF_RET(make_small(hipe_trap_count));
}

BIF_RETTYPE hipe_bifs_trap_count_clear_0(BIF_ALIST_0)
{
    unsigned int count = hipe_trap_count;
    hipe_trap_count = 0;
    BIF_RET(make_small(count));
}

/*****************************************************************************
 * BIFs for benchmarking. These only do useful things if
 * __BENCHMARK__ is defined in beam/benchmark.h. For documentation
 * about how to add new counters or maintain the existing counters,
 * see benchmark.h.
 *
 * If benchmarking is not enabled all BIFs will return false. If the
 * required benchmark feature is not enabled, the counter will remain
 * zero.
 *
 * process_info/0 -> { Number of live processes,
 *		       Processes spawned in total }
 *
 *   Live processes are increased when a new process is created, and
 *   decreased when a process dies. Processes spawned is increased
 *   when a process is created.
 *
 *
 * process_info_clear/0 -> true
 *
 *   Will reset the processes spawned-counters to zero. If this is
 *   done at some improper time, live processes may become a negative
 *   value. This is not a problem in itself, just as long as you know
 *   about it.
 *
 *
 * message_info/0 -> { Messages sent,
 *		       Messages copied,
 *		       Ego messages (sender = receiver),
 *		       Words sent,
 *		       Words copied,
 *		       Words preallocated }
 *
 *   Counting the words sent in a shared heap system will affect
 *   runtime performance since it means that we have to calculate the
 *   size of the mesage. With private heaps, this is done anyway and
 *   will not affect performance.
 *
 *
 * message_info_clear/0 -> true
 *
 *   Reset the message counters to zero.
 *
 *
 * message_sizes/0 -> true
 *
 *   Displays a text-mode bar diagram with message sizes. There are no
 *   guaranties that this is printed in a way the Erlang system is
 *   supposed to print things.
 *
 *
 * gc_info/0 -> { Minor collections,
 *		  Major collections,
 *		  Used heap,
 *		  Allocated heap,
 *		  Max used heap,
 *		  Max allocated heap }
 *
 *   Information about private heap garbage collections. Number of
 *   minor and major collections, how much heap is used and allocated
 *   and how much heap has been in use and allocated at most since the
 *   counters were reset.
 *
 *
 * shared_gc_info/0 -> { Minor collections of the shared heap,
 *			 Major collections of the shared heap,
 *			 Used shared heap,
 *			 Allocated shared heap,
 *			 Max used shared heap,
 *			 Max allocated shared heap }
 *
 *   The same as above, but for the shared heap / message area. Note,
 *   that in a shared heap system the max used heap and max allocated
 *   heap are mostly the same, since the heap allways is filled before
 *   a garbage collection, and most garbage collections do not enlarge
 *   the heap. The private heap numbers are much more interesting.
 *
 *
 * incremental_gc_info/0 -> { Complete minor GC cycles,
 *			      Complete major GC cycles,
 *			      Minor GC stages,
 *			      Major GC stages }
 *
 *
 * gc_info_clear/0 -> true
 *
 *   Reset counters for both private and shared garbage collection.
 *
 *
 * BM Timers
 * ---------
 *
 * All timers returns tuples of the kind: { Minutes, Seconds, Milliseconds }
 * except for the max times in garbage collection where times are normally
 * small. The tuple is therefor: { Seconds, Milliseconds, Microseconds }
 *
 * system_timer/0 -> Mutator time
 *
 *   This timer is not a real-time clock, it only runs when a process
 *   is scheduled to run. You can not find out the accual time a
 *   program has taken to run using this timer.
 *
 *
 * system_timer_clear/0 -> true
 *
 *   Reset system timer to zero.
 *
 *
 * send_timer/0 -> { Send time,
 *		     Copy time,
 *		     Size time }
 *
 *   Time spent in sending messages. The copy time and size time are
 *   only active if the copying is needed in send. Copying of data
 *   into ETS-tables etc is not timed with this timer.
 *
 *
 * send_timer_clear/0 -> true
 *
 *   Reset send timers to zero.
 *
 *
 * gc_timer/0 -> { Time in minor collection,
 *		   Time in major collection,
 *		   Max time in minor collection (�s),
 *		   Max time in major collection (�s) }
 *
 *   Total time spent in garbage collection of the private heaps. The
 *   max times are for one separate collection.
 *
 *
 * shared_gc_timer/0 -> { Time in minor collection,
 *			  Time in major collection,
 *			  Max time in minor collection (�s),
 *			  Max time in major collection (�s) }
 *
 *   Total time spent in garbage collection of the shared heap /
 *   message area. The max times are for one separate collection.
 *
 *
 * gc_timer_clear/0 -> true
 *
 *   Reset private and shared garbage collection timers to zero. Note,
 *   that the max-times are also reset.
 *
 *
 * misc_timer/0 -> { Misc 0, Misc 1, Misc 2 }
 *
 *   Timers for debug purposes. In a normal system, these timers are
 *   never used. Add these timers at places where you want to time
 *   something not covered here. Use BM_SWAP_TIMER(from,to) to start
 *   one of the misc timers.
 *
 *   ... code timed by the system timer ...
 *   BM_SWAP_TIMER(system,misc1);
 *   ... code we want to time ...
 *   BM_SWAP_TIMER(misc1,system);
 *   ... back on system time ...
 *
 *
 * misc_timer_clear/0 -> true
 *
 *   Reset misc timers to zero.
 */

BIF_RETTYPE hipe_bifs_process_info_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#ifndef BM_COUNTERS
    Uint processes_busy	   = 0;
    Uint processes_spawned = 0;
#endif
    Eterm *hp;

    hp = HAlloc(BIF_P, 3);
    BIF_RET(TUPLE2(hp,
		   make_small(processes_busy),
		   make_small(processes_spawned)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_process_info_clear_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#ifdef BM_COUNTERS
    processes_spawned = 0;
#endif
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_message_info_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    Eterm *hp;
#ifndef BM_COUNTERS
    unsigned long messages_sent	  = 0;
    unsigned long messages_copied = 0;
    unsigned long messages_ego	  = 0;
#endif
#ifndef BM_MESSAGE_SIZES
    unsigned long words_sent   = 0;
    unsigned long words_copied = 0;
    unsigned long words_prealloc = 0;
#endif

    hp = HAlloc(BIF_P, 7);
    BIF_RET(TUPLE6(hp,
		   make_small(messages_sent),
		   make_small(messages_copied),
		   make_small(messages_ego),
		   make_small(words_sent),
		   make_small(words_copied),
		   make_small(words_prealloc)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_message_info_clear_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#ifdef BM_COUNTERS
    messages_sent   = 0;
    messages_copied = 0;
    messages_ego    = 0;
#endif
#ifdef BM_MESSAGE_SIZES
    words_sent	 = 0;
    words_copied = 0;
    words_prealloc = 0;
    {
	int i;
	for (i = 0; i < 1000; i++)
	    message_sizes[i] = 0;
    }
#endif
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_message_sizes_0(BIF_ALIST_0)
{
#ifdef BM_MESSAGE_SIZES
    int i, j, max = 0;
    int tmp[12] = {0,0,0,0,0,0,0,0,0,0,0,0};

    for (i = 0; i < 65; i++) {
	tmp[0] += message_sizes[i];
	if (tmp[0] > max)
	    max = tmp[0];
    }
    for (i = 65; i < 999; i++) {
	tmp[i / 100 + 1] += message_sizes[i];
	if (tmp[i / 100 + 1] > max)
	    max = tmp[i / 100 + 1];
    }
    tmp[11] = message_sizes[999];
    if (tmp[11] > max)
	max = tmp[11];
    for (i = -1; i < 11; i++) {
	int num = (tmp[i + 1] * 50) / max;
	if (i == -1)
	    printf("\n\r  0 -  64: (%6d) |", tmp[0]);
	else if (i == 0)
	    printf("\n\r 65 -  99: (%6d) |", tmp[1]);
	else if (i == 10)
	    printf("\n\r  >= 1000: (%6d) |", tmp[11]);
	else
	    printf("\n\r%3d - %3d: (%6d) |", i * 100, i * 100 + 99,
		   tmp[i + 1]);

	for (j = 0; j < num; j++)
	    printf(".");
    }
    printf("\n\r");

    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#ifndef BM_COUNTERS
    Uint minor_gc = 0;
    Uint major_gc = 0;
#endif
#ifndef BM_HEAP_SIZES
    Uint max_used_heap	    = 0;
    Uint max_allocated_heap = 0;
#endif
    Eterm *hp;
    Uint used_heap = (BIF_P->htop - BIF_P->heap) +
		     (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) +
		     MBUF_SIZE(BIF_P);

    Uint alloc_heap = (BIF_P->hend - BIF_P->heap) +
		      (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) +
		      MBUF_SIZE(BIF_P);

    hp = HAlloc(BIF_P, 7);
    BIF_RET(TUPLE6(hp,
		   make_small((Uint)minor_gc),
		   make_small((Uint)major_gc),
		   make_small((Uint)used_heap),
		   make_small((Uint)alloc_heap),
		   make_small(max_used_heap),
		   make_small(max_allocated_heap)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#if !(defined(BM_COUNTERS))
    Uint minor_global_gc = 0;
    Uint major_global_gc = 0;
#endif
#ifndef BM_HEAP_SIZES
    Uint max_used_global_heap	   = 0;
    Uint max_allocated_global_heap = 0;
#endif
    Eterm *hp;

    Uint tmp_used_heap = 0;
    Uint tmp_allocated_heap = 0;

    hp = HAlloc(BIF_P, 7);
    BIF_RET(TUPLE6(hp,
		   make_small((uint)minor_global_gc),
		   make_small((uint)major_global_gc),
		   make_small(tmp_used_heap),
		   make_small(tmp_allocated_heap),
		   make_small(max_used_global_heap),
		   make_small(max_allocated_global_heap)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
#if !defined(BM_COUNTERS)
    Uint minor_gc_cycles = 0;
    Uint major_gc_cycles = 0;
    Uint minor_gc_stages = 0;
    Uint major_gc_stages = 0;
#endif
    Eterm *hp;

    hp = HAlloc(BIF_P, 5);
    BIF_RET(TUPLE4(hp,
		   make_small(minor_gc_cycles),
		   make_small(major_gc_cycles),
		   make_small(minor_gc_stages),
		   make_small(major_gc_stages)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__

#ifdef BM_COUNTERS
    minor_gc	    = 0;
    major_gc	    = 0;
#endif

#ifdef BM_HEAP_SIZES
    max_used_heap	      = 0;
    max_allocated_heap	      = 0;
    max_used_global_heap      = 0;
    max_allocated_global_heap = 0;
#endif

    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0)
{
#ifdef BM_TIMERS
    int i;
    int total_time = 0, n = 0;
    int left = 0, right = 0, mid = 0;

    printf("Pause times in minor collection:\r\n");
    for (i = 0; i < MAX_PAUSE_TIME; i++) {
	if (pause_times[i] > 0) {
	    printf("%d: %ld\r\n", i, pause_times[i]);
	    total_time += pause_times[i] * i;
	    n += pause_times[i];

	    if (i > mid)
		right += pause_times[i];

	    while (right > left) {
		left += pause_times[mid++];
		right -= pause_times[mid];
	    }
	}
    }

    printf("Number of collections: %d\r\n", n);
    printf("Total collection time: %d\r\n", total_time);
    if (n > 0)
	printf("Mean pause time: %d\r\n", total_time / n);

    printf("Geometrical mean: %d\r\n", mid);

    total_time = 0; n = 0;
    left = 0; right = 0; mid = 0;
    printf("Pause times in major collection:\r\n");
    for (i = 0; i < MAX_PAUSE_TIME; i++) {
	if (pause_times_old[i] > 0) {
	    printf("%d: %ld\r\n", i, pause_times_old[i]);
	    total_time += pause_times_old[i] * i;
	    n += pause_times_old[i];
	}
    }

    printf("Number of collections: %d\r\n", n);
    printf("Total collection time: %d\r\n", total_time);
    if (n > 0)
	printf("Mean pause time: %d\r\n", total_time / n);

    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

/* XXX: these macros have free variables */
#ifdef BM_TIMERS
#if USE_PERFCTR
#define MAKE_TIME(_timer_) {			      \
    BM_TIMER_T tmp = _timer_##_time;		      \
    milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \
    tmp /= 1000;				      \
    sec = (uint)(tmp - ((int)(tmp / 60)) * 60);	      \
    min = (uint)tmp / 60;			      }

#define MAKE_MICRO_TIME(_timer_) {		      \
    BM_TIMER_T tmp = _timer_##_time * 1000;	      \
    micro = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \
    tmp /= 1000;				      \
    milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \
    sec = (uint)tmp / 1000;			      }

#else
#define MAKE_TIME(_timer_) {			      \
    BM_TIMER_T tmp = _timer_##_time / 1000000;	      \
    milli = tmp % 1000;				      \
    tmp /= 1000;				      \
    sec = tmp % 60;				      \
    min = tmp / 60;				      }

#define MAKE_MICRO_TIME(_timer_) {		      \
    BM_TIMER_T tmp = _timer_##_time / 1000;	      \
    micro = tmp % 1000;				      \
    tmp /= 1000;				      \
    milli = tmp % 1000;				      \
    sec = tmp / 1000;				      }

#endif
#else
#define MAKE_TIME(_timer_)
#define MAKE_MICRO_TIME(_timer_)
#endif

BIF_RETTYPE hipe_bifs_system_timer_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    uint min = 0;
    uint sec = 0;
    uint milli = 0;
    Eterm *hp;

    hp = HAlloc(BIF_P, 4);
    MAKE_TIME(system);
    BIF_RET(TUPLE3(hp,
		   make_small(min),
		   make_small(sec),
		   make_small(milli)));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_system_timer_clear_0(BIF_ALIST_0)
{
#ifdef BM_TIMERS
    system_time = 0;
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_send_timer_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    uint min   = 0;
    uint sec   = 0;
    uint milli = 0;
    Eterm *hp;
    Eterm sendtime, copytime, sizetime;

    hp = HAlloc(BIF_P, 4 * 4);

    MAKE_TIME(send);
    sendtime = TUPLE3(hp,
		      make_small(min),
		      make_small(sec),
		      make_small(milli));
    hp += 4;

    MAKE_TIME(copy);
    copytime = TUPLE3(hp,
		      make_small(min),
		      make_small(sec),
		      make_small(milli));
    hp += 4;

    MAKE_TIME(size);
    sizetime = TUPLE3(hp,
		      make_small(min),
		      make_small(sec),
		      make_small(milli));
    hp += 4;
    BIF_RET(TUPLE3(hp, sendtime, copytime, sizetime));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_send_timer_clear_0(BIF_ALIST_0)
{
#ifdef BM_TIMERS
    send_time = 0;
    copy_time = 0;
    size_time = 0;
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_gc_timer_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    Eterm *hp;
    uint min = 0;
    uint sec = 0;
    uint milli = 0;
    uint micro = 0;
    Eterm minor, major, max_min, max_maj;

    hp = HAlloc(BIF_P, 4 * 4 + 5);

    MAKE_TIME(minor_gc);
    minor = TUPLE3(hp,
		   make_small(min),
		   make_small(sec),
		   make_small(milli));
    hp += 4;

    MAKE_TIME(major_gc);
    major = TUPLE3(hp,
		   make_small(min),
		   make_small(sec),
		   make_small(milli));
    hp += 4;

    MAKE_MICRO_TIME(max_minor);
    max_min = TUPLE3(hp,
		     make_small(sec),
		     make_small(milli),
		     make_small(micro));
    hp += 4;

    MAKE_MICRO_TIME(max_major);
    max_maj = TUPLE3(hp,
		     make_small(sec),
		     make_small(milli),
		     make_small(micro));
    hp += 4;

    BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_shared_gc_timer_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    Eterm *hp;
    uint min = 0;
    uint sec = 0;
    uint milli = 0;
    uint micro = 0;
    Eterm minor, major, max_min, max_maj;

    hp = HAlloc(BIF_P, 4 * 4 + 5);

    MAKE_TIME(minor_global_gc);
    minor = TUPLE3(hp,
		   make_small(min),
		   make_small(sec),
		   make_small(milli));
    hp += 4;

    MAKE_TIME(major_global_gc);
    major = TUPLE3(hp,
		   make_small(min),
		   make_small(sec),
		   make_small(milli));
    hp += 4;

    MAKE_MICRO_TIME(max_global_minor);
    max_min = TUPLE3(hp,
		     make_small(sec),
		     make_small(milli),
		     make_small(micro));
    hp += 4;

    MAKE_MICRO_TIME(max_global_major);
    max_maj = TUPLE3(hp,
		     make_small(sec),
		     make_small(milli),
		     make_small(micro));
    hp += 4;

    BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_gc_timer_clear_0(BIF_ALIST_0)
{
#ifdef BM_TIMERS
    minor_gc_time	  = 0;
    major_gc_time	  = 0;
    max_minor_time	  = 0;
    max_major_time	  = 0;
    minor_global_gc_time  = 0;
    major_global_gc_time  = 0;
    max_global_minor_time = 0;
    max_global_major_time = 0;
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_misc_timer_0(BIF_ALIST_0)
{
#ifdef __BENCHMARK__
    uint min   = 0;
    uint sec   = 0;
    uint milli = 0;
    Eterm *hp;
    Eterm misctime1, misctime2, misctime3;

    hp = HAlloc(BIF_P, 4 * 4);

    MAKE_TIME(misc0);
    misctime1 = TUPLE3(hp,
		       make_small(min),
		       make_small(sec),
		       make_small(milli));
    hp += 4;

    MAKE_TIME(misc1);
    misctime2 = TUPLE3(hp,
		       make_small(min),
		       make_small(sec),
		       make_small(milli));
    hp += 4;

    MAKE_TIME(misc2);
    misctime3 = TUPLE3(hp,
		       make_small(min),
		       make_small(sec),
		       make_small(milli));
    hp += 4;
    BIF_RET(TUPLE3(hp, misctime1, misctime2, misctime3));
#else
    BIF_RET(am_false);
#endif
}

BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0)
{
#ifdef BM_TIMERS
    misc0_time = 0;
    misc1_time = 0;
    misc2_time = 0;
    BIF_RET(am_true);
#else
    BIF_RET(am_false);
#endif
}

#undef MAKE_TIME
#undef MAKE_MICRO_TIME

/*
 * HiPE hrvtime().
 * These implementations are currently available:
 * + On Linux with the perfctr extension we can use the process'
 *   virtualised time-stamp counter. To enable this mode you must
 *   pass `--with-perfctr=/path/to/perfctr' when configuring.
 * + The fallback, which is the same as {X,_} = runtime(statistics).
 */

static double fallback_get_hrvtime(void)
{
    unsigned long ms_user;

    elapsed_time_both(&ms_user, NULL, NULL, NULL);
    return (double)ms_user;
}

#if USE_PERFCTR

#include "hipe_perfctr.h"
static int hrvtime_started;	/* 0: closed, +1: perfctr, -1: fallback */
#define hrvtime_is_started()	(hrvtime_started != 0)

static void start_hrvtime(void)
{
    if (hipe_perfctr_hrvtime_open() >= 0)
	hrvtime_started = 1;
    else
	hrvtime_started = -1;
}

static void stop_hrvtime(void)
{
    if (hrvtime_started > 0)
	hipe_perfctr_hrvtime_close();
    hrvtime_started = 0;
}

static double get_hrvtime(void)
{
    if (hrvtime_started > 0)
	return hipe_perfctr_hrvtime_get();
    else
	return fallback_get_hrvtime();
}

#else	/* !USE_PERFCTR */

/*
 * Fallback, if nothing better exists.
 * This is the same as {X,_} = statistics(runtime), which uses
 * times(2) on Unix systems.
 */

#define hrvtime_is_started()	1
#define start_hrvtime()		do{}while(0)
#define stop_hrvtime()		do{}while(0)
#define get_hrvtime()		fallback_get_hrvtime()

#endif	/* !USE_PERFCTR */

BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0)
{
    Eterm *hp;
    Eterm res;
    FloatDef f;

    if (!hrvtime_is_started())
	start_hrvtime();
    f.fd = get_hrvtime();
    hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT);
    res = make_float(hp);
    PUT_DOUBLE(f, hp);
    BIF_RET(res);
}

BIF_RETTYPE hipe_bifs_stop_hrvtime_0(BIF_ALIST_0)
{
    stop_hrvtime();
    BIF_RET(am_true);
}