view gcc/brig/brig-lang.c @ 16:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line source

/* brig-lang.c -- brig (HSAIL) input gcc interface.
   Copyright (C) 2016-2017 Free Software Foundation, Inc.
   Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
   for General Processor Tech.

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 3, 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 COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "ansidecl.h"
#include "coretypes.h"
#include "opts.h"
#include "tree.h"
#include "tree-iterator.h"
#include "print-tree.h"
#include "stringpool.h"
#include "basic-block.h"
#include "gimple-expr.h"
#include "gimplify.h"
#include "dumpfile.h"
#include "stor-layout.h"
#include "toplev.h"
#include "debug.h"
#include "options.h"
#include "flags.h"
#include "convert.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "target.h"
#include "vec.h"
#include "brigfrontend/brig-to-generic.h"
#include "machmode.h"
#include "fold-const.h"
#include "common/common-target.h"
#include <mpfr.h>
#include "brig-c.h"
#include "brig-builtins.h"

static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
static tree handle_const_attribute (tree *, tree, tree, int, bool *);
static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);

/* This file is based on Go frontent'd go-lang.c and gogo-tree.cc.  */

/* If -v set.  */

int gccbrig_verbose = 0;

/* Language-dependent contents of a type.  */

struct GTY (()) lang_type
{
  char dummy;
};

/* Language-dependent contents of a decl.  */

struct GTY ((variable_size)) lang_decl
{
  char dummy;
};

/* Language-dependent contents of an identifier.  This must include a
   tree_identifier.  */

struct GTY (()) lang_identifier
{
  struct tree_identifier common;
};

/* The resulting tree type.  */

union GTY ((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
	    chain_next ("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
			"TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN "
			"(&%h.generic)) : NULL"))) lang_tree_node
{
  union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
  struct lang_identifier GTY ((tag ("1"))) identifier;
};

/* We don't use language_function.  */

struct GTY (()) language_function
{
  int dummy;
};


/* The option mask.  */

static unsigned int
brig_langhook_option_lang_mask (void)
{
  return CL_BRIG;
}

/* Initialize the options structure.  */

static void
brig_langhook_init_options_struct (struct gcc_options *opts)
{
  /* Signed overflow is precisely defined.  */
  opts->x_flag_wrapv = 1;

  /* If we set this to one, the whole program optimizations internalize
     all global variables, making them invisible to the dyn loader (and
     thus the HSA runtime implementation).  */
  opts->x_flag_whole_program = 0;

  /* The builtin math functions should not set errno.  */
  opts->x_flag_errno_math = 0;
  opts->frontend_set_flag_errno_math = false;

  opts->x_flag_exceptions = 0;
  opts->x_flag_non_call_exceptions = 0;

  opts->x_flag_finite_math_only = 0;
  opts->x_flag_signed_zeros = 1;

  opts->x_optimize = 3;
}

/* Handle Brig specific options.  Return 0 if we didn't do anything.  */

