/* src/vm/jit/stubs.cpp - JIT stubs

   Copyright (C) 1996-2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
   Copyright (C) 2008 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 <stdint.h>

// Include machine dependent headers.
#include "md.h"

#include "mm/dumpmemory.hpp"

#include "vm/method.hpp"
#include "vm/options.h"

#include "vm/jit/abi.h"
#include "vm/jit/code.hpp"
#include "vm/jit/codegen-common.hpp"
#include "vm/jit/disass.h"
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/show.hpp"
#include "vm/jit/stubs.hpp"


/**
 * Wrapper for codegen_emit_stub_compiler.
 *
 * @param m Method object.
 *
 * @return Pointer to the compiler stub code.
 */
void* CompilerStub::generate(methodinfo *m)
{
	jitdata     *jd;
	codegendata *cd;
	ptrint      *d;                     /* pointer to data memory             */
	u1          *c;                     /* pointer to code memory             */

	// Create new dump memory area.
	DumpMemoryArea dma;

	/* allocate required data structures */

	jd = (jitdata*) DumpMemory::allocate(sizeof(jitdata));

	jd->m     = m;
	jd->cd    = (codegendata*) DumpMemory::allocate(sizeof(codegendata));
	jd->flags = 0;

	/* get required compiler data */

	cd = jd->cd;

#if !defined(JIT_COMPILER_VIA_SIGNAL)
	/* allocate code memory */

	c = CNEW(u1, 3 * SIZEOF_VOID_P + get_code_size());

	/* set pointers correctly */

	d = (ptrint *) c;

	cd->mcodebase = c;

	c = c + 3 * SIZEOF_VOID_P;
	cd->mcodeptr = c;

	/* NOTE: The codeinfo pointer is actually a pointer to the
	   methodinfo (this fakes a codeinfo structure). */

	d[0] = (ptrint) asm_call_jit_compiler;
	d[1] = (ptrint) m;
	d[2] = (ptrint) &d[1];                                    /* fake code->m */

	/* call the emit function */

	codegen_emit_stub_compiler(jd);

#if defined(ENABLE_STATISTICS)
	if (opt_stat)
		count_cstub_len += 3 * SIZEOF_VOID_P + get_code_size();
#endif

	/* flush caches */

	md_cacheflush(cd->mcodebase, 3 * SIZEOF_VOID_P + get_code_size());
#else
	/* Allocate code memory. */

	c = CNEW(uint8_t, 2 * SIZEOF_VOID_P + get_code_size());

	/* Set pointers correctly. */

	d = (ptrint *) c;

	cd->mcodebase = c;

	c = c + 2 * SIZEOF_VOID_P;
	cd->mcodeptr = c;

	/* NOTE: The codeinfo pointer is actually a pointer to the
	   methodinfo (this fakes a codeinfo structure). */

	d[0] = (ptrint) m;
	d[1] = (ptrint) &d[0];                                    /* fake code->m */

	/* Emit the trap instruction. */

	emit_trap_compiler(cd);

#if defined(ENABLE_STATISTICS)
	if (opt_stat)
		count_cstub_len += 2 * SIZEOF_VOID_P + get_code_size();
#endif

	/* Flush caches. */

	md_cacheflush(cd->mcodebase, 2 * SIZEOF_VOID_P + get_code_size());
#endif

	/* return native stub code */

	return c;
}


/**
 * Free a compiler stub from memory.
 *
 * @param 
 */
void CompilerStub::remove(void* stub)
{
	// Pass size 1 to keep the intern function happy.
	CFREE(stub, 1);
}


/* codegen_disassemble_nativestub **********************************************

   Disassembles the generated builtin or native stub.

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

#if defined(ENABLE_DISASSEMBLER)
static void codegen_disassemble_stub(methodinfo *m, u1 *start, u1 *end)
{
	printf("Stub code: ");
	if (m->clazz != NULL)
		utf_fprint_printable_ascii_classname(stdout, m->clazz->name);
	else
		printf("NULL");
	printf(".");
	utf_fprint_printable_ascii(stdout, m->name);
	utf_fprint_printable_ascii(stdout, m->descriptor);
	printf("\nLength: %d\n\n", (s4) (end - start));

	DISASSEMBLE(start, end);
}
#endif


/**
 * Wrapper for codegen_emit_stub_native.
 *
 * @param m   Method object.
 * @param bte Builtin function structure.
 */
