avr.c: ("insn-codes.h"...
authorAnatoly Sokolov <aesok@post.ru>
Thu, 14 Apr 2011 09:24:34 +0000 (13:24 +0400)
committerGeorg-Johann Lay <gjl@gcc.gnu.org>
Thu, 14 Apr 2011 09:24:34 +0000 (09:24 +0000)
* config/avr/avr.c: ("insn-codes.h", "optabs.h", "langhooks.h"):
New Includes
(avr_init_builtins, avr_expand_builtin,
avr_expand_delay_cycles, avr_expand_unop_builtin,
avr_expand_binop_builtin ): New functions.
(avr_builtin_id): New enum
(struct avr_builtin_description): New struct
(bdesc_1arg, bdesc_2arg): New arrays describing some RTL builtins.
(TARGET_INIT_BUILTINS, TARGET_EXPAND_BUILTIN): Define.

* config/avr/avr.md (UNSPEC_FMUL, UNSPEC_FMULS, UNSPEC_FMULSU,
UNSPECV_ENABLE_IRQS, UNSPECV_NOP, UNSPECV_SLEEP, UNSPECV_WDR,
UNSPECV_DELAY_CYCLES): new enumeration values
(UNSPEC_SEI, UNSPEC_CLI): Remove enumeration values
("enable_interrupt"): Use UNSPECV_ENABLE_IRQS
("disable_interrupt"): Use UNSPECV_ENABLE_IRQS
("*rotlqi3_4"): rename insn to "rotlqi3_4"
("delay_cycles_1", "delay_cycles_2", "delay_cycles_3",
"delay_cycles_4", "nopv", "sleep", "wdr", "fmul", "fmuls",
"fmulsu"): New insns

* config/avr/avr-c.c: fix line endings
(avr_cpu_cpp_builtins): New builtin defines: __BUILTIN_AVR_NOP,
__BUILTIN_AVR_SEI, __BUILTIN_AVR_CLI, __BUILTIN_AVR_WDR,
__BUILTIN_AVR_SLEEP, __BUILTIN_AVR_SWAP,
__BUILTIN_AVR_DELAY_CYCLES, __BUILTIN_AVR_FMUL,
__BUILTIN_AVR_FMULS, __BUILTIN_AVR_FMULSU.

* doc/extend.texi (AVR Built-in Functions): New node
(Target Builtins): Add documentation of AVR
built-in functions.

Co-Authored-By: Eric Weddington <eric.weddington@atmel.com>
Co-Authored-By: Georg-Johann Lay <avr@gjlay.de>
From-SVN: r172416

gcc/ChangeLog
gcc/config/avr/avr-c.c
gcc/config/avr/avr.c
gcc/config/avr/avr.md
gcc/doc/extend.texi

index 6ca6b2852ebe2c54ea50a43edd1e71227b446fa0..6d0c01a20414259e66334832b6aa3481b42e5f7f 100644 (file)
@@ -1,3 +1,39 @@
+2011-04-14  Anatoly Sokolov  <aesok@post.ru>
+            Eric Weddington  <eric.weddington@atmel.com>
+            Georg-Johann Lay <avr@gjlay.de> 
+       
+       * config/avr/avr.c: ("insn-codes.h", "optabs.h", "langhooks.h"):
+       New Includes
+       (avr_init_builtins, avr_expand_builtin,
+       avr_expand_delay_cycles, avr_expand_unop_builtin,
+       avr_expand_binop_builtin ): New functions.
+       (avr_builtin_id): New enum
+       (struct avr_builtin_description): New struct
+       (bdesc_1arg, bdesc_2arg): New arrays describing some RTL builtins.
+       (TARGET_INIT_BUILTINS, TARGET_EXPAND_BUILTIN): Define.
+       
+       * config/avr/avr.md (UNSPEC_FMUL, UNSPEC_FMULS, UNSPEC_FMULSU,
+       UNSPECV_ENABLE_IRQS, UNSPECV_NOP, UNSPECV_SLEEP, UNSPECV_WDR, 
+       UNSPECV_DELAY_CYCLES): new enumeration values
+       (UNSPEC_SEI, UNSPEC_CLI): Remove enumeration values
+       ("enable_interrupt"): Use UNSPECV_ENABLE_IRQS
+       ("disable_interrupt"): Use UNSPECV_ENABLE_IRQS
+       ("*rotlqi3_4"): rename insn to "rotlqi3_4"
+       ("delay_cycles_1", "delay_cycles_2", "delay_cycles_3",
+       "delay_cycles_4", "nopv", "sleep", "wdr", "fmul", "fmuls",
+       "fmulsu"): New insns
+       
+       * config/avr/avr-c.c: fix line endings
+       (avr_cpu_cpp_builtins): New builtin defines: __BUILTIN_AVR_NOP,
+       __BUILTIN_AVR_SEI, __BUILTIN_AVR_CLI, __BUILTIN_AVR_WDR,
+       __BUILTIN_AVR_SLEEP, __BUILTIN_AVR_SWAP,
+       __BUILTIN_AVR_DELAY_CYCLES, __BUILTIN_AVR_FMUL,
+       __BUILTIN_AVR_FMULS, __BUILTIN_AVR_FMULSU.
+       
+       * doc/extend.texi (AVR Built-in Functions): New node
+       (Target Builtins): Add documentation of AVR
+       built-in functions.
+
 2011-04-14  Georg-Johann Lay  <avr@gjlay.de>
 
        PR target/44643
index 05e8e8b30e6af093be6cc065d67afeea48ec0dfe..ec314d2a1399a2107df07c2e340d5eab0c98aa15 100644 (file)
-/* Copyright (C) 2009, 2010\r
-   Free Software Foundation, Inc.\r
-   Contributed by Anatoly Sokolov (aesok@post.ru)\r
-\r
-   This file is part of GCC.\r
-\r
-   GCC is free software; you can redistribute it and/or modify\r
-   it under the terms of the GNU General Public License as published by\r
-   the Free Software Foundation; either version 3, or (at your option)\r
-   any later version.\r
-   \r
-   GCC is distributed in the hope that it will be useful,\r
-   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-   GNU General Public License for more details.\r
-   \r
-   You should have received a copy of the GNU General Public License\r
-   along with GCC; see the file COPYING3.  If not see\r
-   <http://www.gnu.org/licenses/>.  */\r
-\r
-\r
-#include "config.h"\r
-#include "system.h"\r
-#include "coretypes.h"\r
-#include "tm.h"\r
-#include "tm_p.h"\r
-#include "cpplib.h"\r
-#include "tree.h"\r
-#include "c-family/c-common.h"\r
-\r
-/* Not included in avr.c since this requires C front end.  */\r
-\r
-/* Worker function for TARGET_CPU_CPP_BUILTINS.  */\r
-\r
-void\r
-avr_cpu_cpp_builtins (struct cpp_reader *pfile)\r
-{\r
-  builtin_define_std ("AVR");\r
-\r
-  if (avr_current_arch->macro)\r
-    cpp_define (pfile, avr_current_arch->macro);\r
-  if (avr_extra_arch_macro)\r
-    cpp_define (pfile, avr_extra_arch_macro);\r
-  if (avr_current_arch->have_elpm)\r
-    cpp_define (pfile, "__AVR_HAVE_RAMPZ__");\r
-  if (avr_current_arch->have_elpm)\r
-    cpp_define (pfile, "__AVR_HAVE_ELPM__");\r
-  if (avr_current_arch->have_elpmx)\r
-    cpp_define (pfile, "__AVR_HAVE_ELPMX__");\r
-  if (avr_current_arch->have_movw_lpmx)\r
-    {\r
-      cpp_define (pfile, "__AVR_HAVE_MOVW__");\r
-      cpp_define (pfile, "__AVR_HAVE_LPMX__");\r
-    }\r
-  if (avr_current_arch->asm_only)\r
-    cpp_define (pfile, "__AVR_ASM_ONLY__");\r
-  if (avr_current_arch->have_mul)\r
-    {\r
-      cpp_define (pfile, "__AVR_ENHANCED__");\r
-      cpp_define (pfile, "__AVR_HAVE_MUL__");\r
-    }\r
-  if (avr_current_arch->have_jmp_call)\r
-    {\r
-      cpp_define (pfile, "__AVR_MEGA__");\r
-      cpp_define (pfile, "__AVR_HAVE_JMP_CALL__");\r
-    }\r
-  if (avr_current_arch->have_eijmp_eicall)\r
-    {\r
-      cpp_define (pfile, "__AVR_HAVE_EIJMP_EICALL__");\r
-      cpp_define (pfile, "__AVR_3_BYTE_PC__");\r
-    }\r
-  else\r
-    {\r
-      cpp_define (pfile, "__AVR_2_BYTE_PC__");\r
-    }\r
-\r
-  if (avr_current_device->short_sp)\r
-    cpp_define (pfile, "__AVR_HAVE_8BIT_SP__");\r
-  else\r
-    cpp_define (pfile, "__AVR_HAVE_16BIT_SP__");\r
-\r
-  if (TARGET_NO_INTERRUPTS)\r
-    cpp_define (pfile, "__NO_INTERRUPTS__");\r
-}\r
-\r
+/* Copyright (C) 2009, 2010
+   Free Software Foundation, Inc.
+   Contributed by Anatoly Sokolov (aesok@post.ru)
+
+   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 "coretypes.h"
+#include "tm.h"
+#include "tm_p.h"
+#include "cpplib.h"
+#include "tree.h"
+#include "c-family/c-common.h"
+
+/* Not included in avr.c since this requires C front end.  */
+
+/* Worker function for TARGET_CPU_CPP_BUILTINS.  */
+
+void
+avr_cpu_cpp_builtins (struct cpp_reader *pfile)
+{
+  builtin_define_std ("AVR");
+
+  if (avr_current_arch->macro)
+    cpp_define (pfile, avr_current_arch->macro);
+  if (avr_extra_arch_macro)
+    cpp_define (pfile, avr_extra_arch_macro);
+  if (avr_current_arch->have_elpm)
+    cpp_define (pfile, "__AVR_HAVE_RAMPZ__");
+  if (avr_current_arch->have_elpm)
+    cpp_define (pfile, "__AVR_HAVE_ELPM__");
+  if (avr_current_arch->have_elpmx)
+    cpp_define (pfile, "__AVR_HAVE_ELPMX__");
+  if (avr_current_arch->have_movw_lpmx)
+    {
+      cpp_define (pfile, "__AVR_HAVE_MOVW__");
+      cpp_define (pfile, "__AVR_HAVE_LPMX__");
+    }
+  if (avr_current_arch->asm_only)
+    cpp_define (pfile, "__AVR_ASM_ONLY__");
+  if (avr_current_arch->have_mul)
+    {
+      cpp_define (pfile, "__AVR_ENHANCED__");
+      cpp_define (pfile, "__AVR_HAVE_MUL__");
+    }
+  if (avr_current_arch->have_jmp_call)
+    {
+      cpp_define (pfile, "__AVR_MEGA__");
+      cpp_define (pfile, "__AVR_HAVE_JMP_CALL__");
+    }
+  if (avr_current_arch->have_eijmp_eicall)
+    {
+      cpp_define (pfile, "__AVR_HAVE_EIJMP_EICALL__");
+      cpp_define (pfile, "__AVR_3_BYTE_PC__");
+    }
+  else
+    {
+      cpp_define (pfile, "__AVR_2_BYTE_PC__");
+    }
+
+  if (avr_current_device->short_sp)
+    cpp_define (pfile, "__AVR_HAVE_8BIT_SP__");
+  else
+    cpp_define (pfile, "__AVR_HAVE_16BIT_SP__");
+
+  if (TARGET_NO_INTERRUPTS)
+    cpp_define (pfile, "__NO_INTERRUPTS__");
+
+  /* Define builtin macros so that the user can
+     easily query if or if not a specific builtin
+     is available. */
+
+  cpp_define (pfile, "__BUILTIN_AVR_NOP");
+  cpp_define (pfile, "__BUILTIN_AVR_SEI");
+  cpp_define (pfile, "__BUILTIN_AVR_CLI");
+  cpp_define (pfile, "__BUILTIN_AVR_WDR");
+  cpp_define (pfile, "__BUILTIN_AVR_SLEEP");
+  cpp_define (pfile, "__BUILTIN_AVR_SWAP");
+  cpp_define (pfile, "__BUILTIN_AVR_DELAY_CYCLES");
+
+  if (AVR_HAVE_MUL)
+    {
+      cpp_define (pfile, "__BUILTIN_AVR_FMUL");
+      cpp_define (pfile, "__BUILTIN_AVR_FMULS");
+      cpp_define (pfile, "__BUILTIN_AVR_FMULSU");
+    }
+}
index ca06431aa83497fd53b6da4e94917c5201b33c5e..500a5b287aee0916cb78d85fcc00f2ade7532f4d 100644 (file)
@@ -29,6 +29,7 @@
 #include "insn-config.h"
 #include "conditions.h"
 #include "insn-attr.h"
+#include "insn-codes.h"
 #include "flags.h"
 #include "reload.h"
 #include "tree.h"
@@ -38,7 +39,9 @@
 #include "obstack.h"
 #include "function.h"
 #include "recog.h"
+#include "optabs.h"
 #include "ggc.h"
+#include "langhooks.h"
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
@@ -91,6 +94,8 @@ static bool avr_rtx_costs (rtx, int, int, int *, bool);
 static int avr_address_cost (rtx, bool);
 static bool avr_return_in_memory (const_tree, const_tree);
 static struct machine_function * avr_init_machine_status (void);
+static void avr_init_builtins (void);
+static rtx avr_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static rtx avr_builtin_setjmp_frame_value (void);
 static bool avr_hard_regno_scratch_ok (unsigned int);
 static unsigned int avr_case_values_threshold (void);
@@ -253,6 +258,13 @@ static const struct default_options avr_option_optimization_table[] =
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
 
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS avr_init_builtins
+
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN avr_expand_builtin
+
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 static void
@@ -6432,4 +6444,331 @@ unsigned int avr_case_values_threshold (void)
   return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
 }
 