static bool
brig_langhook_handle_option
  (size_t scode, const char *arg ATTRIBUTE_UNUSED,
  int value ATTRIBUTE_UNUSED, int kind ATTRIBUTE_UNUSED,
  location_t loc ATTRIBUTE_UNUSED,
  const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
{
  enum opt_code code = (enum opt_code) scode;
  switch (code)
    {
    case OPT_v:
      gccbrig_verbose = 1;
      break;
    default:
      break;
    }
  return 1;
}

/* Run after parsing options.  */

static bool
brig_langhook_post_options (const char **pfilename ATTRIBUTE_UNUSED)
{
  if (flag_excess_precision_cmdline == EXCESS_PRECISION_DEFAULT)
    flag_excess_precision_cmdline = EXCESS_PRECISION_STANDARD;

  /* gccbrig casts pointers around like crazy, TBAA produces
     broken code if not force disabling it.  */
  flag_strict_aliasing = 0;

  /* Returning false means that the backend should be used.  */
  return false;
}

static size_t
get_file_size (FILE *file)
{
  size_t size;
  fseek (file, 0, SEEK_END);
  size = (size_t) ftell (file);
  fseek (file, 0, SEEK_SET);
  return size;
}

static void
brig_langhook_parse_file (void)
{
  brig_to_generic brig_to_gen;

  std::vector <char*> brig_blobs;

  for (unsigned int i = 0; i < num_in_fnames; ++i)
    {

      FILE *f;
      f = fopen (in_fnames[i], "r");
      size_t fsize = get_file_size (f);
      char *brig_blob = new char[fsize];
      if (fread (brig_blob, 1, fsize, f) != fsize)
	{
	  error ("could not read the BRIG file");
	  exit (1);
	}
      fclose (f);

      brig_to_gen.analyze (brig_blob);
      brig_blobs.push_back (brig_blob);
    }

  for (size_t i = 0; i < brig_blobs.size(); ++i)
    {
      char *brig_blob = brig_blobs.at(i);
      brig_to_gen.parse (brig_blob);
    }

  brig_to_gen.write_globals ();

  for (size_t i = 0; i < brig_blobs.size (); ++i)
    delete brig_blobs[i];
}

static tree
brig_langhook_type_for_size (unsigned int bits,
			     int unsignedp)
{
  /* Copied from go-lang.c  */
  tree type;
  if (unsignedp)
    {
      if (bits == INT_TYPE_SIZE)
        type = unsigned_type_node;
      else if (bits == CHAR_TYPE_SIZE)
        type = unsigned_char_type_node;
      else if (bits == SHORT_TYPE_SIZE)
        type = short_unsigned_type_node;
      else if (bits == LONG_TYPE_SIZE)
        type = long_unsigned_type_node;
      else if (bits == LONG_LONG_TYPE_SIZE)
        type = long_long_unsigned_type_node;
      else
        type = make_unsigned_type(bits);
    }
  else
    {
      if (bits == INT_TYPE_SIZE)
        type = integer_type_node;
      else if (bits == CHAR_TYPE_SIZE)
        type = signed_char_type_node;
      else if (bits == SHORT_TYPE_SIZE)
        type = short_integer_type_node;
      else if (bits == LONG_TYPE_SIZE)
        type = long_integer_type_node;
      else if (bits == LONG_LONG_TYPE_SIZE)
        type = long_long_integer_type_node;
      else
        type = make_signed_type(bits);
    }
  return type;
}

static tree
brig_langhook_type_for_mode (machine_mode mode, int unsignedp)
{
  if (mode == TYPE_MODE (void_type_node))
    return void_type_node;

  if (VECTOR_MODE_P (mode))
    {
      tree inner;

      inner = brig_langhook_type_for_mode (GET_MODE_INNER (mode), unsignedp);
      if (inner != NULL_TREE)
	return build_vector_type_for_mode (inner, mode);
      gcc_unreachable ();
      return NULL_TREE;
    }

  scalar_int_mode imode;
  scalar_float_mode fmode;
  if (is_int_mode (mode, &imode))
    {
      switch (GET_MODE_BITSIZE (imode))
	{
	case 32:
	  return float_type_node;
	case 64:
	  return double_type_node;
	default:
	  /* We have to check for long double in order to support
	     i386 excess precision.  */
	  if (imode == TYPE_MODE (long_double_type_node))
	    return long_double_type_node;

	  gcc_unreachable ();
	  return NULL_TREE;
	}
    }
  else if (is_float_mode (mode, &fmode))
    return brig_langhook_type_for_size (GET_MODE_BITSIZE (fmode), unsignedp);
  else
    {
      /* E.g., build_common_builtin_nodes () asks for modes/builtins
	       we do not generate or need.  Just ignore them silently for now.
      */
      return NULL_TREE;
    }
  return NULL_TREE;
}

static tree
brig_langhook_builtin_function (tree decl)
{
  return decl;
}

static GTY(()) tree registered_builtin_types;

static void
brig_langhook_register_builtin_type (tree type, const char *name)
{
  tree decl;

  if (!TYPE_NAME (type))
    {
      decl = build_decl (UNKNOWN_LOCATION, TYPE_DECL,
			 get_identifier (name), type);
      DECL_ARTIFICIAL (decl) = 1;
      TYPE_NAME (type) = decl;
    }

  registered_builtin_types = tree_cons (0, type, registered_builtin_types);
}


/* Return true if we are in the global binding level.  */

static bool
brig_langhook_global_bindings_p (void)
{
  return current_function_decl == NULL_TREE;
}

/* Push a declaration into the current binding level.  From Go: We can't
   usefully implement this since we don't want to convert from tree
   back to one of our internal data structures.  I think the only way
   this is used is to record a decl which is to be returned by
   getdecls, and we could implement it for that purpose if
   necessary.  */

static tree
brig_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED)
{
  gcc_unreachable ();
}

