/* Statement-expressions parsing for Ksi.
   Copyright (C) 2001, 2002 Michal Moskal (malekith@pld-linux.org)

This file is part of GCC.

GCC 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.

GCC 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 GCC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include "ksi.h"
#include "ggc.h"

/* bind(EXP) -> scope */
tree compile_bind(tree *type)
{
	struct term_state ctx;
	tree exp, block, vars;
	
	pushlevel(0);

	init_term(&ctx, kw_bind);
	exp = compile(get_keyword(&ctx), type);
	fini_term(&ctx);
	
	vars = get_vars();
	block = poplevel(1, 0, 0);

	return build(BIND_EXPR, *type, vars, exp, block);
}

/* return(EXP) */
/* Note: return(const(type(int, 32), 10)) won't return 10, */
/* return(modify(result(), const(type(int, 32), 10))) will. */
tree compile_return(tree *type)
{
	struct term_state ctx;
	tree exp;

	init_term(&ctx, kw_return);
	exp = compile(get_keyword(&ctx), type);
	fini_term(&ctx);

	*type = void_type_node;
	
	exp = build(RETURN_EXPR, *type, exp);

	return exp;
}

/* loop(EXP) */
tree compile_loop(tree *type)
{
	struct term_state ctx;
	tree exp;

	init_term(&ctx, kw_loop);
	exp = compile(get_keyword(&ctx), type);
	fini_term(&ctx);

	*type = void_type_node;
	
	exp = build(LOOP_EXPR, *type, exp);

	TREE_SIDE_EFFECTS(exp) = 1;

	return exp;
}

/* exit(COND) */
/* exit(const(type(bool), 1)) == break */
tree compile_exit_loop(tree *type)
{
	struct term_state ctx;
	tree exp;

	init_term(&ctx, kw_exit_loop);
	exp = compile(get_keyword(&ctx), type);
	fini_term(&ctx);

	if (TREE_CODE(*type) != BOOLEAN_TYPE)
		fatal_error("condition for (exit_loop ...) has non-bool type");

	*type = void_type_node;
	
	exp = build(EXIT_EXPR, *type, exp);

	return exp;
}

#ifdef LABELS
/* labels are somewhat diffrent then other identifiers, */
/* they don't disappear until end of function. */

static tree labels_chain;

static tree find_label(tree id)
{
	tree ptr;

	for (ptr = labels_chain; ptr; ptr = TREE_CHAIN(ptr))
		if (DECL_NAME(ptr) == id) 
			return ptr;
	
	return NULL;
}

/* label(name) */
tree compile_label(tree *type)
{
	struct term_state ctx;
	tree decl, name, exp;

	init_term(&ctx, kw_label);
	name = get_id(&ctx);

	fini_term(&ctx);

	if ((decl = find_label(name))) {
		if (TREE_LANG_FLAG_0(decl))	/* has been defined */
			fatal_error("multiple definition of label '%s'",
				    IDENTIFIER_POINTER(name));
	} else {
		decl = build_decl(LABEL_DECL, name, void_type_node);
		DECL_CONTEXT(decl) = current_function_decl;
		TREE_CHAIN(decl) = labels_chain;
		labels_chain = decl;
	}
	
	TREE_LANG_FLAG_0(decl) = 1;

	*type = void_type_node;
	
	exp = build(LABEL_EXPR, *type, decl);

	return exp;
}

/* goto(name) */
tree compile_goto(tree *type)
{
	struct term_state ctx;
	tree id, decl, exp;

	init_term(&ctx, kw_goto);
	
	id = get_id(&ctx);

	fini_term(&ctx);

	if ((decl = find_label(id)) == NULL) {
		decl = build_decl(LABEL_DECL, id, void_type_node);
		DECL_CONTEXT(decl) = current_function_decl;
		TREE_LANG_FLAG_0(decl) = 0;
		
		TREE_CHAIN(decl) = labels_chain;
		labels_chain = decl;
	}

	TREE_LANG_FLAG_1(decl) = 1;
	
	*type = void_type_node;
	
	exp = build(GOTO_EXPR, *type, decl);

	return exp;
}
#endif