+/* Helper for __builtin_avr_delay_cycles */
+
+static void
+avr_expand_delay_cycles (rtx operands0)
+{
+  unsigned HOST_WIDE_INT cycles = UINTVAL (operands0);
+  unsigned HOST_WIDE_INT cycles_used;
+  unsigned HOST_WIDE_INT loop_count;
+  
+  if (IN_RANGE (cycles, 83886082, 0xFFFFFFFF))
+    {
+      loop_count = ((cycles - 9) / 6) + 1;
+      cycles_used = ((loop_count - 1) * 6) + 9;
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 262145, 83886081))
+    {
+      loop_count = ((cycles - 7) / 5) + 1;
+      if (loop_count > 0xFFFFFF)
+        loop_count = 0xFFFFFF;
+      cycles_used = ((loop_count - 1) * 5) + 7;
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 768, 262144))
+    {
+      loop_count = ((cycles - 5) / 4) + 1;
+      if (loop_count > 0xFFFF)
+        loop_count = 0xFFFF;
+      cycles_used = ((loop_count - 1) * 4) + 5;
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode)));
+      cycles -= cycles_used;
+    }
+  
+  if (IN_RANGE (cycles, 6, 767))
+    {
+      loop_count = cycles / 3;
+      if (loop_count > 255) 
+        loop_count = 255;
+      cycles_used = loop_count * 3;
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode)));
+      cycles -= cycles_used;
+      }
+  
+  while (cycles >= 2)
+    {
+      emit_insn (gen_nopv (GEN_INT(2)));
+      cycles -= 2;
+    }
+
+  if (cycles == 1)
+    {
+      emit_insn (gen_nopv (GEN_INT(1)));
+      cycles--;
+    }
+}
+
+/* IDs for all the AVR builtins.  */
+
+enum avr_builtin_id
+  {
+    AVR_BUILTIN_NOP,
+    AVR_BUILTIN_SEI,
+    AVR_BUILTIN_CLI,
+    AVR_BUILTIN_WDR,
+    AVR_BUILTIN_SLEEP,
+    AVR_BUILTIN_SWAP,
+    AVR_BUILTIN_FMUL,
+    AVR_BUILTIN_FMULS,
+    AVR_BUILTIN_FMULSU,
+    AVR_BUILTIN_DELAY_CYCLES
+  };
+
+#define DEF_BUILTIN(NAME, TYPE, CODE)                                   \
+  do                                                                    \
+    {                                                                   \
+      add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,        \
+                            NULL, NULL_TREE);                           \
+    } while (0)
+
+
+/* Implement `TARGET_INIT_BUILTINS' */
+/* Set up all builtin functions for this target.  */
+
+static void
+avr_init_builtins (void)
+{
+  tree void_ftype_void
+    = build_function_type (void_type_node, void_list_node);
+  tree uchar_ftype_uchar
+    = build_function_type_list (unsigned_char_type_node, 
+                                unsigned_char_type_node,
+                                NULL_TREE);
+  tree uint_ftype_uchar_uchar
+    = build_function_type_list (unsigned_type_node, 
+                                unsigned_char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_char
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                char_type_node, 
+                                NULL_TREE);
+  tree int_ftype_char_uchar
+    = build_function_type_list (integer_type_node, 
+                                char_type_node,
+                                unsigned_char_type_node, 
+                                NULL_TREE);
+  tree void_ftype_ulong
+    = build_function_type_list (void_type_node, 
+                                long_unsigned_type_node,
+                                NULL_TREE);
+
+  DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP);
+  DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI);
+  DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI);
+  DEF_BUILTIN ("__builtin_avr_wdr", void_ftype_void, AVR_BUILTIN_WDR);
+  DEF_BUILTIN ("__builtin_avr_sleep", void_ftype_void, AVR_BUILTIN_SLEEP);
+  DEF_BUILTIN ("__builtin_avr_swap", uchar_ftype_uchar, AVR_BUILTIN_SWAP);
+  DEF_BUILTIN ("__builtin_avr_delay_cycles", void_ftype_ulong, 
+               AVR_BUILTIN_DELAY_CYCLES);
+
+  if (AVR_HAVE_MUL)
+    {
+      /* FIXME: If !AVR_HAVE_MUL, make respective functions available
+         in libgcc. For fmul and fmuls this is straight forward with
+         upcoming fixed point support. */
+      
+      DEF_BUILTIN ("__builtin_avr_fmul", uint_ftype_uchar_uchar, 
+                   AVR_BUILTIN_FMUL);
+      DEF_BUILTIN ("__builtin_avr_fmuls", int_ftype_char_char, 
+                   AVR_BUILTIN_FMULS);
+      DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, 
+                   AVR_BUILTIN_FMULSU);
+    }
+}
+
+#undef DEF_BUILTIN
+
+struct avr_builtin_description
+{
+  const enum insn_code icode;
+  const char *const name;
+  const enum avr_builtin_id id;
+};
+
+static const struct avr_builtin_description
+bdesc_1arg[] =
+  {
+    { CODE_FOR_rotlqi3_4, "__builtin_avr_swap", AVR_BUILTIN_SWAP }
+  };
+
+static const struct avr_builtin_description
+bdesc_2arg[] =
+  {
+    { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL },
+    { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS },
+    { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }
+  };
+
+/* Subroutine of avr_expand_builtin to take care of unop insns.  */
+
+static rtx
+avr_expand_unop_builtin (enum insn_code icode, tree exp,
+                         rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if (op0mode == SImode && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  gcc_assert (op0mode == mode0 || op0mode == VOIDmode);
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+
+  pat = GEN_FCN (icode) (target, op0);
+  if (! pat)
+    return 0;
+  
+  emit_insn (pat);
+  
+  return target;
+}
+
+
+/* Subroutine of avr_expand_builtin to take care of binop insns.  */
+
+static rtx
+avr_expand_binop_builtin (enum insn_code icode, tree exp, rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+  rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+  enum machine_mode op0mode = GET_MODE (op0);
+  enum machine_mode op1mode = GET_MODE (op1);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    {
+      target = gen_reg_rtx (tmode);
+    }
+
+  if ((op0mode == SImode || op0mode == VOIDmode) && mode0 == HImode)
+    {
+      op0mode = HImode;
+      op0 = gen_lowpart (HImode, op0);
+    }
+  
+  if ((op1mode == SImode || op1mode == VOIDmode) && mode1 == HImode)
+    {
+      op1mode = HImode;
+      op1 = gen_lowpart (HImode, op1);
+    }
+  
+  /* In case the insn wants input operands in modes different from
+     the result, abort.  */
+  
+  gcc_assert ((op0mode == mode0 || op0mode == VOIDmode)
+              && (op1mode == mode1 || op1mode == VOIDmode));
+
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+  
+  if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+    op1 = copy_to_mode_reg (mode1, op1);
+
+  pat = GEN_FCN (icode) (target, op0, op1);
+  
+  if (! pat)
+    return 0;
+
+  emit_insn (pat);
+  return target;
+}
+
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+static rtx
+avr_expand_builtin (tree exp, rtx target,
+                    rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
+{
+  size_t i;
+  const struct avr_builtin_description *d;
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  unsigned int id = DECL_FUNCTION_CODE (fndecl);
+  tree arg0;
+  rtx op0;
+
+  switch (id)
+    {
+    case AVR_BUILTIN_NOP:
+      emit_insn (gen_nopv (GEN_INT(1)));
+      return 0;
+      
+    case AVR_BUILTIN_SEI:
+      emit_insn (gen_enable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_CLI:
+      emit_insn (gen_disable_interrupt ());
+      return 0;
+      
+    case AVR_BUILTIN_WDR:
+      emit_insn (gen_wdr ());
+      return 0;
+      
+    case AVR_BUILTIN_SLEEP:
+      emit_insn (gen_sleep ());
+      return 0;
+      
+    case AVR_BUILTIN_DELAY_CYCLES:
+      {
+        arg0 = CALL_EXPR_ARG (exp, 0);
+        op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+
+        if (! CONST_INT_P (op0))
+          error ("__builtin_avr_delay_cycles expects a compile time integer constant.");
+
+        avr_expand_delay_cycles (op0);
+        return 0;
+      }
+    }
+
+  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_unop_builtin (d->icode, exp, target);
+
+  for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
+    if (d->id == id)
+      return avr_expand_binop_builtin (d->icode, exp, target);
+
+  gcc_unreachable ();
+}
+
+
 #include "gt-avr.h"
index 9c375e4c4baa4521282ad7309e7ccc75afd17614..86e052aa08acb0d5a06ad3ffd529889d83acbf22 100644 (file)
@@ -35,9 +35,6 @@
 ;;  ~  Output 'r' if not AVR_HAVE_JMP_CALL.
 ;;  !  Output 'e' if AVR_HAVE_EIJMP_EICALL.
 
-;; UNSPEC usage:
-;;  0  Length of a string, see "strlenhi".
-;;  1  Jump by register pair Z or by table addressed by Z, see "casesi".
 
 (define_constants
   [(REG_X      26)
    
    (SREG_ADDR   0x5F)
    (RAMPZ_ADDR  0x5B)
-   
-   (UNSPEC_STRLEN      0)
-   (UNSPEC_INDEX_JMP   1)
-   (UNSPEC_SEI         2)
-   (UNSPEC_CLI         3)
-
-   (UNSPECV_PROLOGUE_SAVES     0)
-   (UNSPECV_EPILOGUE_RESTORES  1)
-   (UNSPECV_WRITE_SP_IRQ_ON    2)
-   (UNSPECV_WRITE_SP_IRQ_OFF   3)
-   (UNSPECV_GOTO_RECEIVER      4)])
+   ])
+
+(define_c_enum "unspec"
+  [UNSPEC_STRLEN
+   UNSPEC_INDEX_JMP
+   UNSPEC_FMUL
+   UNSPEC_FMULS
+   UNSPEC_FMULSU
+   ])
+
+(define_c_enum "unspecv"
+  [UNSPECV_PROLOGUE_SAVES
+   UNSPECV_EPILOGUE_RESTORES
+   UNSPECV_WRITE_SP_IRQ_ON
+   UNSPECV_WRITE_SP_IRQ_OFF
+   UNSPECV_GOTO_RECEIVER
+   UNSPECV_ENABLE_IRQS
+   UNSPECV_NOP
+   UNSPECV_SLEEP
+   UNSPECV_WDR
+   UNSPECV_DELAY_CYCLES
+   ])
+    
 
 (include "predicates.md")
 (include "constraints.md")
     FAIL;
 }")
 