/* This hook is used to get the current list of declarations as trees.
   From Go: We don't support that; instead we use the write_globals hook.
   This can't simply crash because it is called by -gstabs.  */

static tree
brig_langhook_getdecls (void)
{
  return NULL;
}

static int
brig_langhook_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
			     gimple_seq *post_p ATTRIBUTE_UNUSED)
{

  /* Strip off the static chain info that appears to function
     calls for some strange reason even though we don't add
     nested functions.  Maybe something wrong with the function
     declaration contexts? */
  if (TREE_CODE (*expr_p) == CALL_EXPR
      && CALL_EXPR_STATIC_CHAIN (*expr_p) != NULL_TREE)
    CALL_EXPR_STATIC_CHAIN (*expr_p) = NULL_TREE;
  return GS_UNHANDLED;
}

static tree
brig_langhook_eh_personality (void)
{
  gcc_unreachable ();
}

/* Functions called directly by the generic backend.
   Adapted from go-lang.c.  */

tree
convert (tree type, tree expr)
{
  if (type == error_mark_node || expr == error_mark_node
      || TREE_TYPE (expr) == error_mark_node)
    return error_mark_node;

  if (type == TREE_TYPE (expr))
    return expr;

  if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
    return fold_convert (type, expr);

  switch (TREE_CODE (type))
    {
    case VOID_TYPE:
    case BOOLEAN_TYPE:
      return fold_convert (type, expr);
    case INTEGER_TYPE:
      return fold (convert_to_integer (type, expr));
    case REAL_TYPE:
      return fold (convert_to_real (type, expr));
    case VECTOR_TYPE:
      return fold (convert_to_vector (type, expr));
    case POINTER_TYPE:
      return build1 (VIEW_CONVERT_EXPR, type, convert (size_type_node, expr));
    default:
      break;
    }

  gcc_unreachable ();
}

static GTY (()) tree brig_gc_root;

/* Preserve trees that we create from the garbage collector.  */

void
brig_preserve_from_gc (tree t)
{
  brig_gc_root = tree_cons (NULL_TREE, t, brig_gc_root);
}

/* Convert an identifier for use in an error message.  */

const char *
brig_localize_identifier (const char *ident)
{
  return identifier_to_locale (ident);
}

/* Define supported attributes and their handlers. Code copied from
   lto-lang.c */

/* Table of machine-independent attributes supported in GIMPLE.  */
const struct attribute_spec brig_attribute_table[] =
{
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
       do_diagnostic } */
  { "leaf",		      0, 0, true,  false, false,
			      handle_leaf_attribute, false },
  { "const",                  0, 0, true,  false, false,
			      handle_const_attribute, false },
  { "pure",                   0, 0, true,  false, false,
			      handle_pure_attribute, false },
  { "nothrow",                0, 0, true,  false, false,
			      handle_nothrow_attribute, false },
  { "returns_twice",          0, 0, true,  false, false,
			      handle_returns_twice_attribute, false },
  { NULL,                     0, 0, false, false, false, NULL, false }
};