struct block {
	struct block *chain;
	tree label;
	tree block;
	tree type;
};

static struct block *block_chain;

void fini_labels()
{
#ifdef LABELS
	tree ptr;

	for (ptr = labels_chain; ptr; ptr = TREE_CHAIN(ptr))
		if (!TREE_LANG_FLAG_0(ptr))
			fatal_error("label '%s' used but not defined",
				    IDENTIFIER_POINTER(DECL_NAME(ptr)));
		else if (!TREE_LANG_FLAG_1(ptr))
			warning("label '%s' defined but not used",
				IDENTIFIER_POINTER(DECL_NAME(ptr)));
	
	labels_chain = NULL;
#endif
	if (block_chain != NULL)
		ice();
}

tree compile_exit(tree *type)
{
	struct term_state ctx;
	struct block *b;
	tree exp, id;
	
	init_term(&ctx, kw_block);
	
	id = get_id(&ctx);

	if (optional_term_arg_present(&ctx)) {
		exp = compile(get_keyword(&ctx), type);
	} else {
		*type = void_type_node;
		exp = build1(NOP_EXPR, *type, integer_zero_node);
	}
	
	fini_term(&ctx);

	for (b = block_chain; b; b = b->chain)
		if (DECL_NAME(b->label) == id)
			break;
	
	if (b == NULL)
		fatal_error("(exit ...) from undefined block '%s'",
			    IDENTIFIER_POINTER(id));
	
	same_types(kw_exit, *type, b->type);

	*type = void_type_node;

	exp = build(EXIT_BLOCK_EXPR, *type, b->block, 
		    /* this workaround for not implemented return_value */
		    /* in EXIT_BLOCK_EXPR */
		    b->type == void_type_node ? NULL_TREE : exp);

	TREE_SIDE_EFFECTS(exp) = 1;
	
	/* Java front end states, that this needs to be done, but
	 * it doesn't do so :)
	 * exp = build(COMPOUND_EXPR, *type, integer_zero_node, exp); 
	 */

	return exp;
}

tree compile_block(tree *type)
{
	struct term_state ctx;
	struct block b;
	keyword_t kw;
	tree decl, id, exp;
	
	init_term(&ctx, kw_block);
	
	id = get_id(&ctx);

	decl = build_decl(LABEL_DECL, id, void_type_node);
	DECL_CONTEXT(decl) = current_function_decl;
	
	b.label = decl;
	b.chain = block_chain;
	block_chain = &b;
	
	kw = get_keyword(&ctx);
	if (type_keyword(kw)) {
		b.type = parse_type(kw);
		kw = get_keyword(&ctx);
	} else
		b.type = void_type_node;
		
	b.block = build(LABELED_BLOCK_EXPR, b.type, decl, NULL_TREE);
	
	exp = compile(kw, type);
	
	fini_term(&ctx);
	
	/* don't check in case void is expected */
	if (b.type != void_type_node)
		same_types(kw_block, b.type, *type);
		
	/* pop it off */
	if (block_chain != &b)
		ice();
	block_chain = block_chain->chain;
	
	*type = b.type;

	LABELED_BLOCK_BODY(b.block) = exp;
	TREE_SIDE_EFFECTS(b.block) = TREE_SIDE_EFFECTS(exp);

	return b.block;
}

tree compile_try_finally(tree *type)
{
	struct term_state ctx;
	tree exp, cleanup;
	tree exp_type, cleanup_type;

	init_term(&ctx, kw_loop);
	exp = compile(get_keyword(&ctx), &exp_type);
	cleanup = compile(get_keyword(&ctx), &cleanup_type);
	fini_term(&ctx);

	*type = exp_type;
	
	exp = build(TRY_FINALLY_EXPR, *type, exp, cleanup);

	return exp;
}

tree dbx_last_input_file;
int dbx_last_line, dbx_last_col;

