/*	$Id: inline.c,v 1.17 2007/09/09 10:01:01 ragge Exp $	*/
/*
 * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include "pass1.h"

#include <stdarg.h>

/*
 * ilink from ipole points to the next struct in the list of functions.
 */
static struct istat {
	struct istat *ilink;
	char *name;
	int type;
#define	NOTYETR	0	/* saved but not yet referenced */
#define	NOTYETW	1	/* saved and referenced but not yet written out */
#define	WRITTEN	2	/* is written out */
#define	NOTYETD	3	/* referenced but not yet saved */
	struct interpass shead;
} *ipole, *cifun;

#define	IP_REF	(MAXIP+1)

int isinlining, recovernodes;
int inlnodecnt, inlstatcnt;

#define	ialloc() permalloc(sizeof(struct istat)); inlstatcnt++
#define	nalloc() permalloc(sizeof(NODE))

static void
tcnt(NODE *p)
{
	inlnodecnt++;
}

static struct istat *
findfun(char *name)
{
	struct istat *is = ipole;
	while (is) {
		if (is->name == name)
			return is;
		is = is->ilink;
	}
	return NULL;
}

static void
refnode(char *str)
{
	struct interpass *ip;

	if (sdebug)
		printf("refnode(%s)\n", str);

	ip = permalloc(sizeof(*ip));
	ip->type = IP_REF;
	ip->ip_name = str;
	inline_addarg(ip);
}

void
inline_addarg(struct interpass *ip)
{
	DLIST_INSERT_BEFORE(&cifun->shead, ip, qelem);
	if (ip->type == IP_NODE)
		walkf(ip->ip_node, tcnt); /* Count as saved */
}

/*
 * Called to setup for inlining of a new function.
 */
void
inline_start(char *name)
{
	struct istat *is;

	if (sdebug)
		printf("inline_start(\"%s\")\n", name);

	if (isinlining)
		cerror("already inlining function");

	if ((is = findfun(name)) == 0) {
		is = ialloc();
		is->ilink = ipole;
		ipole = is;
		is->name = name;
		is->type = NOTYETR;
	} else {
		if (is->type != NOTYETD)
			cerror("inline function already defined");
		is->type = NOTYETW;
	}
	DLIST_INIT(&is->shead, qelem);
	cifun = is;
	isinlining++;
}

void
inline_end()
{
	if (sdebug)
		printf("inline_end()\n");

	isinlining = 0;
}

/*
 * Called when an inline function is found, to be sure that it will
 * be written out.
 * The function may not be defined when inline_ref() is called.
 */
void
inline_ref(char *name)
{
	struct istat *w = ipole;

	if (sdebug)
		printf("inline_ref(\"%s\")\n", name);
	if (isinlining) {
		refnode(name);
	} else {
		while (w != NULL) {
			if (w->name == name) {
				if (w->type == NOTYETR)
					w->type = NOTYETW;
				return; /* setup for writeout */
			}
			w = w->ilink;
		}
		/* function not yet defined, print out when found */
		w = ialloc();
		w->ilink = ipole;
		ipole = w;
		w->name = name;
		w->type = NOTYETD;
	}
}

static void
puto(struct istat *w)
{
	struct interpass *ip, *nip;

	/* if -O, list will be saved again so foreach cannot be used */
	ip = DLIST_NEXT(&w->shead, qelem);
	while (ip != (&w->shead)) {
		nip = DLIST_NEXT(ip, qelem);
		DLIST_REMOVE(ip, qelem);
		if (ip->type == IP_REF)
			inline_ref(ip->ip_name);
		else
			pass2_compile(ip);
		ip = nip;
	}
	DLIST_INIT(&w->shead, qelem);
}

/*
 * printout functions that are referenced.
 */
void
inline_prtout()
{
	struct istat *w = ipole;
	int gotone = 0;

	if (w == NULL)
		return;
	recovernodes++;
	while (w != NULL) {
		if (w->type == NOTYETW) {
			puto(w);
			w->type = WRITTEN;
			gotone++;
		}
		w = w->ilink;
	}
	if (gotone)
		inline_prtout();
	recovernodes--;
}


syntax highlighted by Code2HTML, v. 0.9.1