From 43ea65025fe00ea78bbbc2ce7c4b1e8f6e758d9b Mon Sep 17 00:00:00 2001 From: Anatoly Sokolov Date: Thu, 14 Apr 2011 13:24:34 +0400 Subject: [PATCH] avr.c: ("insn-codes.h"... * 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 Co-Authored-By: Georg-Johann Lay From-SVN: r172416 --- gcc/ChangeLog | 36 +++++ gcc/config/avr/avr-c.c | 188 ++++++++++++----------- gcc/config/avr/avr.c | 339 +++++++++++++++++++++++++++++++++++++++++ gcc/config/avr/avr.md | 184 +++++++++++++++++++--- gcc/doc/extend.texi | 39 +++++ 5 files changed, 680 insertions(+), 106 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6ca6b2852eb..6d0c01a2041 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,39 @@ +2011-04-14 Anatoly Sokolov + Eric Weddington + Georg-Johann Lay + + * 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 PR target/44643 diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index 05e8e8b30e6..ec314d2a139 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -1,85 +1,103 @@ -/* 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 - . */ - - -#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__"); -} - +/* 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 + . */ + + +#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"); + } +} diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index ca06431aa83..500a5b287ae 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -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; 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" diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 9c375e4c4ba..86e052aa08a 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -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) @@ -50,17 +47,29 @@ (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") @@ -1489,7 +1498,7 @@ 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)))] @@ -3130,21 +3139,19 @@ ;; 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" @@ -3246,3 +3253,138 @@ 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")]) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 0728b501299..67a4b1f8583 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -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 -- 2.30.2