#include <stdio.h>
#include "autodefs.h"
#include "activation.h"

/*
 * AIX and MacOS refer to the stack pointer as `r1', while Linux and
 * SysV refer to it as `%r1'.  OSX_BUILD is the wrong macro to test,
 * but it gets the job done.
 */
#ifdef OSX_BUILD
# define r(n) "r"#n
# define PC_OFFSET 2		/* word offset of PC from r1 */
#else
# define r(n) "%%r"#n
# define PC_OFFSET 1
#endif

/*
 * A leaf definition of getCurrentActivation can be compiled to a
 * single lwz instruction followed by blr.  In this case,
 * getCurrentActivation() shares a frame with it's caller, and ends up
 * returning the caller of the current activation.
 */
static void forceNonLeaf() { }

int getCurrentActivation(int skipCount) {
  int ret = -1;
  /* caller stack pointer is at offset 0 of current */
  asm ("lwz %0, 0(" r(1) ")" : "=r" (ret));
  while (skipCount--)
    ret = getCaller(ret);
  return ret;
}

int getCaller(int frameHandle) { return *(int *)frameHandle; }

void *callerLocalsEnd(int frameHandle) {
  return (void *) (frameHandle - 72);
}

/*
 * ppc stores the return address in the caller's frame, before
 * adjusting the stack with stwu.
 */
static inline void *getPC(int frameHandle) {
    void *pc = ((void **) frameHandle)[PC_OFFSET];
    // fprintf(stderr, "getPC(%p) => %p\n", frameHandle, pc);
    return pc;
}

static inline void setPC(int frameHandle, void * absPC) {
  // fprintf(stderr, "setPC(%p, %p)\n", frameHandle, absPC);
  ((void **) frameHandle)[PC_OFFSET] = absPC;
}

void *getCallerPC(int frameHandle) {
  return getPC(frameHandle);//getPC(getCaller(frameHandle));
}

void setCallerPC(int frameHandle, void *absPC) {
  setPC(frameHandle, absPC);//setPC(getCaller(frameHandle), absPC);
}
