/* src/vm/jit/x86_64/md.c - machine dependent x86_64 functions

   Copyright (C) 1996-2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
   Copyright (C) 2009 Theobroma Systems Ltd.

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/


#include "config.h"

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include "vm/jit/x86_64/codegen.h"
#include "vm/jit/x86_64/md-abi.h"

#include "vm/vm.hpp"

#include "vm/jit/codegen-common.hpp"
#include "vm/jit/executionstate.h"
#include "vm/jit/jit.hpp"
#include "vm/jit/trap.hpp"


/* md_init *********************************************************************

   Do some machine dependent initialization.

*******************************************************************************/

void md_init(void)
{
	/* nothing to do */
}


/* md_jit_method_patch_address *************************************************

   Gets the patch address of the currently compiled method. The offset
   is extracted from the load instruction(s) before the jump and added
   to the right base address (PV or REG_METHODPTR).

   INVOKESTATIC/SPECIAL:

   4d 8b 15 e2 fe ff ff             mov    -286(%rip),%r10
   49 ff d2                         rex64Z callq  *%r10

   INVOKEVIRTUAL:

   4c 8b 17                         mov    (%rdi),%r10
   49 8b 82 00 00 00 00             mov    0x0(%r10),%rax
   48 ff d3                         rex64 callq  *%rax

   INVOKEINTERFACE:

   4c 8b 17                         mov    (%rdi),%r10
   4d 8b 92 00 00 00 00             mov    0x0(%r10),%r10
   49 8b 82 00 00 00 00             mov    0x0(%r10),%rax
   48 ff d3                         rex64 callq  *%r11

*******************************************************************************/

void *md_jit_method_patch_address(void *pv, void *ra, void *mptr)
{
	uint8_t *pc;
	uint8_t  mcode;
	int32_t  offset;
	void    *pa;                        /* patch address                      */

	/* go back to the actual call instruction (3-bytes) */

	pc = ((uint8_t *) ra) - 3;

	/* get the last byte of the call */

	mcode = pc[2];

	/* check for the different calls */

	if (mcode == 0xd2) {
		/* INVOKESTATIC/SPECIAL */

		/* Get the offset from the instruction (the offset address is
		   4-bytes before the call instruction). */

		offset = *((int32_t *) (pc - 4));

		/* add the offset to the return address (IP-relative addressing) */

		pa = pc + offset;
	}
	else if (mcode == 0xd3) {
		/* INVOKEVIRTUAL/INTERFACE */

		/* return NULL if no mptr was specified (used for replacement) */

		if (mptr == NULL)
			return NULL;

		/* Get the offset from the instruction (the offset address is
		   4-bytes before the call instruction). */

		offset = *((int32_t *) (pc - 4));

		/* add the offset to the method pointer */

		pa = ((uint8_t *) mptr) + offset;
	}
	else {
		/* catch any problems */

		vm_abort("md_jit_method_patch_address: unknown instruction %x", mcode);

		/* keep compiler happy */

		pa = NULL;
	}

	return pa;
}


/**
 * Decode the trap instruction at the given PC.
 *
 * @param trp information about trap to be filled
 * @param sig signal number
 * @param xpc exception PC
 * @param es execution state of the machine
 * @return true if trap was decoded successfully, false otherwise.
 */
bool md_trap_decode(trapinfo_t* trp, int sig, void* _xpc, executionstate_t* es)
{
	uint8_t* xpc = (uint8_t*) _xpc;

#if 0
	// Check for StackOverflowException.
	threads_check_stackoverflow(sp);
#endif

	switch (sig) {
	case TRAP_SIGFPE:
		// Check for ArithmeticException.
		trp->type  = TRAP_ArithmeticException;
		trp->value = 0;
		return true;

	case TRAP_SIGILL:
		// Check for valid trap instruction.
		if (patcher_is_valid_trap_instruction_at(xpc)) {
			trp->type  = TRAP_PATCHER;
			trp->value = 0;
			return true;
		}
		return false;

	case TRAP_SIGSEGV:
	{
		// Get exception-throwing instruction.
		uint8_t opc = M_ALD_MEM_GET_OPC(xpc);
		uint8_t mod = M_ALD_MEM_GET_MOD(xpc);
		uint8_t rm  = M_ALD_MEM_GET_RM(xpc);

		// Check for hardware exception, for values
		// see emit_mov_mem_reg and emit_mem.
		if ((opc == 0x8b) && (mod == 0) && (rm == 4)) {
			int32_t d    = M_ALD_MEM_GET_REG(xpc);
			int32_t disp = M_ALD_MEM_GET_DISP(xpc);

			// We use the exception type as load displacement.
			trp->type  = disp;
			trp->value = es->intregs[d];
			return true;
		}

		// Default case is a normal NullPointerException.
		else {
			trp->type  = TRAP_NullPointerException;
			trp->value = 0;
			return true;
		}
	}
	
	default:
		return false;
	}
}


/* md_patch_replacement_point **************************************************

   Patch the given replacement point.

*******************************************************************************/

#if defined(ENABLE_REPLACEMENT)
void md_patch_replacement_point(u1 *pc, u1 *savedmcode, bool revert)
{
	u2 mcode;

	if (revert) {
		/* write saved machine code */
		*(u2*)(pc) = *(u2*)(savedmcode);
	}
	else {
		/* save the current machine code */
		*(u2*)(savedmcode) = *(u2*)(pc);

		/* build the machine code for the patch */
		mcode = 0x0b0f;

		/* write new machine code */
		*(u2*)(pc) = (u2) mcode;
	}

    /* XXX if required asm_cacheflush(pc,8); */
}
#endif /* defined(ENABLE_REPLACEMENT) */


/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 * vim:noexpandtab:sw=4:ts=4:
 */