/* Attribute handlers.  */
/* Handle a "leaf" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_leaf_attribute (tree *node, tree name,
		       tree ARG_UNUSED (args),
		       int ARG_UNUSED (flags), bool *no_add_attrs)
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (OPT_Wattributes, "%qE attribute ignored", name);
      *no_add_attrs = true;
    }
  if (!TREE_PUBLIC (*node))
    {
      warning (OPT_Wattributes,
	       "%qE attribute has no effect on unit local functions", name);
      *no_add_attrs = true;
    }

  return NULL_TREE;
}

/* Handle a "const" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_const_attribute (tree *node, tree ARG_UNUSED (name),
			tree ARG_UNUSED (args), int ARG_UNUSED (flags),
			bool * ARG_UNUSED (no_add_attrs))
{
  tree type = TREE_TYPE (*node);

  /* See FIXME comment on noreturn in c_common_attribute_table.  */
  if (TREE_CODE (*node) == FUNCTION_DECL)
    TREE_READONLY (*node) = 1;
  else if (TREE_CODE (type) == POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
    TREE_TYPE (*node)
      = build_pointer_type
	(build_type_variant (TREE_TYPE (type), 1,
			     TREE_THIS_VOLATILE (TREE_TYPE (type))));
  else
    gcc_unreachable ();

  return NULL_TREE;
}

/* Handle a "pure" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_pure_attribute (tree *node, tree ARG_UNUSED (name),
		       tree ARG_UNUSED (args), int ARG_UNUSED (flags),
		       bool * ARG_UNUSED (no_add_attrs))
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    DECL_PURE_P (*node) = 1;
  else
    gcc_unreachable ();

  return NULL_TREE;
}

/* Handle a "nothrow" attribute; arguments as in
   struct attribute_spec.handler.  */

static tree
handle_nothrow_attribute (tree *node, tree ARG_UNUSED (name),
			  tree ARG_UNUSED (args), int ARG_UNUSED (flags),
			  bool * ARG_UNUSED (no_add_attrs))
{
  if (TREE_CODE (*node) == FUNCTION_DECL)
    TREE_NOTHROW (*node) = 1;
  else
    gcc_unreachable ();

  return NULL_TREE;
}

/* Handle a "returns_twice" attribute.  */

static tree
handle_returns_twice_attribute (tree *node, tree ARG_UNUSED (name),
				tree ARG_UNUSED (args),
				int ARG_UNUSED (flags),
				bool * ARG_UNUSED (no_add_attrs))
{
  gcc_assert (TREE_CODE (*node) == FUNCTION_DECL);

  DECL_IS_RETURNS_TWICE (*node) = 1;

  return NULL_TREE;
}


/* Built-in initialization code cribbed from lto-lang.c which cribbed it
   from c-common.c.  */


static GTY(()) tree built_in_attributes[(int) ATTR_LAST];


static GTY(()) tree builtin_types[(int) BT_LAST + 1];

static GTY(()) tree string_type_node;
static GTY(()) tree const_string_type_node;
static GTY(()) tree wint_type_node;
static GTY(()) tree intmax_type_node;
static GTY(()) tree uintmax_type_node;
static GTY(()) tree signed_size_type_node;

/* Flags needed to process builtins.def.  */
int flag_isoc94;
int flag_isoc99;
int flag_isoc11;

static void
def_fn_type (builtin_type def, builtin_type ret, bool var, int n, ...)
{
  tree t;
  tree *args = XALLOCAVEC (tree, n);
  va_list list;
  int i;
  bool err = false;

  va_start (list, n);
  for (i = 0; i < n; ++i)
    {
      builtin_type a = (builtin_type) va_arg (list, int);
      t = builtin_types[a];
      if (t == error_mark_node)
	err = true;
      args[i] = t;
    }
  va_end (list);

  t = builtin_types[ret];
  if (err)
    t = error_mark_node;
  if (t == error_mark_node)
    ;
  else if (var)
    t = build_varargs_function_type_array (t, n, args);
  else
    t = build_function_type_array (t, n, args);

  builtin_types[def] = t;
}