-(define_insn "*rotlqi3_4"
+(define_insn "rotlqi3_4"
   [(set (match_operand:QI 0 "register_operand" "=r")
        (rotate:QI (match_operand:QI 1 "register_operand" "0")
                   (const_int 4)))]
 
 ;; Enable Interrupts
 (define_insn "enable_interrupt"
-  [(unspec [(const_int 0)] UNSPEC_SEI)]
+  [(unspec_volatile [(const_int 1)] UNSPECV_ENABLE_IRQS)]
   ""
   "sei"
   [(set_attr "length" "1")
-  (set_attr "cc" "none")
-  ])
+   (set_attr "cc" "none")])
 
 ;; Disable Interrupts
 (define_insn "disable_interrupt"
-  [(unspec [(const_int 0)] UNSPEC_CLI)]
+  [(unspec_volatile [(const_int 0)] UNSPECV_ENABLE_IRQS)]
   ""
   "cli"
   [(set_attr "length" "1")
-  (set_attr "cc" "none")
-  ])
+   (set_attr "cc" "none")])
 
 ;;  Library prologue saves
 (define_insn "call_prologue_saves"
     expand_epilogue (true /* sibcall_p */);
     DONE;
   })
+
+;; Some instructions resp. instruction sequences available
+;; via builtins.
+
+(define_insn "delay_cycles_1"
+  [(unspec_volatile [(match_operand:QI 0 "const_int_operand" "n")
+                     (const_int 1)]
+                    UNSPECV_DELAY_CYCLES)
+   (clobber (match_scratch:QI 1 "=&d"))]
+  ""
+  "ldi %1,lo8(%0)
+       1: dec %1
+       brne 1b"
+  [(set_attr "length" "3")
+   (set_attr "cc" "clobber")])
+
+(define_insn "delay_cycles_2"
+  [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n")
+                     (const_int 2)]
+                    UNSPECV_DELAY_CYCLES)
+   (clobber (match_scratch:HI 1 "=&w"))]
+  ""
+  "ldi %A1,lo8(%0)
+       ldi %B1,hi8(%0)
+       1: sbiw %A1,1
+       brne 1b"
+  [(set_attr "length" "4")
+   (set_attr "cc" "clobber")])
+
+(define_insn "delay_cycles_3"
+  [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
+                     (const_int 3)]
+                    UNSPECV_DELAY_CYCLES)
+   (clobber (match_scratch:QI 1 "=&d"))
+   (clobber (match_scratch:QI 2 "=&d"))
+   (clobber (match_scratch:QI 3 "=&d"))]
+  ""
+  "ldi %1,lo8(%0)
+       ldi %2,hi8(%0)
+       ldi %3,hlo8(%0)
+       1: subi %1,1
+       sbci %2,0
+       sbci %3,0
+       brne 1b"
+  [(set_attr "length" "7")
+   (set_attr "cc" "clobber")])
+
+(define_insn "delay_cycles_4"
+  [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
+                     (const_int 4)]
+                    UNSPECV_DELAY_CYCLES)
+   (clobber (match_scratch:QI 1 "=&d"))
+   (clobber (match_scratch:QI 2 "=&d"))
+   (clobber (match_scratch:QI 3 "=&d"))
+   (clobber (match_scratch:QI 4 "=&d"))]
+  ""
+  "ldi %1,lo8(%0)
+       ldi %2,hi8(%0)
+       ldi %3,hlo8(%0)
+       ldi %4,hhi8(%0)
+       1: subi %1,1
+       sbci %2,0
+       sbci %3,0
+       sbci %4,0
+       brne 1b"
+  [(set_attr "length" "9")
+   (set_attr "cc" "clobber")])
+
+;; CPU instructions
+
+;; NOP taking 1 or 2 Ticks 
+(define_insn "nopv"
+  [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "P,K")] 
+                    UNSPECV_NOP)]
+  ""
+  "@
+       nop
+       rjmp ."
+  [(set_attr "length" "1")
+   (set_attr "cc" "none")])
+
+;; SLEEP
+(define_insn "sleep"
+  [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP)]
+  ""
+  "sleep"
+  [(set_attr "length" "1")
+   (set_attr "cc" "none")])
+;; WDR
+(define_insn "wdr"
+  [(unspec_volatile [(const_int 0)] UNSPECV_WDR)]
+  ""
+  "wdr"
+  [(set_attr "length" "1")
+   (set_attr "cc" "none")])
+  
+;; FMUL
+(define_insn "fmul"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+        (unspec:HI [(match_operand:QI 1 "register_operand" "a")
+                    (match_operand:QI 2 "register_operand" "a")]
+                   UNSPEC_FMUL))]
+  "AVR_HAVE_MUL"
+  "fmul %1,%2
+       movw %0,r0
+       clr __zero_reg__"
+  [(set_attr "length" "3")
+   (set_attr "cc" "clobber")])
+
+;; FMULS
+(define_insn "fmuls"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+        (unspec:HI [(match_operand:QI 1 "register_operand" "a")
+                    (match_operand:QI 2 "register_operand" "a")]
+                   UNSPEC_FMULS))]
+  "AVR_HAVE_MUL"
+  "fmuls %1,%2
+       movw %0,r0
+       clr __zero_reg__"
+  [(set_attr "length" "3")
+   (set_attr "cc" "clobber")])
+
+;; FMULSU
+(define_insn "fmulsu"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+        (unspec:HI [(match_operand:QI 1 "register_operand" "a")
+                    (match_operand:QI 2 "register_operand" "a")]
+                   UNSPEC_FMULSU))]
+  "AVR_HAVE_MUL"
+  "fmulsu %1,%2
+       movw %0,r0
+       clr __zero_reg__"
+  [(set_attr "length" "3")
+   (set_attr "cc" "clobber")])
index 0728b501299fdbf4947c34e0c2ab8975db12d0a1..67a4b1f8583fdcdae4c7178f75cd31f01c5215c0 100644 (file)
@@ -7924,6 +7924,7 @@ instructions, but allow the compiler to schedule those calls.
 * Alpha Built-in Functions::
 * ARM iWMMXt Built-in Functions::
 * ARM NEON Intrinsics::