void parse_input_file(void)
{
	struct term_state ctx;
	struct arg a;

	init_term(&ctx, kw_input_file);
	get_term_arg(&ctx, arg_string, &a);
	dbx_last_input_file = get_identifier(a.u.val_string.data);
	fini_term(&ctx);

	dbx_last_line = dbx_last_col = 1;
}

/* (wfl LINE [COLUMN] EXP) */
tree compile_wfl(tree *type)
{
	struct arg a;
	struct term_state ctx;
	tree exp;
	keyword_t kw;

	dbx_last_col = 0;
	
	init_term(&ctx, kw_wfl);
	get_term_arg(&ctx, arg_int|arg_uint, &a);
	dbx_last_line = a.u.val_int.low;
	get_term_arg(&ctx, arg_int|arg_uint|arg_kw, &a);
	if (a.sel & (arg_int|arg_uint)) {
		dbx_last_col = a.u.val_int.low;
		kw = get_keyword(&ctx);
	} else
		kw = a.u.val_kw;
	
	exp = compile(kw, type);
	fini_term(&ctx);

	
	exp = build(EXPR_WITH_FILE_LOCATION, *type, exp, 
		    dbx_last_input_file, NULL);
	EXPR_WFL_SET_LINECOL(exp, dbx_last_line, dbx_last_col);
	EXPR_WFL_EMIT_LINE_NOTE(exp) = 1;
	
	return exp;
}

static tree call_arguments(tree fntype, const char *fnname)
{
	tree ptr, t1 = NULL, t2, args = NULL, arg;
	struct list_state lctx;
	keyword_t kw;
	
	expect_list(&lctx);
	
	ptr = TYPE_ARG_TYPES(fntype);
	
	for (;;) {
		if (ptr) {
			t1 = TREE_VALUE(ptr);
			if (t1 == void_type_node)
				break;
		} else
			t1 = NULL;
			
		kw = get_keyword_from_list(&lctx);
		if (kw == kw__error) {
			if (ptr)
				fatal_error("to few arguments in call to "
					    "%s()", fnname);
			else
				break;
		}
				    
		arg = compile(kw, &t2);
		
		/* produced message could be confusing... */
		if (t1)
			same_types_ex(kw_call, t2, t1, 1);
		
		arg = tree_cons(NULL, arg, NULL);
		args = chainon(args, arg);

		if (ptr)
			ptr = TREE_CHAIN(ptr);
	}

	if (get_keyword_from_list(&lctx) != kw__error)
		fatal_error("to many arguments in call to %s()",
			    fnname);
	
	return args;
}

static tree build_call(tree fndecl, tree *type)
{
	tree fntype, args, fnptr, exp;
	const char *fnname;
	
	fntype = build_type_variant(TREE_TYPE(fndecl),
				    TREE_READONLY(fndecl),
				    TREE_THIS_VOLATILE(fndecl));
	fnptr = build1(ADDR_EXPR, build_pointer_type(fntype), fndecl);
	fnname = IDENTIFIER_POINTER(DECL_NAME(fndecl));
	
	/* return type: */
	*type = TREE_TYPE(fntype);
	
	args = call_arguments(fntype, fnname);

	exp = build(CALL_EXPR, *type, fnptr, args, NULL_TREE);

	return exp;
}

/* call(NAME, ARGS...) */
tree compile_call(tree *type, int side)
{
	tree fndecl, exp;
	struct term_state ctx;
	struct arg a;
	
	init_term(&ctx, side ? kw_call : kw_fcall);
	get_term_arg(&ctx, arg_id, &a);
	fndecl = lookup_id(a.u.val_id, FUNCTION_DECL);
	if (fndecl == NULL)
		fatal_error("use of undefined function %s(...)", a.u.val_id);
	exp = build_call(fndecl, type);
	fini_term(&ctx);
	
	TREE_SIDE_EFFECTS(exp) = side;

	return exp;
}