/* Used to help initialize the builtin-types.def table.  When a type of
   the correct size doesn't exist, use error_mark_node instead of NULL.
   The later results in segfaults even when a decl using the type doesn't
   get invoked.  */

static tree
builtin_type_for_size (int size, bool unsignedp)
{
  tree type = brig_langhook_type_for_size (size, unsignedp);
  return type ? type : error_mark_node;
}

/* Support for DEF_BUILTIN.  */

static void
def_builtin_1 (enum built_in_function fncode, const char *name,
	       enum built_in_class fnclass, tree fntype, tree libtype,
	       bool both_p, bool fallback_p, bool nonansi_p,
	       tree fnattrs, bool implicit_p)
{
  tree decl;
  const char *libname;

  if (fntype == error_mark_node)
    return;

  libname = name + strlen ("__builtin_");
  decl = add_builtin_function (name, fntype, fncode, fnclass,
			       (fallback_p ? libname : NULL),
			       fnattrs);

  if (both_p
      && !flag_no_builtin
      && !(nonansi_p && flag_no_nonansi_builtin))
    add_builtin_function (libname, libtype, fncode, fnclass,
			  NULL, fnattrs);

  set_builtin_decl (fncode, decl, implicit_p);
}


/* Initialize the attribute table for all the supported builtins.  */

static void
brig_init_attributes (void)
{
  /* Fill in the built_in_attributes array.  */
#define DEF_ATTR_NULL_TREE(ENUM)				\
  built_in_attributes[(int) ENUM] = NULL_TREE;
#define DEF_ATTR_INT(ENUM, VALUE)				\
  built_in_attributes[(int) ENUM] = build_int_cst (NULL_TREE, VALUE);
#define DEF_ATTR_STRING(ENUM, VALUE)				\
  built_in_attributes[(int) ENUM] = build_string (strlen (VALUE), VALUE);
#define DEF_ATTR_IDENT(ENUM, STRING)				\
  built_in_attributes[(int) ENUM] = get_identifier (STRING);
#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN)	\
  built_in_attributes[(int) ENUM]			\
    = tree_cons (built_in_attributes[(int) PURPOSE],	\
		 built_in_attributes[(int) VALUE],	\
		 built_in_attributes[(int) CHAIN]);
#include "builtin-attrs.def"
#undef DEF_ATTR_NULL_TREE
#undef DEF_ATTR_INT
#undef DEF_ATTR_STRING
#undef DEF_ATTR_IDENT
#undef DEF_ATTR_TREE_LIST
}

/* Create builtin types and functions.  VA_LIST_REF_TYPE_NODE and
   VA_LIST_ARG_TYPE_NODE are used in builtin-types.def.  */

