/* { dg-require-effective-target return_address } */
/* { dg-options "-finstrument-functions" } */
/* { dg-xfail-run-if "" { powerpc-ibm-aix* } } */

extern void abort(void);

#define ASSERT(X)	if (!(X)) abort ();
#define NOCHK __attribute__ ((no_instrument_function))

int entry_calls, exit_calls;
void (*last_fn_entered)();
void (*last_fn_exited)();

__attribute__((noinline)) int main() NOCHK;

__attribute__((noinline)) void foo() {
    ASSERT(last_fn_entered == foo);
}

__attribute__((noinline)) static void foo2() {
    ASSERT(entry_calls == 1 && exit_calls == 0);
    ASSERT(last_fn_entered == foo2);
    foo();
    ASSERT(entry_calls == 2 && exit_calls == 1);
    ASSERT(last_fn_entered == foo);
    ASSERT(last_fn_exited == foo);
}

__attribute__((noinline)) void nfoo(void) NOCHK;
void nfoo() {
    ASSERT(entry_calls == 2 && exit_calls == 2);
    ASSERT(last_fn_entered == foo);
    ASSERT(last_fn_exited == foo2);
    foo();
    ASSERT(entry_calls == 3 && exit_calls == 3);
    ASSERT(last_fn_entered == foo);
    ASSERT(last_fn_exited == foo);
}

int main() {
    ASSERT(entry_calls == 0 && exit_calls == 0);

    foo2();

    ASSERT(entry_calls == 2 && exit_calls == 2);
    ASSERT(last_fn_entered == foo);
    ASSERT(last_fn_exited == foo2);

    nfoo();

    ASSERT(entry_calls == 3 && exit_calls == 3);
    ASSERT(last_fn_entered == foo);

    return 0;
}

void __cyg_profile_func_enter(void *, void *) NOCHK;
void __cyg_profile_func_exit(void *, void *) NOCHK;

__attribute__((noinline)) void __cyg_profile_func_enter(void *fn, void *parent) {
    entry_calls++;
    last_fn_entered = (void (*)())fn;
}
__attribute__((noinline)) void __cyg_profile_func_exit(void *fn, void *parent) {
    exit_calls++;
    last_fn_exited = (void (*)())fn;
}