/* (ptr_call EXP ARGS...) */
tree compile_ptr_call(tree *type, int side)
{
	tree exp, fntype, fnptr, args;
	struct term_state ctx;
	
	init_term(&ctx, side ? kw_ptr_call : kw_ptr_fcall);

	fnptr = compile(get_keyword(&ctx), &fntype);

	if (TREE_CODE(fntype) != POINTER_TYPE ||
	    TREE_CODE(TREE_TYPE(fntype)) != FUNCTION_TYPE)
		fatal_error("called object is not a function pointer");

	fntype = TREE_TYPE(fntype);	/* strip POINTER_TYPE */
	
	/* return type: */
	*type = TREE_TYPE(fntype);
	
	args = call_arguments(fntype, "FUNC_PTR_EXPRESSION");

	exp = build(CALL_EXPR, *type, fnptr, args, NULL_TREE);
	fini_term(&ctx);
	
	TREE_SIDE_EFFECTS(exp) = side;

	return exp;
}

static tree builtin_chain = NULL;

/* builtin(NAME, ARGS...) */
tree compile_builtin(tree *type, int side)
{
	tree fndecl, exp, id;
	struct term_state ctx;
	
	init_term(&ctx, side ? kw_builtin : kw_fbuiltin);
	id = get_id(&ctx);
	for (fndecl = builtin_chain; fndecl; fndecl = TREE_CHAIN(fndecl))
		if (DECL_NAME(fndecl) == id)
			break;
	if (fndecl == NULL)
		fatal_error("use of undefined builtin %s(...)", 
			    IDENTIFIER_POINTER(id));
	
	/* XXX hack to avoid ICE, to be removed if it's fixed */
	if (DECL_FUNCTION_CODE(fndecl) == BUILT_IN_LONGJMP)
		DECL_UNINLINABLE(current_function_decl) = 1;
			    
	exp = build_call(fndecl, type);
	fini_term(&ctx);
	
	TREE_SIDE_EFFECTS(exp) = side;

	return exp;
}

static void add_builtin(const char *name, tree type, int code,
			const char *library_name)
{
	tree decl;
	
	decl = build_decl(FUNCTION_DECL, get_identifier(name), type);
	DECL_EXTERNAL(decl) = 1;
	TREE_PUBLIC(decl) = 1;
	if (library_name)
		SET_DECL_ASSEMBLER_NAME(decl,
					get_identifier(library_name));
	make_decl_rtl(decl, NULL);
	DECL_CONTEXT(decl) = NULL;
	DECL_BUILT_IN_CLASS(decl) = BUILT_IN_NORMAL;
	DECL_FUNCTION_CODE(decl) = code;

	TREE_CHAIN(decl) = builtin_chain;
	builtin_chain = decl;
}