static void
brig_define_builtins (tree va_list_ref_type_node ATTRIBUTE_UNUSED,
		     tree va_list_arg_type_node ATTRIBUTE_UNUSED)
{
#define DEF_PRIMITIVE_TYPE(ENUM, VALUE) \
  builtin_types[ENUM] = VALUE;
#define DEF_FUNCTION_TYPE_0(ENUM, RETURN) \
  def_fn_type (ENUM, RETURN, 0, 0);
#define DEF_FUNCTION_TYPE_1(ENUM, RETURN, ARG1) \
  def_fn_type (ENUM, RETURN, 0, 1, ARG1);
#define DEF_FUNCTION_TYPE_2(ENUM, RETURN, ARG1, ARG2) \
  def_fn_type (ENUM, RETURN, 0, 2, ARG1, ARG2);
#define DEF_FUNCTION_TYPE_3(ENUM, RETURN, ARG1, ARG2, ARG3) \
  def_fn_type (ENUM, RETURN, 0, 3, ARG1, ARG2, ARG3);
#define DEF_FUNCTION_TYPE_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \
  def_fn_type (ENUM, RETURN, 0, 4, ARG1, ARG2, ARG3, ARG4);
#define DEF_FUNCTION_TYPE_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5)	\
  def_fn_type (ENUM, RETURN, 0, 5, ARG1, ARG2, ARG3, ARG4, ARG5);
#define DEF_FUNCTION_TYPE_6(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			    ARG6)					\
  def_fn_type (ENUM, RETURN, 0, 6, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6);
#define DEF_FUNCTION_TYPE_7(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			    ARG6, ARG7)					\
  def_fn_type (ENUM, RETURN, 0, 7, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7);
#define DEF_FUNCTION_TYPE_8(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			    ARG6, ARG7, ARG8)				\
  def_fn_type (ENUM, RETURN, 0, 8, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6,	\
	       ARG7, ARG8);
#define DEF_FUNCTION_TYPE_9(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			    ARG6, ARG7, ARG8, ARG9)			\
  def_fn_type (ENUM, RETURN, 0, 9, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6,	\
	       ARG7, ARG8, ARG9);
#define DEF_FUNCTION_TYPE_10(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			     ARG6, ARG7, ARG8, ARG9, ARG10)		 \
  def_fn_type (ENUM, RETURN, 0, 10, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6,	 \
	       ARG7, ARG8, ARG9, ARG10);
#define DEF_FUNCTION_TYPE_11(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
			     ARG6, ARG7, ARG8, ARG9, ARG10, ARG11)	 \
  def_fn_type (ENUM, RETURN, 0, 11, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6,	 \
	       ARG7, ARG8, ARG9, ARG10, ARG11);
#define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) \
  def_fn_type (ENUM, RETURN, 1, 0);
#define DEF_FUNCTION_TYPE_VAR_1(ENUM, RETURN, ARG1) \
  def_fn_type (ENUM, RETURN, 1, 1, ARG1);
#define DEF_FUNCTION_TYPE_VAR_2(ENUM, RETURN, ARG1, ARG2) \
  def_fn_type (ENUM, RETURN, 1, 2, ARG1, ARG2);
#define DEF_FUNCTION_TYPE_VAR_3(ENUM, RETURN, ARG1, ARG2, ARG3) \
  def_fn_type (ENUM, RETURN, 1, 3, ARG1, ARG2, ARG3);
#define DEF_FUNCTION_TYPE_VAR_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \
  def_fn_type (ENUM, RETURN, 1, 4, ARG1, ARG2, ARG3, ARG4);
#define DEF_FUNCTION_TYPE_VAR_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) \
  def_fn_type (ENUM, RETURN, 1, 5, ARG1, ARG2, ARG3, ARG4, ARG5);
#define DEF_FUNCTION_TYPE_VAR_6(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
				 ARG6)	\
  def_fn_type (ENUM, RETURN, 1, 6, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6);
#define DEF_FUNCTION_TYPE_VAR_7(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
				ARG6, ARG7)				\
  def_fn_type (ENUM, RETURN, 1, 7, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7);
#define DEF_POINTER_TYPE(ENUM, TYPE) \
  builtin_types[(int) ENUM] = build_pointer_type (builtin_types[(int) TYPE]);

#include "builtin-types.def"

#undef DEF_PRIMITIVE_TYPE
#undef DEF_FUNCTION_TYPE_0
#undef DEF_FUNCTION_TYPE_1
#undef DEF_FUNCTION_TYPE_2
#undef DEF_FUNCTION_TYPE_3
#undef DEF_FUNCTION_TYPE_4
#undef DEF_FUNCTION_TYPE_5
#undef DEF_FUNCTION_TYPE_6
#undef DEF_FUNCTION_TYPE_7
#undef DEF_FUNCTION_TYPE_8
#undef DEF_FUNCTION_TYPE_9
#undef DEF_FUNCTION_TYPE_10
#undef DEF_FUNCTION_TYPE_11
#undef DEF_FUNCTION_TYPE_VAR_0
#undef DEF_FUNCTION_TYPE_VAR_1
#undef DEF_FUNCTION_TYPE_VAR_2
#undef DEF_FUNCTION_TYPE_VAR_3
#undef DEF_FUNCTION_TYPE_VAR_4
#undef DEF_FUNCTION_TYPE_VAR_5
#undef DEF_FUNCTION_TYPE_VAR_6
#undef DEF_FUNCTION_TYPE_VAR_7
#undef DEF_POINTER_TYPE
  builtin_types[(int) BT_LAST] = NULL_TREE;

  brig_init_attributes ();