+* AVR Built-in Functions::
 * Blackfin Built-in Functions::
 * FR-V Built-in Functions::
 * X86 Built-in Functions::
@@ -8175,6 +8176,44 @@ when the @option{-mfpu=neon} switch is used:
 
 @include arm-neon-intrinsics.texi
 
+@node AVR Built-in Functions
+@subsection AVR Built-in Functions
+
+For each built-in function for AVR, there is an equally named,
+uppercase built-in macro defined. That way users can easily query if
+or if not a specific built-in is implemented or not. For example, if
+@code{__builtin_avr_nop} is available the macro
+@code{__BUILTIN_AVR_NOP} is defined to @code{1} and undefined otherwise.
+
+The following built-in functions map to the respective machine
+instruction, i.e. @code{nop}, @code{sei}, @code{cli}, @code{sleep},
+@code{wdr}, @code{swap}, @code{fmul}, @code{fmuls}
+resp. @code{fmulsu}. The latter three are only available if the AVR
+device actually supports multiplication.
+
+@smallexample
+void __builtin_avr_nop (void)
+void __builtin_avr_sei (void)
+void __builtin_avr_cli (void)
+void __builtin_avr_sleep (void)
+void __builtin_avr_wdr (void)
+unsigned char __builtin_avr_swap (unsigned char)
+unsigned int __builtin_avr_fmul (unsigned char, unsigned char)
+int __builtin_avr_fmuls (char, char)
+int __builtin_avr_fmulsu (char, unsigned char)
+@end smallexample
+
+In order to delay execution for a specific number of cycles, GCC
+implements
+@smallexample
+void __builtin_avr_delay_cycles (unsigned long ticks)
+@end smallexample
+
+@code{ticks} is the number of ticks to delay execution. Note that this
+built-in does not take into account the effect of interrupts which
+might increase delay time. @code{ticks} must be a compile time
+integer constant; delays with a variable number of cycles are not supported.
+
 @node Blackfin Built-in Functions
 @subsection Blackfin Built-in Functions