void BuiltinStub::generate(methodinfo* m, builtintable_entry* bte)
{
	jitdata  *jd;
	codeinfo *code;
	int       skipparams;

	// Create new dump memory area.
	DumpMemoryArea dma;

	/* Create JIT data structure. */

	jd = jit_jitdata_new(m);

	/* Get required compiler data. */

	code = jd->code;

	/* Stubs are non-leaf methods. */

	code_unflag_leafmethod(code);

	/* setup code generation stuff */

	codegen_setup(jd);

	/* Set the number of native arguments we need to skip. */

	skipparams = 0;

	/* generate the code */

#if defined(ENABLE_JIT)
# if defined(ENABLE_INTRP)
	if (!opt_intrp) {
# endif
		assert(bte->fp != NULL);
		codegen_emit_stub_native(jd, bte->md, bte->fp, skipparams);
# if defined(ENABLE_INTRP)
	}
# endif
#endif

	/* reallocate the memory and finish the code generation */

	codegen_finish(jd);

	/* set the stub entry point in the builtin table */

	bte->stub = code->entrypoint;

#if defined(ENABLE_STATISTICS)
	if (opt_stat)
		size_stub_native += code->mcodelength;
#endif

#if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
	/* disassemble native stub */

	if (opt_DisassembleStubs) {
		codegen_disassemble_stub(m,
								 (u1 *) (ptrint) code->entrypoint,
								 (u1 *) (ptrint) code->entrypoint + (code->mcodelength - jd->cd->dseglen));

		/* show data segment */

		if (opt_showddatasegment)
			dseg_display(jd);
	}
#endif /* !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER) */
}


/**
 * Wrapper for codegen_emit_stub_native.
 *
 * @param m Method object of the native function.
 * @param f Native function pointer.
 *
 * @return The codeinfo representing the stub code.
 */
codeinfo* NativeStub::generate(methodinfo* m, functionptr f)
{
	jitdata     *jd;
	codeinfo    *code;
	methoddesc  *md;
	methoddesc  *nmd;	
	int          skipparams;

	// Create new dump memory area.
	DumpMemoryArea dma;

	/* Create JIT data structure. */

	jd = jit_jitdata_new(m);

	/* Get required compiler data. */

	code = jd->code;

	/* Stubs are non-leaf methods. */

	code_unflag_leafmethod(code);

	/* set the flags for the current JIT run */

#if defined(ENABLE_PROFILING)
	if (opt_prof)
		jd->flags |= JITDATA_FLAG_INSTRUMENT;
#endif

	if (opt_verbosecall)
		jd->flags |= JITDATA_FLAG_VERBOSECALL;

	/* setup code generation stuff */

#if defined(ENABLE_JIT)
# if defined(ENABLE_INTRP)
	if (!opt_intrp)
# endif
		reg_setup(jd);
#endif

	codegen_setup(jd);

	/* create new method descriptor with additional native parameters */

	md = m->parseddesc;

	/* Set the number of native arguments we need to skip. */

	if (m->flags & ACC_STATIC)
		skipparams = 2;
	else
		skipparams = 1;
	
	nmd = (methoddesc*) DumpMemory::allocate(sizeof(methoddesc) - sizeof(typedesc) +
											 md->paramcount * sizeof(typedesc) +
											 skipparams * sizeof(typedesc));

	nmd->paramcount = md->paramcount + skipparams;

	nmd->params = (paramdesc*) DumpMemory::allocate(sizeof(paramdesc) * nmd->paramcount);

	nmd->paramtypes[0].type = TYPE_ADR; /* add environment pointer            */

	if (m->flags & ACC_STATIC)
		nmd->paramtypes[1].type = TYPE_ADR; /* add class pointer              */

	MCOPY(nmd->paramtypes + skipparams, md->paramtypes, typedesc,
		  md->paramcount);

#if defined(ENABLE_JIT)
# if defined(ENABLE_INTRP)
	if (!opt_intrp)
# endif
		/* pre-allocate the arguments for the native ABI */

		md_param_alloc_native(nmd);
#endif

	/* generate the code */

#if defined(ENABLE_JIT)
# if defined(ENABLE_INTRP)
	if (opt_intrp)
		intrp_createnativestub(f, jd, nmd);
	else
# endif
		codegen_emit_stub_native(jd, nmd, f, skipparams);
#else
	intrp_createnativestub(f, jd, nmd);
#endif

	/* reallocate the memory and finish the code generation */

	codegen_finish(jd);

#if defined(ENABLE_STATISTICS)
	/* must be done after codegen_finish() */

	if (opt_stat)
		size_stub_native += code->mcodelength;
#endif

#if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
	/* disassemble native stub */

	if (opt_DisassembleStubs) {
# if defined(ENABLE_DEBUG_FILTER)
		if (m->filtermatches & SHOW_FILTER_FLAG_SHOW_METHOD)
# endif
		{
			codegen_disassemble_stub(m,
									 (u1 *) (ptrint) code->entrypoint,
									 (u1 *) (ptrint) code->entrypoint + (code->mcodelength - jd->cd->dseglen));

			/* show data segment */

			if (opt_showddatasegment)
				dseg_display(jd);
		}
	}
#endif /* !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER) */

	/* return native stub code */

	return code;
}


/**
 * Free a native stub from memory.
 *
 * @param stub Pointer to stub memory.
 */    
void NativeStub::remove(void* stub)
{
	// Pass size 1 to keep the intern function happy.
	CFREE(stub, 1);
}


/*
 * 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:
 */