#define DEF_BUILTIN(ENUM, NAME, CLASS, TYPE, LIBTYPE, BOTH_P, FALLBACK_P,\
		    NONANSI_P, ATTRS, IMPLICIT, COND)			\
    if (NAME && COND)							\
      def_builtin_1 (ENUM, NAME, CLASS, builtin_types[(int) TYPE],	\
		     builtin_types[(int) LIBTYPE], BOTH_P, FALLBACK_P,	\
		     NONANSI_P, built_in_attributes[(int) ATTRS], IMPLICIT);

#undef DEF_HSAIL_BUILTIN
#define DEF_HSAIL_BUILTIN(ENUM, HSAIL_OPCODE, HSAIL_TYPE, NAME, TYPE, ATTRS) \
  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
              false, true, true, ATTRS, false, true)

/* HSAIL atomic builtins do not have separate identifying opcodes.  */

#undef DEF_HSAIL_ATOMIC_BUILTIN
#define DEF_HSAIL_ATOMIC_BUILTIN(ENUM, ATOMIC_OPCODE, HSAIL_TYPE, NAME, \
       TYPE, ATTRS) \
  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
              false, true, true, ATTRS, false, true)

/* HSAIL saturating arithmetics builtins.  */

#undef DEF_HSAIL_SAT_BUILTIN
#define DEF_HSAIL_SAT_BUILTIN(ENUM, BRIG_OPCODE, HSAIL_TYPE, NAME, \
       TYPE, ATTRS) \
  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
              false, true, true, ATTRS, false, true)

/* HSAIL builtins used internally by the frontend.  */

#undef DEF_HSAIL_INTR_BUILTIN
#define DEF_HSAIL_INTR_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
              false, true, true, ATTRS, false, true)

/* HSAIL saturated conversions.  */

#undef DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN
#define DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN(ENUM, HSAIL_DEST_TYPE, HSAIL_SRC_TYPE, \
  NAME, TYPE, ATTRS) \
  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
              false, true, true, ATTRS, false, true)

#include "builtins.def"
}

/* Build nodes that would have be created by the C front-end; necessary
   for including builtin-types.def and ultimately builtins.def.  Borrowed
   from lto-lang.c.  */

static void
brig_build_c_type_nodes (void)
{
  gcc_assert (void_type_node);

  void_list_node = build_tree_list (NULL_TREE, void_type_node);
  string_type_node = build_pointer_type (char_type_node);
  const_string_type_node
    = build_pointer_type (build_qualified_type (char_type_node,
						TYPE_QUAL_CONST));

  if (strcmp (SIZE_TYPE, "unsigned int") == 0)
    {
      intmax_type_node = integer_type_node;
      uintmax_type_node = unsigned_type_node;
      signed_size_type_node = integer_type_node;
    }
  else if (strcmp (SIZE_TYPE, "long unsigned int") == 0)
    {
      intmax_type_node = long_integer_type_node;
      uintmax_type_node = long_unsigned_type_node;
      signed_size_type_node = long_integer_type_node;
    }
  else if (strcmp (SIZE_TYPE, "long long unsigned int") == 0)
    {
      intmax_type_node = long_long_integer_type_node;
      uintmax_type_node = long_long_unsigned_type_node;
      signed_size_type_node = long_long_integer_type_node;
    }
  else
    {
      int i;

      signed_size_type_node = NULL_TREE;
      for (i = 0; i < NUM_INT_N_ENTS; i++)
	if (int_n_enabled_p[i])
	  {
	    char name[50];
	    sprintf (name, "__int%d unsigned", int_n_data[i].bitsize);

	    if (strcmp (name, SIZE_TYPE) == 0)
	      {
		intmax_type_node = int_n_trees[i].signed_type;
		uintmax_type_node = int_n_trees[i].unsigned_type;
		signed_size_type_node = int_n_trees[i].signed_type;
	      }
	  }
      if (signed_size_type_node == NULL_TREE)
	gcc_unreachable ();
    }

  wint_type_node = unsigned_type_node;
  pid_type_node = integer_type_node;
}