void init_builtins(void)
{
	tree apply_type,
	    bcmp_ftype,
	    bzero_ftype,
	    default_function_type,
	    double_ftype_double,
	    float_ftype_float,
	    int_ftype_cptr_cptr_sizet,
	    int_ftype_cstring_cstring,
	    int_ftype_cstring_cstring_sizet,
	    int_ftype_int,
	    ldouble_ftype_ldouble,
	    longjmp_type,
	    memcpy_ftype,
	    memset_ftype,
	    ptr_ftype,
	    ptr_ftype_sizetype,
	    ptr_ftype_unsigned,
	    setjmp_type,
	    sizet_ftype_cstring_cstring,
	    stdarg_start_type,
	    string_ftype_cstring_cstring,
	    string_ftype_cstring_int,
	    string_ftype_string_cstring,
	    string_ftype_string_cstring_sizet,
	    strlen_ftype,
	    va_copy_type,
	    va_end_type,
	    varargs_start_type,
	    void_ftype,
	    void_ftype_ptr,
	    endlink,
	    int_endlink,
	    double_endlink,
	    cstring_endlink,
	    unsigned_endlink,
	    sizetype_endlink, 
	    string_type_node, 
	    const_string_type_node,
	    va_list_arg_type_node,
	    va_list_ref_type_node;

	string_type_node = build_pointer_type(char_type_node);
	const_string_type_node = string_type_node;
	    /* = build_pointer_type(build_type_variant(char_type_node, 1, 0));
	     * Ksi doesn't handle const-antything... */
	default_function_type =
	    build_function_type(integer_type_node, NULL_TREE);
  
	if (TREE_CODE (va_list_type_node) == ARRAY_TYPE) {
		va_list_arg_type_node = va_list_ref_type_node =
			build_pointer_type (TREE_TYPE (va_list_type_node));
	} else { 
		va_list_arg_type_node = va_list_type_node;
		va_list_ref_type_node = 
			build_reference_type(va_list_type_node);
	}

	endlink = void_list_node;
	int_endlink = tree_cons(NULL_TREE, integer_type_node, endlink);
	double_endlink = tree_cons(NULL_TREE, double_type_node, endlink);
	unsigned_endlink =
	    tree_cons(NULL_TREE, unsigned_type_node, endlink);
	cstring_endlink =
	    tree_cons(NULL_TREE, const_string_type_node, endlink);

	ptr_ftype = build_function_type(ptr_type_node, NULL_TREE);
	ptr_ftype_unsigned =
	    build_function_type(ptr_type_node, unsigned_endlink);
	sizetype_endlink =
	    tree_cons(NULL_TREE, TYPE_DOMAIN(sizetype), endlink);
	/* We realloc here because sizetype could be int or unsigned.  S'ok.  */
	ptr_ftype_sizetype =
	    build_function_type(ptr_type_node, sizetype_endlink);

	void_ftype = build_function_type(void_type_node, endlink);
	void_ftype_ptr
	    = build_function_type(void_type_node,
				  tree_cons(NULL_TREE, ptr_type_node,
					    endlink));

	float_ftype_float
	    = build_function_type(float_type_node,
				  tree_cons(NULL_TREE, float_type_node,
					    endlink));

	double_ftype_double
	    = build_function_type(double_type_node, double_endlink);

	ldouble_ftype_ldouble
	    = build_function_type(long_double_type_node,
				  tree_cons(NULL_TREE,
					    long_double_type_node,
					    endlink));

	int_ftype_int
	    = build_function_type(integer_type_node, int_endlink);
	int_ftype_cptr_cptr_sizet
	    = build_function_type(integer_type_node,
				  tree_cons(NULL_TREE, const_ptr_type_node,
					    tree_cons(NULL_TREE,
						      const_ptr_type_node,
						      sizetype_endlink)));

	/* Prototype for strcpy/strcat.  */
	string_ftype_string_cstring
	    = build_function_type(string_type_node,
				  tree_cons(NULL_TREE, string_type_node,
					    cstring_endlink));

	/* Prototype for strncpy/strncat.  */
	string_ftype_string_cstring_sizet
	    = build_function_type(string_type_node,
				  tree_cons(NULL_TREE, string_type_node,
					    tree_cons(NULL_TREE,
						      const_string_type_node,
						      sizetype_endlink)));


	/* Prototype for strcmp.  */
	int_ftype_cstring_cstring
	    = build_function_type(integer_type_node,
				  tree_cons(NULL_TREE,
					    const_string_type_node,
					    cstring_endlink));

	/* Prototype for strspn/strcspn.  */
	sizet_ftype_cstring_cstring
	    = build_function_type(sizetype,
				  tree_cons(NULL_TREE,
					    const_string_type_node,
					    cstring_endlink));

	/* Prototype for strncmp.  */
	int_ftype_cstring_cstring_sizet
	    = build_function_type(integer_type_node,
				  tree_cons(NULL_TREE,
					    const_string_type_node,
					    tree_cons(NULL_TREE,
						      const_string_type_node,
						      sizetype_endlink)));

	/* Prototype for strstr, strpbrk, etc.  */
	string_ftype_cstring_cstring
	    = build_function_type(string_type_node,
				  tree_cons(NULL_TREE,
					    const_string_type_node,
					    cstring_endlink));

	/* Prototype for strchr.  */
	string_ftype_cstring_int
	    = build_function_type(string_type_node,
				  tree_cons(NULL_TREE,
					    const_string_type_node,
					    int_endlink));

	/* Prototype for strlen.  */
	strlen_ftype = build_function_type(sizetype, cstring_endlink);

	/* Prototype for memcpy.  */
	memcpy_ftype
	    = build_function_type(ptr_type_node,
				  tree_cons(NULL_TREE, ptr_type_node,
					    tree_cons(NULL_TREE,
						      const_ptr_type_node,
						      sizetype_endlink)));

	/* Prototype for memset.  */
	memset_ftype
	    = build_function_type(ptr_type_node,
				  tree_cons(NULL_TREE, ptr_type_node,
					    tree_cons(NULL_TREE,
						      integer_type_node,
						      sizetype_endlink)));

	/* Prototype for bzero.  */
	bzero_ftype
	    = build_function_type(void_type_node,
				  tree_cons(NULL_TREE, ptr_type_node,
					    sizetype_endlink));

	/* Prototype for bcmp.  */
	bcmp_ftype
	    = build_function_type(integer_type_node,
				  tree_cons(NULL_TREE, const_ptr_type_node,
					    tree_cons(NULL_TREE,
						      const_ptr_type_node,
						      sizetype_endlink)));
	setjmp_type =
	    build_function_type(integer_type_node,
				tree_cons(NULL_TREE, ptr_type_node,
					  endlink));
	longjmp_type =
	    build_function_type(void_type_node,
				tree_cons(NULL_TREE, ptr_type_node,
					  int_endlink));
	apply_type =
	    build_function_type(ptr_type_node,
				tree_cons(NULL_TREE,
					  build_pointer_type
					  (build_function_type
					   (void_type_node, NULL_TREE)),
					  tree_cons(NULL_TREE,
						    ptr_type_node,
						    sizetype_endlink)));
	va_copy_type =
	    build_function_type(void_type_node,
				tree_cons(NULL_TREE, va_list_ref_type_node,
					  tree_cons(NULL_TREE,
						    va_list_arg_type_node,
						    endlink)));
	va_end_type =
	    build_function_type(void_type_node,
				tree_cons(NULL_TREE, va_list_ref_type_node,
					  endlink));
	stdarg_start_type =
	    build_function_type(void_type_node,
				tree_cons(NULL_TREE, va_list_ref_type_node,
					  NULL_TREE));
	varargs_start_type =
	    build_function_type(void_type_node,
				tree_cons(NULL_TREE, va_list_ref_type_node,
					  endlink));
					  
	add_builtin("constant_p", default_function_type, 
		    BUILT_IN_CONSTANT_P, NULL);
	add_builtin("return_address", ptr_ftype_unsigned, 
		    BUILT_IN_RETURN_ADDRESS, NULL);
	add_builtin("frame_address", ptr_ftype_unsigned, 
		    BUILT_IN_FRAME_ADDRESS, NULL);
	add_builtin("alloca", ptr_ftype_sizetype, BUILT_IN_ALLOCA, NULL);
	add_builtin("index", string_ftype_cstring_int, BUILT_IN_INDEX, NULL);
	add_builtin("rindex", string_ftype_cstring_int, BUILT_IN_RINDEX, NULL);
	add_builtin("bzero", bzero_ftype, BUILT_IN_BZERO, NULL);
	add_builtin("bcmp", bcmp_ftype, BUILT_IN_BCMP, NULL);
	add_builtin("saveregs", ptr_ftype, BUILT_IN_SAVEREGS, NULL);
	add_builtin("classify_type", default_function_type, 
		    BUILT_IN_CLASSIFY_TYPE, NULL);
	add_builtin("next_arg", ptr_ftype, BUILT_IN_NEXT_ARG, NULL);
	add_builtin("args_info", int_ftype_int, BUILT_IN_ARGS_INFO, NULL);
	add_builtin("setjmp", setjmp_type , BUILT_IN_SETJMP, NULL);
	add_builtin("longjmp", longjmp_type, BUILT_IN_LONGJMP, NULL);
	add_builtin("trap", void_ftype, BUILT_IN_TRAP, NULL);
	add_builtin("apply_args", ptr_ftype, BUILT_IN_APPLY_ARGS, NULL);
	add_builtin("apply", apply_type, BUILT_IN_APPLY, NULL);
	add_builtin("return", void_ftype_ptr, BUILT_IN_RETURN, NULL);
	add_builtin("varargs_start", varargs_start_type, 
		    BUILT_IN_VARARGS_START, NULL);
	add_builtin("stdarg_start", stdarg_start_type, 
		    BUILT_IN_STDARG_START, NULL);
	add_builtin("va_end", va_end_type, BUILT_IN_VA_END, NULL);
	add_builtin("va_copy", va_copy_type, BUILT_IN_VA_COPY, NULL);
	add_builtin("memcpy", memcpy_ftype, BUILT_IN_MEMCPY, NULL);
	add_builtin("memcmp", int_ftype_cptr_cptr_sizet, BUILT_IN_MEMCMP, NULL);
	add_builtin("memset", memset_ftype, BUILT_IN_MEMSET, NULL);
	add_builtin("strcmp", int_ftype_cstring_cstring, BUILT_IN_STRCMP, NULL);
	add_builtin("strncmp", int_ftype_cstring_cstring_sizet, 
		    BUILT_IN_STRNCMP, NULL);
	add_builtin("strstr", string_ftype_cstring_cstring, 
		    BUILT_IN_STRSTR, NULL);
	add_builtin("strpbrk", string_ftype_cstring_cstring,
		    BUILT_IN_STRPBRK, NULL);
	add_builtin("strchr", string_ftype_cstring_int,
		    BUILT_IN_STRCHR, NULL);
	add_builtin("strrchr", string_ftype_cstring_int,
		    BUILT_IN_STRRCHR, NULL);
	add_builtin("strcpy", string_ftype_string_cstring,
		    BUILT_IN_STRCPY, NULL);
	add_builtin("strncpy", string_ftype_string_cstring_sizet,
		    BUILT_IN_STRNCPY, NULL);
	add_builtin("strcat", string_ftype_string_cstring,
		    BUILT_IN_STRCAT, NULL);
	add_builtin("strncat", string_ftype_string_cstring_sizet,
		    BUILT_IN_STRNCAT, NULL);
	add_builtin("strspn", sizet_ftype_cstring_cstring,
		    BUILT_IN_STRSPN, NULL);
	add_builtin("strcspn", sizet_ftype_cstring_cstring,
		    BUILT_IN_STRCSPN, NULL);
	add_builtin("strlen", strlen_ftype, BUILT_IN_STRLEN, NULL);
	add_builtin("sqrtf", float_ftype_float, BUILT_IN_SQRTF, NULL);
	add_builtin("sqrt", double_ftype_double, BUILT_IN_SQRT, NULL);
	add_builtin("sqrtl", ldouble_ftype_ldouble, BUILT_IN_SQRTL, NULL);
	add_builtin("sinf", float_ftype_float, BUILT_IN_SINF, NULL);
	add_builtin("sin", double_ftype_double, BUILT_IN_SIN, NULL);
	add_builtin("sinl", ldouble_ftype_ldouble, BUILT_IN_SINL, NULL);
	add_builtin("cosf", float_ftype_float, BUILT_IN_COSF, NULL);
	add_builtin("cos", double_ftype_double, BUILT_IN_COS, NULL);
	add_builtin("cosl", ldouble_ftype_ldouble, BUILT_IN_COSL, NULL);
#ifdef EH_RETURN_DATA_REGNO
	add_builtin("eh_return_data_regno", int_ftype_int, 
		    BUILT_IN_EH_RETURN_DATA_REGNO, NULL);
#endif

	ggc_add_tree_root(&builtin_chain, 1);
}