static bool
brig_langhook_init (void)
{
  build_common_tree_nodes (false);

  /* Builtin initialization related code borrowed from lto-lang.c.  */
  void_list_node = build_tree_list (NULL_TREE, void_type_node);

  brig_build_c_type_nodes ();

  if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
    {
      tree x = build_pointer_type (TREE_TYPE (va_list_type_node));
      brig_define_builtins (x, x);
    }
  else
    {
      brig_define_builtins (build_reference_type (va_list_type_node),
			    va_list_type_node);
    }

  targetm.init_builtins ();
  build_common_builtin_nodes ();

  return true;
}

#undef LANG_HOOKS_NAME
#undef LANG_HOOKS_INIT
#undef LANG_HOOKS_OPTION_LANG_MASK
#undef LANG_HOOKS_INIT_OPTIONS_STRUCT
#undef LANG_HOOKS_HANDLE_OPTION
#undef LANG_HOOKS_POST_OPTIONS
#undef LANG_HOOKS_PARSE_FILE
#undef LANG_HOOKS_TYPE_FOR_MODE
#undef LANG_HOOKS_TYPE_FOR_SIZE
#undef LANG_HOOKS_REGISTER_BUILTIN_TYPE
#undef LANG_HOOKS_BUILTIN_FUNCTION
#undef LANG_HOOKS_GLOBAL_BINDINGS_P
#undef LANG_HOOKS_PUSHDECL
#undef LANG_HOOKS_GETDECLS
#undef LANG_HOOKS_WRITE_GLOBALS
#undef LANG_HOOKS_GIMPLIFY_EXPR
#undef LANG_HOOKS_EH_PERSONALITY

#define LANG_HOOKS_NAME "GNU Brig"
#define LANG_HOOKS_INIT brig_langhook_init
#define LANG_HOOKS_OPTION_LANG_MASK brig_langhook_option_lang_mask
#define LANG_HOOKS_INIT_OPTIONS_STRUCT brig_langhook_init_options_struct
#define LANG_HOOKS_HANDLE_OPTION brig_langhook_handle_option
#define LANG_HOOKS_POST_OPTIONS brig_langhook_post_options
#define LANG_HOOKS_PARSE_FILE brig_langhook_parse_file
#define LANG_HOOKS_TYPE_FOR_MODE brig_langhook_type_for_mode
#define LANG_HOOKS_TYPE_FOR_SIZE brig_langhook_type_for_size
#define LANG_HOOKS_REGISTER_BUILTIN_TYPE brig_langhook_register_builtin_type
#define LANG_HOOKS_BUILTIN_FUNCTION brig_langhook_builtin_function
#define LANG_HOOKS_GLOBAL_BINDINGS_P brig_langhook_global_bindings_p
#define LANG_HOOKS_PUSHDECL brig_langhook_pushdecl
#define LANG_HOOKS_GETDECLS brig_langhook_getdecls
#define LANG_HOOKS_GIMPLIFY_EXPR brig_langhook_gimplify_expr
#define LANG_HOOKS_EH_PERSONALITY brig_langhook_eh_personality

/* Attribute hooks.  */
#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE brig_attribute_table

struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;

#include "gt-brig-brig-lang.h"
#include "gtype-brig.h"