From af0c51ac87ab2a87caa03fa108f0d164987a2764 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Thu, 28 Mar 2013 11:14:20 -0700 Subject: [GCC 4.8] Initial check-in of GCC 4.8.0 Change-Id: I0719d8a6d0f69b367a6ab6f10eb75622dbf12771 --- gcc-4.8/gcc/config/avr/avr-arch.h | 156 + gcc-4.8/gcc/config/avr/avr-c.c | 401 + gcc-4.8/gcc/config/avr/avr-devices.c | 114 + gcc-4.8/gcc/config/avr/avr-dimode.md | 479 ++ gcc-4.8/gcc/config/avr/avr-fixed.md | 528 ++ gcc-4.8/gcc/config/avr/avr-log.c | 399 + gcc-4.8/gcc/config/avr/avr-mcus.def | 337 + gcc-4.8/gcc/config/avr/avr-modes.def | 33 + gcc-4.8/gcc/config/avr/avr-protos.h | 163 + gcc-4.8/gcc/config/avr/avr-stdint.h | 66 + gcc-4.8/gcc/config/avr/avr-tables.opt | 808 ++ gcc-4.8/gcc/config/avr/avr.c | 12250 +++++++++++++++++++++++++++ gcc-4.8/gcc/config/avr/avr.h | 604 ++ gcc-4.8/gcc/config/avr/avr.md | 6357 ++++++++++++++ gcc-4.8/gcc/config/avr/avr.opt | 80 + gcc-4.8/gcc/config/avr/avrlibc.h | 30 + gcc-4.8/gcc/config/avr/builtins.def | 169 + gcc-4.8/gcc/config/avr/constraints.md | 238 + gcc-4.8/gcc/config/avr/driver-avr.c | 150 + gcc-4.8/gcc/config/avr/elf.h | 41 + gcc-4.8/gcc/config/avr/gen-avr-mmcu-texi.c | 130 + gcc-4.8/gcc/config/avr/genmultilib.awk | 216 + gcc-4.8/gcc/config/avr/genopt.sh | 59 + gcc-4.8/gcc/config/avr/predicates.md | 275 + gcc-4.8/gcc/config/avr/rtems.h | 27 + gcc-4.8/gcc/config/avr/stdfix.h | 236 + gcc-4.8/gcc/config/avr/t-avr | 83 + gcc-4.8/gcc/config/avr/t-multilib | 283 + gcc-4.8/gcc/config/avr/t-rtems | 3 + 29 files changed, 24715 insertions(+) create mode 100644 gcc-4.8/gcc/config/avr/avr-arch.h create mode 100644 gcc-4.8/gcc/config/avr/avr-c.c create mode 100644 gcc-4.8/gcc/config/avr/avr-devices.c create mode 100644 gcc-4.8/gcc/config/avr/avr-dimode.md create mode 100644 gcc-4.8/gcc/config/avr/avr-fixed.md create mode 100644 gcc-4.8/gcc/config/avr/avr-log.c create mode 100644 gcc-4.8/gcc/config/avr/avr-mcus.def create mode 100644 gcc-4.8/gcc/config/avr/avr-modes.def create mode 100644 gcc-4.8/gcc/config/avr/avr-protos.h create mode 100644 gcc-4.8/gcc/config/avr/avr-stdint.h create mode 100644 gcc-4.8/gcc/config/avr/avr-tables.opt create mode 100644 gcc-4.8/gcc/config/avr/avr.c create mode 100644 gcc-4.8/gcc/config/avr/avr.h create mode 100644 gcc-4.8/gcc/config/avr/avr.md create mode 100644 gcc-4.8/gcc/config/avr/avr.opt create mode 100644 gcc-4.8/gcc/config/avr/avrlibc.h create mode 100644 gcc-4.8/gcc/config/avr/builtins.def create mode 100644 gcc-4.8/gcc/config/avr/constraints.md create mode 100644 gcc-4.8/gcc/config/avr/driver-avr.c create mode 100644 gcc-4.8/gcc/config/avr/elf.h create mode 100644 gcc-4.8/gcc/config/avr/gen-avr-mmcu-texi.c create mode 100644 gcc-4.8/gcc/config/avr/genmultilib.awk create mode 100755 gcc-4.8/gcc/config/avr/genopt.sh create mode 100644 gcc-4.8/gcc/config/avr/predicates.md create mode 100644 gcc-4.8/gcc/config/avr/rtems.h create mode 100644 gcc-4.8/gcc/config/avr/stdfix.h create mode 100644 gcc-4.8/gcc/config/avr/t-avr create mode 100644 gcc-4.8/gcc/config/avr/t-multilib create mode 100644 gcc-4.8/gcc/config/avr/t-rtems (limited to 'gcc-4.8/gcc/config/avr') diff --git a/gcc-4.8/gcc/config/avr/avr-arch.h b/gcc-4.8/gcc/config/avr/avr-arch.h new file mode 100644 index 000000000..27cea736e --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-arch.h @@ -0,0 +1,156 @@ +/* Definitions of types that are used to store AVR architecture and + device information. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Georg-Johann Lay (avr@gjlay.de) + +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 +. */ + + +/* This enum supplies indices into the avr_arch_types[] table below. */ + +enum avr_arch +{ + ARCH_UNKNOWN, + ARCH_AVR1, + ARCH_AVR2, + ARCH_AVR25, + ARCH_AVR3, + ARCH_AVR31, + ARCH_AVR35, + ARCH_AVR4, + ARCH_AVR5, + ARCH_AVR51, + ARCH_AVR6, + ARCH_AVRXMEGA2, + ARCH_AVRXMEGA4, + ARCH_AVRXMEGA5, + ARCH_AVRXMEGA6, + ARCH_AVRXMEGA7 +}; + + +/* Architecture-specific properties. */ + +typedef struct +{ + /* Assembler only. */ + int asm_only; + + /* Core have 'MUL*' instructions. */ + int have_mul; + + /* Core have 'CALL' and 'JMP' instructions. */ + int have_jmp_call; + + /* Core have 'MOVW' and 'LPM Rx,Z' instructions. */ + int have_movw_lpmx; + + /* Core have 'ELPM' instructions. */ + int have_elpm; + + /* Core have 'ELPM Rx,Z' instructions. */ + int have_elpmx; + + /* Core have 'EICALL' and 'EIJMP' instructions. */ + int have_eijmp_eicall; + + /* This is an XMEGA core. */ + int xmega_p; + + /* This core has the RAMPD special function register + and thus also the RAMPX, RAMPY and RAMPZ registers. */ + int have_rampd; + + /* Default start of data section address for architecture. */ + int default_data_section_start; + + /* Offset between SFR address and RAM address: + SFR-address = RAM-address - sfr_offset */ + int sfr_offset; + + /* Architecture id to built-in define __AVR_ARCH__ (NULL -> no macro) */ + const char *const macro; + + /* Architecture name. */ + const char *const arch_name; +} avr_arch_t; + + +/* Device-specific properties. */ + +typedef struct +{ + /* Device name. */ + const char *const name; + + /* Index in avr_arch_types[]. */ + enum avr_arch arch; + + /* Must lie outside user's namespace. NULL == no macro. */ + const char *const macro; + + /* Stack pointer have 8 bits width. */ + int short_sp; + + /* Some AVR devices have a core erratum when skipping a 2-word instruction. + Skip instructions are: SBRC, SBRS, SBIC, SBIS, CPSE. + Problems will occur with return address is IRQ executes during the + skip sequence. + + A support ticket from Atmel returned the following information: + + Subject: (ATTicket:644469) On AVR skip-bug core Erratum + From: avr@atmel.com Date: 2011-07-27 + (Please keep the subject when replying to this mail) + + This errata exists only in AT90S8515 and ATmega103 devices. + + For information please refer the following respective errata links + http://www.atmel.com/dyn/resources/prod_documents/doc2494.pdf + http://www.atmel.com/dyn/resources/prod_documents/doc1436.pdf */ + + /* Core Erratum: Must not skip 2-word instruction. */ + int errata_skip; + + /* Start of data section. */ + int data_section_start; + + /* Number of 64k segments in the flash. */ + int n_flash; + + /* Name of device library. */ + const char *const library_name; +} avr_mcu_t; + +/* Map architecture to its texinfo string. */ + +typedef struct +{ + /* Architecture ID. */ + enum avr_arch arch; + + /* textinfo source to describe the archtiecture. */ + const char *texinfo; +} avr_arch_info_t; + +/* Preprocessor macros to define depending on MCU type. */ + +extern const avr_arch_t avr_arch_types[]; +extern const avr_arch_t *avr_current_arch; + +extern const avr_mcu_t avr_mcu_types[]; +extern const avr_mcu_t *avr_current_device; diff --git a/gcc-4.8/gcc/config/avr/avr-c.c b/gcc-4.8/gcc/config/avr/avr-c.c new file mode 100644 index 000000000..4e64405a3 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-c.c @@ -0,0 +1,401 @@ +/* Copyright (C) 2009-2013 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 + . */ + +/* Not included in avr.c since this requires C front end. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tm_p.h" +#include "cpplib.h" +#include "tree.h" +#include "target.h" +#include "c-family/c-common.h" +#include "langhooks.h" + + +/* IDs for all the AVR builtins. */ + +enum avr_builtin_id + { +#define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME) \ + AVR_BUILTIN_ ## NAME, +#include "builtins.def" +#undef DEF_BUILTIN + + AVR_BUILTIN_COUNT + }; + + +/* Implement `TARGET_RESOLVE_OVERLOADED_PLUGIN'. */ + +static tree +avr_resolve_overloaded_builtin (unsigned int iloc, tree fndecl, void *vargs) +{ + tree type0, type1, fold = NULL_TREE; + enum avr_builtin_id id = AVR_BUILTIN_COUNT; + location_t loc = (location_t) iloc; + vec &args = * (vec*) vargs; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + default: + break; + + case AVR_BUILTIN_ABSFX: + if (args.length() != 1) + { + error_at (loc, "%qs expects 1 argument but %d given", + "absfx", (int) args.length()); + + fold = error_mark_node; + break; + } + + type0 = TREE_TYPE (args[0]); + + if (!FIXED_POINT_TYPE_P (type0)) + { + error_at (loc, "%qs expects a fixed-point value as argument", + "absfx"); + + fold = error_mark_node; + } + + switch (TYPE_MODE (type0)) + { + case QQmode: id = AVR_BUILTIN_ABSHR; break; + case HQmode: id = AVR_BUILTIN_ABSR; break; + case SQmode: id = AVR_BUILTIN_ABSLR; break; + case DQmode: id = AVR_BUILTIN_ABSLLR; break; + + case HAmode: id = AVR_BUILTIN_ABSHK; break; + case SAmode: id = AVR_BUILTIN_ABSK; break; + case DAmode: id = AVR_BUILTIN_ABSLK; break; + case TAmode: id = AVR_BUILTIN_ABSLLK; break; + + case UQQmode: + case UHQmode: + case USQmode: + case UDQmode: + case UHAmode: + case USAmode: + case UDAmode: + case UTAmode: + warning_at (loc, 0, "using %qs with unsigned type has no effect", + "absfx"); + return args[0]; + + default: + error_at (loc, "no matching fixed-point overload found for %qs", + "absfx"); + + fold = error_mark_node; + break; + } + + fold = targetm.builtin_decl (id, true); + + if (fold != error_mark_node) + fold = build_function_call_vec (loc, fold, &args, NULL); + + break; // absfx + + case AVR_BUILTIN_ROUNDFX: + if (args.length() != 2) + { + error_at (loc, "%qs expects 2 arguments but %d given", + "roundfx", (int) args.length()); + + fold = error_mark_node; + break; + } + + type0 = TREE_TYPE (args[0]); + type1 = TREE_TYPE (args[1]); + + if (!FIXED_POINT_TYPE_P (type0)) + { + error_at (loc, "%qs expects a fixed-point value as first argument", + "roundfx"); + + fold = error_mark_node; + } + + if (!INTEGRAL_TYPE_P (type1)) + { + error_at (loc, "%qs expects an integer value as second argument", + "roundfx"); + + fold = error_mark_node; + } + + switch (TYPE_MODE (type0)) + { + case QQmode: id = AVR_BUILTIN_ROUNDHR; break; + case HQmode: id = AVR_BUILTIN_ROUNDR; break; + case SQmode: id = AVR_BUILTIN_ROUNDLR; break; + case DQmode: id = AVR_BUILTIN_ROUNDLLR; break; + + case UQQmode: id = AVR_BUILTIN_ROUNDUHR; break; + case UHQmode: id = AVR_BUILTIN_ROUNDUR; break; + case USQmode: id = AVR_BUILTIN_ROUNDULR; break; + case UDQmode: id = AVR_BUILTIN_ROUNDULLR; break; + + case HAmode: id = AVR_BUILTIN_ROUNDHK; break; + case SAmode: id = AVR_BUILTIN_ROUNDK; break; + case DAmode: id = AVR_BUILTIN_ROUNDLK; break; + case TAmode: id = AVR_BUILTIN_ROUNDLLK; break; + + case UHAmode: id = AVR_BUILTIN_ROUNDUHK; break; + case USAmode: id = AVR_BUILTIN_ROUNDUK; break; + case UDAmode: id = AVR_BUILTIN_ROUNDULK; break; + case UTAmode: id = AVR_BUILTIN_ROUNDULLK; break; + + default: + error_at (loc, "no matching fixed-point overload found for %qs", + "roundfx"); + + fold = error_mark_node; + break; + } + + fold = targetm.builtin_decl (id, true); + + if (fold != error_mark_node) + fold = build_function_call_vec (loc, fold, &args, NULL); + + break; // roundfx + + case AVR_BUILTIN_COUNTLSFX: + if (args.length() != 1) + { + error_at (loc, "%qs expects 1 argument but %d given", + "countlsfx", (int) args.length()); + + fold = error_mark_node; + break; + } + + type0 = TREE_TYPE (args[0]); + + if (!FIXED_POINT_TYPE_P (type0)) + { + error_at (loc, "%qs expects a fixed-point value as first argument", + "countlsfx"); + + fold = error_mark_node; + } + + switch (TYPE_MODE (type0)) + { + case QQmode: id = AVR_BUILTIN_COUNTLSHR; break; + case HQmode: id = AVR_BUILTIN_COUNTLSR; break; + case SQmode: id = AVR_BUILTIN_COUNTLSLR; break; + case DQmode: id = AVR_BUILTIN_COUNTLSLLR; break; + + case UQQmode: id = AVR_BUILTIN_COUNTLSUHR; break; + case UHQmode: id = AVR_BUILTIN_COUNTLSUR; break; + case USQmode: id = AVR_BUILTIN_COUNTLSULR; break; + case UDQmode: id = AVR_BUILTIN_COUNTLSULLR; break; + + case HAmode: id = AVR_BUILTIN_COUNTLSHK; break; + case SAmode: id = AVR_BUILTIN_COUNTLSK; break; + case DAmode: id = AVR_BUILTIN_COUNTLSLK; break; + case TAmode: id = AVR_BUILTIN_COUNTLSLLK; break; + + case UHAmode: id = AVR_BUILTIN_COUNTLSUHK; break; + case USAmode: id = AVR_BUILTIN_COUNTLSUK; break; + case UDAmode: id = AVR_BUILTIN_COUNTLSULK; break; + case UTAmode: id = AVR_BUILTIN_COUNTLSULLK; break; + + default: + error_at (loc, "no matching fixed-point overload found for %qs", + "countlsfx"); + + fold = error_mark_node; + break; + } + + fold = targetm.builtin_decl (id, true); + + if (fold != error_mark_node) + fold = build_function_call_vec (loc, fold, &args, NULL); + + break; // countlsfx + } + + return fold; +} + + +/* Implement `REGISTER_TARGET_PRAGMAS'. */ + +void +avr_register_target_pragmas (void) +{ + int i; + + gcc_assert (ADDR_SPACE_GENERIC == ADDR_SPACE_RAM); + + /* Register address spaces. The order must be the same as in the respective + enum from avr.h (or designated initializers must be used in avr.c). */ + + for (i = 0; i < ADDR_SPACE_COUNT; i++) + { + gcc_assert (i == avr_addrspace[i].id); + + if (!ADDR_SPACE_GENERIC_P (i)) + c_register_addr_space (avr_addrspace[i].name, avr_addrspace[i].id); + } + + targetm.resolve_overloaded_builtin = avr_resolve_overloaded_builtin; +} + + +/* Transform LO into uppercase and write the result to UP. + You must provide enough space for UP. Return UP. */ + +static char* +avr_toupper (char *up, const char *lo) +{ + char *up0 = up; + + for (; *lo; lo++, up++) + *up = TOUPPER (*lo); + + *up = '\0'; + + return up0; +} + +/* Worker function for TARGET_CPU_CPP_BUILTINS. */ + +void +avr_cpu_cpp_builtins (struct cpp_reader *pfile) +{ + int i; + + builtin_define_std ("AVR"); + + if (avr_current_arch->macro) + cpp_define_formatted (pfile, "__AVR_ARCH__=%s", avr_current_arch->macro); + if (avr_current_device->macro) + cpp_define (pfile, avr_current_device->macro); + if (AVR_HAVE_RAMPD) cpp_define (pfile, "__AVR_HAVE_RAMPD__"); + if (AVR_HAVE_RAMPX) cpp_define (pfile, "__AVR_HAVE_RAMPX__"); + if (AVR_HAVE_RAMPY) cpp_define (pfile, "__AVR_HAVE_RAMPY__"); + if (AVR_HAVE_RAMPZ) cpp_define (pfile, "__AVR_HAVE_RAMPZ__"); + if (AVR_HAVE_ELPM) cpp_define (pfile, "__AVR_HAVE_ELPM__"); + if (AVR_HAVE_ELPMX) cpp_define (pfile, "__AVR_HAVE_ELPMX__"); + if (AVR_HAVE_MOVW) cpp_define (pfile, "__AVR_HAVE_MOVW__"); + if (AVR_HAVE_LPMX) cpp_define (pfile, "__AVR_HAVE_LPMX__"); + + if (avr_current_arch->asm_only) + cpp_define (pfile, "__AVR_ASM_ONLY__"); + if (AVR_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_XMEGA) + cpp_define (pfile, "__AVR_XMEGA__"); + 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_HAVE_8BIT_SP) + cpp_define (pfile, "__AVR_HAVE_8BIT_SP__"); + else + cpp_define (pfile, "__AVR_HAVE_16BIT_SP__"); + + if (avr_sp8) + cpp_define (pfile, "__AVR_SP8__"); + + if (AVR_HAVE_SPH) + cpp_define (pfile, "__AVR_HAVE_SPH__"); + + if (TARGET_NO_INTERRUPTS) + cpp_define (pfile, "__NO_INTERRUPTS__"); + + if (avr_current_device->errata_skip) + { + cpp_define (pfile, "__AVR_ERRATA_SKIP__"); + + if (avr_current_arch->have_jmp_call) + cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__"); + } + + cpp_define_formatted (pfile, "__AVR_SFR_OFFSET__=0x%x", + avr_current_arch->sfr_offset); + +#ifdef WITH_AVRLIBC + cpp_define (pfile, "__WITH_AVRLIBC__"); +#endif /* WITH_AVRLIBC */ + + /* Define builtin macros so that the user can easily query whether + non-generic address spaces (and which) are supported or not. + This is only supported for C. For C++, a language extension is needed + (as mentioned in ISO/IEC DTR 18037; Annex F.2) which is not + implemented in GCC up to now. */ + + if (!strcmp (lang_hooks.name, "GNU C")) + { + for (i = 0; i < ADDR_SPACE_COUNT; i++) + if (!ADDR_SPACE_GENERIC_P (i) + /* Only supply __FLASH macro if the address space is reasonable + for this target. The address space qualifier itself is still + supported, but using it will throw an error. */ + && avr_addrspace[i].segment < avr_current_device->n_flash) + { + const char *name = avr_addrspace[i].name; + char *Name = (char*) alloca (1 + strlen (name)); + + cpp_define (pfile, avr_toupper (Name, name)); + } + } + + /* Define builtin macros so that the user can easily query whether or + not a specific builtin is available. */ + +#define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME) \ + cpp_define (pfile, "__BUILTIN_AVR_" #NAME); +#include "builtins.def" +#undef DEF_BUILTIN + + /* Builtin macros for the __int24 and __uint24 type. */ + + cpp_define_formatted (pfile, "__INT24_MAX__=8388607%s", + INT_TYPE_SIZE == 8 ? "LL" : "L"); + cpp_define (pfile, "__INT24_MIN__=(-__INT24_MAX__-1)"); + cpp_define_formatted (pfile, "__UINT24_MAX__=16777215%s", + INT_TYPE_SIZE == 8 ? "ULL" : "UL"); +} diff --git a/gcc-4.8/gcc/config/avr/avr-devices.c b/gcc-4.8/gcc/config/avr/avr-devices.c new file mode 100644 index 000000000..48a952318 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-devices.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2009-2013 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 + . */ + +#ifndef IN_GEN_AVR_MMCU_TEXI +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#endif /* IN_GEN_AVR_MMCU_TEXI */ + +/* List of all known AVR MCU architectures. + Order as of enum avr_arch from avr.h. */ + +const avr_arch_t +avr_arch_types[] = +{ + /* unknown device specified */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, NULL, "avr2" }, + /* + A M J LM E E E X R d S S O A + S U M PO L L I M A a t F ff r + M L P MV P P J E M t a R s c + XW M M M G P a r e h + X P A D t t ID */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "1", "avr1" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "2", "avr2" }, + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "25", "avr25" }, + { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "3", "avr3" }, + { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 32, "31", "avr31" }, + { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "35", "avr35" }, + { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "4", "avr4" }, + { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "5", "avr5" }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, "51", "avr51" }, + { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 32, "6", "avr6" }, + + { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0x2000, 0, "102", "avrxmega2" }, + { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0x2000, 0, "104", "avrxmega4" }, + { 0, 1, 1, 1, 1, 1, 0, 1, 1, 0x2000, 0, "105", "avrxmega5" }, + { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0x2000, 0, "106", "avrxmega6" }, + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0x2000, 0, "107", "avrxmega7" } +}; + +const avr_arch_info_t +avr_texinfo[] = +{ + { ARCH_AVR1, + "This ISA is implemented by the minimal AVR core and supported " + "for assembler only." }, + { ARCH_AVR2, + "``Classic'' devices with up to 8@tie{}KiB of program memory." }, + { ARCH_AVR25, + "``Classic'' devices with up to 8@tie{}KiB of program memory and with " + "the @code{MOVW} instruction." }, + { ARCH_AVR3, + "``Classic'' devices with 16@tie{}KiB up to 64@tie{}KiB of " + " program memory." }, + { ARCH_AVR31, + "``Classic'' devices with 128@tie{}KiB of program memory." }, + { ARCH_AVR35, + "``Classic'' devices with 16@tie{}KiB up to 64@tie{}KiB of " + "program memory and with the @code{MOVW} instruction." }, + { ARCH_AVR4, + "``Enhanced'' devices with up to 8@tie{}KiB of program memory." }, + { ARCH_AVR5, + "``Enhanced'' devices with 16@tie{}KiB up to 64@tie{}KiB of " + "program memory." }, + { ARCH_AVR51, + "``Enhanced'' devices with 128@tie{}KiB of program memory." }, + { ARCH_AVR6, + "``Enhanced'' devices with 3-byte PC, i.e.@: with more than 128@tie{}KiB " + "of program memory." }, + { ARCH_AVRXMEGA2, + "``XMEGA'' devices with more than 8@tie{}KiB and up to 64@tie{}KiB " + "of program memory." }, + { ARCH_AVRXMEGA4, + "``XMEGA'' devices with more than 64@tie{}KiB and up to 128@tie{}KiB " + "of program memory." }, + { ARCH_AVRXMEGA5, + "``XMEGA'' devices with more than 64@tie{}KiB and up to 128@tie{}KiB " + "of program memory and more than 64@tie{}KiB of RAM." }, + { ARCH_AVRXMEGA6, + "``XMEGA'' devices with more than 128@tie{}KiB of program memory." }, + { ARCH_AVRXMEGA7, + "``XMEGA'' devices with more than 128@tie{}KiB of program memory " + "and more than 64@tie{}KiB of RAM." } +}; + +const avr_mcu_t +avr_mcu_types[] = +{ +#define AVR_MCU(NAME, ARCH, MACRO, SP8, ERR_SKIP, DATA_SEC, N_FLASH, LIBNAME)\ + { NAME, ARCH, MACRO, SP8, ERR_SKIP, DATA_SEC, N_FLASH, LIBNAME }, +#include "avr-mcus.def" +#undef AVR_MCU + /* End of list. */ + { NULL, ARCH_UNKNOWN, NULL, 0, 0, 0, 0, NULL } +}; + diff --git a/gcc-4.8/gcc/config/avr/avr-dimode.md b/gcc-4.8/gcc/config/avr/avr-dimode.md new file mode 100644 index 000000000..c96ba6e97 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-dimode.md @@ -0,0 +1,479 @@ +;; Machine description for GNU compiler, +;; for Atmel AVR micro controllers. +;; Copyright (C) 1998-2013 Free Software Foundation, Inc. +;; Contributed by Georg Lay (avr@gjlay.de) +;; +;; 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 +;; . + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; The purpose of this file is to provide a light-weight DImode +;; implementation for AVR. The trouble with DImode is that tree -> RTL +;; lowering leads to really unpleasant code for operations that don't +;; work byte-wise like NEG, PLUS, MINUS, etc. Defining optabs entries for +;; them won't help because the optab machinery assumes these operations +;; are cheap and does not check if a libgcc implementation is available. +;; +;; The DImode insns are all straight forward -- except movdi. The approach +;; of this implementation is to provide DImode insns without the burden of +;; introducing movdi. +;; +;; The caveat is that if there are insns for some mode, there must also be a +;; respective move insn that describes reloads. Therefore, this +;; implementation uses an accumulator-based model with two hard-coded, +;; accumulator-like registers +;; +;; A[] = reg:DI 18 +;; B[] = reg:DI 10 +;; +;; so that no DImode insn contains pseudos or needs reloading. + +(define_constants + [(ACC_A 18) + (ACC_B 10)]) + +;; Supported modes that are 8 bytes wide +(define_mode_iterator ALL8 [DI DQ UDQ DA UDA TA UTA]) + +(define_mode_iterator ALL8U [UDQ UDA UTA]) +(define_mode_iterator ALL8S [ DQ DA TA]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Addition +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; "adddi3" +;; "adddq3" "addudq3" +;; "addda3" "adduda3" +;; "addta3" "adduta3" +(define_expand "add3" + [(parallel [(match_operand:ALL8 0 "general_operand" "") + (match_operand:ALL8 1 "general_operand" "") + (match_operand:ALL8 2 "general_operand" "")])] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (DImode == mode + && s8_operand (operands[2], VOIDmode)) + { + emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]); + emit_insn (gen_adddi3_const8_insn ()); + } + else if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_add3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_add3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +;; "adddi3_insn" +;; "adddq3_insn" "addudq3_insn" +;; "addda3_insn" "adduda3_insn" +;; "addta3_insn" "adduta3_insn" +(define_insn "add3_insn" + [(set (reg:ALL8 ACC_A) + (plus:ALL8 (reg:ALL8 ACC_A) + (reg:ALL8 ACC_B)))] + "avr_have_dimode" + "%~call __adddi3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +(define_insn "adddi3_const8_insn" + [(set (reg:DI ACC_A) + (plus:DI (reg:DI ACC_A) + (sign_extend:DI (reg:QI REG_X))))] + "avr_have_dimode" + "%~call __adddi3_s8" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +;; "adddi3_const_insn" +;; "adddq3_const_insn" "addudq3_const_insn" +;; "addda3_const_insn" "adduda3_const_insn" +;; "addta3_const_insn" "adduta3_const_insn" +(define_insn "add3_const_insn" + [(set (reg:ALL8 ACC_A) + (plus:ALL8 (reg:ALL8 ACC_A) + (match_operand:ALL8 0 "const_operand" "n Ynn")))] + "avr_have_dimode + && !s8_operand (operands[0], VOIDmode)" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "clobber")]) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subtraction +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; "subdi3" +;; "subdq3" "subudq3" +;; "subda3" "subuda3" +;; "subta3" "subuta3" +(define_expand "sub3" + [(parallel [(match_operand:ALL8 0 "general_operand" "") + (match_operand:ALL8 1 "general_operand" "") + (match_operand:ALL8 2 "general_operand" "")])] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_sub3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_sub3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +;; "subdi3_insn" +;; "subdq3_insn" "subudq3_insn" +;; "subda3_insn" "subuda3_insn" +;; "subta3_insn" "subuta3_insn" +(define_insn "sub3_insn" + [(set (reg:ALL8 ACC_A) + (minus:ALL8 (reg:ALL8 ACC_A) + (reg:ALL8 ACC_B)))] + "avr_have_dimode" + "%~call __subdi3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "set_czn")]) + +;; "subdi3_const_insn" +;; "subdq3_const_insn" "subudq3_const_insn" +;; "subda3_const_insn" "subuda3_const_insn" +;; "subta3_const_insn" "subuta3_const_insn" +(define_insn "sub3_const_insn" + [(set (reg:ALL8 ACC_A) + (minus:ALL8 (reg:ALL8 ACC_A) + (match_operand:ALL8 0 "const_operand" "n Ynn")))] + "avr_have_dimode" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "clobber")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Signed Saturating Addition and Subtraction +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_expand "3" + [(set (match_operand:ALL8S 0 "general_operand" "") + (ss_addsub:ALL8S (match_operand:ALL8S 1 "general_operand" "") + (match_operand:ALL8S 2 "general_operand" "")))] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +(define_insn "3_insn" + [(set (reg:ALL8S ACC_A) + (ss_addsub:ALL8S (reg:ALL8S ACC_A) + (reg:ALL8S ACC_B)))] + "avr_have_dimode" + "%~call __3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +(define_insn "3_const_insn" + [(set (reg:ALL8S ACC_A) + (ss_addsub:ALL8S (reg:ALL8S ACC_A) + (match_operand:ALL8S 0 "const_operand" "n Ynn")))] + "avr_have_dimode" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "clobber")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Unsigned Saturating Addition and Subtraction +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_expand "3" + [(set (match_operand:ALL8U 0 "general_operand" "") + (us_addsub:ALL8U (match_operand:ALL8U 1 "general_operand" "") + (match_operand:ALL8U 2 "general_operand" "")))] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +(define_insn "3_insn" + [(set (reg:ALL8U ACC_A) + (us_addsub:ALL8U (reg:ALL8U ACC_A) + (reg:ALL8U ACC_B)))] + "avr_have_dimode" + "%~call __3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +(define_insn "3_const_insn" + [(set (reg:ALL8U ACC_A) + (us_addsub:ALL8U (reg:ALL8U ACC_A) + (match_operand:ALL8U 0 "const_operand" "n Ynn")))] + "avr_have_dimode" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "clobber")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Negation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_expand "negdi2" + [(parallel [(match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" "")])] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (DImode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + emit_insn (gen_negdi2_insn ()); + emit_move_insn (operands[0], acc_a); + DONE; + }) + +(define_insn "negdi2_insn" + [(set (reg:DI ACC_A) + (neg:DI (reg:DI ACC_A)))] + "avr_have_dimode" + "%~call __negdi2" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Comparison +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_expand "conditional_jump" + [(set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" [(cc0) + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "avr_have_dimode") + +;; "cbranchdi4" +;; "cbranchdq4" "cbranchudq4" +;; "cbranchda4" "cbranchuda4" +;; "cbranchta4" "cbranchuta4" +(define_expand "cbranch4" + [(parallel [(match_operand:ALL8 1 "register_operand" "") + (match_operand:ALL8 2 "nonmemory_operand" "") + (match_operator 0 "ordered_comparison_operator" [(cc0) + (const_int 0)]) + (label_ref (match_operand 3 "" ""))])] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (s8_operand (operands[2], VOIDmode)) + { + emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]); + emit_insn (gen_compare_const8_di2 ()); + } + else if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_compare_const_2 (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_compare_2 ()); + } + + emit_jump_insn (gen_conditional_jump (operands[0], operands[3])); + DONE; + }) + +;; "compare_di2" +;; "compare_dq2" "compare_udq2" +;; "compare_da2" "compare_uda2" +;; "compare_ta2" "compare_uta2" +(define_insn "compare_2" + [(set (cc0) + (compare (reg:ALL8 ACC_A) + (reg:ALL8 ACC_B)))] + "avr_have_dimode" + "%~call __cmpdi2" + [(set_attr "adjust_len" "call") + (set_attr "cc" "compare")]) + +(define_insn "compare_const8_di2" + [(set (cc0) + (compare (reg:DI ACC_A) + (sign_extend:DI (reg:QI REG_X))))] + "avr_have_dimode" + "%~call __cmpdi2_s8" + [(set_attr "adjust_len" "call") + (set_attr "cc" "compare")]) + +;; "compare_const_di2" +;; "compare_const_dq2" "compare_const_udq2" +;; "compare_const_da2" "compare_const_uda2" +;; "compare_const_ta2" "compare_const_uta2" +(define_insn "compare_const_2" + [(set (cc0) + (compare (reg:ALL8 ACC_A) + (match_operand:ALL8 0 "const_operand" "n Ynn"))) + (clobber (match_scratch:QI 1 "=&d"))] + "avr_have_dimode + && !s8_operand (operands[0], VOIDmode)" + { + return avr_out_compare64 (insn, operands, NULL); + } + [(set_attr "adjust_len" "compare64") + (set_attr "cc" "compare")]) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Shifts and Rotate +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_code_iterator di_shifts + [ashift ashiftrt lshiftrt rotate]) + +;; Shift functions from libgcc are called without defining these insns, +;; but with them we can describe their reduced register footprint. + +;; "ashldi3" "ashrdi3" "lshrdi3" "rotldi3" +;; "ashldq3" "ashrdq3" "lshrdq3" "rotldq3" +;; "ashlda3" "ashrda3" "lshrda3" "rotlda3" +;; "ashlta3" "ashrta3" "lshrta3" "rotlta3" +;; "ashludq3" "ashrudq3" "lshrudq3" "rotludq3" +;; "ashluda3" "ashruda3" "lshruda3" "rotluda3" +;; "ashluta3" "ashruta3" "lshruta3" "rotluta3" +(define_expand "3" + [(parallel [(match_operand:ALL8 0 "general_operand" "") + (di_shifts:ALL8 (match_operand:ALL8 1 "general_operand" "") + (match_operand:QI 2 "general_operand" ""))])] + "avr_have_dimode" + { + rtx acc_a = gen_rtx_REG (mode, ACC_A); + + emit_move_insn (acc_a, operands[1]); + emit_move_insn (gen_rtx_REG (QImode, 16), operands[2]); + emit_insn (gen_3_insn ()); + emit_move_insn (operands[0], acc_a); + DONE; + }) + +;; "ashldi3_insn" "ashrdi3_insn" "lshrdi3_insn" "rotldi3_insn" +;; "ashldq3_insn" "ashrdq3_insn" "lshrdq3_insn" "rotldq3_insn" +;; "ashlda3_insn" "ashrda3_insn" "lshrda3_insn" "rotlda3_insn" +;; "ashlta3_insn" "ashrta3_insn" "lshrta3_insn" "rotlta3_insn" +;; "ashludq3_insn" "ashrudq3_insn" "lshrudq3_insn" "rotludq3_insn" +;; "ashluda3_insn" "ashruda3_insn" "lshruda3_insn" "rotluda3_insn" +;; "ashluta3_insn" "ashruta3_insn" "lshruta3_insn" "rotluta3_insn" +(define_insn "3_insn" + [(set (reg:ALL8 ACC_A) + (di_shifts:ALL8 (reg:ALL8 ACC_A) + (reg:QI 16)))] + "avr_have_dimode" + "%~call __di3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +;; "umulsidi3" +;; "mulsidi3" +(define_expand "mulsidi3" + [(parallel [(match_operand:DI 0 "register_operand" "") + (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" "") + ;; Just to mention the iterator + (clobber (any_extend:SI (match_dup 1)))])] + "avr_have_dimode" + { + emit_move_insn (gen_rtx_REG (SImode, 22), operands[1]); + emit_move_insn (gen_rtx_REG (SImode, 18), operands[2]); + emit_insn (gen_mulsidi3_insn()); + // Use emit_move_insn and not open-coded expand because of missing movdi + emit_move_insn (operands[0], gen_rtx_REG (DImode, ACC_A)); + DONE; + }) + +;; "umulsidi3_insn" +;; "mulsidi3_insn" +(define_insn "mulsidi3_insn" + [(set (reg:DI ACC_A) + (mult:DI (any_extend:DI (reg:SI 18)) + (any_extend:DI (reg:SI 22)))) + (clobber (reg:HI REG_X)) + (clobber (reg:HI REG_Z))] + "avr_have_dimode" + "%~call __mulsidi3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) diff --git a/gcc-4.8/gcc/config/avr/avr-fixed.md b/gcc-4.8/gcc/config/avr/avr-fixed.md new file mode 100644 index 000000000..7d9b525ef --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-fixed.md @@ -0,0 +1,528 @@ +;; This file contains instructions that support fixed-point operations +;; for Atmel AVR micro controllers. +;; Copyright (C) 2012-2013 Free Software Foundation, Inc. +;; +;; Contributed by Sean D'Epagnier (sean@depagnier.com) +;; Georg-Johann Lay (avr@gjlay.de) + +;; 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 +;; . + +(define_mode_iterator ALL1Q [QQ UQQ]) +(define_mode_iterator ALL2Q [HQ UHQ]) +(define_mode_iterator ALL2A [HA UHA]) +(define_mode_iterator ALL4A [SA USA]) +(define_mode_iterator ALL2QA [HQ UHQ HA UHA]) +(define_mode_iterator ALL4QA [SQ USQ SA USA]) +(define_mode_iterator ALL124QA [ QQ HQ HA SA SQ + UQQ UHQ UHA USA USQ]) + +(define_mode_iterator ALL2S [HQ HA]) +(define_mode_iterator ALL4S [SA SQ]) +(define_mode_iterator ALL24S [ HQ HA SA SQ]) +(define_mode_iterator ALL124S [ QQ HQ HA SA SQ]) +(define_mode_iterator ALL124U [UQQ UHQ UHA USA USQ]) + +;;; Conversions + +(define_mode_iterator FIXED_A + [QQ UQQ + HQ UHQ HA UHA + SQ USQ SA USA + DQ UDQ DA UDA + TA UTA + QI HI SI DI]) + +;; Same so that be can build cross products + +(define_mode_iterator FIXED_B + [QQ UQQ + HQ UHQ HA UHA + SQ USQ SA USA + DQ UDQ DA UDA + TA UTA + QI HI SI DI]) + +(define_insn "fract2" + [(set (match_operand:FIXED_A 0 "register_operand" "=r") + (fract_convert:FIXED_A + (match_operand:FIXED_B 1 "register_operand" "r")))] + "mode != mode" + { + return avr_out_fract (insn, operands, true, NULL); + } + [(set_attr "cc" "clobber") + (set_attr "adjust_len" "sfract")]) + +(define_insn "fractuns2" + [(set (match_operand:FIXED_A 0 "register_operand" "=r") + (unsigned_fract_convert:FIXED_A + (match_operand:FIXED_B 1 "register_operand" "r")))] + "mode != mode" + { + return avr_out_fract (insn, operands, false, NULL); + } + [(set_attr "cc" "clobber") + (set_attr "adjust_len" "ufract")]) + +;****************************************************************************** +;** Saturated Addition and Subtraction +;****************************************************************************** + +;; Fixme: It would be nice if we could expand the 32-bit versions to a +;; transparent libgcc call if $2 is a REG. Problem is that it is +;; not possible to describe that addition is commutative. +;; And defining register classes/constraintrs for the involved hard +;; registers and let IRA do the work, yields inacceptable bloated code. +;; Thus, we have to live with the up to 11 instructions that are output +;; for these 32-bit saturated operations. + +;; "ssaddqq3" "ssaddhq3" "ssaddha3" "ssaddsq3" "ssaddsa3" +;; "sssubqq3" "sssubhq3" "sssubha3" "sssubsq3" "sssubsa3" +(define_insn "3" + [(set (match_operand:ALL124S 0 "register_operand" "=??d,d") + (ss_addsub:ALL124S (match_operand:ALL124S 1 "register_operand" "0,0") + (match_operand:ALL124S 2 "nonmemory_operand" "r,Ynn")))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "cc" "clobber") + (set_attr "adjust_len" "plus")]) + +;; "usadduqq3" "usadduhq3" "usadduha3" "usaddusq3" "usaddusa3" +;; "ussubuqq3" "ussubuhq3" "ussubuha3" "ussubusq3" "ussubusa3" +(define_insn "3" + [(set (match_operand:ALL124U 0 "register_operand" "=??r,d") + (us_addsub:ALL124U (match_operand:ALL124U 1 "register_operand" "0,0") + (match_operand:ALL124U 2 "nonmemory_operand" "r,Ynn")))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "cc" "clobber") + (set_attr "adjust_len" "plus")]) + +;****************************************************************************** +;** Saturated Negation and Absolute Value +;****************************************************************************** + +;; Fixme: This will always result in 0. Dunno why simplify-rtx.c says +;; "unknown" on how to optimize this. libgcc call would be in order, +;; but the performance is *PLAIN* *HORROR* because the optimizers don't +;; manage to optimize out MEMCPY that's sprincled all over fixed-bit.c */ + +(define_expand "usneg2" + [(parallel [(match_operand:ALL124U 0 "register_operand" "") + (match_operand:ALL124U 1 "nonmemory_operand" "")])] + "" + { + emit_move_insn (operands[0], CONST0_RTX (mode)); + DONE; + }) + +(define_insn "ssnegqq2" + [(set (match_operand:QQ 0 "register_operand" "=r") + (ss_neg:QQ (match_operand:QQ 1 "register_operand" "0")))] + "" + "neg %0\;brvc 0f\;dec %0\;0:" + [(set_attr "cc" "clobber") + (set_attr "length" "3")]) + +(define_insn "ssabsqq2" + [(set (match_operand:QQ 0 "register_operand" "=r") + (ss_abs:QQ (match_operand:QQ 1 "register_operand" "0")))] + "" + "sbrc %0,7\;neg %0\;sbrc %0,7\;dec %0" + [(set_attr "cc" "clobber") + (set_attr "length" "4")]) + +;; "ssneghq2" "ssnegha2" "ssnegsq2" "ssnegsa2" +;; "ssabshq2" "ssabsha2" "ssabssq2" "ssabssa2" +(define_expand "2" + [(set (match_dup 2) + (match_operand:ALL24S 1 "register_operand" "")) + (set (match_dup 2) + (ss_abs_neg:ALL24S (match_dup 2))) + (set (match_operand:ALL24S 0 "register_operand" "") + (match_dup 2))] + "" + { + operands[2] = gen_rtx_REG (mode, 26 - GET_MODE_SIZE (mode)); + }) + +;; "*ssneghq2" "*ssnegha2" +;; "*ssabshq2" "*ssabsha2" +(define_insn "*2" + [(set (reg:ALL2S 24) + (ss_abs_neg:ALL2S (reg:ALL2S 24)))] + "" + "%~call ___2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*ssnegsq2" "*ssnegsa2" +;; "*ssabssq2" "*ssabssa2" +(define_insn "*2" + [(set (reg:ALL4S 22) + (ss_abs_neg:ALL4S (reg:ALL4S 22)))] + "" + "%~call ___4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;****************************************************************************** +; mul + +;; "mulqq3" "muluqq3" +(define_expand "mul3" + [(parallel [(match_operand:ALL1Q 0 "register_operand" "") + (match_operand:ALL1Q 1 "register_operand" "") + (match_operand:ALL1Q 2 "register_operand" "")])] + "" + { + emit_insn (AVR_HAVE_MUL + ? gen_mul3_enh (operands[0], operands[1], operands[2]) + : gen_mul3_nomul (operands[0], operands[1], operands[2])); + DONE; + }) + +(define_insn "mulqq3_enh" + [(set (match_operand:QQ 0 "register_operand" "=r") + (mult:QQ (match_operand:QQ 1 "register_operand" "a") + (match_operand:QQ 2 "register_operand" "a")))] + "AVR_HAVE_MUL" + "fmuls %1,%2\;dec r1\;brvs 0f\;inc r1\;0:\;mov %0,r1\;clr __zero_reg__" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +(define_insn "muluqq3_enh" + [(set (match_operand:UQQ 0 "register_operand" "=r") + (mult:UQQ (match_operand:UQQ 1 "register_operand" "r") + (match_operand:UQQ 2 "register_operand" "r")))] + "AVR_HAVE_MUL" + "mul %1,%2\;mov %0,r1\;clr __zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +(define_expand "mulqq3_nomul" + [(set (reg:QQ 24) + (match_operand:QQ 1 "register_operand" "")) + (set (reg:QQ 25) + (match_operand:QQ 2 "register_operand" "")) + ;; "*mulqq3.call" + (parallel [(set (reg:QQ 23) + (mult:QQ (reg:QQ 24) + (reg:QQ 25))) + (clobber (reg:QI 22)) + (clobber (reg:HI 24))]) + (set (match_operand:QQ 0 "register_operand" "") + (reg:QQ 23))] + "!AVR_HAVE_MUL") + +(define_expand "muluqq3_nomul" + [(set (reg:UQQ 22) + (match_operand:UQQ 1 "register_operand" "")) + (set (reg:UQQ 24) + (match_operand:UQQ 2 "register_operand" "")) + ;; "*umulqihi3.call" + (parallel [(set (reg:HI 24) + (mult:HI (zero_extend:HI (reg:QI 22)) + (zero_extend:HI (reg:QI 24)))) + (clobber (reg:QI 21)) + (clobber (reg:HI 22))]) + (set (match_operand:UQQ 0 "register_operand" "") + (reg:UQQ 25))] + "!AVR_HAVE_MUL") + +(define_insn "*mulqq3.call" + [(set (reg:QQ 23) + (mult:QQ (reg:QQ 24) + (reg:QQ 25))) + (clobber (reg:QI 22)) + (clobber (reg:HI 24))] + "!AVR_HAVE_MUL" + "%~call __mulqq3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; "mulhq3" "muluhq3" +;; "mulha3" "muluha3" +(define_expand "mul3" + [(set (reg:ALL2QA 18) + (match_operand:ALL2QA 1 "register_operand" "")) + (set (reg:ALL2QA 26) + (match_operand:ALL2QA 2 "register_operand" "")) + ;; "*mulhq3.call.enh" + (parallel [(set (reg:ALL2QA 24) + (mult:ALL2QA (reg:ALL2QA 18) + (reg:ALL2QA 26))) + (clobber (reg:HI 22))]) + (set (match_operand:ALL2QA 0 "register_operand" "") + (reg:ALL2QA 24))] + "AVR_HAVE_MUL") + +;; "*mulhq3.call" "*muluhq3.call" +;; "*mulha3.call" "*muluha3.call" +(define_insn "*mul3.call" + [(set (reg:ALL2QA 24) + (mult:ALL2QA (reg:ALL2QA 18) + (reg:ALL2QA 26))) + (clobber (reg:HI 22))] + "AVR_HAVE_MUL" + "%~call __mul3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; On the enhanced core, don't clobber either input and use a separate output + +;; "mulsa3" "mulusa3" +(define_expand "mul3" + [(set (reg:ALL4A 16) + (match_operand:ALL4A 1 "register_operand" "")) + (set (reg:ALL4A 20) + (match_operand:ALL4A 2 "register_operand" "")) + (set (reg:ALL4A 24) + (mult:ALL4A (reg:ALL4A 16) + (reg:ALL4A 20))) + (set (match_operand:ALL4A 0 "register_operand" "") + (reg:ALL4A 24))] + "AVR_HAVE_MUL") + +;; "*mulsa3.call" "*mulusa3.call" +(define_insn "*mul3.call" + [(set (reg:ALL4A 24) + (mult:ALL4A (reg:ALL4A 16) + (reg:ALL4A 20)))] + "AVR_HAVE_MUL" + "%~call __mul3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +; / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / +; div + +(define_code_iterator usdiv [udiv div]) + +;; "divqq3" "udivuqq3" +(define_expand "3" + [(set (reg:ALL1Q 25) + (match_operand:ALL1Q 1 "register_operand" "")) + (set (reg:ALL1Q 22) + (match_operand:ALL1Q 2 "register_operand" "")) + (parallel [(set (reg:ALL1Q 24) + (usdiv:ALL1Q (reg:ALL1Q 25) + (reg:ALL1Q 22))) + (clobber (reg:QI 25))]) + (set (match_operand:ALL1Q 0 "register_operand" "") + (reg:ALL1Q 24))]) + +;; "*divqq3.call" "*udivuqq3.call" +(define_insn "*3.call" + [(set (reg:ALL1Q 24) + (usdiv:ALL1Q (reg:ALL1Q 25) + (reg:ALL1Q 22))) + (clobber (reg:QI 25))] + "" + "%~call __3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "divhq3" "udivuhq3" +;; "divha3" "udivuha3" +(define_expand "3" + [(set (reg:ALL2QA 26) + (match_operand:ALL2QA 1 "register_operand" "")) + (set (reg:ALL2QA 22) + (match_operand:ALL2QA 2 "register_operand" "")) + (parallel [(set (reg:ALL2QA 24) + (usdiv:ALL2QA (reg:ALL2QA 26) + (reg:ALL2QA 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))]) + (set (match_operand:ALL2QA 0 "register_operand" "") + (reg:ALL2QA 24))]) + +;; "*divhq3.call" "*udivuhq3.call" +;; "*divha3.call" "*udivuha3.call" +(define_insn "*3.call" + [(set (reg:ALL2QA 24) + (usdiv:ALL2QA (reg:ALL2QA 26) + (reg:ALL2QA 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))] + "" + "%~call __3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; Note the first parameter gets passed in already offset by 2 bytes + +;; "divsa3" "udivusa3" +(define_expand "3" + [(set (reg:ALL4A 24) + (match_operand:ALL4A 1 "register_operand" "")) + (set (reg:ALL4A 18) + (match_operand:ALL4A 2 "register_operand" "")) + (parallel [(set (reg:ALL4A 22) + (usdiv:ALL4A (reg:ALL4A 24) + (reg:ALL4A 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))]) + (set (match_operand:ALL4A 0 "register_operand" "") + (reg:ALL4A 22))]) + +;; "*divsa3.call" "*udivusa3.call" +(define_insn "*3.call" + [(set (reg:ALL4A 22) + (usdiv:ALL4A (reg:ALL4A 24) + (reg:ALL4A 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] + "" + "%~call __3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;****************************************************************************** +;** Rounding +;****************************************************************************** + +;; "roundqq3" "rounduqq3" +;; "roundhq3" "rounduhq3" "roundha3" "rounduha3" +;; "roundsq3" "roundusq3" "roundsa3" "roundusa3" +(define_expand "round3" + [(set (match_dup 4) + (match_operand:ALL124QA 1 "register_operand" "")) + (set (reg:QI 24) + (match_dup 5)) + (parallel [(set (match_dup 3) + (unspec:ALL124QA [(match_dup 4) + (reg:QI 24)] UNSPEC_ROUND)) + (clobber (match_dup 4))]) + (set (match_operand:ALL124QA 0 "register_operand" "") + (match_dup 3)) + (use (match_operand:HI 2 "nonmemory_operand" ""))] + "" + { + if (CONST_INT_P (operands[2]) + && !(optimize_size + && 4 == GET_MODE_SIZE (mode))) + { + emit_insn (gen_round3_const (operands[0], operands[1], operands[2])); + DONE; + } + + // Input and output of the libgcc function + const unsigned int regno_in[] = { -1, 22, 22, -1, 18 }; + const unsigned int regno_out[] = { -1, 24, 24, -1, 22 }; + + operands[3] = gen_rtx_REG (mode, regno_out[(size_t) GET_MODE_SIZE (mode)]); + operands[4] = gen_rtx_REG (mode, regno_in[(size_t) GET_MODE_SIZE (mode)]); + operands[5] = simplify_gen_subreg (QImode, force_reg (HImode, operands[2]), HImode, 0); + // $2 is no more needed, but is referenced for expand. + operands[2] = const0_rtx; + }) + +;; Expand rounding with known rounding points inline so that the addend / mask +;; will be consumed by operation with immediate operands and there is no +;; need for a shift with variable offset. + +;; "roundqq3_const" "rounduqq3_const" +;; "roundhq3_const" "rounduhq3_const" "roundha3_const" "rounduha3_const" +;; "roundsq3_const" "roundusq3_const" "roundsa3_const" "roundusa3_const" +(define_expand "round3_const" + [(parallel [(match_operand:ALL124QA 0 "register_operand" "") + (match_operand:ALL124QA 1 "register_operand" "") + (match_operand:HI 2 "const_int_operand" "")])] + "" + { + // The rounding point RP is $2. The smallest fractional + // bit that is not cleared by the rounding is 2^(-RP). + + enum machine_mode imode = int_mode_for_mode (mode); + int fbit = (int) GET_MODE_FBIT (mode); + + // Add-Saturate 1/2 * 2^(-RP) + + double_int i_add = double_int_zero.set_bit (fbit-1 - INTVAL (operands[2])); + rtx x_add = const_fixed_from_double_int (i_add, mode); + + if (SIGNED_FIXED_POINT_MODE_P (mode)) + emit_move_insn (operands[0], + gen_rtx_SS_PLUS (mode, operands[1], x_add)); + else + emit_move_insn (operands[0], + gen_rtx_US_PLUS (mode, operands[1], x_add)); + + // Keep all bits from RP and higher: ... 2^(-RP) + // Clear all bits from RP+1 and lower: 2^(-RP-1) ... + // Rounding point ^^^^^^^ + // Added above ^^^^^^^^^ + + rtx xreg = simplify_gen_subreg (imode, operands[0], mode, 0); + rtx xmask = immed_double_int_const (-i_add - i_add, imode); + + if (SImode == imode) + emit_insn (gen_andsi3 (xreg, xreg, xmask)); + else if (HImode == imode) + emit_insn (gen_andhi3 (xreg, xreg, xmask)); + else if (QImode == imode) + emit_insn (gen_andqi3 (xreg, xreg, xmask)); + else + gcc_unreachable(); + + DONE; + }) + + +;; "*roundqq3.libgcc" "*rounduqq3.libgcc" +(define_insn "*round3.libgcc" + [(set (reg:ALL1Q 24) + (unspec:ALL1Q [(reg:ALL1Q 22) + (reg:QI 24)] UNSPEC_ROUND)) + (clobber (reg:ALL1Q 22))] + "" + "%~call __round3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*roundhq3.libgcc" "*rounduhq3.libgcc" +;; "*roundha3.libgcc" "*rounduha3.libgcc" +(define_insn "*round3.libgcc" + [(set (reg:ALL2QA 24) + (unspec:ALL2QA [(reg:ALL2QA 22) + (reg:QI 24)] UNSPEC_ROUND)) + (clobber (reg:ALL2QA 22))] + "" + "%~call __round3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*roundsq3.libgcc" "*roundusq3.libgcc" +;; "*roundsa3.libgcc" "*roundusa3.libgcc" +(define_insn "*round3.libgcc" + [(set (reg:ALL4QA 22) + (unspec:ALL4QA [(reg:ALL4QA 18) + (reg:QI 24)] UNSPEC_ROUND)) + (clobber (reg:ALL4QA 18))] + "" + "%~call __round3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) diff --git a/gcc-4.8/gcc/config/avr/avr-log.c b/gcc-4.8/gcc/config/avr/avr-log.c new file mode 100644 index 000000000..9e538e601 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-log.c @@ -0,0 +1,399 @@ +/* Subroutines for log output for Atmel AVR back end. + Copyright (C) 2011-2013 Free Software Foundation, Inc. + Contributed by Georg-Johann Lay (avr@gjlay.de) + + 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 "rtl.h" +#include "tree.h" +#include "output.h" +#include "input.h" +#include "function.h" +#include "tm_p.h" +#include "tree-pass.h" /* for current_pass */ + +/* This file supplies some functions for AVR back-end developers + with a printf-like interface. The functions are called through + macros avr_edump or avr_fdump from avr-protos.h: + + avr_edump (const char *fmt, ...); + + avr_fdump (FILE *stream, const char *fmt, ...); + + avr_edump (fmt, ...) is a shortcut for avr_fdump (stderr, fmt, ...) + + == known %-codes == + + b: bool + r: rtx + t: tree + T: tree (brief) + C: enum rtx_code + m: enum machine_mode + R: enum reg_class + D: double_int (signed decimal) + X: double_int (unsigned hex) + L: insn list + H: location_t + + == no arguments == + + A: call abort() + f: current_function_name() + F: caller (via __FUNCTION__) + P: Pass name and number + ?: Print caller, current function and pass info + !: Ditto, but only print if in a pass with static pass number, + else return. + + == same as printf == + + %: % + c: char + s: string + d: int (decimal) + x: int (hex) +*/ + +/* Set according to -mlog= option. */ +avr_log_t avr_log; + +/* The caller as of __FUNCTION__ */ +static const char *avr_log_caller = "?"; + +/* The worker function implementing the %-codes */ +static void avr_log_vadump (FILE*, const char*, va_list); + +/* As we have no variadic macros, avr_edump maps to a call to + avr_log_set_caller_e which saves __FUNCTION__ to avr_log_caller and + returns a function pointer to avr_log_fdump_e. avr_log_fdump_e + gets the printf-like arguments and calls avr_log_vadump, the + worker function. avr_fdump works the same way. */ + +/* Provide avr_log_fdump_e/f so that avr_log_set_caller_e/_f can return + their address. */ + +static int +avr_log_fdump_e (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + avr_log_vadump (stderr, fmt, ap); + va_end (ap); + + return 1; +} + +static int +avr_log_fdump_f (FILE *stream, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + if (stream) + avr_log_vadump (stream, fmt, ap); + va_end (ap); + + return 1; +} + +/* Macros avr_edump/avr_fdump map to calls of the following two functions, + respectively. You don't need to call them directly. */ + +int (* +avr_log_set_caller_e (const char *caller) + )(const char*, ...) +{ + avr_log_caller = caller; + + return avr_log_fdump_e; +} + +int (* +avr_log_set_caller_f (const char *caller) + )(FILE*, const char*, ...) +{ + avr_log_caller = caller; + + return avr_log_fdump_f; +} + + +/* Copy-paste from double-int.c:double_int_split_digit (it's static there). + Splits last digit of *CST (taken as unsigned) in BASE and returns it. */ + +static unsigned +avr_double_int_pop_digit (double_int *cst, unsigned base) +{ + double_int drem; + + *cst = cst->udivmod (double_int::from_uhwi (base), (int) FLOOR_DIV_EXPR, + &drem); + + return (unsigned) drem.to_uhwi(); +} + + +/* Dump VAL as hex value to FILE. */ + +static void +avr_dump_double_int_hex (FILE *file, double_int val) +{ + unsigned digit[4]; + + digit[0] = avr_double_int_pop_digit (&val, 1 << 16); + digit[1] = avr_double_int_pop_digit (&val, 1 << 16); + digit[2] = avr_double_int_pop_digit (&val, 1 << 16); + digit[3] = avr_double_int_pop_digit (&val, 1 << 16); + + fprintf (file, "0x"); + + if (digit[3] | digit[2]) + fprintf (file, "%04x%04x", digit[3], digit[2]); + + if (digit[3] | digit[2] | digit[1] | digit[0]) + fprintf (file, "%04x%04x", digit[1], digit[0]); + else + fprintf (file, "0"); +} + + +/* Worker function implementing the %-codes and forwarding to + respective print/dump function. */ + +static void +avr_log_vadump (FILE *file, const char *fmt, va_list ap) +{ + char bs[3] = {'\\', '?', '\0'}; + + while (*fmt) + { + switch (*fmt++) + { + default: + fputc (*(fmt-1), file); + break; + + case '\\': + bs[1] = *fmt++; + fputs (bs, file); + break; + + case '%': + switch (*fmt++) + { + case '%': + fputc ('%', file); + break; + + case 't': + { + tree t = va_arg (ap, tree); + if (NULL_TREE == t) + fprintf (file, ""); + else + { + if (stderr == file) + debug_tree (t); + else + { + print_node (file, "", t, 0); + putc ('\n', file); + } + } + break; + } + + case 'T': + print_node_brief (file, "", va_arg (ap, tree), 3); + break; + + case 'd': + fprintf (file, "%d", va_arg (ap, int)); + break; + + case 'D': + dump_double_int (file, va_arg (ap, double_int), false); + break; + + case 'X': + avr_dump_double_int_hex (file, va_arg (ap, double_int)); + break; + + case 'x': + fprintf (file, "%x", va_arg (ap, int)); + break; + + case 'b': + fprintf (file, "%s", va_arg (ap, int) ? "true" : "false"); + break; + + case 'c': + fputc (va_arg (ap, int), file); + break; + + case 'r': + print_inline_rtx (file, va_arg (ap, rtx), 0); + break; + + case 'L': + { + rtx insn = va_arg (ap, rtx); + + while (insn) + { + print_inline_rtx (file, insn, 0); + fprintf (file, "\n"); + insn = NEXT_INSN (insn); + } + break; + } + + case 'f': + if (cfun && cfun->decl) + fputs (current_function_name(), file); + break; + + case 's': + { + const char *str = va_arg (ap, char*); + fputs (str ? str : "(null)", file); + } + break; + + case 'm': + fputs (GET_MODE_NAME ((enum machine_mode) va_arg (ap, int)), + file); + break; + + case 'C': + fputs (rtx_name[va_arg (ap, int)], file); + break; + + case 'R': + fputs (reg_class_names[va_arg (ap, int)], file); + break; + + case 'F': + fputs (avr_log_caller, file); + break; + + case 'H': + { + location_t loc = va_arg (ap, location_t); + + if (BUILTINS_LOCATION == loc) + fprintf (file, ""); + else if (UNKNOWN_LOCATION == loc) + fprintf (file, ""); + else + fprintf (file, "%s:%d", + LOCATION_FILE (loc), LOCATION_LINE (loc)); + + break; + } + + case '!': + if (!current_pass) + return; + /* FALLTHRU */ + + case '?': + avr_log_fdump_f (file, "%F[%f:%P]"); + break; + + case 'P': + if (current_pass) + fprintf (file, "%s(%d)", + current_pass->name, + current_pass->static_pass_number); + else + fprintf (file, "pass=?"); + + break; + + case 'A': + fflush (file); + abort(); + + default: + /* Unknown %-code: Stop printing */ + + fprintf (file, "??? %%%c ???%s\n", *(fmt-1), fmt); + fmt = ""; + + break; + } + break; /* % */ + } + } + + fflush (file); +} + + +/* Called from avr.c:avr_option_override(). + Parse argument of -mlog= and set respective fields in avr_log. */ + +void +avr_log_set_avr_log (void) +{ + bool all = TARGET_ALL_DEBUG != 0; + + if (all || avr_log_details) + { + /* Adding , at beginning and end of string makes searching easier. */ + + char *str = (char*) alloca (3 + strlen (avr_log_details)); + bool info; + + str[0] = ','; + strcat (stpcpy (str+1, avr_log_details), ","); + + all |= NULL != strstr (str, ",all,"); + info = NULL != strstr (str, ",?,"); + + if (info) + fprintf (stderr, "\n-mlog="); + +#define SET_DUMP_DETAIL(S) \ + do { \ + avr_log.S = (all || NULL != strstr (str, "," #S ",")); \ + if (info) \ + fprintf (stderr, #S ","); \ + } while (0) + + SET_DUMP_DETAIL (address_cost); + SET_DUMP_DETAIL (builtin); + SET_DUMP_DETAIL (constraints); + SET_DUMP_DETAIL (legitimate_address_p); + SET_DUMP_DETAIL (legitimize_address); + SET_DUMP_DETAIL (legitimize_reload_address); + SET_DUMP_DETAIL (progmem); + SET_DUMP_DETAIL (rtx_costs); + +#undef SET_DUMP_DETAIL + + if (info) + fprintf (stderr, "?\n\n"); + } +} diff --git a/gcc-4.8/gcc/config/avr/avr-mcus.def b/gcc-4.8/gcc/config/avr/avr-mcus.def new file mode 100644 index 000000000..2a730ac7a --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-mcus.def @@ -0,0 +1,337 @@ +/* AVR MCUs. + Copyright (C) 2009-2013 Free Software Foundation, Inc. + + 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 + . */ + +/* List of all known AVR MCU types. If updated, cd to $(builddir)/gcc and run + + $ make avr-mcus + + This will regenerate / update the following source files: + + - $(srcdir)/config/avr/t-multilib + - $(srcdir)/config/avr/avr-tables.opt + - $(srcdir)/doc/avr-mmcu.texi + + After that, rebuild everything and check-in the new sources to the repo. + The device list below has to be kept in sync with AVR-LibC. + + + Before including this file, define a macro: + + AVR_MCU (NAME, ARCH, MACRO, SHORT_SP, ERRATA_SKIP, DATA_SEC, N_FLASH, + LIBRARY_NAME) + + where the arguments are the fields of avr_mcu_t: + + NAME Accept -mmcu= + + ARCH Specifies the multilib variant together with SHORT_SP + + MACRO If NULL, this is a core and not a device. If non-NULL, + supply respective built-in macro. + + SHORT_SP The device / multilib has an 8-bit stack pointer (no SPH). + + ERRATA_SKIP Apply work-around for the "skip 32-bit instruction" + silicon bug: Don't skip 32-bit instrctions. + + DATA_SEC First address of SRAM, used in -Tdata= by the driver. + + N_FLASH Number of 64 KiB flash segments, rounded up. + + LIBRARY_NAME Used by the driver to linke startup code from avr-libc + as of crt.o + + "avr2" must be first for the "0" default to work as intended. */ + +/* Classic, <= 8K. */ +AVR_MCU ("avr2", ARCH_AVR2, NULL, 0, 1, 0x0060, 6, "s8515") +AVR_MCU ("at90s2313", ARCH_AVR2, "__AVR_AT90S2313__", 1, 0, 0x0060, 1, "s2313") +AVR_MCU ("at90s2323", ARCH_AVR2, "__AVR_AT90S2323__", 1, 0, 0x0060, 1, "s2323") +AVR_MCU ("at90s2333", ARCH_AVR2, "__AVR_AT90S2333__", 1, 0, 0x0060, 1, "s2333") +AVR_MCU ("at90s2343", ARCH_AVR2, "__AVR_AT90S2343__", 1, 0, 0x0060, 1, "s2343") +AVR_MCU ("attiny22", ARCH_AVR2, "__AVR_ATtiny22__", 1, 0, 0x0060, 1, "tn22") +AVR_MCU ("attiny26", ARCH_AVR2, "__AVR_ATtiny26__", 1, 0, 0x0060, 1, "tn26") +AVR_MCU ("at90s4414", ARCH_AVR2, "__AVR_AT90S4414__", 0, 0, 0x0060, 1, "s4414") +AVR_MCU ("at90s4433", ARCH_AVR2, "__AVR_AT90S4433__", 1, 0, 0x0060, 1, "s4433") +AVR_MCU ("at90s4434", ARCH_AVR2, "__AVR_AT90S4434__", 0, 0, 0x0060, 1, "s4434") +AVR_MCU ("at90s8515", ARCH_AVR2, "__AVR_AT90S8515__", 0, 1, 0x0060, 1, "s8515") +AVR_MCU ("at90c8534", ARCH_AVR2, "__AVR_AT90C8534__", 0, 0, 0x0060, 1, "c8534") +AVR_MCU ("at90s8535", ARCH_AVR2, "__AVR_AT90S8535__", 0, 0, 0x0060, 1, "s8535") +/* Classic + MOVW, <= 8K. */ +AVR_MCU ("avr25", ARCH_AVR25, NULL, 0, 0, 0x0060, 1, "tn85") +AVR_MCU ("ata6289", ARCH_AVR25, "__AVR_ATA6289__", 0, 0, 0x0100, 1, "a6289") +AVR_MCU ("ata5272", ARCH_AVR25, "__AVR_ATA5272__", 0, 0, 0x0100, 1, "a5272") +AVR_MCU ("attiny13", ARCH_AVR25, "__AVR_ATtiny13__", 1, 0, 0x0060, 1, "tn13") +AVR_MCU ("attiny13a", ARCH_AVR25, "__AVR_ATtiny13A__", 1, 0, 0x0060, 1, "tn13a") +AVR_MCU ("attiny2313", ARCH_AVR25, "__AVR_ATtiny2313__", 1, 0, 0x0060, 1, "tn2313") +AVR_MCU ("attiny2313a", ARCH_AVR25, "__AVR_ATtiny2313A__", 1, 0, 0x0060, 1, "tn2313a") +AVR_MCU ("attiny24", ARCH_AVR25, "__AVR_ATtiny24__", 1, 0, 0x0060, 1, "tn24") +AVR_MCU ("attiny24a", ARCH_AVR25, "__AVR_ATtiny24A__", 1, 0, 0x0060, 1, "tn24a") +AVR_MCU ("attiny4313", ARCH_AVR25, "__AVR_ATtiny4313__", 0, 0, 0x0060, 1, "tn4313") +AVR_MCU ("attiny44", ARCH_AVR25, "__AVR_ATtiny44__", 0, 0, 0x0060, 1, "tn44") +AVR_MCU ("attiny44a", ARCH_AVR25, "__AVR_ATtiny44A__", 0, 0, 0x0060, 1, "tn44a") +AVR_MCU ("attiny84", ARCH_AVR25, "__AVR_ATtiny84__", 0, 0, 0x0060, 1, "tn84") +AVR_MCU ("attiny84a", ARCH_AVR25, "__AVR_ATtiny84A__", 0, 0, 0x0060, 1, "tn84") +AVR_MCU ("attiny25", ARCH_AVR25, "__AVR_ATtiny25__", 1, 0, 0x0060, 1, "tn25") +AVR_MCU ("attiny45", ARCH_AVR25, "__AVR_ATtiny45__", 0, 0, 0x0060, 1, "tn45") +AVR_MCU ("attiny85", ARCH_AVR25, "__AVR_ATtiny85__", 0, 0, 0x0060, 1, "tn85") +AVR_MCU ("attiny261", ARCH_AVR25, "__AVR_ATtiny261__", 1, 0, 0x0060, 1, "tn261") +AVR_MCU ("attiny261a", ARCH_AVR25, "__AVR_ATtiny261A__", 1, 0, 0x0060, 1, "tn261a") +AVR_MCU ("attiny461", ARCH_AVR25, "__AVR_ATtiny461__", 0, 0, 0x0060, 1, "tn461") +AVR_MCU ("attiny461a", ARCH_AVR25, "__AVR_ATtiny461A__", 0, 0, 0x0060, 1, "tn461a") +AVR_MCU ("attiny861", ARCH_AVR25, "__AVR_ATtiny861__", 0, 0, 0x0060, 1, "tn861") +AVR_MCU ("attiny861a", ARCH_AVR25, "__AVR_ATtiny861A__", 0, 0, 0x0060, 1, "tn861a") +AVR_MCU ("attiny43u", ARCH_AVR25, "__AVR_ATtiny43U__", 0, 0, 0x0060, 1, "tn43u") +AVR_MCU ("attiny87", ARCH_AVR25, "__AVR_ATtiny87__", 0, 0, 0x0100, 1, "tn87") +AVR_MCU ("attiny48", ARCH_AVR25, "__AVR_ATtiny48__", 0, 0, 0x0100, 1, "tn48") +AVR_MCU ("attiny88", ARCH_AVR25, "__AVR_ATtiny88__", 0, 0, 0x0100, 1, "tn88") +AVR_MCU ("at86rf401", ARCH_AVR25, "__AVR_AT86RF401__", 0, 0, 0x0060, 1, "86401") +/* Classic, > 8K, <= 64K. */ +AVR_MCU ("avr3", ARCH_AVR3, NULL, 0, 0, 0x0060, 1, "43355") +AVR_MCU ("at43usb355", ARCH_AVR3, "__AVR_AT43USB355__", 0, 0, 0x0060, 1, "43355") +AVR_MCU ("at76c711", ARCH_AVR3, "__AVR_AT76C711__", 0, 0, 0x0060, 1, "76711") +/* Classic, == 128K. */ +AVR_MCU ("avr31", ARCH_AVR31, NULL, 0, 1, 0x0060, 2, "m103") +AVR_MCU ("atmega103", ARCH_AVR31, "__AVR_ATmega103__", 0, 1, 0x0060, 2, "m103") +AVR_MCU ("at43usb320", ARCH_AVR31, "__AVR_AT43USB320__", 0, 0, 0x0060, 2, "43320") +/* Classic + MOVW + JMP/CALL. */ +AVR_MCU ("avr35", ARCH_AVR35, NULL, 0, 0, 0x0100, 1, "usb162") +AVR_MCU ("ata5505", ARCH_AVR35, "__AVR_ATA5505__", 0, 0, 0x0100, 1, "a5505") +AVR_MCU ("at90usb82", ARCH_AVR35, "__AVR_AT90USB82__", 0, 0, 0x0100, 1, "usb82") +AVR_MCU ("at90usb162", ARCH_AVR35, "__AVR_AT90USB162__", 0, 0, 0x0100, 1, "usb162") +AVR_MCU ("atmega8u2", ARCH_AVR35, "__AVR_ATmega8U2__", 0, 0, 0x0100, 1, "m8u2") +AVR_MCU ("atmega16u2", ARCH_AVR35, "__AVR_ATmega16U2__", 0, 0, 0x0100, 1, "m16u2") +AVR_MCU ("atmega32u2", ARCH_AVR35, "__AVR_ATmega32U2__", 0, 0, 0x0100, 1, "m32u2") +AVR_MCU ("attiny167", ARCH_AVR35, "__AVR_ATtiny167__", 0, 0, 0x0100, 1, "tn167") +AVR_MCU ("attiny1634", ARCH_AVR35, "__AVR_ATtiny1634__", 0, 0, 0x0100, 1, "tn1634") +/* Enhanced, <= 8K. */ +AVR_MCU ("avr4", ARCH_AVR4, NULL, 0, 0, 0x0060, 1, "m8") +AVR_MCU ("ata6285", ARCH_AVR4, "__AVR_ATA6285__", 0, 0, 0x0100, 1, "a6285") +AVR_MCU ("ata6286", ARCH_AVR4, "__AVR_ATA6286__", 0, 0, 0x0100, 1, "a6286") +AVR_MCU ("atmega8", ARCH_AVR4, "__AVR_ATmega8__", 0, 0, 0x0060, 1, "m8") +AVR_MCU ("atmega8a", ARCH_AVR4, "__AVR_ATmega8A__", 0, 0, 0x0060, 1, "m8a") +AVR_MCU ("atmega48", ARCH_AVR4, "__AVR_ATmega48__", 0, 0, 0x0100, 1, "m48") +AVR_MCU ("atmega48a", ARCH_AVR4, "__AVR_ATmega48A__", 0, 0, 0x0100, 1, "m48a") +AVR_MCU ("atmega48p", ARCH_AVR4, "__AVR_ATmega48P__", 0, 0, 0x0100, 1, "m48p") +AVR_MCU ("atmega48pa", ARCH_AVR4, "__AVR_ATmega48PA__", 0, 0, 0x0100, 1, "m48pa") +AVR_MCU ("atmega88", ARCH_AVR4, "__AVR_ATmega88__", 0, 0, 0x0100, 1, "m88") +AVR_MCU ("atmega88a", ARCH_AVR4, "__AVR_ATmega88A__", 0, 0, 0x0100, 1, "m88a") +AVR_MCU ("atmega88p", ARCH_AVR4, "__AVR_ATmega88P__", 0, 0, 0x0100, 1, "m88p") +AVR_MCU ("atmega88pa", ARCH_AVR4, "__AVR_ATmega88PA__", 0, 0, 0x0100, 1, "m88pa") +AVR_MCU ("atmega8515", ARCH_AVR4, "__AVR_ATmega8515__", 0, 0, 0x0060, 1, "m8515") +AVR_MCU ("atmega8535", ARCH_AVR4, "__AVR_ATmega8535__", 0, 0, 0x0060, 1, "m8535") +AVR_MCU ("atmega8hva", ARCH_AVR4, "__AVR_ATmega8HVA__", 0, 0, 0x0100, 1, "m8hva") +AVR_MCU ("at90pwm1", ARCH_AVR4, "__AVR_AT90PWM1__", 0, 0, 0x0100, 1, "90pwm1") +AVR_MCU ("at90pwm2", ARCH_AVR4, "__AVR_AT90PWM2__", 0, 0, 0x0100, 1, "90pwm2") +AVR_MCU ("at90pwm2b", ARCH_AVR4, "__AVR_AT90PWM2B__", 0, 0, 0x0100, 1, "90pwm2b") +AVR_MCU ("at90pwm3", ARCH_AVR4, "__AVR_AT90PWM3__", 0, 0, 0x0100, 1, "90pwm3") +AVR_MCU ("at90pwm3b", ARCH_AVR4, "__AVR_AT90PWM3B__", 0, 0, 0x0100, 1, "90pwm3b") +AVR_MCU ("at90pwm81", ARCH_AVR4, "__AVR_AT90PWM81__", 0, 0, 0x0100, 1, "90pwm81") +/* Enhanced, > 8K, <= 64K. */ +AVR_MCU ("avr5", ARCH_AVR5, NULL, 0, 0, 0x0060, 1, "m16") +AVR_MCU ("ata5790", ARCH_AVR5, "__AVR_ATA5790__", 0, 0, 0x0100, 1, "a5790") +AVR_MCU ("ata5790n", ARCH_AVR5, "__AVR_ATA5790N__", 0, 0, 0x0100, 1, "a5790n") +AVR_MCU ("ata5795", ARCH_AVR5, "__AVR_ATA5795__", 0, 0, 0x0100, 1, "a5795") +AVR_MCU ("atmega16", ARCH_AVR5, "__AVR_ATmega16__", 0, 0, 0x0060, 1, "m16") +AVR_MCU ("atmega16a", ARCH_AVR5, "__AVR_ATmega16A__", 0, 0, 0x0060, 1, "m16a") +AVR_MCU ("atmega161", ARCH_AVR5, "__AVR_ATmega161__", 0, 0, 0x0060, 1, "m161") +AVR_MCU ("atmega162", ARCH_AVR5, "__AVR_ATmega162__", 0, 0, 0x0100, 1, "m162") +AVR_MCU ("atmega163", ARCH_AVR5, "__AVR_ATmega163__", 0, 0, 0x0060, 1, "m163") +AVR_MCU ("atmega164a", ARCH_AVR5, "__AVR_ATmega164A__", 0, 0, 0x0100, 1, "m164a") +AVR_MCU ("atmega164p", ARCH_AVR5, "__AVR_ATmega164P__", 0, 0, 0x0100, 1, "m164p") +AVR_MCU ("atmega164pa", ARCH_AVR5, "__AVR_ATmega164PA__", 0, 0, 0x0100, 1, "m164pa") +AVR_MCU ("atmega165", ARCH_AVR5, "__AVR_ATmega165__", 0, 0, 0x0100, 1, "m165") +AVR_MCU ("atmega165a", ARCH_AVR5, "__AVR_ATmega165A__", 0, 0, 0x0100, 1, "m165a") +AVR_MCU ("atmega165p", ARCH_AVR5, "__AVR_ATmega165P__", 0, 0, 0x0100, 1, "m165p") +AVR_MCU ("atmega165pa", ARCH_AVR5, "__AVR_ATmega165PA__", 0, 0, 0x0100, 1, "m165pa") +AVR_MCU ("atmega168", ARCH_AVR5, "__AVR_ATmega168__", 0, 0, 0x0100, 1, "m168") +AVR_MCU ("atmega168a", ARCH_AVR5, "__AVR_ATmega168A__", 0, 0, 0x0100, 1, "m168a") +AVR_MCU ("atmega168p", ARCH_AVR5, "__AVR_ATmega168P__", 0, 0, 0x0100, 1, "m168p") +AVR_MCU ("atmega168pa", ARCH_AVR5, "__AVR_ATmega168PA__", 0, 0, 0x0100, 1, "m168pa") +AVR_MCU ("atmega169", ARCH_AVR5, "__AVR_ATmega169__", 0, 0, 0x0100, 1, "m169") +AVR_MCU ("atmega169a", ARCH_AVR5, "__AVR_ATmega169A__", 0, 0, 0x0100, 1, "m169a") +AVR_MCU ("atmega169p", ARCH_AVR5, "__AVR_ATmega169P__", 0, 0, 0x0100, 1, "m169p") +AVR_MCU ("atmega169pa", ARCH_AVR5, "__AVR_ATmega169PA__", 0, 0, 0x0100, 1, "m169pa") +AVR_MCU ("atmega16hva", ARCH_AVR5, "__AVR_ATmega16HVA__", 0, 0, 0x0100, 1, "m16hva") +AVR_MCU ("atmega16hvb", ARCH_AVR5, "__AVR_ATmega16HVB__", 0, 0, 0x0100, 1, "m16hvb") +AVR_MCU ("atmega16hvbrevb", ARCH_AVR5, "__AVR_ATmega16HVBREVB__", 0, 0, 0x0100, 1, "m16hvbrevb") +AVR_MCU ("atmega16m1", ARCH_AVR5, "__AVR_ATmega16M1__", 0, 0, 0x0100, 1, "m16m1") +AVR_MCU ("atmega16u4", ARCH_AVR5, "__AVR_ATmega16U4__", 0, 0, 0x0100, 1, "m16u4") +AVR_MCU ("atmega26hvg", ARCH_AVR5, "__AVR_ATmega26HVG__", 0, 0, 0x0100, 1, "m26hvg") +AVR_MCU ("atmega32a", ARCH_AVR5, "__AVR_ATmega32A__", 0, 0, 0x0060, 1, "m32a") +AVR_MCU ("atmega32", ARCH_AVR5, "__AVR_ATmega32__", 0, 0, 0x0060, 1, "m32") +AVR_MCU ("atmega32a", ARCH_AVR5, "__AVR_ATmega32A__", 0, 0, 0x0060, 1, "m32a") +AVR_MCU ("atmega323", ARCH_AVR5, "__AVR_ATmega323__", 0, 0, 0x0060, 1, "m323") +AVR_MCU ("atmega324a", ARCH_AVR5, "__AVR_ATmega324A__", 0, 0, 0x0100, 1, "m324a") +AVR_MCU ("atmega324p", ARCH_AVR5, "__AVR_ATmega324P__", 0, 0, 0x0100, 1, "m324p") +AVR_MCU ("atmega324pa", ARCH_AVR5, "__AVR_ATmega324PA__", 0, 0, 0x0100, 1, "m324pa") +AVR_MCU ("atmega325", ARCH_AVR5, "__AVR_ATmega325__", 0, 0, 0x0100, 1, "m325") +AVR_MCU ("atmega325a", ARCH_AVR5, "__AVR_ATmega325A__", 0, 0, 0x0100, 1, "m325a") +AVR_MCU ("atmega325p", ARCH_AVR5, "__AVR_ATmega325P__", 0, 0, 0x0100, 1, "m325p") +AVR_MCU ("atmega3250", ARCH_AVR5, "__AVR_ATmega3250__", 0, 0, 0x0100, 1, "m3250") +AVR_MCU ("atmega3250a", ARCH_AVR5, "__AVR_ATmega3250A__", 0, 0, 0x0100, 1, "m3250a") +AVR_MCU ("atmega3250p", ARCH_AVR5, "__AVR_ATmega3250P__", 0, 0, 0x0100, 1, "m3250p") +AVR_MCU ("atmega3250pa", ARCH_AVR5, "__AVR_ATmega3250PA__", 0, 0, 0x0100, 1, "m3250pa") +AVR_MCU ("atmega328", ARCH_AVR5, "__AVR_ATmega328__", 0, 0, 0x0100, 1, "m328") +AVR_MCU ("atmega328p", ARCH_AVR5, "__AVR_ATmega328P__", 0, 0, 0x0100, 1, "m328p") +AVR_MCU ("atmega329", ARCH_AVR5, "__AVR_ATmega329__", 0, 0, 0x0100, 1, "m329") +AVR_MCU ("atmega329a", ARCH_AVR5, "__AVR_ATmega329A__", 0, 0, 0x0100, 1, "m329a") +AVR_MCU ("atmega329p", ARCH_AVR5, "__AVR_ATmega329P__", 0, 0, 0x0100, 1, "m329p") +AVR_MCU ("atmega329pa", ARCH_AVR5, "__AVR_ATmega329PA__", 0, 0, 0x0100, 1, "m329pa") +AVR_MCU ("atmega3290", ARCH_AVR5, "__AVR_ATmega3290__", 0, 0, 0x0100, 1, "m3290") +AVR_MCU ("atmega3290a", ARCH_AVR5, "__AVR_ATmega3290A__", 0, 0, 0x0100, 1, "m3290a") +AVR_MCU ("atmega3290p", ARCH_AVR5, "__AVR_ATmega3290P__", 0, 0, 0x0100, 1, "m3290p") +AVR_MCU ("atmega3290pa", ARCH_AVR5, "__AVR_ATmega3290PA__", 0, 0, 0x0100, 1, "m3290pa") +AVR_MCU ("atmega32c1", ARCH_AVR5, "__AVR_ATmega32C1__", 0, 0, 0x0100, 1, "m32c1") +AVR_MCU ("atmega32m1", ARCH_AVR5, "__AVR_ATmega32M1__", 0, 0, 0x0100, 1, "m32m1") +AVR_MCU ("atmega32u4", ARCH_AVR5, "__AVR_ATmega32U4__", 0, 0, 0x0100, 1, "m32u4") +AVR_MCU ("atmega32u6", ARCH_AVR5, "__AVR_ATmega32U6__", 0, 0, 0x0100, 1, "m32u6") +AVR_MCU ("atmega406", ARCH_AVR5, "__AVR_ATmega406__", 0, 0, 0x0100, 1, "m406") +AVR_MCU ("atmega64", ARCH_AVR5, "__AVR_ATmega64__", 0, 0, 0x0100, 1, "m64") +AVR_MCU ("atmega64a", ARCH_AVR5, "__AVR_ATmega64A__", 0, 0, 0x0100, 1, "m64a") +AVR_MCU ("atmega640", ARCH_AVR5, "__AVR_ATmega640__", 0, 0, 0x0200, 1, "m640") +AVR_MCU ("atmega644", ARCH_AVR5, "__AVR_ATmega644__", 0, 0, 0x0100, 1, "m644") +AVR_MCU ("atmega644a", ARCH_AVR5, "__AVR_ATmega644A__", 0, 0, 0x0100, 1, "m644a") +AVR_MCU ("atmega644p", ARCH_AVR5, "__AVR_ATmega644P__", 0, 0, 0x0100, 1, "m644p") +AVR_MCU ("atmega644pa", ARCH_AVR5, "__AVR_ATmega644PA__", 0, 0, 0x0100, 1, "m644pa") +AVR_MCU ("atmega645", ARCH_AVR5, "__AVR_ATmega645__", 0, 0, 0x0100, 1, "m645") +AVR_MCU ("atmega645a", ARCH_AVR5, "__AVR_ATmega645A__", 0, 0, 0x0100, 1, "m645a") +AVR_MCU ("atmega645p", ARCH_AVR5, "__AVR_ATmega645P__", 0, 0, 0x0100, 1, "m645p") +AVR_MCU ("atmega6450", ARCH_AVR5, "__AVR_ATmega6450__", 0, 0, 0x0100, 1, "m6450") +AVR_MCU ("atmega6450a", ARCH_AVR5, "__AVR_ATmega6450A__", 0, 0, 0x0100, 1, "m6450a") +AVR_MCU ("atmega6450p", ARCH_AVR5, "__AVR_ATmega6450P__", 0, 0, 0x0100, 1, "m6450p") +AVR_MCU ("atmega649", ARCH_AVR5, "__AVR_ATmega649__", 0, 0, 0x0100, 1, "m649") +AVR_MCU ("atmega649a", ARCH_AVR5, "__AVR_ATmega649A__", 0, 0, 0x0100, 1, "m649a") +AVR_MCU ("atmega649p", ARCH_AVR5, "__AVR_ATmega649P__", 0, 0, 0x0100, 1, "m649p") +AVR_MCU ("atmega6490", ARCH_AVR5, "__AVR_ATmega6490__", 0, 0, 0x0100, 1, "m6490") +AVR_MCU ("atmega16hva", ARCH_AVR5, "__AVR_ATmega16HVA__", 0, 0, 0x0100, 1, "m16hva") +AVR_MCU ("atmega16hva2", ARCH_AVR5, "__AVR_ATmega16HVA2__", 0, 0, 0x0100, 1, "m16hva2") +AVR_MCU ("atmega16hvb", ARCH_AVR5, "__AVR_ATmega16HVB__", 0, 0, 0x0100, 1, "m16hvb") +AVR_MCU ("atmega32hvb", ARCH_AVR5, "__AVR_ATmega32HVB__", 0, 0, 0x0100, 1, "m32hvb") +AVR_MCU ("atmega6490a", ARCH_AVR5, "__AVR_ATmega6490A__", 0, 0, 0x0100, 1, "m6490a") +AVR_MCU ("atmega6490p", ARCH_AVR5, "__AVR_ATmega6490P__", 0, 0, 0x0100, 1, "m6490p") +AVR_MCU ("atmega64c1", ARCH_AVR5, "__AVR_ATmega64C1__", 0, 0, 0x0100, 1, "m64c1") +AVR_MCU ("atmega64m1", ARCH_AVR5, "__AVR_ATmega64M1__", 0, 0, 0x0100, 1, "m64m1") +AVR_MCU ("atmega64hve", ARCH_AVR5, "__AVR_ATmega64HVE__", 0, 0, 0x0100, 1, "m64hve") +AVR_MCU ("atmega64rfa2", ARCH_AVR5, "__AVR_ATmega64RFA2__", 0, 0, 0x0200, 1, "m64rfa2") +AVR_MCU ("atmega64rfr2", ARCH_AVR5, "__AVR_ATmega64RFR2__", 0, 0, 0x0200, 1, "m64rfr2") +AVR_MCU ("atmega32hvb", ARCH_AVR5, "__AVR_ATmega32HVB__", 0, 0, 0x0100, 1, "m32hvb") +AVR_MCU ("atmega32hvbrevb", ARCH_AVR5, "__AVR_ATmega32HVBREVB__", 0, 0, 0x0100, 1, "m32hvbrevb") +AVR_MCU ("atmega16hva2", ARCH_AVR5, "__AVR_ATmega16HVA2__", 0, 0, 0x0100, 1, "m16hva2") +AVR_MCU ("atmega48hvf", ARCH_AVR5, "__AVR_ATmega48HVF__", 0, 0, 0x0100, 1, "m48hvf") +AVR_MCU ("at90can32", ARCH_AVR5, "__AVR_AT90CAN32__", 0, 0, 0x0100, 1, "can32") +AVR_MCU ("at90can64", ARCH_AVR5, "__AVR_AT90CAN64__", 0, 0, 0x0100, 1, "can64") +AVR_MCU ("at90pwm161", ARCH_AVR5, "__AVR_AT90PWM161__", 0, 0, 0x0100, 1, "90pwm161") +AVR_MCU ("at90pwm216", ARCH_AVR5, "__AVR_AT90PWM216__", 0, 0, 0x0100, 1, "90pwm216") +AVR_MCU ("at90pwm316", ARCH_AVR5, "__AVR_AT90PWM316__", 0, 0, 0x0100, 1, "90pwm316") +AVR_MCU ("atmega32c1", ARCH_AVR5, "__AVR_ATmega32C1__", 0, 0, 0x0100, 1, "m32c1") +AVR_MCU ("atmega64c1", ARCH_AVR5, "__AVR_ATmega64C1__", 0, 0, 0x0100, 1, "m64c1") +AVR_MCU ("atmega16m1", ARCH_AVR5, "__AVR_ATmega16M1__", 0, 0, 0x0100, 1, "m16m1") +AVR_MCU ("atmega32m1", ARCH_AVR5, "__AVR_ATmega32M1__", 0, 0, 0x0100, 1, "m32m1") +AVR_MCU ("atmega64m1", ARCH_AVR5, "__AVR_ATmega64M1__", 0, 0, 0x0100, 1, "m64m1") +AVR_MCU ("atmega16u4", ARCH_AVR5, "__AVR_ATmega16U4__", 0, 0, 0x0100, 1, "m16u4") +AVR_MCU ("atmega32u4", ARCH_AVR5, "__AVR_ATmega32U4__", 0, 0, 0x0100, 1, "m32u4") +AVR_MCU ("atmega32u6", ARCH_AVR5, "__AVR_ATmega32U6__", 0, 0, 0x0100, 1, "m32u6") +AVR_MCU ("at90scr100", ARCH_AVR5, "__AVR_AT90SCR100__", 0, 0, 0x0100, 1, "90scr100") +AVR_MCU ("at90usb646", ARCH_AVR5, "__AVR_AT90USB646__", 0, 0, 0x0100, 1, "usb646") +AVR_MCU ("at90usb647", ARCH_AVR5, "__AVR_AT90USB647__", 0, 0, 0x0100, 1, "usb647") +AVR_MCU ("at94k", ARCH_AVR5, "__AVR_AT94K__", 0, 0, 0x0060, 1, "at94k") +AVR_MCU ("m3000", ARCH_AVR5, "__AVR_M3000__", 0, 0, 0x1000, 1, "m3000") +/* Enhanced, == 128K. */ +AVR_MCU ("avr51", ARCH_AVR51, NULL, 0, 0, 0x0100, 2, "m128") +AVR_MCU ("atmega128", ARCH_AVR51, "__AVR_ATmega128__", 0, 0, 0x0100, 2, "m128") +AVR_MCU ("atmega128a", ARCH_AVR51, "__AVR_ATmega128A__", 0, 0, 0x0100, 2, "m128a") +AVR_MCU ("atmega1280", ARCH_AVR51, "__AVR_ATmega1280__", 0, 0, 0x0200, 2, "m1280") +AVR_MCU ("atmega1281", ARCH_AVR51, "__AVR_ATmega1281__", 0, 0, 0x0200, 2, "m1281") +AVR_MCU ("atmega1284", ARCH_AVR51, "__AVR_ATmega1284__", 0, 0, 0x0100, 2, "m1284") +AVR_MCU ("atmega1284p", ARCH_AVR51, "__AVR_ATmega1284P__", 0, 0, 0x0100, 2, "m1284p") +AVR_MCU ("atmega128rfa1", ARCH_AVR51, "__AVR_ATmega128RFA1__", 0, 0, 0x0200, 2, "m128rfa1") +AVR_MCU ("at90can128", ARCH_AVR51, "__AVR_AT90CAN128__", 0, 0, 0x0100, 2, "can128") +AVR_MCU ("at90usb1286", ARCH_AVR51, "__AVR_AT90USB1286__", 0, 0, 0x0100, 2, "usb1286") +AVR_MCU ("at90usb1287", ARCH_AVR51, "__AVR_AT90USB1287__", 0, 0, 0x0100, 2, "usb1287") +/* 3-Byte PC. */ +AVR_MCU ("avr6", ARCH_AVR6, NULL, 0, 0, 0x0200, 4, "m2561") +AVR_MCU ("atmega2560", ARCH_AVR6, "__AVR_ATmega2560__", 0, 0, 0x0200, 4, "m2560") +AVR_MCU ("atmega2561", ARCH_AVR6, "__AVR_ATmega2561__", 0, 0, 0x0200, 4, "m2561") +/* Xmega, 16K <= Flash < 64K, RAM <= 64K */ +AVR_MCU ("avrxmega2", ARCH_AVRXMEGA2, NULL, 0, 0, 0x2000, 1, "x32a4") +AVR_MCU ("atxmega16a4", ARCH_AVRXMEGA2, "__AVR_ATxmega16A4__", 0, 0, 0x2000, 1, "x16a4") +AVR_MCU ("atxmega16d4", ARCH_AVRXMEGA2, "__AVR_ATxmega16D4__", 0, 0, 0x2000, 1, "x16d4") +AVR_MCU ("atxmega16x1", ARCH_AVRXMEGA2, "__AVR_ATxmega16X1__", 0, 0, 0x2000, 1, "x16x1") +AVR_MCU ("atxmega32a4", ARCH_AVRXMEGA2, "__AVR_ATxmega32A4__", 0, 0, 0x2000, 1, "x32a4") +AVR_MCU ("atxmega32d4", ARCH_AVRXMEGA2, "__AVR_ATxmega32D4__", 0, 0, 0x2000, 1, "x32d4") +AVR_MCU ("atxmega32x1", ARCH_AVRXMEGA2, "__AVR_ATxmega32X1__", 0, 0, 0x2000, 1, "x32x1") +AVR_MCU ("atmxt112sl", ARCH_AVRXMEGA2, "__AVR_ATMXT112SL__", 0, 0, 0x2000, 1, "mxt112sl") +AVR_MCU ("atmxt224", ARCH_AVRXMEGA2, "__AVR_ATMXT224__", 0, 0, 0x2000, 1, "mxt224") +AVR_MCU ("atmxt224e", ARCH_AVRXMEGA2, "__AVR_ATMXT224E__", 0, 0, 0x2000, 1, "mxt224e") +AVR_MCU ("atmxt336s", ARCH_AVRXMEGA2, "__AVR_ATMXT336S__", 0, 0, 0x2000, 1, "mxt336s") +AVR_MCU ("atxmega16a4u", ARCH_AVRXMEGA2, "__AVR_ATxmega16A4U__", 0, 0, 0x2000, 1, "x16a4u") +AVR_MCU ("atxmega16c4", ARCH_AVRXMEGA2, "__AVR_ATxmega16C4__", 0, 0, 0x2000, 1, "x16c4") +AVR_MCU ("atxmega32a4u", ARCH_AVRXMEGA2, "__AVR_ATxmega32A4U__", 0, 0, 0x2000, 1, "x32a4u") +AVR_MCU ("atxmega32c4", ARCH_AVRXMEGA2, "__AVR_ATxmega32C4__", 0, 0, 0x2000, 1, "x32c4") +AVR_MCU ("atxmega32e5", ARCH_AVRXMEGA2, "__AVR_ATxmega32E5__", 0, 0, 0x2000, 1, "x32e5") +/* Xmega, 64K < Flash <= 128K, RAM <= 64K */ +AVR_MCU ("avrxmega4", ARCH_AVRXMEGA4, NULL, 0, 0, 0x2000, 2, "x64a4") +AVR_MCU ("atxmega64a3", ARCH_AVRXMEGA4, "__AVR_ATxmega64A3__", 0, 0, 0x2000, 2, "x64a3") +AVR_MCU ("atxmega64d3", ARCH_AVRXMEGA4, "__AVR_ATxmega64D3__", 0, 0, 0x2000, 2, "x64d3") +AVR_MCU ("atxmega64a3u", ARCH_AVRXMEGA4, "__AVR_ATxmega64A3U__", 0, 0, 0x2000, 2, "x64a3u") +AVR_MCU ("atxmega64a4u", ARCH_AVRXMEGA4, "__AVR_ATxmega64A4U__", 0, 0, 0x2000, 2, "x64a4u") +AVR_MCU ("atxmega64b1", ARCH_AVRXMEGA4, "__AVR_ATxmega64B1__", 0, 0, 0x2000, 2, "x64b1") +AVR_MCU ("atxmega64b3", ARCH_AVRXMEGA4, "__AVR_ATxmega64B3__", 0, 0, 0x2000, 2, "x64b3") +AVR_MCU ("atxmega64c3", ARCH_AVRXMEGA4, "__AVR_ATxmega64C3__", 0, 0, 0x2000, 2, "x64c3") +AVR_MCU ("atxmega64d4", ARCH_AVRXMEGA4, "__AVR_ATxmega64D4__", 0, 0, 0x2000, 2, "x64d4") +/* Xmega, 64K < Flash <= 128K, RAM > 64K */ +AVR_MCU ("avrxmega5", ARCH_AVRXMEGA5, NULL, 0, 0, 0x2000, 2, "x64a1") +AVR_MCU ("atxmega64a1", ARCH_AVRXMEGA5, "__AVR_ATxmega64A1__", 0, 0, 0x2000, 2, "x64a1") +AVR_MCU ("atxmega64a1u", ARCH_AVRXMEGA5, "__AVR_ATxmega64A1U__", 0, 0, 0x2000, 2, "x64a1u") +/* Xmega, 128K < Flash, RAM <= 64K */ +AVR_MCU ("avrxmega6", ARCH_AVRXMEGA6, NULL, 0, 0, 0x2000, 6, "x128a3") +AVR_MCU ("atxmega128a3", ARCH_AVRXMEGA6, "__AVR_ATxmega128A3__", 0, 0, 0x2000, 3, "x128a3") +AVR_MCU ("atxmega128d3", ARCH_AVRXMEGA6, "__AVR_ATxmega128D3__", 0, 0, 0x2000, 3, "x128d3") +AVR_MCU ("atxmega192a3", ARCH_AVRXMEGA6, "__AVR_ATxmega192A3__", 0, 0, 0x2000, 4, "x192a3") +AVR_MCU ("atxmega192d3", ARCH_AVRXMEGA6, "__AVR_ATxmega192D3__", 0, 0, 0x2000, 4, "x192d3") +AVR_MCU ("atxmega256a3", ARCH_AVRXMEGA6, "__AVR_ATxmega256A3__", 0, 0, 0x2000, 5, "x256a3") +AVR_MCU ("atxmega256a3b", ARCH_AVRXMEGA6, "__AVR_ATxmega256A3B__", 0, 0, 0x2000, 5, "x256a3b") +AVR_MCU ("atxmega256a3bu", ARCH_AVRXMEGA6, "__AVR_ATxmega256A3BU__", 0, 0, 0x2000, 5, "x256a3bu") +AVR_MCU ("atxmega256d3", ARCH_AVRXMEGA6, "__AVR_ATxmega256D3__", 0, 0, 0x2000, 5, "x256d3") +AVR_MCU ("atxmega128a3u", ARCH_AVRXMEGA6, "__AVR_ATxmega128A3U__", 0, 0, 0x2000, 3, "x128a3u") +AVR_MCU ("atxmega128b1", ARCH_AVRXMEGA6, "__AVR_ATxmega128B1__", 0, 0, 0x2000, 3, "x128b1") +AVR_MCU ("atxmega128b3", ARCH_AVRXMEGA6, "__AVR_ATxmega128B3__", 0, 0, 0x2000, 3, "x128b3") +AVR_MCU ("atxmega128c3", ARCH_AVRXMEGA6, "__AVR_ATxmega128C3__", 0, 0, 0x2000, 3, "x128c3") +AVR_MCU ("atxmega128d4", ARCH_AVRXMEGA6, "__AVR_ATxmega128D4__", 0, 0, 0x2000, 3, "x128d4") +AVR_MCU ("atmxt540s", ARCH_AVRXMEGA6, "__AVR_ATMXT540S__", 0, 0, 0x2000, 2, "mxt540s") +AVR_MCU ("atmxt540sreva", ARCH_AVRXMEGA6, "__AVR_ATMXT540SREVA__", 0, 0, 0x2000, 2, "mxt540sreva") +AVR_MCU ("atxmega192a3u", ARCH_AVRXMEGA6, "__AVR_ATxmega192A3U__", 0, 0, 0x2000, 4, "x192a3u") +AVR_MCU ("atxmega192c3", ARCH_AVRXMEGA6, "__AVR_ATxmega192C3__", 0, 0, 0x2000, 4, "x192c3") +AVR_MCU ("atxmega256a3u", ARCH_AVRXMEGA6, "__AVR_ATxmega256A3U__", 0, 0, 0x2000, 5, "x256a3u") +AVR_MCU ("atxmega256c3", ARCH_AVRXMEGA6, "__AVR_ATxmega256C3__", 0, 0, 0x2000, 5, "x256c3") +AVR_MCU ("atxmega384c3", ARCH_AVRXMEGA6, "__AVR_ATxmega384C3__", 0, 0, 0x2000, 6, "x384c3") +AVR_MCU ("atxmega384d3", ARCH_AVRXMEGA6, "__AVR_ATxmega384D3__", 0, 0, 0x2000, 6, "x384d3") +/* Xmega, 128K < Flash, RAM > 64K RAM. */ +AVR_MCU ("avrxmega7", ARCH_AVRXMEGA7, NULL, 0, 0, 0x2000, 3, "x128a1") +AVR_MCU ("atxmega128a1", ARCH_AVRXMEGA7, "__AVR_ATxmega128A1__", 0, 0, 0x2000, 3, "x128a1") +AVR_MCU ("atxmega128a1u", ARCH_AVRXMEGA7, "__AVR_ATxmega128A1U__", 0, 0, 0x2000, 3, "x128a1u") +AVR_MCU ("atxmega128a4u", ARCH_AVRXMEGA7, "__AVR_ATxmega128A4U__", 0, 0, 0x2000, 3, "x128a4u") +/* Assembler only. */ +AVR_MCU ("avr1", ARCH_AVR1, NULL, 0, 0, 0x0060, 1, "s1200") +AVR_MCU ("at90s1200", ARCH_AVR1, "__AVR_AT90S1200__", 0, 0, 0x0060, 1, "s1200") +AVR_MCU ("attiny11", ARCH_AVR1, "__AVR_ATtiny11__", 0, 0, 0x0060, 1, "tn11") +AVR_MCU ("attiny12", ARCH_AVR1, "__AVR_ATtiny12__", 0, 0, 0x0060, 1, "tn12") +AVR_MCU ("attiny15", ARCH_AVR1, "__AVR_ATtiny15__", 0, 0, 0x0060, 1, "tn15") +AVR_MCU ("attiny28", ARCH_AVR1, "__AVR_ATtiny28__", 0, 0, 0x0060, 1, "tn28") diff --git a/gcc-4.8/gcc/config/avr/avr-modes.def b/gcc-4.8/gcc/config/avr/avr-modes.def new file mode 100644 index 000000000..7d2886fed --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-modes.def @@ -0,0 +1,33 @@ +/* Copyright (C) 2012-2013 Free Software Foundation, Inc. + + 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 + . */ + +FRACTIONAL_INT_MODE (PSI, 24, 3); + +/* Make TA and UTA 64 bits wide. + 128 bit wide modes would be insane on a 8-bit machine. + This needs special treatment in avr.c and avr-lib.h. */ + +ADJUST_BYTESIZE (TA, 8); +ADJUST_ALIGNMENT (TA, 1); +ADJUST_IBIT (TA, 16); +ADJUST_FBIT (TA, 47); + +ADJUST_BYTESIZE (UTA, 8); +ADJUST_ALIGNMENT (UTA, 1); +ADJUST_IBIT (UTA, 16); +ADJUST_FBIT (UTA, 48); diff --git a/gcc-4.8/gcc/config/avr/avr-protos.h b/gcc-4.8/gcc/config/avr/avr-protos.h new file mode 100644 index 000000000..5246d0637 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-protos.h @@ -0,0 +1,163 @@ +/* Prototypes for exported functions defined in avr.c + + Copyright (C) 2000-2013 Free Software Foundation, Inc. + Contributed by Denis Chertykov (chertykov@gmail.com) + + 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 + . */ + + +extern int avr_function_arg_regno_p (int r); +extern void avr_cpu_cpp_builtins (struct cpp_reader * pfile); +extern enum reg_class avr_regno_reg_class (int r); +extern void asm_globalize_label (FILE *file, const char *name); +extern void avr_adjust_reg_alloc_order (void); +extern int avr_initial_elimination_offset (int from, int to); +extern int avr_simple_epilogue (void); +extern int avr_hard_regno_rename_ok (unsigned int, unsigned int); +extern rtx avr_return_addr_rtx (int count, rtx tem); +extern void avr_register_target_pragmas (void); +extern void avr_init_expanders (void); + +#ifdef TREE_CODE +extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool); +extern void asm_output_external (FILE *file, tree decl, char *name); +extern int avr_progmem_p (tree decl, tree attributes); + +#ifdef RTX_CODE /* inside TREE_CODE */ +extern void avr_init_cumulative_args (CUMULATIVE_ARGS*, tree, rtx, tree); +#endif /* RTX_CODE inside TREE_CODE */ + +#endif /* TREE_CODE */ + +#ifdef RTX_CODE +extern int avr_hard_regno_call_part_clobbered (unsigned, enum machine_mode); +extern const char *output_movqi (rtx insn, rtx operands[], int *l); +extern const char *output_movhi (rtx insn, rtx operands[], int *l); +extern const char *output_movsisf (rtx insn, rtx operands[], int *l); +extern const char *avr_out_tstsi (rtx, rtx*, int*); +extern const char *avr_out_tsthi (rtx, rtx*, int*); +extern const char *avr_out_tstpsi (rtx, rtx*, int*); +extern const char *avr_out_compare (rtx, rtx*, int*); +extern const char *avr_out_compare64 (rtx, rtx*, int*); +extern const char *ret_cond_branch (rtx x, int len, int reverse); +extern const char *avr_out_movpsi (rtx, rtx*, int*); + +extern const char *ashlqi3_out (rtx insn, rtx operands[], int *len); +extern const char *ashlhi3_out (rtx insn, rtx operands[], int *len); +extern const char *ashlsi3_out (rtx insn, rtx operands[], int *len); + +extern const char *ashrqi3_out (rtx insn, rtx operands[], int *len); +extern const char *ashrhi3_out (rtx insn, rtx operands[], int *len); +extern const char *ashrsi3_out (rtx insn, rtx operands[], int *len); + +extern const char *lshrqi3_out (rtx insn, rtx operands[], int *len); +extern const char *lshrhi3_out (rtx insn, rtx operands[], int *len); +extern const char *lshrsi3_out (rtx insn, rtx operands[], int *len); + +extern const char *avr_out_ashlpsi3 (rtx, rtx*, int*); +extern const char *avr_out_ashrpsi3 (rtx, rtx*, int*); +extern const char *avr_out_lshrpsi3 (rtx, rtx*, int*); + +extern bool avr_rotate_bytes (rtx operands[]); + +extern const char* avr_out_fract (rtx, rtx[], bool, int*); +extern rtx avr_to_int_mode (rtx); + +extern void avr_expand_prologue (void); +extern void avr_expand_epilogue (bool); +extern bool avr_emit_movmemhi (rtx*); +extern int avr_epilogue_uses (int regno); +extern int avr_starting_frame_offset (void); + +extern void avr_output_addr_vec_elt (FILE *stream, int value); +extern const char *avr_out_sbxx_branch (rtx insn, rtx operands[]); +extern const char* avr_out_bitop (rtx, rtx*, int*); +extern const char* avr_out_plus (rtx, rtx*, int* =NULL, int* =NULL); +extern const char* avr_out_addto_sp (rtx*, int*); +extern const char* avr_out_xload (rtx, rtx*, int*); +extern const char* avr_out_movmem (rtx, rtx*, int*); +extern const char* avr_out_insert_bits (rtx*, int*); +extern bool avr_popcount_each_byte (rtx, int, int); +extern bool avr_has_nibble_0xf (rtx); + +extern int extra_constraint_Q (rtx x); +extern int avr_adjust_insn_length (rtx insn, int len); +extern const char* output_reload_inhi (rtx*, rtx, int*); +extern const char* output_reload_insisf (rtx*, rtx, int*); +extern const char* avr_out_reload_inpsi (rtx*, rtx, int*); +extern const char* avr_out_lpm (rtx, rtx*, int*); +extern void avr_notice_update_cc (rtx body, rtx insn); +extern int reg_unused_after (rtx insn, rtx reg); +extern int _reg_unused_after (rtx insn, rtx reg); +extern int avr_jump_mode (rtx x, rtx insn); +extern int test_hard_reg_class (enum reg_class rclass, rtx x); +extern int jump_over_one_insn_p (rtx insn, rtx dest); + +extern int avr_hard_regno_mode_ok (int regno, enum machine_mode mode); +extern void avr_final_prescan_insn (rtx insn, rtx *operand, int num_operands); +extern int avr_simplify_comparison_p (enum machine_mode mode, + RTX_CODE op, rtx x); +extern RTX_CODE avr_normalize_condition (RTX_CODE condition); +extern void out_shift_with_cnt (const char *templ, rtx insn, + rtx operands[], int *len, int t_len); +extern enum reg_class avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE); +extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE); +extern rtx avr_incoming_return_addr_rtx (void); +extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int)); +extern bool avr_mem_flash_p (rtx); +extern bool avr_mem_memx_p (rtx); +extern bool avr_load_libgcc_p (rtx); +extern bool avr_xload_libgcc_p (enum machine_mode); + +extern rtx lpm_reg_rtx; +extern rtx lpm_addr_reg_rtx; +extern rtx tmp_reg_rtx; +extern rtx zero_reg_rtx; +extern rtx all_regs_rtx[32]; +extern rtx rampz_rtx; + +#endif /* RTX_CODE */ + +#ifdef REAL_VALUE_TYPE +extern void asm_output_float (FILE *file, REAL_VALUE_TYPE n); +#endif + +extern bool avr_have_dimode; + +/* From avr-log.c */ + +#define avr_edump (avr_log_set_caller_e (__FUNCTION__)) +#define avr_fdump (avr_log_set_caller_f (__FUNCTION__)) + +extern int (*avr_log_set_caller_e (const char*))(const char*, ...); +extern int (*avr_log_set_caller_f (const char*))(FILE*, const char*, ...); + +extern void avr_log_set_avr_log (void); + +typedef struct +{ + unsigned address_cost :1; + unsigned builtin :1; + unsigned constraints :1; + unsigned legitimate_address_p :1; + unsigned legitimize_address :1; + unsigned legitimize_reload_address :1; + unsigned progmem :1; + unsigned rtx_costs :1; +} avr_log_t; + +extern avr_log_t avr_log; diff --git a/gcc-4.8/gcc/config/avr/avr-stdint.h b/gcc-4.8/gcc/config/avr/avr-stdint.h new file mode 100644 index 000000000..8e7278f38 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-stdint.h @@ -0,0 +1,66 @@ +/* Definitions for types on systems using newlib. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + +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 +. */ + +/* + The intention of this file is to supply definitions that work with + avr-gcc's -mint8 that sets int to an 8-bit type. + + This file is intended to yield the same results as newlib-stdint.h, + but there are some differences to newlib-stdint.h: + + - AVR is an 8-bit architecture that cannot access 16-bit values + atomically, this SIG_ATOMIC_TYPE is "char". + + - For the same reason, [u]int_fast8_t is defined as 8-bit type. + +*/ + +#define SIG_ATOMIC_TYPE "char" + +#define INT8_TYPE "signed char" +#define INT16_TYPE (INT_TYPE_SIZE == 16 ? "short int" : "long int") +#define INT32_TYPE (INT_TYPE_SIZE == 16 ? "long int" : "long long int") +#define INT64_TYPE (INT_TYPE_SIZE == 16 ? "long long int" : 0) +#define UINT8_TYPE "unsigned char" +#define UINT16_TYPE (INT_TYPE_SIZE == 16 ? "short unsigned int" : "long unsigned int") +#define UINT32_TYPE (INT_TYPE_SIZE == 16 ? "long unsigned int" : "long long unsigned int") +#define UINT64_TYPE (INT_TYPE_SIZE == 16 ? "long long unsigned int" : 0) + +#define INT_LEAST8_TYPE INT8_TYPE +#define INT_LEAST16_TYPE INT16_TYPE +#define INT_LEAST32_TYPE INT32_TYPE +#define INT_LEAST64_TYPE INT64_TYPE +#define UINT_LEAST8_TYPE UINT8_TYPE +#define UINT_LEAST16_TYPE UINT16_TYPE +#define UINT_LEAST32_TYPE UINT32_TYPE +#define UINT_LEAST64_TYPE UINT64_TYPE + +#define INT_FAST8_TYPE INT8_TYPE +#define INT_FAST16_TYPE (INT_TYPE_SIZE == 16 ? "int" : INT16_TYPE) +#define INT_FAST32_TYPE INT32_TYPE +#define INT_FAST64_TYPE INT64_TYPE +#define UINT_FAST8_TYPE UINT8_TYPE +#define UINT_FAST16_TYPE (INT_TYPE_SIZE == 16 ? "unsigned int" : UINT16_TYPE) +#define UINT_FAST32_TYPE UINT32_TYPE +#define UINT_FAST64_TYPE UINT64_TYPE + +#define INTPTR_TYPE PTRDIFF_TYPE +#ifndef UINTPTR_TYPE +#define UINTPTR_TYPE SIZE_TYPE +#endif diff --git a/gcc-4.8/gcc/config/avr/avr-tables.opt b/gcc-4.8/gcc/config/avr/avr-tables.opt new file mode 100644 index 000000000..1a451bda4 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr-tables.opt @@ -0,0 +1,808 @@ +; -*- buffer-read-only: t -*- +; Generated automatically by genopt.sh from avr-mcus.def. + +; Copyright (C) 2011-2013 Free Software Foundation, Inc. +; +; 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 +; . + +Enum +Name(avr_mcu) Type(int) +Known MCU names: + +EnumValue +Enum(avr_mcu) String(avr2) Value(0) + +EnumValue +Enum(avr_mcu) String(at90s2313) Value(1) + +EnumValue +Enum(avr_mcu) String(at90s2323) Value(2) + +EnumValue +Enum(avr_mcu) String(at90s2333) Value(3) + +EnumValue +Enum(avr_mcu) String(at90s2343) Value(4) + +EnumValue +Enum(avr_mcu) String(attiny22) Value(5) + +EnumValue +Enum(avr_mcu) String(attiny26) Value(6) + +EnumValue +Enum(avr_mcu) String(at90s4414) Value(7) + +EnumValue +Enum(avr_mcu) String(at90s4433) Value(8) + +EnumValue +Enum(avr_mcu) String(at90s4434) Value(9) + +EnumValue +Enum(avr_mcu) String(at90s8515) Value(10) + +EnumValue +Enum(avr_mcu) String(at90c8534) Value(11) + +EnumValue +Enum(avr_mcu) String(at90s8535) Value(12) + +EnumValue +Enum(avr_mcu) String(avr25) Value(13) + +EnumValue +Enum(avr_mcu) String(ata6289) Value(14) + +EnumValue +Enum(avr_mcu) String(ata5272) Value(15) + +EnumValue +Enum(avr_mcu) String(attiny13) Value(16) + +EnumValue +Enum(avr_mcu) String(attiny13a) Value(17) + +EnumValue +Enum(avr_mcu) String(attiny2313) Value(18) + +EnumValue +Enum(avr_mcu) String(attiny2313a) Value(19) + +EnumValue +Enum(avr_mcu) String(attiny24) Value(20) + +EnumValue +Enum(avr_mcu) String(attiny24a) Value(21) + +EnumValue +Enum(avr_mcu) String(attiny4313) Value(22) + +EnumValue +Enum(avr_mcu) String(attiny44) Value(23) + +EnumValue +Enum(avr_mcu) String(attiny44a) Value(24) + +EnumValue +Enum(avr_mcu) String(attiny84) Value(25) + +EnumValue +Enum(avr_mcu) String(attiny84a) Value(26) + +EnumValue +Enum(avr_mcu) String(attiny25) Value(27) + +EnumValue +Enum(avr_mcu) String(attiny45) Value(28) + +EnumValue +Enum(avr_mcu) String(attiny85) Value(29) + +EnumValue +Enum(avr_mcu) String(attiny261) Value(30) + +EnumValue +Enum(avr_mcu) String(attiny261a) Value(31) + +EnumValue +Enum(avr_mcu) String(attiny461) Value(32) + +EnumValue +Enum(avr_mcu) String(attiny461a) Value(33) + +EnumValue +Enum(avr_mcu) String(attiny861) Value(34) + +EnumValue +Enum(avr_mcu) String(attiny861a) Value(35) + +EnumValue +Enum(avr_mcu) String(attiny43u) Value(36) + +EnumValue +Enum(avr_mcu) String(attiny87) Value(37) + +EnumValue +Enum(avr_mcu) String(attiny48) Value(38) + +EnumValue +Enum(avr_mcu) String(attiny88) Value(39) + +EnumValue +Enum(avr_mcu) String(at86rf401) Value(40) + +EnumValue +Enum(avr_mcu) String(avr3) Value(41) + +EnumValue +Enum(avr_mcu) String(at43usb355) Value(42) + +EnumValue +Enum(avr_mcu) String(at76c711) Value(43) + +EnumValue +Enum(avr_mcu) String(avr31) Value(44) + +EnumValue +Enum(avr_mcu) String(atmega103) Value(45) + +EnumValue +Enum(avr_mcu) String(at43usb320) Value(46) + +EnumValue +Enum(avr_mcu) String(avr35) Value(47) + +EnumValue +Enum(avr_mcu) String(ata5505) Value(48) + +EnumValue +Enum(avr_mcu) String(at90usb82) Value(49) + +EnumValue +Enum(avr_mcu) String(at90usb162) Value(50) + +EnumValue +Enum(avr_mcu) String(atmega8u2) Value(51) + +EnumValue +Enum(avr_mcu) String(atmega16u2) Value(52) + +EnumValue +Enum(avr_mcu) String(atmega32u2) Value(53) + +EnumValue +Enum(avr_mcu) String(attiny167) Value(54) + +EnumValue +Enum(avr_mcu) String(attiny1634) Value(55) + +EnumValue +Enum(avr_mcu) String(avr4) Value(56) + +EnumValue +Enum(avr_mcu) String(ata6285) Value(57) + +EnumValue +Enum(avr_mcu) String(ata6286) Value(58) + +EnumValue +Enum(avr_mcu) String(atmega8) Value(59) + +EnumValue +Enum(avr_mcu) String(atmega8a) Value(60) + +EnumValue +Enum(avr_mcu) String(atmega48) Value(61) + +EnumValue +Enum(avr_mcu) String(atmega48a) Value(62) + +EnumValue +Enum(avr_mcu) String(atmega48p) Value(63) + +EnumValue +Enum(avr_mcu) String(atmega48pa) Value(64) + +EnumValue +Enum(avr_mcu) String(atmega88) Value(65) + +EnumValue +Enum(avr_mcu) String(atmega88a) Value(66) + +EnumValue +Enum(avr_mcu) String(atmega88p) Value(67) + +EnumValue +Enum(avr_mcu) String(atmega88pa) Value(68) + +EnumValue +Enum(avr_mcu) String(atmega8515) Value(69) + +EnumValue +Enum(avr_mcu) String(atmega8535) Value(70) + +EnumValue +Enum(avr_mcu) String(atmega8hva) Value(71) + +EnumValue +Enum(avr_mcu) String(at90pwm1) Value(72) + +EnumValue +Enum(avr_mcu) String(at90pwm2) Value(73) + +EnumValue +Enum(avr_mcu) String(at90pwm2b) Value(74) + +EnumValue +Enum(avr_mcu) String(at90pwm3) Value(75) + +EnumValue +Enum(avr_mcu) String(at90pwm3b) Value(76) + +EnumValue +Enum(avr_mcu) String(at90pwm81) Value(77) + +EnumValue +Enum(avr_mcu) String(avr5) Value(78) + +EnumValue +Enum(avr_mcu) String(ata5790) Value(79) + +EnumValue +Enum(avr_mcu) String(ata5790n) Value(80) + +EnumValue +Enum(avr_mcu) String(ata5795) Value(81) + +EnumValue +Enum(avr_mcu) String(atmega16) Value(82) + +EnumValue +Enum(avr_mcu) String(atmega16a) Value(83) + +EnumValue +Enum(avr_mcu) String(atmega161) Value(84) + +EnumValue +Enum(avr_mcu) String(atmega162) Value(85) + +EnumValue +Enum(avr_mcu) String(atmega163) Value(86) + +EnumValue +Enum(avr_mcu) String(atmega164a) Value(87) + +EnumValue +Enum(avr_mcu) String(atmega164p) Value(88) + +EnumValue +Enum(avr_mcu) String(atmega164pa) Value(89) + +EnumValue +Enum(avr_mcu) String(atmega165) Value(90) + +EnumValue +Enum(avr_mcu) String(atmega165a) Value(91) + +EnumValue +Enum(avr_mcu) String(atmega165p) Value(92) + +EnumValue +Enum(avr_mcu) String(atmega165pa) Value(93) + +EnumValue +Enum(avr_mcu) String(atmega168) Value(94) + +EnumValue +Enum(avr_mcu) String(atmega168a) Value(95) + +EnumValue +Enum(avr_mcu) String(atmega168p) Value(96) + +EnumValue +Enum(avr_mcu) String(atmega168pa) Value(97) + +EnumValue +Enum(avr_mcu) String(atmega169) Value(98) + +EnumValue +Enum(avr_mcu) String(atmega169a) Value(99) + +EnumValue +Enum(avr_mcu) String(atmega169p) Value(100) + +EnumValue +Enum(avr_mcu) String(atmega169pa) Value(101) + +EnumValue +Enum(avr_mcu) String(atmega16hva) Value(102) + +EnumValue +Enum(avr_mcu) String(atmega16hvb) Value(103) + +EnumValue +Enum(avr_mcu) String(atmega16hvbrevb) Value(104) + +EnumValue +Enum(avr_mcu) String(atmega16m1) Value(105) + +EnumValue +Enum(avr_mcu) String(atmega16u4) Value(106) + +EnumValue +Enum(avr_mcu) String(atmega26hvg) Value(107) + +EnumValue +Enum(avr_mcu) String(atmega32a) Value(108) + +EnumValue +Enum(avr_mcu) String(atmega32) Value(109) + +EnumValue +Enum(avr_mcu) String(atmega32a) Value(110) + +EnumValue +Enum(avr_mcu) String(atmega323) Value(111) + +EnumValue +Enum(avr_mcu) String(atmega324a) Value(112) + +EnumValue +Enum(avr_mcu) String(atmega324p) Value(113) + +EnumValue +Enum(avr_mcu) String(atmega324pa) Value(114) + +EnumValue +Enum(avr_mcu) String(atmega325) Value(115) + +EnumValue +Enum(avr_mcu) String(atmega325a) Value(116) + +EnumValue +Enum(avr_mcu) String(atmega325p) Value(117) + +EnumValue +Enum(avr_mcu) String(atmega3250) Value(118) + +EnumValue +Enum(avr_mcu) String(atmega3250a) Value(119) + +EnumValue +Enum(avr_mcu) String(atmega3250p) Value(120) + +EnumValue +Enum(avr_mcu) String(atmega3250pa) Value(121) + +EnumValue +Enum(avr_mcu) String(atmega328) Value(122) + +EnumValue +Enum(avr_mcu) String(atmega328p) Value(123) + +EnumValue +Enum(avr_mcu) String(atmega329) Value(124) + +EnumValue +Enum(avr_mcu) String(atmega329a) Value(125) + +EnumValue +Enum(avr_mcu) String(atmega329p) Value(126) + +EnumValue +Enum(avr_mcu) String(atmega329pa) Value(127) + +EnumValue +Enum(avr_mcu) String(atmega3290) Value(128) + +EnumValue +Enum(avr_mcu) String(atmega3290a) Value(129) + +EnumValue +Enum(avr_mcu) String(atmega3290p) Value(130) + +EnumValue +Enum(avr_mcu) String(atmega3290pa) Value(131) + +EnumValue +Enum(avr_mcu) String(atmega32c1) Value(132) + +EnumValue +Enum(avr_mcu) String(atmega32m1) Value(133) + +EnumValue +Enum(avr_mcu) String(atmega32u4) Value(134) + +EnumValue +Enum(avr_mcu) String(atmega32u6) Value(135) + +EnumValue +Enum(avr_mcu) String(atmega406) Value(136) + +EnumValue +Enum(avr_mcu) String(atmega64) Value(137) + +EnumValue +Enum(avr_mcu) String(atmega64a) Value(138) + +EnumValue +Enum(avr_mcu) String(atmega640) Value(139) + +EnumValue +Enum(avr_mcu) String(atmega644) Value(140) + +EnumValue +Enum(avr_mcu) String(atmega644a) Value(141) + +EnumValue +Enum(avr_mcu) String(atmega644p) Value(142) + +EnumValue +Enum(avr_mcu) String(atmega644pa) Value(143) + +EnumValue +Enum(avr_mcu) String(atmega645) Value(144) + +EnumValue +Enum(avr_mcu) String(atmega645a) Value(145) + +EnumValue +Enum(avr_mcu) String(atmega645p) Value(146) + +EnumValue +Enum(avr_mcu) String(atmega6450) Value(147) + +EnumValue +Enum(avr_mcu) String(atmega6450a) Value(148) + +EnumValue +Enum(avr_mcu) String(atmega6450p) Value(149) + +EnumValue +Enum(avr_mcu) String(atmega649) Value(150) + +EnumValue +Enum(avr_mcu) String(atmega649a) Value(151) + +EnumValue +Enum(avr_mcu) String(atmega649p) Value(152) + +EnumValue +Enum(avr_mcu) String(atmega6490) Value(153) + +EnumValue +Enum(avr_mcu) String(atmega16hva) Value(154) + +EnumValue +Enum(avr_mcu) String(atmega16hva2) Value(155) + +EnumValue +Enum(avr_mcu) String(atmega16hvb) Value(156) + +EnumValue +Enum(avr_mcu) String(atmega32hvb) Value(157) + +EnumValue +Enum(avr_mcu) String(atmega6490a) Value(158) + +EnumValue +Enum(avr_mcu) String(atmega6490p) Value(159) + +EnumValue +Enum(avr_mcu) String(atmega64c1) Value(160) + +EnumValue +Enum(avr_mcu) String(atmega64m1) Value(161) + +EnumValue +Enum(avr_mcu) String(atmega64hve) Value(162) + +EnumValue +Enum(avr_mcu) String(atmega64rfa2) Value(163) + +EnumValue +Enum(avr_mcu) String(atmega64rfr2) Value(164) + +EnumValue +Enum(avr_mcu) String(atmega32hvb) Value(165) + +EnumValue +Enum(avr_mcu) String(atmega32hvbrevb) Value(166) + +EnumValue +Enum(avr_mcu) String(atmega16hva2) Value(167) + +EnumValue +Enum(avr_mcu) String(atmega48hvf) Value(168) + +EnumValue +Enum(avr_mcu) String(at90can32) Value(169) + +EnumValue +Enum(avr_mcu) String(at90can64) Value(170) + +EnumValue +Enum(avr_mcu) String(at90pwm161) Value(171) + +EnumValue +Enum(avr_mcu) String(at90pwm216) Value(172) + +EnumValue +Enum(avr_mcu) String(at90pwm316) Value(173) + +EnumValue +Enum(avr_mcu) String(atmega32c1) Value(174) + +EnumValue +Enum(avr_mcu) String(atmega64c1) Value(175) + +EnumValue +Enum(avr_mcu) String(atmega16m1) Value(176) + +EnumValue +Enum(avr_mcu) String(atmega32m1) Value(177) + +EnumValue +Enum(avr_mcu) String(atmega64m1) Value(178) + +EnumValue +Enum(avr_mcu) String(atmega16u4) Value(179) + +EnumValue +Enum(avr_mcu) String(atmega32u4) Value(180) + +EnumValue +Enum(avr_mcu) String(atmega32u6) Value(181) + +EnumValue +Enum(avr_mcu) String(at90scr100) Value(182) + +EnumValue +Enum(avr_mcu) String(at90usb646) Value(183) + +EnumValue +Enum(avr_mcu) String(at90usb647) Value(184) + +EnumValue +Enum(avr_mcu) String(at94k) Value(185) + +EnumValue +Enum(avr_mcu) String(m3000) Value(186) + +EnumValue +Enum(avr_mcu) String(avr51) Value(187) + +EnumValue +Enum(avr_mcu) String(atmega128) Value(188) + +EnumValue +Enum(avr_mcu) String(atmega128a) Value(189) + +EnumValue +Enum(avr_mcu) String(atmega1280) Value(190) + +EnumValue +Enum(avr_mcu) String(atmega1281) Value(191) + +EnumValue +Enum(avr_mcu) String(atmega1284) Value(192) + +EnumValue +Enum(avr_mcu) String(atmega1284p) Value(193) + +EnumValue +Enum(avr_mcu) String(atmega128rfa1) Value(194) + +EnumValue +Enum(avr_mcu) String(at90can128) Value(195) + +EnumValue +Enum(avr_mcu) String(at90usb1286) Value(196) + +EnumValue +Enum(avr_mcu) String(at90usb1287) Value(197) + +EnumValue +Enum(avr_mcu) String(avr6) Value(198) + +EnumValue +Enum(avr_mcu) String(atmega2560) Value(199) + +EnumValue +Enum(avr_mcu) String(atmega2561) Value(200) + +EnumValue +Enum(avr_mcu) String(avrxmega2) Value(201) + +EnumValue +Enum(avr_mcu) String(atxmega16a4) Value(202) + +EnumValue +Enum(avr_mcu) String(atxmega16d4) Value(203) + +EnumValue +Enum(avr_mcu) String(atxmega16x1) Value(204) + +EnumValue +Enum(avr_mcu) String(atxmega32a4) Value(205) + +EnumValue +Enum(avr_mcu) String(atxmega32d4) Value(206) + +EnumValue +Enum(avr_mcu) String(atxmega32x1) Value(207) + +EnumValue +Enum(avr_mcu) String(atmxt112sl) Value(208) + +EnumValue +Enum(avr_mcu) String(atmxt224) Value(209) + +EnumValue +Enum(avr_mcu) String(atmxt224e) Value(210) + +EnumValue +Enum(avr_mcu) String(atmxt336s) Value(211) + +EnumValue +Enum(avr_mcu) String(atxmega16a4u) Value(212) + +EnumValue +Enum(avr_mcu) String(atxmega16c4) Value(213) + +EnumValue +Enum(avr_mcu) String(atxmega32a4u) Value(214) + +EnumValue +Enum(avr_mcu) String(atxmega32c4) Value(215) + +EnumValue +Enum(avr_mcu) String(atxmega32e5) Value(216) + +EnumValue +Enum(avr_mcu) String(avrxmega4) Value(217) + +EnumValue +Enum(avr_mcu) String(atxmega64a3) Value(218) + +EnumValue +Enum(avr_mcu) String(atxmega64d3) Value(219) + +EnumValue +Enum(avr_mcu) String(atxmega64a3u) Value(220) + +EnumValue +Enum(avr_mcu) String(atxmega64a4u) Value(221) + +EnumValue +Enum(avr_mcu) String(atxmega64b1) Value(222) + +EnumValue +Enum(avr_mcu) String(atxmega64b3) Value(223) + +EnumValue +Enum(avr_mcu) String(atxmega64c3) Value(224) + +EnumValue +Enum(avr_mcu) String(atxmega64d4) Value(225) + +EnumValue +Enum(avr_mcu) String(avrxmega5) Value(226) + +EnumValue +Enum(avr_mcu) String(atxmega64a1) Value(227) + +EnumValue +Enum(avr_mcu) String(atxmega64a1u) Value(228) + +EnumValue +Enum(avr_mcu) String(avrxmega6) Value(229) + +EnumValue +Enum(avr_mcu) String(atxmega128a3) Value(230) + +EnumValue +Enum(avr_mcu) String(atxmega128d3) Value(231) + +EnumValue +Enum(avr_mcu) String(atxmega192a3) Value(232) + +EnumValue +Enum(avr_mcu) String(atxmega192d3) Value(233) + +EnumValue +Enum(avr_mcu) String(atxmega256a3) Value(234) + +EnumValue +Enum(avr_mcu) String(atxmega256a3b) Value(235) + +EnumValue +Enum(avr_mcu) String(atxmega256a3bu) Value(236) + +EnumValue +Enum(avr_mcu) String(atxmega256d3) Value(237) + +EnumValue +Enum(avr_mcu) String(atxmega128a3u) Value(238) + +EnumValue +Enum(avr_mcu) String(atxmega128b1) Value(239) + +EnumValue +Enum(avr_mcu) String(atxmega128b3) Value(240) + +EnumValue +Enum(avr_mcu) String(atxmega128c3) Value(241) + +EnumValue +Enum(avr_mcu) String(atxmega128d4) Value(242) + +EnumValue +Enum(avr_mcu) String(atmxt540s) Value(243) + +EnumValue +Enum(avr_mcu) String(atmxt540sreva) Value(244) + +EnumValue +Enum(avr_mcu) String(atxmega192a3u) Value(245) + +EnumValue +Enum(avr_mcu) String(atxmega192c3) Value(246) + +EnumValue +Enum(avr_mcu) String(atxmega256a3u) Value(247) + +EnumValue +Enum(avr_mcu) String(atxmega256c3) Value(248) + +EnumValue +Enum(avr_mcu) String(atxmega384c3) Value(249) + +EnumValue +Enum(avr_mcu) String(atxmega384d3) Value(250) + +EnumValue +Enum(avr_mcu) String(avrxmega7) Value(251) + +EnumValue +Enum(avr_mcu) String(atxmega128a1) Value(252) + +EnumValue +Enum(avr_mcu) String(atxmega128a1u) Value(253) + +EnumValue +Enum(avr_mcu) String(atxmega128a4u) Value(254) + +EnumValue +Enum(avr_mcu) String(avr1) Value(255) + +EnumValue +Enum(avr_mcu) String(at90s1200) Value(256) + +EnumValue +Enum(avr_mcu) String(attiny11) Value(257) + +EnumValue +Enum(avr_mcu) String(attiny12) Value(258) + +EnumValue +Enum(avr_mcu) String(attiny15) Value(259) + +EnumValue +Enum(avr_mcu) String(attiny28) Value(260) + diff --git a/gcc-4.8/gcc/config/avr/avr.c b/gcc-4.8/gcc/config/avr/avr.c new file mode 100644 index 000000000..c916d6bcf --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr.c @@ -0,0 +1,12250 @@ +/* Subroutines for insn-output.c for ATMEL AVR micro controllers + Copyright (C) 1998-2013 Free Software Foundation, Inc. + Contributed by Denis Chertykov (chertykov@gmail.com) + + 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 "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-attr.h" +#include "insn-codes.h" +#include "flags.h" +#include "reload.h" +#include "tree.h" +#include "output.h" +#include "expr.h" +#include "c-family/c-common.h" +#include "diagnostic-core.h" +#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" +#include "params.h" +#include "df.h" + +/* Maximal allowed offset for an address in the LD command */ +#define MAX_LD_OFFSET(MODE) (64 - (signed)GET_MODE_SIZE (MODE)) + +/* Return true if STR starts with PREFIX and false, otherwise. */ +#define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX))) + +/* The 4 bits starting at SECTION_MACH_DEP are reserved to store the + address space where data is to be located. + As the only non-generic address spaces are all located in flash, + this can be used to test if data shall go into some .progmem* section. + This must be the rightmost field of machine dependent section flags. */ +#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP) + +/* Similar 4-bit region for SYMBOL_REF_FLAGS. */ +#define AVR_SYMBOL_FLAG_PROGMEM (0xf * SYMBOL_FLAG_MACH_DEP) + +/* Similar 4-bit region in SYMBOL_REF_FLAGS: + Set address-space AS in SYMBOL_REF_FLAGS of SYM */ +#define AVR_SYMBOL_SET_ADDR_SPACE(SYM,AS) \ + do { \ + SYMBOL_REF_FLAGS (sym) &= ~AVR_SYMBOL_FLAG_PROGMEM; \ + SYMBOL_REF_FLAGS (sym) |= (AS) * SYMBOL_FLAG_MACH_DEP; \ + } while (0) + +/* Read address-space from SYMBOL_REF_FLAGS of SYM */ +#define AVR_SYMBOL_GET_ADDR_SPACE(SYM) \ + ((SYMBOL_REF_FLAGS (sym) & AVR_SYMBOL_FLAG_PROGMEM) \ + / SYMBOL_FLAG_MACH_DEP) + +/* Known address spaces. The order must be the same as in the respective + enum from avr.h (or designated initialized must be used). */ +const avr_addrspace_t avr_addrspace[ADDR_SPACE_COUNT] = +{ + { ADDR_SPACE_RAM, 0, 2, "", 0, NULL }, + { ADDR_SPACE_FLASH, 1, 2, "__flash", 0, ".progmem.data" }, + { ADDR_SPACE_FLASH1, 1, 2, "__flash1", 1, ".progmem1.data" }, + { ADDR_SPACE_FLASH2, 1, 2, "__flash2", 2, ".progmem2.data" }, + { ADDR_SPACE_FLASH3, 1, 2, "__flash3", 3, ".progmem3.data" }, + { ADDR_SPACE_FLASH4, 1, 2, "__flash4", 4, ".progmem4.data" }, + { ADDR_SPACE_FLASH5, 1, 2, "__flash5", 5, ".progmem5.data" }, + { ADDR_SPACE_MEMX, 1, 3, "__memx", 0, ".progmemx.data" }, +}; + + +/* Holding RAM addresses of some SFRs used by the compiler and that + are unique over all devices in an architecture like 'avr4'. */ + +typedef struct +{ + /* SREG: The processor status */ + int sreg; + + /* RAMPX, RAMPY, RAMPD and CCP of XMEGA */ + int ccp; + int rampd; + int rampx; + int rampy; + + /* RAMPZ: The high byte of 24-bit address used with ELPM */ + int rampz; + + /* SP: The stack pointer and its low and high byte */ + int sp_l; + int sp_h; +} avr_addr_t; + +static avr_addr_t avr_addr; + + +/* Prototypes for local helper functions. */ + +static const char* out_movqi_r_mr (rtx, rtx[], int*); +static const char* out_movhi_r_mr (rtx, rtx[], int*); +static const char* out_movsi_r_mr (rtx, rtx[], int*); +static const char* out_movqi_mr_r (rtx, rtx[], int*); +static const char* out_movhi_mr_r (rtx, rtx[], int*); +static const char* out_movsi_mr_r (rtx, rtx[], int*); + +static int get_sequence_length (rtx insns); +static int sequent_regs_live (void); +static const char *ptrreg_to_str (int); +static const char *cond_string (enum rtx_code); +static int avr_num_arg_regs (enum machine_mode, const_tree); +static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, + int, bool); +static void output_reload_in_const (rtx*, rtx, int*, bool); +static struct machine_function * avr_init_machine_status (void); + + +/* Prototypes for hook implementors if needed before their implementation. */ + +static bool avr_rtx_costs (rtx, int, int, int, int*, bool); + + +/* Allocate registers from r25 to r8 for parameters for function calls. */ +#define FIRST_CUM_REG 26 + +/* Implicit target register of LPM instruction (R0) */ +extern GTY(()) rtx lpm_reg_rtx; +rtx lpm_reg_rtx; + +/* (Implicit) address register of LPM instruction (R31:R30 = Z) */ +extern GTY(()) rtx lpm_addr_reg_rtx; +rtx lpm_addr_reg_rtx; + +/* Temporary register RTX (reg:QI TMP_REGNO) */ +extern GTY(()) rtx tmp_reg_rtx; +rtx tmp_reg_rtx; + +/* Zeroed register RTX (reg:QI ZERO_REGNO) */ +extern GTY(()) rtx zero_reg_rtx; +rtx zero_reg_rtx; + +/* RTXs for all general purpose registers as QImode */ +extern GTY(()) rtx all_regs_rtx[32]; +rtx all_regs_rtx[32]; + +/* SREG, the processor status */ +extern GTY(()) rtx sreg_rtx; +rtx sreg_rtx; + +/* RAMP* special function registers */ +extern GTY(()) rtx rampd_rtx; +extern GTY(()) rtx rampx_rtx; +extern GTY(()) rtx rampy_rtx; +extern GTY(()) rtx rampz_rtx; +rtx rampd_rtx; +rtx rampx_rtx; +rtx rampy_rtx; +rtx rampz_rtx; + +/* RTX containing the strings "" and "e", respectively */ +static GTY(()) rtx xstring_empty; +static GTY(()) rtx xstring_e; + +/* Current architecture. */ +const avr_arch_t *avr_current_arch; + +/* Current device. */ +const avr_mcu_t *avr_current_device; + +/* Section to put switch tables in. */ +static GTY(()) section *progmem_swtable_section; + +/* Unnamed sections associated to __attribute__((progmem)) aka. PROGMEM + or to address space __flash* or __memx. Only used as singletons inside + avr_asm_select_section, but it must not be local there because of GTY. */ +static GTY(()) section *progmem_section[ADDR_SPACE_COUNT]; + +/* Condition for insns/expanders from avr-dimode.md. */ +bool avr_have_dimode = true; + +/* To track if code will use .bss and/or .data. */ +bool avr_need_clear_bss_p = false; +bool avr_need_copy_data_p = false; + + +/* Transform UP into lowercase and write the result to LO. + You must provide enough space for LO. Return LO. */ + +static char* +avr_tolower (char *lo, const char *up) +{ + char *lo0 = lo; + + for (; *up; up++, lo++) + *lo = TOLOWER (*up); + + *lo = '\0'; + + return lo0; +} + + +/* Custom function to count number of set bits. */ + +static inline int +avr_popcount (unsigned int val) +{ + int pop = 0; + + while (val) + { + val &= val-1; + pop++; + } + + return pop; +} + + +/* Constraint helper function. XVAL is a CONST_INT or a CONST_DOUBLE. + Return true if the least significant N_BYTES bytes of XVAL all have a + popcount in POP_MASK and false, otherwise. POP_MASK represents a subset + of integers which contains an integer N iff bit N of POP_MASK is set. */ + +bool +avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask) +{ + int i; + + enum machine_mode mode = GET_MODE (xval); + + if (VOIDmode == mode) + mode = SImode; + + for (i = 0; i < n_bytes; i++) + { + rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i); + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + if (0 == (pop_mask & (1 << avr_popcount (val8)))) + return false; + } + + return true; +} + + +/* Access some RTX as INT_MODE. If X is a CONST_FIXED we can get + the bit representation of X by "casting" it to CONST_INT. */ + +rtx +avr_to_int_mode (rtx x) +{ + enum machine_mode mode = GET_MODE (x); + + return VOIDmode == mode + ? x + : simplify_gen_subreg (int_mode_for_mode (mode), x, mode, 0); +} + + +/* Implement `TARGET_OPTION_OVERRIDE'. */ + +static void +avr_option_override (void) +{ + flag_delete_null_pointer_checks = 0; + + /* caller-save.c looks for call-clobbered hard registers that are assigned + to pseudos that cross calls and tries so save-restore them around calls + in order to reduce the number of stack slots needed. + + This might lead to situations where reload is no more able to cope + with the challenge of AVR's very few address registers and fails to + perform the requested spills. */ + + if (avr_strict_X) + flag_caller_saves = 0; + + /* Unwind tables currently require a frame pointer for correctness, + see toplev.c:process_options(). */ + + if ((flag_unwind_tables + || flag_non_call_exceptions + || flag_asynchronous_unwind_tables) + && !ACCUMULATE_OUTGOING_ARGS) + { + flag_omit_frame_pointer = 0; + } + + avr_current_device = &avr_mcu_types[avr_mcu_index]; + avr_current_arch = &avr_arch_types[avr_current_device->arch]; + + /* RAM addresses of some SFRs common to all devices in respective arch. */ + + /* SREG: Status Register containing flags like I (global IRQ) */ + avr_addr.sreg = 0x3F + avr_current_arch->sfr_offset; + + /* RAMPZ: Address' high part when loading via ELPM */ + avr_addr.rampz = 0x3B + avr_current_arch->sfr_offset; + + avr_addr.rampy = 0x3A + avr_current_arch->sfr_offset; + avr_addr.rampx = 0x39 + avr_current_arch->sfr_offset; + avr_addr.rampd = 0x38 + avr_current_arch->sfr_offset; + avr_addr.ccp = 0x34 + avr_current_arch->sfr_offset; + + /* SP: Stack Pointer (SP_H:SP_L) */ + avr_addr.sp_l = 0x3D + avr_current_arch->sfr_offset; + avr_addr.sp_h = avr_addr.sp_l + 1; + + init_machine_status = avr_init_machine_status; + + avr_log_set_avr_log(); +} + +/* Function to set up the backend function structure. */ + +static struct machine_function * +avr_init_machine_status (void) +{ + return ggc_alloc_cleared_machine_function (); +} + + +/* Implement `INIT_EXPANDERS'. */ +/* The function works like a singleton. */ + +void +avr_init_expanders (void) +{ + int regno; + + for (regno = 0; regno < 32; regno ++) + all_regs_rtx[regno] = gen_rtx_REG (QImode, regno); + + lpm_reg_rtx = all_regs_rtx[LPM_REGNO]; + tmp_reg_rtx = all_regs_rtx[TMP_REGNO]; + zero_reg_rtx = all_regs_rtx[ZERO_REGNO]; + + lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z); + + sreg_rtx = gen_rtx_MEM (QImode, GEN_INT (avr_addr.sreg)); + rampd_rtx = gen_rtx_MEM (QImode, GEN_INT (avr_addr.rampd)); + rampx_rtx = gen_rtx_MEM (QImode, GEN_INT (avr_addr.rampx)); + rampy_rtx = gen_rtx_MEM (QImode, GEN_INT (avr_addr.rampy)); + rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (avr_addr.rampz)); + + xstring_empty = gen_rtx_CONST_STRING (VOIDmode, ""); + xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e"); +} + + +/* Implement `REGNO_REG_CLASS'. */ +/* Return register class for register R. */ + +enum reg_class +avr_regno_reg_class (int r) +{ + static const enum reg_class reg_class_tab[] = + { + R0_REG, + /* r1 - r15 */ + NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, + NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, + NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, + NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, + /* r16 - r23 */ + SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, + SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, + /* r24, r25 */ + ADDW_REGS, ADDW_REGS, + /* X: r26, 27 */ + POINTER_X_REGS, POINTER_X_REGS, + /* Y: r28, r29 */ + POINTER_Y_REGS, POINTER_Y_REGS, + /* Z: r30, r31 */ + POINTER_Z_REGS, POINTER_Z_REGS, + /* SP: SPL, SPH */ + STACK_REG, STACK_REG + }; + + if (r <= 33) + return reg_class_tab[r]; + + return ALL_REGS; +} + + +/* Implement `TARGET_SCALAR_MODE_SUPPORTED_P'. */ + +static bool +avr_scalar_mode_supported_p (enum machine_mode mode) +{ + if (ALL_FIXED_POINT_MODE_P (mode)) + return true; + + if (PSImode == mode) + return true; + + return default_scalar_mode_supported_p (mode); +} + + +/* Return TRUE if DECL is a VAR_DECL located in flash and FALSE, otherwise. */ + +static bool +avr_decl_flash_p (tree decl) +{ + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } + + return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + +/* Return TRUE if DECL is a VAR_DECL located in the 24-bit flash + address space and FALSE, otherwise. */ + +static bool +avr_decl_memx_p (tree decl) +{ + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } + + return (ADDR_SPACE_MEMX == TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + +/* Return TRUE if X is a MEM rtx located in flash and FALSE, otherwise. */ + +bool +avr_mem_flash_p (rtx x) +{ + return (MEM_P (x) + && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x))); +} + + +/* Return TRUE if X is a MEM rtx located in the 24-bit flash + address space and FALSE, otherwise. */ + +bool +avr_mem_memx_p (rtx x) +{ + return (MEM_P (x) + && ADDR_SPACE_MEMX == MEM_ADDR_SPACE (x)); +} + + +/* A helper for the subsequent function attribute used to dig for + attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ + +static inline int +avr_lookup_function_attribute1 (const_tree func, const char *name) +{ + if (FUNCTION_DECL == TREE_CODE (func)) + { + if (NULL_TREE != lookup_attribute (name, DECL_ATTRIBUTES (func))) + { + return true; + } + + func = TREE_TYPE (func); + } + + gcc_assert (TREE_CODE (func) == FUNCTION_TYPE + || TREE_CODE (func) == METHOD_TYPE); + + return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func)); +} + +/* Return nonzero if FUNC is a naked function. */ + +static int +avr_naked_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "naked"); +} + +/* Return nonzero if FUNC is an interrupt function as specified + by the "interrupt" attribute. */ + +static int +avr_interrupt_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "interrupt"); +} + +/* Return nonzero if FUNC is a signal function as specified + by the "signal" attribute. */ + +static int +avr_signal_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "signal"); +} + +/* Return nonzero if FUNC is an OS_task function. */ + +static int +avr_OS_task_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "OS_task"); +} + +/* Return nonzero if FUNC is an OS_main function. */ + +static int +avr_OS_main_function_p (tree func) +{ + return avr_lookup_function_attribute1 (func, "OS_main"); +} + + +/* Implement `TARGET_SET_CURRENT_FUNCTION'. */ +/* Sanity cheching for above function attributes. */ + +static void +avr_set_current_function (tree decl) +{ + location_t loc; + const char *isr; + + if (decl == NULL_TREE + || current_function_decl == NULL_TREE + || current_function_decl == error_mark_node + || ! cfun->machine + || cfun->machine->attributes_checked_p) + return; + + loc = DECL_SOURCE_LOCATION (decl); + + cfun->machine->is_naked = avr_naked_function_p (decl); + cfun->machine->is_signal = avr_signal_function_p (decl); + cfun->machine->is_interrupt = avr_interrupt_function_p (decl); + cfun->machine->is_OS_task = avr_OS_task_function_p (decl); + cfun->machine->is_OS_main = avr_OS_main_function_p (decl); + + isr = cfun->machine->is_interrupt ? "interrupt" : "signal"; + + /* Too much attributes make no sense as they request conflicting features. */ + + if (cfun->machine->is_OS_task + cfun->machine->is_OS_main + + (cfun->machine->is_signal || cfun->machine->is_interrupt) > 1) + error_at (loc, "function attributes %qs, %qs and %qs are mutually" + " exclusive", "OS_task", "OS_main", isr); + + /* 'naked' will hide effects of 'OS_task' and 'OS_main'. */ + + if (cfun->machine->is_naked + && (cfun->machine->is_OS_task || cfun->machine->is_OS_main)) + warning_at (loc, OPT_Wattributes, "function attributes %qs and %qs have" + " no effect on %qs function", "OS_task", "OS_main", "naked"); + + if (cfun->machine->is_interrupt || cfun->machine->is_signal) + { + tree args = TYPE_ARG_TYPES (TREE_TYPE (decl)); + tree ret = TREE_TYPE (TREE_TYPE (decl)); + const char *name = IDENTIFIER_POINTER (DECL_NAME (decl)); + + /* Silently ignore 'signal' if 'interrupt' is present. AVR-LibC startet + using this when it switched from SIGNAL and INTERRUPT to ISR. */ + + if (cfun->machine->is_interrupt) + cfun->machine->is_signal = 0; + + /* Interrupt handlers must be void __vector (void) functions. */ + + if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE) + error_at (loc, "%qs function cannot have arguments", isr); + + if (TREE_CODE (ret) != VOID_TYPE) + error_at (loc, "%qs function cannot return a value", isr); + + /* If the function has the 'signal' or 'interrupt' attribute, ensure + that the name of the function is "__vector_NN" so as to catch + when the user misspells the vector name. */ + + if (!STR_PREFIX_P (name, "__vector")) + warning_at (loc, 0, "%qs appears to be a misspelled %s handler", + name, isr); + } + + /* Don't print the above diagnostics more than once. */ + + cfun->machine->attributes_checked_p = 1; +} + + +/* Implement `ACCUMULATE_OUTGOING_ARGS'. */ + +int +avr_accumulate_outgoing_args (void) +{ + if (!cfun) + return TARGET_ACCUMULATE_OUTGOING_ARGS; + + /* FIXME: For setjmp and in avr_builtin_setjmp_frame_value we don't know + what offset is correct. In some cases it is relative to + virtual_outgoing_args_rtx and in others it is relative to + virtual_stack_vars_rtx. For example code see + gcc.c-torture/execute/built-in-setjmp.c + gcc.c-torture/execute/builtins/sprintf-chk.c */ + + return (TARGET_ACCUMULATE_OUTGOING_ARGS + && !(cfun->calls_setjmp + || cfun->has_nonlocal_label)); +} + + +/* Report contribution of accumulated outgoing arguments to stack size. */ + +static inline int +avr_outgoing_args_size (void) +{ + return ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0; +} + + +/* Implement `STARTING_FRAME_OFFSET'. */ +/* This is the offset from the frame pointer register to the first stack slot + that contains a variable living in the frame. */ + +int +avr_starting_frame_offset (void) +{ + return 1 + avr_outgoing_args_size (); +} + + +/* Return the number of hard registers to push/pop in the prologue/epilogue + of the current function, and optionally store these registers in SET. */ + +static int +avr_regs_to_save (HARD_REG_SET *set) +{ + int reg, count; + int int_or_sig_p = cfun->machine->is_interrupt || cfun->machine->is_signal; + + if (set) + CLEAR_HARD_REG_SET (*set); + count = 0; + + /* No need to save any registers if the function never returns or + has the "OS_task" or "OS_main" attribute. */ + + if (TREE_THIS_VOLATILE (current_function_decl) + || cfun->machine->is_OS_task + || cfun->machine->is_OS_main) + return 0; + + for (reg = 0; reg < 32; reg++) + { + /* Do not push/pop __tmp_reg__, __zero_reg__, as well as + any global register variables. */ + + if (fixed_regs[reg]) + continue; + + if ((int_or_sig_p && !crtl->is_leaf && call_used_regs[reg]) + || (df_regs_ever_live_p (reg) + && (int_or_sig_p || !call_used_regs[reg]) + /* Don't record frame pointer registers here. They are treated + indivitually in prologue. */ + && !(frame_pointer_needed + && (reg == REG_Y || reg == (REG_Y+1))))) + { + if (set) + SET_HARD_REG_BIT (*set, reg); + count++; + } + } + return count; +} + + +/* Implement `TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS' */ + +static bool +avr_allocate_stack_slots_for_args (void) +{ + return !cfun->machine->is_naked; +} + + +/* Return true if register FROM can be eliminated via register TO. */ + +static bool +avr_can_eliminate (const int from, const int to) +{ + return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + || (frame_pointer_needed && to == FRAME_POINTER_REGNUM) + || ((from == FRAME_POINTER_REGNUM + || from == FRAME_POINTER_REGNUM + 1) + && !frame_pointer_needed)); +} + + +/* Implement `TARGET_WARN_FUNC_RETURN'. */ + +static bool +avr_warn_func_return (tree decl) +{ + /* Naked functions are implemented entirely in assembly, including the + return sequence, so suppress warnings about this. */ + + return !avr_naked_function_p (decl); +} + +/* Compute offset between arg_pointer and frame_pointer. */ + +int +avr_initial_elimination_offset (int from, int to) +{ + if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return 0; + else + { + int offset = frame_pointer_needed ? 2 : 0; + int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2; + + offset += avr_regs_to_save (NULL); + return (get_frame_size () + avr_outgoing_args_size() + + avr_pc_size + 1 + offset); + } +} + + +/* Helper for the function below. */ + +static void +avr_adjust_type_node (tree *node, enum machine_mode mode, int sat_p) +{ + *node = make_node (FIXED_POINT_TYPE); + TYPE_SATURATING (*node) = sat_p; + TYPE_UNSIGNED (*node) = UNSIGNED_FIXED_POINT_MODE_P (mode); + TYPE_IBIT (*node) = GET_MODE_IBIT (mode); + TYPE_FBIT (*node) = GET_MODE_FBIT (mode); + TYPE_PRECISION (*node) = GET_MODE_BITSIZE (mode); + TYPE_ALIGN (*node) = 8; + SET_TYPE_MODE (*node, mode); + + layout_type (*node); +} + + +/* Implement `TARGET_BUILD_BUILTIN_VA_LIST'. */ + +static tree +avr_build_builtin_va_list (void) +{ + /* avr-modes.def adjusts [U]TA to be 64-bit modes with 48 fractional bits. + This is more appropriate for the 8-bit machine AVR than 128-bit modes. + The ADJUST_IBIT/FBIT are handled in toplev:init_adjust_machine_modes() + which is auto-generated by genmodes, but the compiler assigns [U]DAmode + to the long long accum modes instead of the desired [U]TAmode. + + Fix this now, right after node setup in tree.c:build_common_tree_nodes(). + This must run before c-cppbuiltin.c:builtin_define_fixed_point_constants() + which built-in defines macros like __ULLACCUM_FBIT__ that are used by + libgcc to detect IBIT and FBIT. */ + + avr_adjust_type_node (&ta_type_node, TAmode, 0); + avr_adjust_type_node (&uta_type_node, UTAmode, 0); + avr_adjust_type_node (&sat_ta_type_node, TAmode, 1); + avr_adjust_type_node (&sat_uta_type_node, UTAmode, 1); + + unsigned_long_long_accum_type_node = uta_type_node; + long_long_accum_type_node = ta_type_node; + sat_unsigned_long_long_accum_type_node = sat_uta_type_node; + sat_long_long_accum_type_node = sat_ta_type_node; + + /* Dispatch to the default handler. */ + + return std_build_builtin_va_list (); +} + + +/* Implement `TARGET_BUILTIN_SETJMP_FRAME_VALUE'. */ +/* Actual start of frame is virtual_stack_vars_rtx this is offset from + frame pointer by +STARTING_FRAME_OFFSET. + Using saved frame = virtual_stack_vars_rtx - STARTING_FRAME_OFFSET + avoids creating add/sub of offset in nonlocal goto and setjmp. */ + +static rtx +avr_builtin_setjmp_frame_value (void) +{ + rtx xval = gen_reg_rtx (Pmode); + emit_insn (gen_subhi3 (xval, virtual_stack_vars_rtx, + gen_int_mode (STARTING_FRAME_OFFSET, Pmode))); + return xval; +} + + +/* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3-byte PC). + This is return address of function. */ + +rtx +avr_return_addr_rtx (int count, rtx tem) +{ + rtx r; + + /* Can only return this function's return address. Others not supported. */ + if (count) + return NULL; + + if (AVR_3_BYTE_PC) + { + r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+2"); + warning (0, "% contains only 2 bytes" + " of address"); + } + else + r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+1"); + + r = gen_rtx_PLUS (Pmode, tem, r); + r = gen_frame_mem (Pmode, memory_address (Pmode, r)); + r = gen_rtx_ROTATE (HImode, r, GEN_INT (8)); + return r; +} + +/* Return 1 if the function epilogue is just a single "ret". */ + +int +avr_simple_epilogue (void) +{ + return (! frame_pointer_needed + && get_frame_size () == 0 + && avr_outgoing_args_size() == 0 + && avr_regs_to_save (NULL) == 0 + && ! cfun->machine->is_interrupt + && ! cfun->machine->is_signal + && ! cfun->machine->is_naked + && ! TREE_THIS_VOLATILE (current_function_decl)); +} + +/* This function checks sequence of live registers. */ + +static int +sequent_regs_live (void) +{ + int reg; + int live_seq = 0; + int cur_seq = 0; + + for (reg = 0; reg < 18; ++reg) + { + if (fixed_regs[reg]) + { + /* Don't recognize sequences that contain global register + variables. */ + + if (live_seq != 0) + return 0; + else + continue; + } + + if (!call_used_regs[reg]) + { + if (df_regs_ever_live_p (reg)) + { + ++live_seq; + ++cur_seq; + } + else + cur_seq = 0; + } + } + + if (!frame_pointer_needed) + { + if (df_regs_ever_live_p (REG_Y)) + { + ++live_seq; + ++cur_seq; + } + else + cur_seq = 0; + + if (df_regs_ever_live_p (REG_Y+1)) + { + ++live_seq; + ++cur_seq; + } + else + cur_seq = 0; + } + else + { + cur_seq += 2; + live_seq += 2; + } + return (cur_seq == live_seq) ? live_seq : 0; +} + +/* Obtain the length sequence of insns. */ + +int +get_sequence_length (rtx insns) +{ + rtx insn; + int length; + + for (insn = insns, length = 0; insn; insn = NEXT_INSN (insn)) + length += get_attr_length (insn); + + return length; +} + + +/* Implement `INCOMING_RETURN_ADDR_RTX'. */ + +rtx +avr_incoming_return_addr_rtx (void) +{ + /* The return address is at the top of the stack. Note that the push + was via post-decrement, which means the actual address is off by one. */ + return gen_frame_mem (HImode, plus_constant (Pmode, stack_pointer_rtx, 1)); +} + +/* Helper for expand_prologue. Emit a push of a byte register. */ + +static void +emit_push_byte (unsigned regno, bool frame_related_p) +{ + rtx mem, reg, insn; + + mem = gen_rtx_POST_DEC (HImode, stack_pointer_rtx); + mem = gen_frame_mem (QImode, mem); + reg = gen_rtx_REG (QImode, regno); + + insn = emit_insn (gen_rtx_SET (VOIDmode, mem, reg)); + if (frame_related_p) + RTX_FRAME_RELATED_P (insn) = 1; + + cfun->machine->stack_usage++; +} + + +/* Helper for expand_prologue. Emit a push of a SFR via tmp_reg. + SFR is a MEM representing the memory location of the SFR. + If CLR_P then clear the SFR after the push using zero_reg. */ + +static void +emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p) +{ + rtx insn; + + gcc_assert (MEM_P (sfr)); + + /* IN __tmp_reg__, IO(SFR) */ + insn = emit_move_insn (tmp_reg_rtx, sfr); + if (frame_related_p) + RTX_FRAME_RELATED_P (insn) = 1; + + /* PUSH __tmp_reg__ */ + emit_push_byte (TMP_REGNO, frame_related_p); + + if (clr_p) + { + /* OUT IO(SFR), __zero_reg__ */ + insn = emit_move_insn (sfr, const0_rtx); + if (frame_related_p) + RTX_FRAME_RELATED_P (insn) = 1; + } +} + +static void +avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set) +{ + rtx insn; + bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal; + int live_seq = sequent_regs_live (); + + HOST_WIDE_INT size_max + = (HOST_WIDE_INT) GET_MODE_MASK (AVR_HAVE_8BIT_SP ? QImode : Pmode); + + bool minimize = (TARGET_CALL_PROLOGUES + && size < size_max + && live_seq + && !isr_p + && !cfun->machine->is_OS_task + && !cfun->machine->is_OS_main); + + if (minimize + && (frame_pointer_needed + || avr_outgoing_args_size() > 8 + || (AVR_2_BYTE_PC && live_seq > 6) + || live_seq > 7)) + { + rtx pattern; + int first_reg, reg, offset; + + emit_move_insn (gen_rtx_REG (HImode, REG_X), + gen_int_mode (size, HImode)); + + pattern = gen_call_prologue_saves (gen_int_mode (live_seq, HImode), + gen_int_mode (live_seq+size, HImode)); + insn = emit_insn (pattern); + RTX_FRAME_RELATED_P (insn) = 1; + + /* Describe the effect of the unspec_volatile call to prologue_saves. + Note that this formulation assumes that add_reg_note pushes the + notes to the front. Thus we build them in the reverse order of + how we want dwarf2out to process them. */ + + /* The function does always set frame_pointer_rtx, but whether that + is going to be permanent in the function is frame_pointer_needed. */ + + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (VOIDmode, (frame_pointer_needed + ? frame_pointer_rtx + : stack_pointer_rtx), + plus_constant (Pmode, stack_pointer_rtx, + -(size + live_seq)))); + + /* Note that live_seq always contains r28+r29, but the other + registers to be saved are all below 18. */ + + first_reg = 18 - (live_seq - 2); + + for (reg = 29, offset = -live_seq + 1; + reg >= first_reg; + reg = (reg == 28 ? 17 : reg - 1), ++offset) + { + rtx m, r; + + m = gen_rtx_MEM (QImode, plus_constant (Pmode, stack_pointer_rtx, + offset)); + r = gen_rtx_REG (QImode, reg); + add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r)); + } + + cfun->machine->stack_usage += size + live_seq; + } + else /* !minimize */ + { + int reg; + + for (reg = 0; reg < 32; ++reg) + if (TEST_HARD_REG_BIT (set, reg)) + emit_push_byte (reg, true); + + if (frame_pointer_needed + && (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))) + { + /* Push frame pointer. Always be consistent about the + ordering of pushes -- epilogue_restores expects the + register pair to be pushed low byte first. */ + + emit_push_byte (REG_Y, true); + emit_push_byte (REG_Y + 1, true); + } + + if (frame_pointer_needed + && size == 0) + { + insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (size != 0) + { + /* Creating a frame can be done by direct manipulation of the + stack or via the frame pointer. These two methods are: + fp = sp + fp -= size + sp = fp + or + sp -= size + fp = sp (*) + the optimum method depends on function type, stack and + frame size. To avoid a complex logic, both methods are + tested and shortest is selected. + + There is also the case where SIZE != 0 and no frame pointer is + needed; this can occur if ACCUMULATE_OUTGOING_ARGS is on. + In that case, insn (*) is not needed in that case. + We use the X register as scratch. This is save because in X + is call-clobbered. + In an interrupt routine, the case of SIZE != 0 together with + !frame_pointer_needed can only occur if the function is not a + leaf function and thus X has already been saved. */ + + int irq_state = -1; + HOST_WIDE_INT size_cfa = size; + rtx fp_plus_insns, fp, my_fp; + + gcc_assert (frame_pointer_needed + || !isr_p + || !crtl->is_leaf); + + fp = my_fp = (frame_pointer_needed + ? frame_pointer_rtx + : gen_rtx_REG (Pmode, REG_X)); + + if (AVR_HAVE_8BIT_SP) + { + /* The high byte (r29) does not change: + Prefer SUBI (1 cycle) over SBIW (2 cycles, same size). */ + + my_fp = all_regs_rtx[FRAME_POINTER_REGNUM]; + } + + /* Cut down size and avoid size = 0 so that we don't run + into ICE like PR52488 in the remainder. */ + + if (size > size_max) + { + /* Don't error so that insane code from newlib still compiles + and does not break building newlib. As PR51345 is implemented + now, there are multilib variants with -msp8. + + If user wants sanity checks he can use -Wstack-usage= + or similar options. + + For CFA we emit the original, non-saturated size so that + the generic machinery is aware of the real stack usage and + will print the above diagnostic as expected. */ + + size = size_max; + } + + size = trunc_int_for_mode (size, GET_MODE (my_fp)); + + /************ Method 1: Adjust frame pointer ************/ + + start_sequence (); + + /* Normally, the dwarf2out frame-related-expr interpreter does + not expect to have the CFA change once the frame pointer is + set up. Thus, we avoid marking the move insn below and + instead indicate that the entire operation is complete after + the frame pointer subtraction is done. */ + + insn = emit_move_insn (fp, stack_pointer_rtx); + if (frame_pointer_needed) + { + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (VOIDmode, fp, stack_pointer_rtx)); + } + + insn = emit_move_insn (my_fp, plus_constant (GET_MODE (my_fp), + my_fp, -size)); + if (frame_pointer_needed) + { + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (VOIDmode, fp, + plus_constant (Pmode, fp, + -size_cfa))); + } + + /* Copy to stack pointer. Note that since we've already + changed the CFA to the frame pointer this operation + need not be annotated if frame pointer is needed. + Always move through unspec, see PR50063. + For meaning of irq_state see movhi_sp_r insn. */ + + if (cfun->machine->is_interrupt) + irq_state = 1; + + if (TARGET_NO_INTERRUPTS + || cfun->machine->is_signal + || cfun->machine->is_OS_main) + irq_state = 0; + + if (AVR_HAVE_8BIT_SP) + irq_state = 2; + + insn = emit_insn (gen_movhi_sp_r (stack_pointer_rtx, + fp, GEN_INT (irq_state))); + if (!frame_pointer_needed) + { + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, + stack_pointer_rtx, + -size_cfa))); + } + + fp_plus_insns = get_insns (); + end_sequence (); + + /************ Method 2: Adjust Stack pointer ************/ + + /* Stack adjustment by means of RCALL . and/or PUSH __TMP_REG__ + can only handle specific offsets. */ + + if (avr_sp_immediate_operand (gen_int_mode (-size, HImode), HImode)) + { + rtx sp_plus_insns; + + start_sequence (); + + insn = emit_move_insn (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + -size)); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, + stack_pointer_rtx, + -size_cfa))); + if (frame_pointer_needed) + { + insn = emit_move_insn (fp, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + + sp_plus_insns = get_insns (); + end_sequence (); + + /************ Use shortest method ************/ + + emit_insn (get_sequence_length (sp_plus_insns) + < get_sequence_length (fp_plus_insns) + ? sp_plus_insns + : fp_plus_insns); + } + else + { + emit_insn (fp_plus_insns); + } + + cfun->machine->stack_usage += size_cfa; + } /* !minimize && size != 0 */ + } /* !minimize */ +} + + +/* Output function prologue. */ + +void +avr_expand_prologue (void) +{ + HARD_REG_SET set; + HOST_WIDE_INT size; + + size = get_frame_size() + avr_outgoing_args_size(); + + cfun->machine->stack_usage = 0; + + /* Prologue: naked. */ + if (cfun->machine->is_naked) + { + return; + } + + avr_regs_to_save (&set); + + if (cfun->machine->is_interrupt || cfun->machine->is_signal) + { + /* Enable interrupts. */ + if (cfun->machine->is_interrupt) + emit_insn (gen_enable_interrupt ()); + + /* Push zero reg. */ + emit_push_byte (ZERO_REGNO, true); + + /* Push tmp reg. */ + emit_push_byte (TMP_REGNO, true); + + /* Push SREG. */ + /* ??? There's no dwarf2 column reserved for SREG. */ + emit_push_sfr (sreg_rtx, false, false /* clr */); + + /* Clear zero reg. */ + emit_move_insn (zero_reg_rtx, const0_rtx); + + /* Prevent any attempt to delete the setting of ZERO_REG! */ + emit_use (zero_reg_rtx); + + /* Push and clear RAMPD/X/Y/Z if present and low-part register is used. + ??? There are no dwarf2 columns reserved for RAMPD/X/Y/Z. */ + + if (AVR_HAVE_RAMPD) + emit_push_sfr (rampd_rtx, false /* frame-related */, true /* clr */); + + if (AVR_HAVE_RAMPX + && TEST_HARD_REG_BIT (set, REG_X) + && TEST_HARD_REG_BIT (set, REG_X + 1)) + { + emit_push_sfr (rampx_rtx, false /* frame-related */, true /* clr */); + } + + if (AVR_HAVE_RAMPY + && (frame_pointer_needed + || (TEST_HARD_REG_BIT (set, REG_Y) + && TEST_HARD_REG_BIT (set, REG_Y + 1)))) + { + emit_push_sfr (rampy_rtx, false /* frame-related */, true /* clr */); + } + + if (AVR_HAVE_RAMPZ + && TEST_HARD_REG_BIT (set, REG_Z) + && TEST_HARD_REG_BIT (set, REG_Z + 1)) + { + emit_push_sfr (rampz_rtx, false /* frame-related */, AVR_HAVE_RAMPD); + } + } /* is_interrupt is_signal */ + + avr_prologue_setup_frame (size, set); + + if (flag_stack_usage_info) + current_function_static_stack_size = cfun->machine->stack_usage; +} + + +/* Implement `TARGET_ASM_FUNCTION_END_PROLOGUE'. */ +/* Output summary at end of function prologue. */ + +static void +avr_asm_function_end_prologue (FILE *file) +{ + if (cfun->machine->is_naked) + { + fputs ("/* prologue: naked */\n", file); + } + else + { + if (cfun->machine->is_interrupt) + { + fputs ("/* prologue: Interrupt */\n", file); + } + else if (cfun->machine->is_signal) + { + fputs ("/* prologue: Signal */\n", file); + } + else + fputs ("/* prologue: function */\n", file); + } + + if (ACCUMULATE_OUTGOING_ARGS) + fprintf (file, "/* outgoing args size = %d */\n", + avr_outgoing_args_size()); + + fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n", + get_frame_size()); + fprintf (file, "/* stack size = %d */\n", + cfun->machine->stack_usage); + /* Create symbol stack offset here so all functions have it. Add 1 to stack + usage for offset so that SP + .L__stack_offset = return address. */ + fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage); +} + + +/* Implement `EPILOGUE_USES'. */ + +int +avr_epilogue_uses (int regno ATTRIBUTE_UNUSED) +{ + if (reload_completed + && cfun->machine + && (cfun->machine->is_interrupt || cfun->machine->is_signal)) + return 1; + return 0; +} + +/* Helper for avr_expand_epilogue. Emit a pop of a byte register. */ + +static void +emit_pop_byte (unsigned regno) +{ + rtx mem, reg; + + mem = gen_rtx_PRE_INC (HImode, stack_pointer_rtx); + mem = gen_frame_mem (QImode, mem); + reg = gen_rtx_REG (QImode, regno); + + emit_insn (gen_rtx_SET (VOIDmode, reg, mem)); +} + +/* Output RTL epilogue. */ + +void +avr_expand_epilogue (bool sibcall_p) +{ + int reg; + int live_seq; + HARD_REG_SET set; + int minimize; + HOST_WIDE_INT size; + bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal; + + size = get_frame_size() + avr_outgoing_args_size(); + + /* epilogue: naked */ + if (cfun->machine->is_naked) + { + gcc_assert (!sibcall_p); + + emit_jump_insn (gen_return ()); + return; + } + + avr_regs_to_save (&set); + live_seq = sequent_regs_live (); + + minimize = (TARGET_CALL_PROLOGUES + && live_seq + && !isr_p + && !cfun->machine->is_OS_task + && !cfun->machine->is_OS_main); + + if (minimize + && (live_seq > 4 + || frame_pointer_needed + || size)) + { + /* Get rid of frame. */ + + if (!frame_pointer_needed) + { + emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + } + + if (size) + { + emit_move_insn (frame_pointer_rtx, + plus_constant (Pmode, frame_pointer_rtx, size)); + } + + emit_insn (gen_epilogue_restores (gen_int_mode (live_seq, HImode))); + return; + } + + if (size) + { + /* Try two methods to adjust stack and select shortest. */ + + int irq_state = -1; + rtx fp, my_fp; + rtx fp_plus_insns; + HOST_WIDE_INT size_max; + + gcc_assert (frame_pointer_needed + || !isr_p + || !crtl->is_leaf); + + fp = my_fp = (frame_pointer_needed + ? frame_pointer_rtx + : gen_rtx_REG (Pmode, REG_X)); + + if (AVR_HAVE_8BIT_SP) + { + /* The high byte (r29) does not change: + Prefer SUBI (1 cycle) over SBIW (2 cycles). */ + + my_fp = all_regs_rtx[FRAME_POINTER_REGNUM]; + } + + /* For rationale see comment in prologue generation. */ + + size_max = (HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (my_fp)); + if (size > size_max) + size = size_max; + size = trunc_int_for_mode (size, GET_MODE (my_fp)); + + /********** Method 1: Adjust fp register **********/ + + start_sequence (); + + if (!frame_pointer_needed) + emit_move_insn (fp, stack_pointer_rtx); + + emit_move_insn (my_fp, plus_constant (GET_MODE (my_fp), my_fp, size)); + + /* Copy to stack pointer. */ + + if (TARGET_NO_INTERRUPTS) + irq_state = 0; + + if (AVR_HAVE_8BIT_SP) + irq_state = 2; + + emit_insn (gen_movhi_sp_r (stack_pointer_rtx, fp, + GEN_INT (irq_state))); + + fp_plus_insns = get_insns (); + end_sequence (); + + /********** Method 2: Adjust Stack pointer **********/ + + if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode)) + { + rtx sp_plus_insns; + + start_sequence (); + + emit_move_insn (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, size)); + + sp_plus_insns = get_insns (); + end_sequence (); + + /************ Use shortest method ************/ + + emit_insn (get_sequence_length (sp_plus_insns) + < get_sequence_length (fp_plus_insns) + ? sp_plus_insns + : fp_plus_insns); + } + else + emit_insn (fp_plus_insns); + } /* size != 0 */ + + if (frame_pointer_needed + && !(cfun->machine->is_OS_task || cfun->machine->is_OS_main)) + { + /* Restore previous frame_pointer. See avr_expand_prologue for + rationale for not using pophi. */ + + emit_pop_byte (REG_Y + 1); + emit_pop_byte (REG_Y); + } + + /* Restore used registers. */ + + for (reg = 31; reg >= 0; --reg) + if (TEST_HARD_REG_BIT (set, reg)) + emit_pop_byte (reg); + + if (isr_p) + { + /* Restore RAMPZ/Y/X/D using tmp_reg as scratch. + The conditions to restore them must be tha same as in prologue. */ + + if (AVR_HAVE_RAMPZ + && TEST_HARD_REG_BIT (set, REG_Z) + && TEST_HARD_REG_BIT (set, REG_Z + 1)) + { + emit_pop_byte (TMP_REGNO); + emit_move_insn (rampz_rtx, tmp_reg_rtx); + } + + if (AVR_HAVE_RAMPY + && (frame_pointer_needed + || (TEST_HARD_REG_BIT (set, REG_Y) + && TEST_HARD_REG_BIT (set, REG_Y + 1)))) + { + emit_pop_byte (TMP_REGNO); + emit_move_insn (rampy_rtx, tmp_reg_rtx); + } + + if (AVR_HAVE_RAMPX + && TEST_HARD_REG_BIT (set, REG_X) + && TEST_HARD_REG_BIT (set, REG_X + 1)) + { + emit_pop_byte (TMP_REGNO); + emit_move_insn (rampx_rtx, tmp_reg_rtx); + } + + if (AVR_HAVE_RAMPD) + { + emit_pop_byte (TMP_REGNO); + emit_move_insn (rampd_rtx, tmp_reg_rtx); + } + + /* Restore SREG using tmp_reg as scratch. */ + + emit_pop_byte (TMP_REGNO); + emit_move_insn (sreg_rtx, tmp_reg_rtx); + + /* Restore tmp REG. */ + emit_pop_byte (TMP_REGNO); + + /* Restore zero REG. */ + emit_pop_byte (ZERO_REGNO); + } + + if (!sibcall_p) + emit_jump_insn (gen_return ()); +} + + +/* Implement `TARGET_ASM_FUNCTION_BEGIN_EPILOGUE'. */ + +static void +avr_asm_function_begin_epilogue (FILE *file) +{ + fprintf (file, "/* epilogue start */\n"); +} + + +/* Implement `TARGET_CANNOT_MODITY_JUMPS_P'. */ + +static bool +avr_cannot_modify_jumps_p (void) +{ + + /* Naked Functions must not have any instructions after + their epilogue, see PR42240 */ + + if (reload_completed + && cfun->machine + && cfun->machine->is_naked) + { + return true; + } + + return false; +} + + +/* Implement `TARGET_MODE_DEPENDENT_ADDRESS_P'. */ + +static bool +avr_mode_dependent_address_p (const_rtx addr ATTRIBUTE_UNUSED, addr_space_t as) +{ + /* FIXME: Non-generic addresses are not mode-dependent in themselves. + This hook just serves to hack around PR rtl-optimization/52543 by + claiming that non-generic addresses were mode-dependent so that + lower-subreg.c will skip these addresses. lower-subreg.c sets up fake + RTXes to probe SET and MEM costs and assumes that MEM is always in the + generic address space which is not true. */ + + return !ADDR_SPACE_GENERIC_P (as); +} + + +/* Helper function for `avr_legitimate_address_p'. */ + +static inline bool +avr_reg_ok_for_addr_p (rtx reg, addr_space_t as, + RTX_CODE outer_code, bool strict) +{ + return (REG_P (reg) + && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), QImode, + as, outer_code, UNKNOWN) + || (!strict + && REGNO (reg) >= FIRST_PSEUDO_REGISTER))); +} + + +/* Return nonzero if X (an RTX) is a legitimate memory address on the target + machine for a memory operand of mode MODE. */ + +static bool +avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) +{ + bool ok = CONSTANT_ADDRESS_P (x); + + switch (GET_CODE (x)) + { + case REG: + ok = avr_reg_ok_for_addr_p (x, ADDR_SPACE_GENERIC, + MEM, strict); + + if (strict + && GET_MODE_SIZE (mode) > 4 + && REG_X == REGNO (x)) + { + ok = false; + } + break; + + case POST_INC: + case PRE_DEC: + ok = avr_reg_ok_for_addr_p (XEXP (x, 0), ADDR_SPACE_GENERIC, + GET_CODE (x), strict); + break; + + case PLUS: + { + rtx reg = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + + if (REG_P (reg) + && CONST_INT_P (op1) + && INTVAL (op1) >= 0) + { + bool fit = IN_RANGE (INTVAL (op1), 0, MAX_LD_OFFSET (mode)); + + if (fit) + { + ok = (! strict + || avr_reg_ok_for_addr_p (reg, ADDR_SPACE_GENERIC, + PLUS, strict)); + + if (reg == frame_pointer_rtx + || reg == arg_pointer_rtx) + { + ok = true; + } + } + else if (frame_pointer_needed + && reg == frame_pointer_rtx) + { + ok = true; + } + } + } + break; + + default: + break; + } + + if (avr_log.legitimate_address_p) + { + avr_edump ("\n%?: ret=%d, mode=%m strict=%d " + "reload_completed=%d reload_in_progress=%d %s:", + ok, mode, strict, reload_completed, reload_in_progress, + reg_renumber ? "(reg_renumber)" : ""); + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode)) + && reg_renumber) + { + avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)), + true_regnum (XEXP (x, 0))); + } + + avr_edump ("\n%r\n", x); + } + + return ok; +} + + +/* Former implementation of TARGET_LEGITIMIZE_ADDRESS, + now only a helper for avr_addr_space_legitimize_address. */ +/* Attempts to replace X with a valid + memory address for an operand of mode MODE */ + +static rtx +avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode) +{ + bool big_offset_p = false; + + x = oldx; + + if (GET_CODE (oldx) == PLUS + && REG_P (XEXP (oldx, 0))) + { + if (REG_P (XEXP (oldx, 1))) + x = force_reg (GET_MODE (oldx), oldx); + else if (CONST_INT_P (XEXP (oldx, 1))) + { + int offs = INTVAL (XEXP (oldx, 1)); + if (frame_pointer_rtx != XEXP (oldx, 0) + && offs > MAX_LD_OFFSET (mode)) + { + big_offset_p = true; + x = force_reg (GET_MODE (oldx), oldx); + } + } + } + + if (avr_log.legitimize_address) + { + avr_edump ("\n%?: mode=%m\n %r\n", mode, oldx); + + if (x != oldx) + avr_edump (" %s --> %r\n", big_offset_p ? "(big offset)" : "", x); + } + + return x; +} + + +/* Implement `LEGITIMIZE_RELOAD_ADDRESS'. */ +/* This will allow register R26/27 to be used where it is no worse than normal + base pointers R28/29 or R30/31. For example, if base offset is greater + than 63 bytes or for R++ or --R addressing. */ + +rtx +avr_legitimize_reload_address (rtx *px, enum machine_mode mode, + int opnum, int type, int addr_type, + int ind_levels ATTRIBUTE_UNUSED, + rtx (*mk_memloc)(rtx,int)) +{ + rtx x = *px; + + if (avr_log.legitimize_reload_address) + avr_edump ("\n%?:%m %r\n", mode, x); + + if (1 && (GET_CODE (x) == POST_INC + || GET_CODE (x) == PRE_DEC)) + { + push_reload (XEXP (x, 0), XEXP (x, 0), &XEXP (x, 0), &XEXP (x, 0), + POINTER_REGS, GET_MODE (x), GET_MODE (x), 0, 0, + opnum, RELOAD_OTHER); + + if (avr_log.legitimize_reload_address) + avr_edump (" RCLASS.1 = %R\n IN = %r\n OUT = %r\n", + POINTER_REGS, XEXP (x, 0), XEXP (x, 0)); + + return x; + } + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && 0 == reg_equiv_constant (REGNO (XEXP (x, 0))) + && CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 1) + { + bool fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode); + + if (fit) + { + if (reg_equiv_address (REGNO (XEXP (x, 0))) != 0) + { + int regno = REGNO (XEXP (x, 0)); + rtx mem = mk_memloc (x, regno); + + push_reload (XEXP (mem, 0), NULL_RTX, &XEXP (mem, 0), NULL, + POINTER_REGS, Pmode, VOIDmode, 0, 0, + 1, (enum reload_type) addr_type); + + if (avr_log.legitimize_reload_address) + avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n", + POINTER_REGS, XEXP (mem, 0), NULL_RTX); + + push_reload (mem, NULL_RTX, &XEXP (x, 0), NULL, + BASE_POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0, + opnum, (enum reload_type) type); + + if (avr_log.legitimize_reload_address) + avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n", + BASE_POINTER_REGS, mem, NULL_RTX); + + return x; + } + } + else if (! (frame_pointer_needed + && XEXP (x, 0) == frame_pointer_rtx)) + { + push_reload (x, NULL_RTX, px, NULL, + POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0, + opnum, (enum reload_type) type); + + if (avr_log.legitimize_reload_address) + avr_edump (" RCLASS.3 = %R\n IN = %r\n OUT = %r\n", + POINTER_REGS, x, NULL_RTX); + + return x; + } + } + + return NULL_RTX; +} + + +/* Implement `TARGET_SECONDARY_RELOAD' */ + +static reg_class_t +avr_secondary_reload (bool in_p, rtx x, + reg_class_t reload_class ATTRIBUTE_UNUSED, + enum machine_mode mode, secondary_reload_info *sri) +{ + if (in_p + && MEM_P (x) + && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)) + && ADDR_SPACE_MEMX != MEM_ADDR_SPACE (x)) + { + /* For the non-generic 16-bit spaces we need a d-class scratch. */ + + switch (mode) + { + default: + gcc_unreachable(); + + case QImode: sri->icode = CODE_FOR_reload_inqi; break; + case QQmode: sri->icode = CODE_FOR_reload_inqq; break; + case UQQmode: sri->icode = CODE_FOR_reload_inuqq; break; + + case HImode: sri->icode = CODE_FOR_reload_inhi; break; + case HQmode: sri->icode = CODE_FOR_reload_inhq; break; + case HAmode: sri->icode = CODE_FOR_reload_inha; break; + case UHQmode: sri->icode = CODE_FOR_reload_inuhq; break; + case UHAmode: sri->icode = CODE_FOR_reload_inuha; break; + + case PSImode: sri->icode = CODE_FOR_reload_inpsi; break; + + case SImode: sri->icode = CODE_FOR_reload_insi; break; + case SFmode: sri->icode = CODE_FOR_reload_insf; break; + case SQmode: sri->icode = CODE_FOR_reload_insq; break; + case SAmode: sri->icode = CODE_FOR_reload_insa; break; + case USQmode: sri->icode = CODE_FOR_reload_inusq; break; + case USAmode: sri->icode = CODE_FOR_reload_inusa; break; + } + } + + return NO_REGS; +} + + +/* Helper function to print assembler resp. track instruction + sequence lengths. Always return "". + + If PLEN == NULL: + Output assembler code from template TPL with operands supplied + by OPERANDS. This is just forwarding to output_asm_insn. + + If PLEN != NULL: + If N_WORDS >= 0 Add N_WORDS to *PLEN. + If N_WORDS < 0 Set *PLEN to -N_WORDS. + Don't output anything. +*/ + +static const char* +avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words) +{ + if (NULL == plen) + { + output_asm_insn (tpl, operands); + } + else + { + if (n_words < 0) + *plen = -n_words; + else + *plen += n_words; + } + + return ""; +} + + +/* Return a pointer register name as a string. */ + +static const char* +ptrreg_to_str (int regno) +{ + switch (regno) + { + case REG_X: return "X"; + case REG_Y: return "Y"; + case REG_Z: return "Z"; + default: + output_operand_lossage ("address operand requires constraint for" + " X, Y, or Z register"); + } + return NULL; +} + +/* Return the condition name as a string. + Used in conditional jump constructing */ + +static const char* +cond_string (enum rtx_code code) +{ + switch (code) + { + case NE: + return "ne"; + case EQ: + return "eq"; + case GE: + if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) + return "pl"; + else + return "ge"; + case LT: + if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) + return "mi"; + else + return "lt"; + case GEU: + return "sh"; + case LTU: + return "lo"; + default: + gcc_unreachable (); + } + + return ""; +} + + +/* Implement `TARGET_PRINT_OPERAND_ADDRESS'. */ +/* Output ADDR to FILE as address. */ + +static void +avr_print_operand_address (FILE *file, rtx addr) +{ + switch (GET_CODE (addr)) + { + case REG: + fprintf (file, ptrreg_to_str (REGNO (addr))); + break; + + case PRE_DEC: + fprintf (file, "-%s", ptrreg_to_str (REGNO (XEXP (addr, 0)))); + break; + + case POST_INC: + fprintf (file, "%s+", ptrreg_to_str (REGNO (XEXP (addr, 0)))); + break; + + default: + if (CONSTANT_ADDRESS_P (addr) + && text_segment_operand (addr, VOIDmode)) + { + rtx x = addr; + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT) + { + /* Assembler gs() will implant word address. Make offset + a byte offset inside gs() for assembler. This is + needed because the more logical (constant+gs(sym)) is not + accepted by gas. For 128K and smaller devices this is ok. + For large devices it will create a trampoline to offset + from symbol which may not be what the user really wanted. */ + + fprintf (file, "gs("); + output_addr_const (file, XEXP (x,0)); + fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC ")", + 2 * INTVAL (XEXP (x, 1))); + if (AVR_3_BYTE_PC) + if (warning (0, "pointer offset from symbol maybe incorrect")) + { + output_addr_const (stderr, addr); + fprintf(stderr,"\n"); + } + } + else + { + fprintf (file, "gs("); + output_addr_const (file, addr); + fprintf (file, ")"); + } + } + else + output_addr_const (file, addr); + } +} + + +/* Implement `TARGET_PRINT_OPERAND_PUNCT_VALID_P'. */ + +static bool +avr_print_operand_punct_valid_p (unsigned char code) +{ + return code == '~' || code == '!'; +} + + +/* Implement `TARGET_PRINT_OPERAND'. */ +/* Output X as assembler operand to file FILE. + For a description of supported %-codes, see top of avr.md. */ + +static void +avr_print_operand (FILE *file, rtx x, int code) +{ + int abcd = 0; + + if (code >= 'A' && code <= 'D') + abcd = code - 'A'; + + if (code == '~') + { + if (!AVR_HAVE_JMP_CALL) + fputc ('r', file); + } + else if (code == '!') + { + if (AVR_HAVE_EIJMP_EICALL) + fputc ('e', file); + } + else if (code == 't' + || code == 'T') + { + static int t_regno = -1; + static int t_nbits = -1; + + if (REG_P (x) && t_regno < 0 && code == 'T') + { + t_regno = REGNO (x); + t_nbits = GET_MODE_BITSIZE (GET_MODE (x)); + } + else if (CONST_INT_P (x) && t_regno >= 0 + && IN_RANGE (INTVAL (x), 0, t_nbits - 1)) + { + int bpos = INTVAL (x); + + fprintf (file, "%s", reg_names[t_regno + bpos / 8]); + if (code == 'T') + fprintf (file, ",%d", bpos % 8); + + t_regno = -1; + } + else + fatal_insn ("operands to %T/%t must be reg + const_int:", x); + } + else if (REG_P (x)) + { + if (x == zero_reg_rtx) + fprintf (file, "__zero_reg__"); + else if (code == 'r' && REGNO (x) < 32) + fprintf (file, "%d", (int) REGNO (x)); + else + fprintf (file, reg_names[REGNO (x) + abcd]); + } + else if (CONST_INT_P (x)) + { + HOST_WIDE_INT ival = INTVAL (x); + + if ('i' != code) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival + abcd); + else if (low_io_address_operand (x, VOIDmode) + || high_io_address_operand (x, VOIDmode)) + { + if (AVR_HAVE_RAMPZ && ival == avr_addr.rampz) + fprintf (file, "__RAMPZ__"); + else if (AVR_HAVE_RAMPY && ival == avr_addr.rampy) + fprintf (file, "__RAMPY__"); + else if (AVR_HAVE_RAMPX && ival == avr_addr.rampx) + fprintf (file, "__RAMPX__"); + else if (AVR_HAVE_RAMPD && ival == avr_addr.rampd) + fprintf (file, "__RAMPD__"); + else if (AVR_XMEGA && ival == avr_addr.ccp) + fprintf (file, "__CCP__"); + else if (ival == avr_addr.sreg) fprintf (file, "__SREG__"); + else if (ival == avr_addr.sp_l) fprintf (file, "__SP_L__"); + else if (ival == avr_addr.sp_h) fprintf (file, "__SP_H__"); + else + { + fprintf (file, HOST_WIDE_INT_PRINT_HEX, + ival - avr_current_arch->sfr_offset); + } + } + else + fatal_insn ("bad address, not an I/O address:", x); + } + else if (MEM_P (x)) + { + rtx addr = XEXP (x, 0); + + if (code == 'm') + { + if (!CONSTANT_P (addr)) + fatal_insn ("bad address, not a constant:", addr); + /* Assembler template with m-code is data - not progmem section */ + if (text_segment_operand (addr, VOIDmode)) + if (warning (0, "accessing data memory with" + " program memory address")) + { + output_addr_const (stderr, addr); + fprintf(stderr,"\n"); + } + output_addr_const (file, addr); + } + else if (code == 'i') + { + avr_print_operand (file, addr, 'i'); + } + else if (code == 'o') + { + if (GET_CODE (addr) != PLUS) + fatal_insn ("bad address, not (reg+disp):", addr); + + avr_print_operand (file, XEXP (addr, 1), 0); + } + else if (code == 'p' || code == 'r') + { + if (GET_CODE (addr) != POST_INC && GET_CODE (addr) != PRE_DEC) + fatal_insn ("bad address, not post_inc or pre_dec:", addr); + + if (code == 'p') + avr_print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */ + else + avr_print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */ + } + else if (GET_CODE (addr) == PLUS) + { + avr_print_operand_address (file, XEXP (addr,0)); + if (REGNO (XEXP (addr, 0)) == REG_X) + fatal_insn ("internal compiler error. Bad address:" + ,addr); + fputc ('+', file); + avr_print_operand (file, XEXP (addr,1), code); + } + else + avr_print_operand_address (file, addr); + } + else if (code == 'i') + { + fatal_insn ("bad address, not an I/O address:", x); + } + else if (code == 'x') + { + /* Constant progmem address - like used in jmp or call */ + if (0 == text_segment_operand (x, VOIDmode)) + if (warning (0, "accessing program memory" + " with data memory address")) + { + output_addr_const (stderr, x); + fprintf(stderr,"\n"); + } + /* Use normal symbol for direct address no linker trampoline needed */ + output_addr_const (file, x); + } + else if (CONST_FIXED_P (x)) + { + HOST_WIDE_INT ival = INTVAL (avr_to_int_mode (x)); + if (code != 0) + output_operand_lossage ("Unsupported code '%c' for fixed-point:", + code); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival); + } + else if (GET_CODE (x) == CONST_DOUBLE) + { + long val; + REAL_VALUE_TYPE rv; + if (GET_MODE (x) != SFmode) + fatal_insn ("internal compiler error. Unknown mode:", x); + REAL_VALUE_FROM_CONST_DOUBLE (rv, x); + REAL_VALUE_TO_TARGET_SINGLE (rv, val); + fprintf (file, "0x%lx", val); + } + else if (GET_CODE (x) == CONST_STRING) + fputs (XSTR (x, 0), file); + else if (code == 'j') + fputs (cond_string (GET_CODE (x)), file); + else if (code == 'k') + fputs (cond_string (reverse_condition (GET_CODE (x))), file); + else + avr_print_operand_address (file, x); +} + + +/* Worker function for `NOTICE_UPDATE_CC'. */ +/* Update the condition code in the INSN. */ + +void +avr_notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn) +{ + rtx set; + enum attr_cc cc = get_attr_cc (insn); + + switch (cc) + { + default: + break; + + case CC_PLUS: + case CC_LDI: + { + rtx *op = recog_data.operand; + int len_dummy, icc; + + /* Extract insn's operands. */ + extract_constrain_insn_cached (insn); + + switch (cc) + { + default: + gcc_unreachable(); + + case CC_PLUS: + avr_out_plus (insn, op, &len_dummy, &icc); + cc = (enum attr_cc) icc; + break; + + case CC_LDI: + + cc = (op[1] == CONST0_RTX (GET_MODE (op[0])) + && reg_overlap_mentioned_p (op[0], zero_reg_rtx)) + /* Loading zero-reg with 0 uses CLR and thus clobbers cc0. */ + ? CC_CLOBBER + /* Any other "r,rL" combination does not alter cc0. */ + : CC_NONE; + + break; + } /* inner switch */ + + break; + } + } /* outer swicth */ + + switch (cc) + { + default: + /* Special values like CC_OUT_PLUS from above have been + mapped to "standard" CC_* values so we never come here. */ + + gcc_unreachable(); + break; + + case CC_NONE: + /* Insn does not affect CC at all. */ + break; + + case CC_SET_N: + CC_STATUS_INIT; + break; + + case CC_SET_ZN: + set = single_set (insn); + CC_STATUS_INIT; + if (set) + { + cc_status.flags |= CC_NO_OVERFLOW; + cc_status.value1 = SET_DEST (set); + } + break; + + case CC_SET_CZN: + /* Insn sets the Z,N,C flags of CC to recog_operand[0]. + The V flag may or may not be known but that's ok because + alter_cond will change tests to use EQ/NE. */ + set = single_set (insn); + CC_STATUS_INIT; + if (set) + { + cc_status.value1 = SET_DEST (set); + cc_status.flags |= CC_OVERFLOW_UNUSABLE; + } + break; + + case CC_COMPARE: + set = single_set (insn); + CC_STATUS_INIT; + if (set) + cc_status.value1 = SET_SRC (set); + break; + + case CC_CLOBBER: + /* Insn doesn't leave CC in a usable state. */ + CC_STATUS_INIT; + break; + } +} + +/* Choose mode for jump insn: + 1 - relative jump in range -63 <= x <= 62 ; + 2 - relative jump in range -2046 <= x <= 2045 ; + 3 - absolute jump (only for ATmega[16]03). */ + +int +avr_jump_mode (rtx x, rtx insn) +{ + int dest_addr = INSN_ADDRESSES (INSN_UID (GET_CODE (x) == LABEL_REF + ? XEXP (x, 0) : x)); + int cur_addr = INSN_ADDRESSES (INSN_UID (insn)); + int jump_distance = cur_addr - dest_addr; + + if (-63 <= jump_distance && jump_distance <= 62) + return 1; + else if (-2046 <= jump_distance && jump_distance <= 2045) + return 2; + else if (AVR_HAVE_JMP_CALL) + return 3; + + return 2; +} + +/* Return an AVR condition jump commands. + X is a comparison RTX. + LEN is a number returned by avr_jump_mode function. + If REVERSE nonzero then condition code in X must be reversed. */ + +const char* +ret_cond_branch (rtx x, int len, int reverse) +{ + RTX_CODE cond = reverse ? reverse_condition (GET_CODE (x)) : GET_CODE (x); + + switch (cond) + { + case GT: + if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) + return (len == 1 ? ("breq .+2" CR_TAB + "brpl %0") : + len == 2 ? ("breq .+4" CR_TAB + "brmi .+2" CR_TAB + "rjmp %0") : + ("breq .+6" CR_TAB + "brmi .+4" CR_TAB + "jmp %0")); + + else + return (len == 1 ? ("breq .+2" CR_TAB + "brge %0") : + len == 2 ? ("breq .+4" CR_TAB + "brlt .+2" CR_TAB + "rjmp %0") : + ("breq .+6" CR_TAB + "brlt .+4" CR_TAB + "jmp %0")); + case GTU: + return (len == 1 ? ("breq .+2" CR_TAB + "brsh %0") : + len == 2 ? ("breq .+4" CR_TAB + "brlo .+2" CR_TAB + "rjmp %0") : + ("breq .+6" CR_TAB + "brlo .+4" CR_TAB + "jmp %0")); + case LE: + if (cc_prev_status.flags & CC_OVERFLOW_UNUSABLE) + return (len == 1 ? ("breq %0" CR_TAB + "brmi %0") : + len == 2 ? ("breq .+2" CR_TAB + "brpl .+2" CR_TAB + "rjmp %0") : + ("breq .+2" CR_TAB + "brpl .+4" CR_TAB + "jmp %0")); + else + return (len == 1 ? ("breq %0" CR_TAB + "brlt %0") : + len == 2 ? ("breq .+2" CR_TAB + "brge .+2" CR_TAB + "rjmp %0") : + ("breq .+2" CR_TAB + "brge .+4" CR_TAB + "jmp %0")); + case LEU: + return (len == 1 ? ("breq %0" CR_TAB + "brlo %0") : + len == 2 ? ("breq .+2" CR_TAB + "brsh .+2" CR_TAB + "rjmp %0") : + ("breq .+2" CR_TAB + "brsh .+4" CR_TAB + "jmp %0")); + default: + if (reverse) + { + switch (len) + { + case 1: + return "br%k1 %0"; + case 2: + return ("br%j1 .+2" CR_TAB + "rjmp %0"); + default: + return ("br%j1 .+4" CR_TAB + "jmp %0"); + } + } + else + { + switch (len) + { + case 1: + return "br%j1 %0"; + case 2: + return ("br%k1 .+2" CR_TAB + "rjmp %0"); + default: + return ("br%k1 .+4" CR_TAB + "jmp %0"); + } + } + } + return ""; +} + + +/* Worker function for `FINAL_PRESCAN_INSN'. */ +/* Output insn cost for next insn. */ + +void +avr_final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED, + int num_operands ATTRIBUTE_UNUSED) +{ + if (avr_log.rtx_costs) + { + rtx set = single_set (insn); + + if (set) + fprintf (asm_out_file, "/* DEBUG: cost = %d. */\n", + set_src_cost (SET_SRC (set), optimize_insn_for_speed_p ())); + else + fprintf (asm_out_file, "/* DEBUG: pattern-cost = %d. */\n", + rtx_cost (PATTERN (insn), INSN, 0, + optimize_insn_for_speed_p())); + } +} + +/* Return 0 if undefined, 1 if always true or always false. */ + +int +avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE op, rtx x) +{ + unsigned int max = (mode == QImode ? 0xff : + mode == HImode ? 0xffff : + mode == PSImode ? 0xffffff : + mode == SImode ? 0xffffffff : 0); + if (max && op && CONST_INT_P (x)) + { + if (unsigned_condition (op) != op) + max >>= 1; + + if (max != (INTVAL (x) & max) + && INTVAL (x) != 0xff) + return 1; + } + return 0; +} + + +/* Worker function for `FUNCTION_ARG_REGNO_P'. */ +/* Returns nonzero if REGNO is the number of a hard + register in which function arguments are sometimes passed. */ + +int +avr_function_arg_regno_p(int r) +{ + return (r >= 8 && r <= 25); +} + + +/* Worker function for `INIT_CUMULATIVE_ARGS'. */ +/* Initializing the variable cum for the state at the beginning + of the argument list. */ + +void +avr_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname, + tree fndecl ATTRIBUTE_UNUSED) +{ + cum->nregs = 18; + cum->regno = FIRST_CUM_REG; + if (!libname && stdarg_p (fntype)) + cum->nregs = 0; + + /* Assume the calle may be tail called */ + + cfun->machine->sibcall_fails = 0; +} + +/* Returns the number of registers to allocate for a function argument. */ + +static int +avr_num_arg_regs (enum machine_mode mode, const_tree type) +{ + int size; + + if (mode == BLKmode) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + /* Align all function arguments to start in even-numbered registers. + Odd-sized arguments leave holes above them. */ + + return (size + 1) & ~1; +} + + +/* Implement `TARGET_FUNCTION_ARG'. */ +/* Controls whether a function argument is passed + in a register, and which register. */ + +static rtx +avr_function_arg (cumulative_args_t cum_v, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int bytes = avr_num_arg_regs (mode, type); + + if (cum->nregs && bytes <= cum->nregs) + return gen_rtx_REG (mode, cum->regno - bytes); + + return NULL_RTX; +} + + +/* Implement `TARGET_FUNCTION_ARG_ADVANCE'. */ +/* Update the summarizer variable CUM to advance past an argument + in the argument list. */ + +static void +avr_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int bytes = avr_num_arg_regs (mode, type); + + cum->nregs -= bytes; + cum->regno -= bytes; + + /* A parameter is being passed in a call-saved register. As the original + contents of these regs has to be restored before leaving the function, + a function must not pass arguments in call-saved regs in order to get + tail-called. */ + + if (cum->regno >= 8 + && cum->nregs >= 0 + && !call_used_regs[cum->regno]) + { + /* FIXME: We ship info on failing tail-call in struct machine_function. + This uses internals of calls.c:expand_call() and the way args_so_far + is used. targetm.function_ok_for_sibcall() needs to be extended to + pass &args_so_far, too. At present, CUMULATIVE_ARGS is target + dependent so that such an extension is not wanted. */ + + cfun->machine->sibcall_fails = 1; + } + + /* Test if all registers needed by the ABI are actually available. If the + user has fixed a GPR needed to pass an argument, an (implicit) function + call will clobber that fixed register. See PR45099 for an example. */ + + if (cum->regno >= 8 + && cum->nregs >= 0) + { + int regno; + + for (regno = cum->regno; regno < cum->regno + bytes; regno++) + if (fixed_regs[regno]) + warning (0, "fixed register %s used to pass parameter to function", + reg_names[regno]); + } + + if (cum->nregs <= 0) + { + cum->nregs = 0; + cum->regno = FIRST_CUM_REG; + } +} + +/* Implement `TARGET_FUNCTION_OK_FOR_SIBCALL' */ +/* Decide whether we can make a sibling call to a function. DECL is the + declaration of the function being targeted by the call and EXP is the + CALL_EXPR representing the call. */ + +static bool +avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee) +{ + tree fntype_callee; + + /* Tail-calling must fail if callee-saved regs are used to pass + function args. We must not tail-call when `epilogue_restores' + is used. Unfortunately, we cannot tell at this point if that + actually will happen or not, and we cannot step back from + tail-calling. Thus, we inhibit tail-calling with -mcall-prologues. */ + + if (cfun->machine->sibcall_fails + || TARGET_CALL_PROLOGUES) + { + return false; + } + + fntype_callee = TREE_TYPE (CALL_EXPR_FN (exp_callee)); + + if (decl_callee) + { + decl_callee = TREE_TYPE (decl_callee); + } + else + { + decl_callee = fntype_callee; + + while (FUNCTION_TYPE != TREE_CODE (decl_callee) + && METHOD_TYPE != TREE_CODE (decl_callee)) + { + decl_callee = TREE_TYPE (decl_callee); + } + } + + /* Ensure that caller and callee have compatible epilogues */ + + if (cfun->machine->is_interrupt + || cfun->machine->is_signal + || cfun->machine->is_naked + || avr_naked_function_p (decl_callee) + /* FIXME: For OS_task and OS_main, this might be over-conservative. */ + || (avr_OS_task_function_p (decl_callee) + != cfun->machine->is_OS_task) + || (avr_OS_main_function_p (decl_callee) + != cfun->machine->is_OS_main)) + { + return false; + } + + return true; +} + +/*********************************************************************** + Functions for outputting various mov's for a various modes +************************************************************************/ + +/* Return true if a value of mode MODE is read from flash by + __load_* function from libgcc. */ + +bool +avr_load_libgcc_p (rtx op) +{ + enum machine_mode mode = GET_MODE (op); + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 2 + && !AVR_HAVE_LPMX + && avr_mem_flash_p (op)); +} + +/* Return true if a value of mode MODE is read by __xload_* function. */ + +bool +avr_xload_libgcc_p (enum machine_mode mode) +{ + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 1 + || avr_current_device->n_flash > 1); +} + + +/* Fixme: This is a hack because secondary reloads don't works as expected. + + Find an unused d-register to be used as scratch in INSN. + EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE + is a register, skip all possible return values that overlap EXCLUDE. + The policy for the returned register is similar to that of + `reg_unused_after', i.e. the returned register may overlap the SET_DEST + of INSN. + + Return a QImode d-register or NULL_RTX if nothing found. */ + +static rtx +avr_find_unused_d_reg (rtx insn, rtx exclude) +{ + int regno; + bool isr_p = (avr_interrupt_function_p (current_function_decl) + || avr_signal_function_p (current_function_decl)); + + for (regno = 16; regno < 32; regno++) + { + rtx reg = all_regs_rtx[regno]; + + if ((exclude + && reg_overlap_mentioned_p (exclude, reg)) + || fixed_regs[regno]) + { + continue; + } + + /* Try non-live register */ + + if (!df_regs_ever_live_p (regno) + && (TREE_THIS_VOLATILE (current_function_decl) + || cfun->machine->is_OS_task + || cfun->machine->is_OS_main + || (!isr_p && call_used_regs[regno]))) + { + return reg; + } + + /* Any live register can be used if it is unused after. + Prologue/epilogue will care for it as needed. */ + + if (df_regs_ever_live_p (regno) + && reg_unused_after (insn, reg)) + { + return reg; + } + } + + return NULL_RTX; +} + + +/* Helper function for the next function in the case where only restricted + version of LPM instruction is available. */ + +static const char* +avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) +{ + rtx dest = xop[0]; + rtx addr = xop[1]; + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + int regno_dest; + + regno_dest = REGNO (dest); + + /* The implicit target register of LPM. */ + xop[3] = lpm_reg_rtx; + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + avr_asm_len ("%4lpm", xop, plen, 1); + + if (regno_dest != LPM_REGNO) + avr_asm_len ("mov %0,%3", xop, plen, 1); + + return ""; + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm" CR_TAB + "push %3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "pop %A0", xop, plen, 6); + + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3", xop, plen, 5); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + + break; /* 2 */ + } + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + if (regno_dest == LPM_REGNO) + avr_asm_len ("%4lpm" CR_TAB + "adiw %2,1", xop, plen, 2); + else + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 2) + avr_asm_len ("%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 3) + avr_asm_len ("%4lpm" CR_TAB + "mov %C0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 4) + avr_asm_len ("%4lpm" CR_TAB + "mov %D0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + return ""; +} + + +/* If PLEN == NULL: Ouput instructions to load a value from a memory location + OP[1] in AS1 to register OP[0]. + If PLEN != 0 set *PLEN to the length in words of the instruction sequence. + Return "". */ + +const char* +avr_out_lpm (rtx insn, rtx *op, int *plen) +{ + rtx xop[7]; + rtx dest = op[0]; + rtx src = SET_SRC (single_set (insn)); + rtx addr; + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + int segment; + RTX_CODE code; + addr_space_t as = MEM_ADDR_SPACE (src); + + if (plen) + *plen = 0; + + if (MEM_P (dest)) + { + warning (0, "writing to address space %qs not supported", + avr_addrspace[MEM_ADDR_SPACE (dest)].name); + + return ""; + } + + addr = XEXP (src, 0); + code = GET_CODE (addr); + + gcc_assert (REG_P (dest)); + gcc_assert (REG == code || POST_INC == code); + + xop[0] = dest; + xop[1] = addr; + xop[2] = lpm_addr_reg_rtx; + xop[4] = xstring_empty; + xop[5] = tmp_reg_rtx; + xop[6] = XEXP (rampz_rtx, 0); + + segment = avr_addrspace[as].segment; + + /* Set RAMPZ as needed. */ + + if (segment) + { + xop[4] = GEN_INT (segment); + xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx); + + if (xop[3] != NULL_RTX) + { + avr_asm_len ("ldi %3,%4" CR_TAB + "out %i6,%3", xop, plen, 2); + } + else if (segment == 1) + { + avr_asm_len ("clr %5" CR_TAB + "inc %5" CR_TAB + "out %i6,%5", xop, plen, 3); + } + else + { + avr_asm_len ("mov %5,%2" CR_TAB + "ldi %2,%4" CR_TAB + "out %i6,%2" CR_TAB + "mov %2,%5", xop, plen, 4); + } + + xop[4] = xstring_e; + + if (!AVR_HAVE_ELPMX) + return avr_out_lpm_no_lpmx (insn, xop, plen); + } + else if (!AVR_HAVE_LPMX) + { + return avr_out_lpm_no_lpmx (insn, xop, plen); + } + + /* We have [E]LPMX: Output reading from Flash the comfortable way. */ + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1); + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %B0,%a2" CR_TAB + "mov %A0,%5", xop, plen, 3); + else + { + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2", xop, plen, 2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + } + + break; /* 2 */ + + case 3: + + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+" CR_TAB + "%4lpm %C0,%a2", xop, plen, 3); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,2", xop, plen, 1); + + break; /* 3 */ + + case 4: + + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+", xop, plen, 2); + + if (REGNO (dest) == REG_Z - 2) + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %C0,%a2" CR_TAB + "mov %D0,%5", xop, plen, 3); + else + { + avr_asm_len ("%4lpm %C0,%a2+" CR_TAB + "%4lpm %D0,%a2", xop, plen, 2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,3", xop, plen, 1); + } + + break; /* 4 */ + } /* n_bytes */ + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + avr_asm_len ("%4lpm %A0,%a2+", xop, plen, 1); + if (n_bytes >= 2) avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1); + if (n_bytes >= 3) avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1); + if (n_bytes >= 4) avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + if (xop[4] == xstring_e && AVR_HAVE_RAMPD) + { + /* Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM. */ + + xop[0] = zero_reg_rtx; + avr_asm_len ("out %i6,%0", xop, plen, 1); + } + + return ""; +} + + +/* Worker function for xload_8 insn. */ + +const char* +avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen) +{ + rtx xop[4]; + + xop[0] = op[0]; + xop[1] = op[1]; + xop[2] = lpm_addr_reg_rtx; + xop[3] = AVR_HAVE_LPMX ? op[0] : lpm_reg_rtx; + + if (plen) + *plen = 0; + + avr_asm_len ("sbrc %1,7" CR_TAB + "ld %3,%a2" CR_TAB + "sbrs %1,7", xop, plen, 3); + + avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, 1); + + if (REGNO (xop[0]) != REGNO (xop[3])) + avr_asm_len ("mov %0,%3", xop, plen, 1); + + return ""; +} + + +const char* +output_movqi (rtx insn, rtx operands[], int *plen) +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (avr_mem_flash_p (src) + || avr_mem_flash_p (dest)) + { + return avr_out_lpm (insn, operands, plen); + } + + gcc_assert (1 == GET_MODE_SIZE (GET_MODE (dest))); + + if (REG_P (dest)) + { + if (REG_P (src)) /* mov r,r */ + { + if (test_hard_reg_class (STACK_REG, dest)) + return avr_asm_len ("out %0,%1", operands, plen, -1); + else if (test_hard_reg_class (STACK_REG, src)) + return avr_asm_len ("in %0,%1", operands, plen, -1); + + return avr_asm_len ("mov %0,%1", operands, plen, -1); + } + else if (CONSTANT_P (src)) + { + output_reload_in_const (operands, NULL_RTX, plen, false); + return ""; + } + else if (MEM_P (src)) + return out_movqi_r_mr (insn, operands, plen); /* mov r,m */ + } + else if (MEM_P (dest)) + { + rtx xop[2]; + + xop[0] = dest; + xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src; + + return out_movqi_mr_r (insn, xop, plen); + } + + return ""; +} + + +const char * +output_movhi (rtx insn, rtx xop[], int *plen) +{ + rtx dest = xop[0]; + rtx src = xop[1]; + + gcc_assert (GET_MODE_SIZE (GET_MODE (dest)) == 2); + + if (avr_mem_flash_p (src) + || avr_mem_flash_p (dest)) + { + return avr_out_lpm (insn, xop, plen); + } + + gcc_assert (2 == GET_MODE_SIZE (GET_MODE (dest))); + + if (REG_P (dest)) + { + if (REG_P (src)) /* mov r,r */ + { + if (test_hard_reg_class (STACK_REG, dest)) + { + if (AVR_HAVE_8BIT_SP) + return avr_asm_len ("out __SP_L__,%A1", xop, plen, -1); + + if (AVR_XMEGA) + return avr_asm_len ("out __SP_L__,%A1" CR_TAB + "out __SP_H__,%B1", xop, plen, -2); + + /* Use simple load of SP if no interrupts are used. */ + + return TARGET_NO_INTERRUPTS + ? avr_asm_len ("out __SP_H__,%B1" CR_TAB + "out __SP_L__,%A1", xop, plen, -2) + : avr_asm_len ("in __tmp_reg__,__SREG__" CR_TAB + "cli" CR_TAB + "out __SP_H__,%B1" CR_TAB + "out __SREG__,__tmp_reg__" CR_TAB + "out __SP_L__,%A1", xop, plen, -5); + } + else if (test_hard_reg_class (STACK_REG, src)) + { + return !AVR_HAVE_SPH + ? avr_asm_len ("in %A0,__SP_L__" CR_TAB + "clr %B0", xop, plen, -2) + + : avr_asm_len ("in %A0,__SP_L__" CR_TAB + "in %B0,__SP_H__", xop, plen, -2); + } + + return AVR_HAVE_MOVW + ? avr_asm_len ("movw %0,%1", xop, plen, -1) + + : avr_asm_len ("mov %A0,%A1" CR_TAB + "mov %B0,%B1", xop, plen, -2); + } /* REG_P (src) */ + else if (CONSTANT_P (src)) + { + return output_reload_inhi (xop, NULL, plen); + } + else if (MEM_P (src)) + { + return out_movhi_r_mr (insn, xop, plen); /* mov r,m */ + } + } + else if (MEM_P (dest)) + { + rtx xop[2]; + + xop[0] = dest; + xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src; + + return out_movhi_mr_r (insn, xop, plen); + } + + fatal_insn ("invalid insn:", insn); + + return ""; +} + +static const char* +out_movqi_r_mr (rtx insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx x = XEXP (src, 0); + + if (CONSTANT_ADDRESS_P (x)) + { + return optimize > 0 && io_address_operand (x, QImode) + ? avr_asm_len ("in %0,%i1", op, plen, -1) + : avr_asm_len ("lds %0,%m1", op, plen, -2); + } + else if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) + { + /* memory access by reg+disp */ + + int disp = INTVAL (XEXP (x, 1)); + + if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + return avr_asm_len ("adiw r28,%o1-63" CR_TAB + "ldd %0,Y+63" CR_TAB + "sbiw r28,%o1-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %0,Y" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -5); + } + else if (REGNO (XEXP (x, 0)) == REG_X) + { + /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude + it but I have this situation with extremal optimizing options. */ + + avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %0,X", op, plen, -2); + + if (!reg_overlap_mentioned_p (dest, XEXP (x,0)) + && !reg_unused_after (insn, XEXP (x,0))) + { + avr_asm_len ("sbiw r26,%o1", op, plen, 1); + } + + return ""; + } + + return avr_asm_len ("ldd %0,%1", op, plen, -1); + } + + return avr_asm_len ("ld %0,%1", op, plen, -1); +} + +static const char* +out_movhi_r_mr (rtx insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + /* "volatile" forces reading low byte first, even if less efficient, + for correct operation with 16-bit I/O registers. */ + int mem_volatile_p = MEM_VOLATILE_P (src); + + if (reg_base > 0) + { + if (reg_dest == reg_base) /* R = (R) */ + return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB + "ld %B0,%1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -3); + + if (reg_base != REG_X) + return avr_asm_len ("ld %A0,%1" CR_TAB + "ldd %B0,%1+1", op, plen, -2); + + avr_asm_len ("ld %A0,X+" CR_TAB + "ld %B0,X", op, plen, -2); + + if (!reg_unused_after (insn, base)) + avr_asm_len ("sbiw r26,1", op, plen, 1); + + return ""; + } + else if (GET_CODE (base) == PLUS) /* (R + i) */ + { + int disp = INTVAL (XEXP (base, 1)); + int reg_base = true_regnum (XEXP (base, 0)); + + if (disp > MAX_LD_OFFSET (GET_MODE (src))) + { + if (REGNO (XEXP (base, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + return disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)) + ? avr_asm_len ("adiw r28,%o1-62" CR_TAB + "ldd %A0,Y+62" CR_TAB + "ldd %B0,Y+63" CR_TAB + "sbiw r28,%o1-62", op, plen, -4) + + : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %A0,Y" CR_TAB + "ldd %B0,Y+1" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -6); + } + + /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude + it but I have this situation with extremal + optimization options. */ + + if (reg_base == REG_X) + return reg_base == reg_dest + ? avr_asm_len ("adiw r26,%o1" CR_TAB + "ld __tmp_reg__,X+" CR_TAB + "ld %B0,X" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -4) + + : avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X" CR_TAB + "sbiw r26,%o1+1", op, plen, -4); + + return reg_base == reg_dest + ? avr_asm_len ("ldd __tmp_reg__,%A1" CR_TAB + "ldd %B0,%B1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -3) + + : avr_asm_len ("ldd %A0,%A1" CR_TAB + "ldd %B0,%B1", op, plen, -2); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + { + if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) + fatal_insn ("incorrect insn:", insn); + + if (!mem_volatile_p) + return avr_asm_len ("ld %B0,%1" CR_TAB + "ld %A0,%1", op, plen, -2); + + return REGNO (XEXP (base, 0)) == REG_X + ? avr_asm_len ("sbiw r26,2" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X" CR_TAB + "sbiw r26,1", op, plen, -4) + + : avr_asm_len ("sbiw %r1,2" CR_TAB + "ld %A0,%p1" CR_TAB + "ldd %B0,%p1+1", op, plen, -3); + } + else if (GET_CODE (base) == POST_INC) /* (R++) */ + { + if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) + fatal_insn ("incorrect insn:", insn); + + return avr_asm_len ("ld %A0,%1" CR_TAB + "ld %B0,%1", op, plen, -2); + } + else if (CONSTANT_ADDRESS_P (base)) + { + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("in %A0,%i1" CR_TAB + "in %B0,%i1+1", op, plen, -2) + + : avr_asm_len ("lds %A0,%m1" CR_TAB + "lds %B0,%m1+1", op, plen, -4); + } + + fatal_insn ("unknown move insn:",insn); + return ""; +} + +static const char* +out_movsi_r_mr (rtx insn, rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + int tmp; + + if (!l) + l = &tmp; + + if (reg_base > 0) + { + if (reg_base == REG_X) /* (R26) */ + { + if (reg_dest == REG_X) + /* "ld r26,-X" is undefined */ + return *l=7, ("adiw r26,3" CR_TAB + "ld r29,X" CR_TAB + "ld r28,-X" CR_TAB + "ld __tmp_reg__,-X" CR_TAB + "sbiw r26,1" CR_TAB + "ld r26,X" CR_TAB + "mov r27,__tmp_reg__"); + else if (reg_dest == REG_X - 2) + return *l=5, ("ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld __tmp_reg__,X+" CR_TAB + "ld %D0,X" CR_TAB + "mov %C0,__tmp_reg__"); + else if (reg_unused_after (insn, base)) + return *l=4, ("ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld %C0,X+" CR_TAB + "ld %D0,X"); + else + return *l=5, ("ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld %C0,X+" CR_TAB + "ld %D0,X" CR_TAB + "sbiw r26,3"); + } + else + { + if (reg_dest == reg_base) + return *l=5, ("ldd %D0,%1+3" CR_TAB + "ldd %C0,%1+2" CR_TAB + "ldd __tmp_reg__,%1+1" CR_TAB + "ld %A0,%1" CR_TAB + "mov %B0,__tmp_reg__"); + else if (reg_base == reg_dest + 2) + return *l=5, ("ld %A0,%1" CR_TAB + "ldd %B0,%1+1" CR_TAB + "ldd __tmp_reg__,%1+2" CR_TAB + "ldd %D0,%1+3" CR_TAB + "mov %C0,__tmp_reg__"); + else + return *l=4, ("ld %A0,%1" CR_TAB + "ldd %B0,%1+1" CR_TAB + "ldd %C0,%1+2" CR_TAB + "ldd %D0,%1+3"); + } + } + else if (GET_CODE (base) == PLUS) /* (R + i) */ + { + int disp = INTVAL (XEXP (base, 1)); + + if (disp > MAX_LD_OFFSET (GET_MODE (src))) + { + if (REGNO (XEXP (base, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + return *l = 6, ("adiw r28,%o1-60" CR_TAB + "ldd %A0,Y+60" CR_TAB + "ldd %B0,Y+61" CR_TAB + "ldd %C0,Y+62" CR_TAB + "ldd %D0,Y+63" CR_TAB + "sbiw r28,%o1-60"); + + return *l = 8, ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %A0,Y" CR_TAB + "ldd %B0,Y+1" CR_TAB + "ldd %C0,Y+2" CR_TAB + "ldd %D0,Y+3" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)"); + } + + reg_base = true_regnum (XEXP (base, 0)); + if (reg_base == REG_X) + { + /* R = (X + d) */ + if (reg_dest == REG_X) + { + *l = 7; + /* "ld r26,-X" is undefined */ + return ("adiw r26,%o1+3" CR_TAB + "ld r29,X" CR_TAB + "ld r28,-X" CR_TAB + "ld __tmp_reg__,-X" CR_TAB + "sbiw r26,1" CR_TAB + "ld r26,X" CR_TAB + "mov r27,__tmp_reg__"); + } + *l = 6; + if (reg_dest == REG_X - 2) + return ("adiw r26,%o1" CR_TAB + "ld r24,X+" CR_TAB + "ld r25,X+" CR_TAB + "ld __tmp_reg__,X+" CR_TAB + "ld r27,X" CR_TAB + "mov r26,__tmp_reg__"); + + return ("adiw r26,%o1" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld %C0,X+" CR_TAB + "ld %D0,X" CR_TAB + "sbiw r26,%o1+3"); + } + if (reg_dest == reg_base) + return *l=5, ("ldd %D0,%D1" CR_TAB + "ldd %C0,%C1" CR_TAB + "ldd __tmp_reg__,%B1" CR_TAB + "ldd %A0,%A1" CR_TAB + "mov %B0,__tmp_reg__"); + else if (reg_dest == reg_base - 2) + return *l=5, ("ldd %A0,%A1" CR_TAB + "ldd %B0,%B1" CR_TAB + "ldd __tmp_reg__,%C1" CR_TAB + "ldd %D0,%D1" CR_TAB + "mov %C0,__tmp_reg__"); + return *l=4, ("ldd %A0,%A1" CR_TAB + "ldd %B0,%B1" CR_TAB + "ldd %C0,%C1" CR_TAB + "ldd %D0,%D1"); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + return *l=4, ("ld %D0,%1" CR_TAB + "ld %C0,%1" CR_TAB + "ld %B0,%1" CR_TAB + "ld %A0,%1"); + else if (GET_CODE (base) == POST_INC) /* (R++) */ + return *l=4, ("ld %A0,%1" CR_TAB + "ld %B0,%1" CR_TAB + "ld %C0,%1" CR_TAB + "ld %D0,%1"); + else if (CONSTANT_ADDRESS_P (base)) + return *l=8, ("lds %A0,%m1" CR_TAB + "lds %B0,%m1+1" CR_TAB + "lds %C0,%m1+2" CR_TAB + "lds %D0,%m1+3"); + + fatal_insn ("unknown move insn:",insn); + return ""; +} + +static const char* +out_movsi_mr_r (rtx insn, rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + int tmp; + + if (!l) + l = &tmp; + + if (CONSTANT_ADDRESS_P (base)) + return *l=8,("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1" CR_TAB + "sts %m0+2,%C1" CR_TAB + "sts %m0+3,%D1"); + if (reg_base > 0) /* (r) */ + { + if (reg_base == REG_X) /* (R26) */ + { + if (reg_src == REG_X) + { + /* "st X+,r26" is undefined */ + if (reg_unused_after (insn, base)) + return *l=6, ("mov __tmp_reg__,r27" CR_TAB + "st X,r26" CR_TAB + "adiw r26,1" CR_TAB + "st X+,__tmp_reg__" CR_TAB + "st X+,r28" CR_TAB + "st X,r29"); + else + return *l=7, ("mov __tmp_reg__,r27" CR_TAB + "st X,r26" CR_TAB + "adiw r26,1" CR_TAB + "st X+,__tmp_reg__" CR_TAB + "st X+,r28" CR_TAB + "st X,r29" CR_TAB + "sbiw r26,3"); + } + else if (reg_base == reg_src + 2) + { + if (reg_unused_after (insn, base)) + return *l=7, ("mov __zero_reg__,%C1" CR_TAB + "mov __tmp_reg__,%D1" CR_TAB + "st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,__zero_reg__" CR_TAB + "st %0,__tmp_reg__" CR_TAB + "clr __zero_reg__"); + else + return *l=8, ("mov __zero_reg__,%C1" CR_TAB + "mov __tmp_reg__,%D1" CR_TAB + "st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,__zero_reg__" CR_TAB + "st %0,__tmp_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,3"); + } + return *l=5, ("st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,%C1" CR_TAB + "st %0,%D1" CR_TAB + "sbiw r26,3"); + } + else + return *l=4, ("st %0,%A1" CR_TAB + "std %0+1,%B1" CR_TAB + "std %0+2,%C1" CR_TAB + "std %0+3,%D1"); + } + else if (GET_CODE (base) == PLUS) /* (R + i) */ + { + int disp = INTVAL (XEXP (base, 1)); + reg_base = REGNO (XEXP (base, 0)); + if (disp > MAX_LD_OFFSET (GET_MODE (dest))) + { + if (reg_base != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) + return *l = 6, ("adiw r28,%o0-60" CR_TAB + "std Y+60,%A1" CR_TAB + "std Y+61,%B1" CR_TAB + "std Y+62,%C1" CR_TAB + "std Y+63,%D1" CR_TAB + "sbiw r28,%o0-60"); + + return *l = 8, ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%A1" CR_TAB + "std Y+1,%B1" CR_TAB + "std Y+2,%C1" CR_TAB + "std Y+3,%D1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)"); + } + if (reg_base == REG_X) + { + /* (X + d) = R */ + if (reg_src == REG_X) + { + *l = 9; + return ("mov __tmp_reg__,r26" CR_TAB + "mov __zero_reg__,r27" CR_TAB + "adiw r26,%o0" CR_TAB + "st X+,__tmp_reg__" CR_TAB + "st X+,__zero_reg__" CR_TAB + "st X+,r28" CR_TAB + "st X,r29" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,%o0+3"); + } + else if (reg_src == REG_X - 2) + { + *l = 9; + return ("mov __tmp_reg__,r26" CR_TAB + "mov __zero_reg__,r27" CR_TAB + "adiw r26,%o0" CR_TAB + "st X+,r24" CR_TAB + "st X+,r25" CR_TAB + "st X+,__tmp_reg__" CR_TAB + "st X,__zero_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,%o0+3"); + } + *l = 6; + return ("adiw r26,%o0" CR_TAB + "st X+,%A1" CR_TAB + "st X+,%B1" CR_TAB + "st X+,%C1" CR_TAB + "st X,%D1" CR_TAB + "sbiw r26,%o0+3"); + } + return *l=4, ("std %A0,%A1" CR_TAB + "std %B0,%B1" CR_TAB + "std %C0,%C1" CR_TAB + "std %D0,%D1"); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + return *l=4, ("st %0,%D1" CR_TAB + "st %0,%C1" CR_TAB + "st %0,%B1" CR_TAB + "st %0,%A1"); + else if (GET_CODE (base) == POST_INC) /* (R++) */ + return *l=4, ("st %0,%A1" CR_TAB + "st %0,%B1" CR_TAB + "st %0,%C1" CR_TAB + "st %0,%D1"); + fatal_insn ("unknown move insn:",insn); + return ""; +} + +const char * +output_movsisf (rtx insn, rtx operands[], int *l) +{ + int dummy; + rtx dest = operands[0]; + rtx src = operands[1]; + int *real_l = l; + + if (avr_mem_flash_p (src) + || avr_mem_flash_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + + if (!l) + l = &dummy; + + gcc_assert (4 == GET_MODE_SIZE (GET_MODE (dest))); + if (REG_P (dest)) + { + if (REG_P (src)) /* mov r,r */ + { + if (true_regnum (dest) > true_regnum (src)) + { + if (AVR_HAVE_MOVW) + { + *l = 2; + return ("movw %C0,%C1" CR_TAB + "movw %A0,%A1"); + } + *l = 4; + return ("mov %D0,%D1" CR_TAB + "mov %C0,%C1" CR_TAB + "mov %B0,%B1" CR_TAB + "mov %A0,%A1"); + } + else + { + if (AVR_HAVE_MOVW) + { + *l = 2; + return ("movw %A0,%A1" CR_TAB + "movw %C0,%C1"); + } + *l = 4; + return ("mov %A0,%A1" CR_TAB + "mov %B0,%B1" CR_TAB + "mov %C0,%C1" CR_TAB + "mov %D0,%D1"); + } + } + else if (CONSTANT_P (src)) + { + return output_reload_insisf (operands, NULL_RTX, real_l); + } + else if (MEM_P (src)) + return out_movsi_r_mr (insn, operands, real_l); /* mov r,m */ + } + else if (MEM_P (dest)) + { + const char *templ; + + if (src == CONST0_RTX (GET_MODE (dest))) + operands[1] = zero_reg_rtx; + + templ = out_movsi_mr_r (insn, operands, real_l); + + if (!real_l) + output_asm_insn (templ, operands); + + operands[1] = src; + return ""; + } + fatal_insn ("invalid insn:", insn); + return ""; +} + + +/* Handle loads of 24-bit types from memory to register. */ + +static const char* +avr_out_load_psi (rtx insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + + if (reg_base > 0) + { + if (reg_base == REG_X) /* (R26) */ + { + if (reg_dest == REG_X) + /* "ld r26,-X" is undefined */ + return avr_asm_len ("adiw r26,2" CR_TAB + "ld r28,X" CR_TAB + "ld __tmp_reg__,-X" CR_TAB + "sbiw r26,1" CR_TAB + "ld r26,X" CR_TAB + "mov r27,__tmp_reg__", op, plen, -6); + else + { + avr_asm_len ("ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld %C0,X", op, plen, -3); + + if (reg_dest != REG_X - 2 + && !reg_unused_after (insn, base)) + { + avr_asm_len ("sbiw r26,2", op, plen, 1); + } + + return ""; + } + } + else /* reg_base != REG_X */ + { + if (reg_dest == reg_base) + return avr_asm_len ("ldd %C0,%1+2" CR_TAB + "ldd __tmp_reg__,%1+1" CR_TAB + "ld %A0,%1" CR_TAB + "mov %B0,__tmp_reg__", op, plen, -4); + else + return avr_asm_len ("ld %A0,%1" CR_TAB + "ldd %B0,%1+1" CR_TAB + "ldd %C0,%1+2", op, plen, -3); + } + } + else if (GET_CODE (base) == PLUS) /* (R + i) */ + { + int disp = INTVAL (XEXP (base, 1)); + + if (disp > MAX_LD_OFFSET (GET_MODE (src))) + { + if (REGNO (XEXP (base, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + return avr_asm_len ("adiw r28,%o1-61" CR_TAB + "ldd %A0,Y+61" CR_TAB + "ldd %B0,Y+62" CR_TAB + "ldd %C0,Y+63" CR_TAB + "sbiw r28,%o1-61", op, plen, -5); + + return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %A0,Y" CR_TAB + "ldd %B0,Y+1" CR_TAB + "ldd %C0,Y+2" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -7); + } + + reg_base = true_regnum (XEXP (base, 0)); + if (reg_base == REG_X) + { + /* R = (X + d) */ + if (reg_dest == REG_X) + { + /* "ld r26,-X" is undefined */ + return avr_asm_len ("adiw r26,%o1+2" CR_TAB + "ld r28,X" CR_TAB + "ld __tmp_reg__,-X" CR_TAB + "sbiw r26,1" CR_TAB + "ld r26,X" CR_TAB + "mov r27,__tmp_reg__", op, plen, -6); + } + + avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %A0,X+" CR_TAB + "ld %B0,X+" CR_TAB + "ld %C0,X", op, plen, -4); + + if (reg_dest != REG_W + && !reg_unused_after (insn, XEXP (base, 0))) + avr_asm_len ("sbiw r26,%o1+2", op, plen, 1); + + return ""; + } + + if (reg_dest == reg_base) + return avr_asm_len ("ldd %C0,%C1" CR_TAB + "ldd __tmp_reg__,%B1" CR_TAB + "ldd %A0,%A1" CR_TAB + "mov %B0,__tmp_reg__", op, plen, -4); + + return avr_asm_len ("ldd %A0,%A1" CR_TAB + "ldd %B0,%B1" CR_TAB + "ldd %C0,%C1", op, plen, -3); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + return avr_asm_len ("ld %C0,%1" CR_TAB + "ld %B0,%1" CR_TAB + "ld %A0,%1", op, plen, -3); + else if (GET_CODE (base) == POST_INC) /* (R++) */ + return avr_asm_len ("ld %A0,%1" CR_TAB + "ld %B0,%1" CR_TAB + "ld %C0,%1", op, plen, -3); + + else if (CONSTANT_ADDRESS_P (base)) + return avr_asm_len ("lds %A0,%m1" CR_TAB + "lds %B0,%m1+1" CR_TAB + "lds %C0,%m1+2", op, plen , -6); + + fatal_insn ("unknown move insn:",insn); + return ""; +} + +/* Handle store of 24-bit type from register or zero to memory. */ + +static const char* +avr_out_store_psi (rtx insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + + if (CONSTANT_ADDRESS_P (base)) + return avr_asm_len ("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1" CR_TAB + "sts %m0+2,%C1", op, plen, -6); + + if (reg_base > 0) /* (r) */ + { + if (reg_base == REG_X) /* (R26) */ + { + gcc_assert (!reg_overlap_mentioned_p (base, src)); + + avr_asm_len ("st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0,%C1", op, plen, -3); + + if (!reg_unused_after (insn, base)) + avr_asm_len ("sbiw r26,2", op, plen, 1); + + return ""; + } + else + return avr_asm_len ("st %0,%A1" CR_TAB + "std %0+1,%B1" CR_TAB + "std %0+2,%C1", op, plen, -3); + } + else if (GET_CODE (base) == PLUS) /* (R + i) */ + { + int disp = INTVAL (XEXP (base, 1)); + reg_base = REGNO (XEXP (base, 0)); + + if (disp > MAX_LD_OFFSET (GET_MODE (dest))) + { + if (reg_base != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) + return avr_asm_len ("adiw r28,%o0-61" CR_TAB + "std Y+61,%A1" CR_TAB + "std Y+62,%B1" CR_TAB + "std Y+63,%C1" CR_TAB + "sbiw r28,%o0-60", op, plen, -5); + + return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%A1" CR_TAB + "std Y+1,%B1" CR_TAB + "std Y+2,%C1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -7); + } + if (reg_base == REG_X) + { + /* (X + d) = R */ + gcc_assert (!reg_overlap_mentioned_p (XEXP (base, 0), src)); + + avr_asm_len ("adiw r26,%o0" CR_TAB + "st X+,%A1" CR_TAB + "st X+,%B1" CR_TAB + "st X,%C1", op, plen, -4); + + if (!reg_unused_after (insn, XEXP (base, 0))) + avr_asm_len ("sbiw r26,%o0+2", op, plen, 1); + + return ""; + } + + return avr_asm_len ("std %A0,%A1" CR_TAB + "std %B0,%B1" CR_TAB + "std %C0,%C1", op, plen, -3); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + return avr_asm_len ("st %0,%C1" CR_TAB + "st %0,%B1" CR_TAB + "st %0,%A1", op, plen, -3); + else if (GET_CODE (base) == POST_INC) /* (R++) */ + return avr_asm_len ("st %0,%A1" CR_TAB + "st %0,%B1" CR_TAB + "st %0,%C1", op, plen, -3); + + fatal_insn ("unknown move insn:",insn); + return ""; +} + + +/* Move around 24-bit stuff. */ + +const char * +avr_out_movpsi (rtx insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + + if (avr_mem_flash_p (src) + || avr_mem_flash_p (dest)) + { + return avr_out_lpm (insn, op, plen); + } + + if (register_operand (dest, VOIDmode)) + { + if (register_operand (src, VOIDmode)) /* mov r,r */ + { + if (true_regnum (dest) > true_regnum (src)) + { + avr_asm_len ("mov %C0,%C1", op, plen, -1); + + if (AVR_HAVE_MOVW) + return avr_asm_len ("movw %A0,%A1", op, plen, 1); + else + return avr_asm_len ("mov %B0,%B1" CR_TAB + "mov %A0,%A1", op, plen, 2); + } + else + { + if (AVR_HAVE_MOVW) + avr_asm_len ("movw %A0,%A1", op, plen, -1); + else + avr_asm_len ("mov %A0,%A1" CR_TAB + "mov %B0,%B1", op, plen, -2); + + return avr_asm_len ("mov %C0,%C1", op, plen, 1); + } + } + else if (CONSTANT_P (src)) + { + return avr_out_reload_inpsi (op, NULL_RTX, plen); + } + else if (MEM_P (src)) + return avr_out_load_psi (insn, op, plen); /* mov r,m */ + } + else if (MEM_P (dest)) + { + rtx xop[2]; + + xop[0] = dest; + xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src; + + return avr_out_store_psi (insn, xop, plen); + } + + fatal_insn ("invalid insn:", insn); + return ""; +} + + +static const char* +out_movqi_mr_r (rtx insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx x = XEXP (dest, 0); + + if (CONSTANT_ADDRESS_P (x)) + { + return optimize > 0 && io_address_operand (x, QImode) + ? avr_asm_len ("out %i0,%1", op, plen, -1) + : avr_asm_len ("sts %m0,%1", op, plen, -2); + } + else if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) + { + /* memory access by reg+disp */ + + int disp = INTVAL (XEXP (x, 1)); + + if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) + return avr_asm_len ("adiw r28,%o0-63" CR_TAB + "std Y+63,%1" CR_TAB + "sbiw r28,%o0-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -5); + } + else if (REGNO (XEXP (x,0)) == REG_X) + { + if (reg_overlap_mentioned_p (src, XEXP (x, 0))) + { + avr_asm_len ("mov __tmp_reg__,%1" CR_TAB + "adiw r26,%o0" CR_TAB + "st X,__tmp_reg__", op, plen, -3); + } + else + { + avr_asm_len ("adiw r26,%o0" CR_TAB + "st X,%1", op, plen, -2); + } + + if (!reg_unused_after (insn, XEXP (x,0))) + avr_asm_len ("sbiw r26,%o0", op, plen, 1); + + return ""; + } + + return avr_asm_len ("std %0,%1", op, plen, -1); + } + + return avr_asm_len ("st %0,%1", op, plen, -1); +} + + +/* Helper for the next function for XMEGA. It does the same + but with low byte first. */ + +static const char* +avr_out_movhi_mr_r_xmega (rtx insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + + /* "volatile" forces writing low byte first, even if less efficient, + for correct operation with 16-bit I/O registers like SP. */ + int mem_volatile_p = MEM_VOLATILE_P (dest); + + if (CONSTANT_ADDRESS_P (base)) + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("out %i0,%A1" CR_TAB + "out %i0+1,%B1", op, plen, -2) + + : avr_asm_len ("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1", op, plen, -4); + + if (reg_base > 0) + { + if (reg_base != REG_X) + return avr_asm_len ("st %0,%A1" CR_TAB + "std %0+1,%B1", op, plen, -2); + + if (reg_src == REG_X) + /* "st X+,r26" and "st -X,r26" are undefined. */ + avr_asm_len ("mov __tmp_reg__,r27" CR_TAB + "st X,r26" CR_TAB + "adiw r26,1" CR_TAB + "st X,__tmp_reg__", op, plen, -4); + else + avr_asm_len ("st X+,%A1" CR_TAB + "st X,%B1", op, plen, -2); + + return reg_unused_after (insn, base) + ? "" + : avr_asm_len ("sbiw r26,1", op, plen, 1); + } + else if (GET_CODE (base) == PLUS) + { + int disp = INTVAL (XEXP (base, 1)); + reg_base = REGNO (XEXP (base, 0)); + if (disp > MAX_LD_OFFSET (GET_MODE (dest))) + { + if (reg_base != REG_Y) + fatal_insn ("incorrect insn:",insn); + + return disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)) + ? avr_asm_len ("adiw r28,%o0-62" CR_TAB + "std Y+62,%A1" CR_TAB + "std Y+63,%B1" CR_TAB + "sbiw r28,%o0-62", op, plen, -4) + + : avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%A1" CR_TAB + "std Y+1,%B1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -6); + } + + if (reg_base != REG_X) + return avr_asm_len ("std %A0,%A1" CR_TAB + "std %B0,%B1", op, plen, -2); + /* (X + d) = R */ + return reg_src == REG_X + ? avr_asm_len ("mov __tmp_reg__,r26" CR_TAB + "mov __zero_reg__,r27" CR_TAB + "adiw r26,%o0" CR_TAB + "st X+,__tmp_reg__" CR_TAB + "st X,__zero_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,%o0+1", op, plen, -7) + + : avr_asm_len ("adiw r26,%o0" CR_TAB + "st X+,%A1" CR_TAB + "st X,%B1" CR_TAB + "sbiw r26,%o0+1", op, plen, -4); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + { + if (!mem_volatile_p) + return avr_asm_len ("st %0,%B1" CR_TAB + "st %0,%A1", op, plen, -2); + + return REGNO (XEXP (base, 0)) == REG_X + ? avr_asm_len ("sbiw r26,2" CR_TAB + "st X+,%A1" CR_TAB + "st X,%B1" CR_TAB + "sbiw r26,1", op, plen, -4) + + : avr_asm_len ("sbiw %r0,2" CR_TAB + "st %p0,%A1" CR_TAB + "std %p0+1,%B1", op, plen, -3); + } + else if (GET_CODE (base) == POST_INC) /* (R++) */ + { + return avr_asm_len ("st %0,%A1" CR_TAB + "st %0,%B1", op, plen, -2); + + } + fatal_insn ("unknown move insn:",insn); + return ""; +} + + +static const char* +out_movhi_mr_r (rtx insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + int mem_volatile_p; + + /* "volatile" forces writing high-byte first (no-xmega) resp. + low-byte first (xmega) even if less efficient, for correct + operation with 16-bit I/O registers like. */ + + if (AVR_XMEGA) + return avr_out_movhi_mr_r_xmega (insn, op, plen); + + mem_volatile_p = MEM_VOLATILE_P (dest); + + if (CONSTANT_ADDRESS_P (base)) + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("out %i0+1,%B1" CR_TAB + "out %i0,%A1", op, plen, -2) + + : avr_asm_len ("sts %m0+1,%B1" CR_TAB + "sts %m0,%A1", op, plen, -4); + + if (reg_base > 0) + { + if (reg_base != REG_X) + return avr_asm_len ("std %0+1,%B1" CR_TAB + "st %0,%A1", op, plen, -2); + + if (reg_src == REG_X) + /* "st X+,r26" and "st -X,r26" are undefined. */ + return !mem_volatile_p && reg_unused_after (insn, src) + ? avr_asm_len ("mov __tmp_reg__,r27" CR_TAB + "st X,r26" CR_TAB + "adiw r26,1" CR_TAB + "st X,__tmp_reg__", op, plen, -4) + + : avr_asm_len ("mov __tmp_reg__,r27" CR_TAB + "adiw r26,1" CR_TAB + "st X,__tmp_reg__" CR_TAB + "sbiw r26,1" CR_TAB + "st X,r26", op, plen, -5); + + return !mem_volatile_p && reg_unused_after (insn, base) + ? avr_asm_len ("st X+,%A1" CR_TAB + "st X,%B1", op, plen, -2) + : avr_asm_len ("adiw r26,1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1", op, plen, -3); + } + else if (GET_CODE (base) == PLUS) + { + int disp = INTVAL (XEXP (base, 1)); + reg_base = REGNO (XEXP (base, 0)); + if (disp > MAX_LD_OFFSET (GET_MODE (dest))) + { + if (reg_base != REG_Y) + fatal_insn ("incorrect insn:",insn); + + return disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)) + ? avr_asm_len ("adiw r28,%o0-62" CR_TAB + "std Y+63,%B1" CR_TAB + "std Y+62,%A1" CR_TAB + "sbiw r28,%o0-62", op, plen, -4) + + : avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "std Y+1,%B1" CR_TAB + "st Y,%A1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -6); + } + + if (reg_base != REG_X) + return avr_asm_len ("std %B0,%B1" CR_TAB + "std %A0,%A1", op, plen, -2); + /* (X + d) = R */ + return reg_src == REG_X + ? avr_asm_len ("mov __tmp_reg__,r26" CR_TAB + "mov __zero_reg__,r27" CR_TAB + "adiw r26,%o0+1" CR_TAB + "st X,__zero_reg__" CR_TAB + "st -X,__tmp_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + "sbiw r26,%o0", op, plen, -7) + + : avr_asm_len ("adiw r26,%o0+1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1" CR_TAB + "sbiw r26,%o0", op, plen, -4); + } + else if (GET_CODE (base) == PRE_DEC) /* (--R) */ + { + return avr_asm_len ("st %0,%B1" CR_TAB + "st %0,%A1", op, plen, -2); + } + else if (GET_CODE (base) == POST_INC) /* (R++) */ + { + if (!mem_volatile_p) + return avr_asm_len ("st %0,%A1" CR_TAB + "st %0,%B1", op, plen, -2); + + return REGNO (XEXP (base, 0)) == REG_X + ? avr_asm_len ("adiw r26,1" CR_TAB + "st X,%B1" CR_TAB + "st -X,%A1" CR_TAB + "adiw r26,2", op, plen, -4) + + : avr_asm_len ("std %p0+1,%B1" CR_TAB + "st %p0,%A1" CR_TAB + "adiw %r0,2", op, plen, -3); + } + fatal_insn ("unknown move insn:",insn); + return ""; +} + +/* Return 1 if frame pointer for current function required. */ + +static bool +avr_frame_pointer_required_p (void) +{ + return (cfun->calls_alloca + || cfun->calls_setjmp + || cfun->has_nonlocal_label + || crtl->args.info.nregs == 0 + || get_frame_size () > 0); +} + +/* Returns the condition of compare insn INSN, or UNKNOWN. */ + +static RTX_CODE +compare_condition (rtx insn) +{ + rtx next = next_real_insn (insn); + + if (next && JUMP_P (next)) + { + rtx pat = PATTERN (next); + rtx src = SET_SRC (pat); + + if (IF_THEN_ELSE == GET_CODE (src)) + return GET_CODE (XEXP (src, 0)); + } + + return UNKNOWN; +} + + +/* Returns true iff INSN is a tst insn that only tests the sign. */ + +static bool +compare_sign_p (rtx insn) +{ + RTX_CODE cond = compare_condition (insn); + return (cond == GE || cond == LT); +} + + +/* Returns true iff the next insn is a JUMP_INSN with a condition + that needs to be swapped (GT, GTU, LE, LEU). */ + +static bool +compare_diff_p (rtx insn) +{ + RTX_CODE cond = compare_condition (insn); + return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0; +} + +/* Returns true iff INSN is a compare insn with the EQ or NE condition. */ + +static bool +compare_eq_p (rtx insn) +{ + RTX_CODE cond = compare_condition (insn); + return (cond == EQ || cond == NE); +} + + +/* Output compare instruction + + compare (XOP[0], XOP[1]) + + for a register XOP[0] and a compile-time constant XOP[1]. Return "". + XOP[2] is an 8-bit scratch register as needed. + + PLEN == NULL: Output instructions. + PLEN != NULL: Set *PLEN to the length (in words) of the sequence. + Don't output anything. */ + +const char* +avr_out_compare (rtx insn, rtx *xop, int *plen) +{ + /* Register to compare and value to compare against. */ + rtx xreg = xop[0]; + rtx xval = xop[1]; + + /* MODE of the comparison. */ + enum machine_mode mode; + + /* Number of bytes to operate on. */ + int i, n_bytes = GET_MODE_SIZE (GET_MODE (xreg)); + + /* Value (0..0xff) held in clobber register xop[2] or -1 if unknown. */ + int clobber_val = -1; + + /* Map fixed mode operands to integer operands with the same binary + representation. They are easier to handle in the remainder. */ + + if (CONST_FIXED_P (xval)) + { + xreg = avr_to_int_mode (xop[0]); + xval = avr_to_int_mode (xop[1]); + } + + mode = GET_MODE (xreg); + + gcc_assert (REG_P (xreg)); + gcc_assert ((CONST_INT_P (xval) && n_bytes <= 4) + || (const_double_operand (xval, VOIDmode) && n_bytes == 8)); + + if (plen) + *plen = 0; + + /* Comparisons == +/-1 and != +/-1 can be done similar to camparing + against 0 by ORing the bytes. This is one instruction shorter. + Notice that 64-bit comparisons are always against reg:ALL8 18 (ACC_A) + and therefore don't use this. */ + + if (!test_hard_reg_class (LD_REGS, xreg) + && compare_eq_p (insn) + && reg_unused_after (insn, xreg)) + { + if (xval == const1_rtx) + { + avr_asm_len ("dec %A0" CR_TAB + "or %A0,%B0", xop, plen, 2); + + if (n_bytes >= 3) + avr_asm_len ("or %A0,%C0", xop, plen, 1); + + if (n_bytes >= 4) + avr_asm_len ("or %A0,%D0", xop, plen, 1); + + return ""; + } + else if (xval == constm1_rtx) + { + if (n_bytes >= 4) + avr_asm_len ("and %A0,%D0", xop, plen, 1); + + if (n_bytes >= 3) + avr_asm_len ("and %A0,%C0", xop, plen, 1); + + return avr_asm_len ("and %A0,%B0" CR_TAB + "com %A0", xop, plen, 2); + } + } + + for (i = 0; i < n_bytes; i++) + { + /* We compare byte-wise. */ + rtx reg8 = simplify_gen_subreg (QImode, xreg, mode, i); + rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i); + + /* 8-bit value to compare with this byte. */ + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + /* Registers R16..R31 can operate with immediate. */ + bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8); + + xop[0] = reg8; + xop[1] = gen_int_mode (val8, QImode); + + /* Word registers >= R24 can use SBIW/ADIW with 0..63. */ + + if (i == 0 + && test_hard_reg_class (ADDW_REGS, reg8)) + { + int val16 = trunc_int_for_mode (INTVAL (xval), HImode); + + if (IN_RANGE (val16, 0, 63) + && (val8 == 0 + || reg_unused_after (insn, xreg))) + { + avr_asm_len ("sbiw %0,%1", xop, plen, 1); + i++; + continue; + } + + if (n_bytes == 2 + && IN_RANGE (val16, -63, -1) + && compare_eq_p (insn) + && reg_unused_after (insn, xreg)) + { + return avr_asm_len ("adiw %0,%n1", xop, plen, 1); + } + } + + /* Comparing against 0 is easy. */ + + if (val8 == 0) + { + avr_asm_len (i == 0 + ? "cp %0,__zero_reg__" + : "cpc %0,__zero_reg__", xop, plen, 1); + continue; + } + + /* Upper registers can compare and subtract-with-carry immediates. + Notice that compare instructions do the same as respective subtract + instruction; the only difference is that comparisons don't write + the result back to the target register. */ + + if (ld_reg_p) + { + if (i == 0) + { + avr_asm_len ("cpi %0,%1", xop, plen, 1); + continue; + } + else if (reg_unused_after (insn, xreg)) + { + avr_asm_len ("sbci %0,%1", xop, plen, 1); + continue; + } + } + + /* Must load the value into the scratch register. */ + + gcc_assert (REG_P (xop[2])); + + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", xop, plen, 1); + clobber_val = (int) val8; + + avr_asm_len (i == 0 + ? "cp %0,%2" + : "cpc %0,%2", xop, plen, 1); + } + + return ""; +} + + +/* Prepare operands of compare_const_di2 to be used with avr_out_compare. */ + +const char* +avr_out_compare64 (rtx insn, rtx *op, int *plen) +{ + rtx xop[3]; + + xop[0] = gen_rtx_REG (DImode, 18); + xop[1] = op[0]; + xop[2] = op[1]; + + return avr_out_compare (insn, xop, plen); +} + +/* Output test instruction for HImode. */ + +const char* +avr_out_tsthi (rtx insn, rtx *op, int *plen) +{ + if (compare_sign_p (insn)) + { + avr_asm_len ("tst %B0", op, plen, -1); + } + else if (reg_unused_after (insn, op[0]) + && compare_eq_p (insn)) + { + /* Faster than sbiw if we can clobber the operand. */ + avr_asm_len ("or %A0,%B0", op, plen, -1); + } + else + { + avr_out_compare (insn, op, plen); + } + + return ""; +} + + +/* Output test instruction for PSImode. */ + +const char* +avr_out_tstpsi (rtx insn, rtx *op, int *plen) +{ + if (compare_sign_p (insn)) + { + avr_asm_len ("tst %C0", op, plen, -1); + } + else if (reg_unused_after (insn, op[0]) + && compare_eq_p (insn)) + { + /* Faster than sbiw if we can clobber the operand. */ + avr_asm_len ("or %A0,%B0" CR_TAB + "or %A0,%C0", op, plen, -2); + } + else + { + avr_out_compare (insn, op, plen); + } + + return ""; +} + + +/* Output test instruction for SImode. */ + +const char* +avr_out_tstsi (rtx insn, rtx *op, int *plen) +{ + if (compare_sign_p (insn)) + { + avr_asm_len ("tst %D0", op, plen, -1); + } + else if (reg_unused_after (insn, op[0]) + && compare_eq_p (insn)) + { + /* Faster than sbiw if we can clobber the operand. */ + avr_asm_len ("or %A0,%B0" CR_TAB + "or %A0,%C0" CR_TAB + "or %A0,%D0", op, plen, -3); + } + else + { + avr_out_compare (insn, op, plen); + } + + return ""; +} + + +/* Generate asm equivalent for various shifts. This only handles cases + that are not already carefully hand-optimized in ?sh??i3_out. + + OPERANDS[0] resp. %0 in TEMPL is the operand to be shifted. + OPERANDS[2] is the shift count as CONST_INT, MEM or REG. + OPERANDS[3] is a QImode scratch register from LD regs if + available and SCRATCH, otherwise (no scratch available) + + TEMPL is an assembler template that shifts by one position. + T_LEN is the length of this template. */ + +void +out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], + int *plen, int t_len) +{ + bool second_label = true; + bool saved_in_tmp = false; + bool use_zero_reg = false; + rtx op[5]; + + op[0] = operands[0]; + op[1] = operands[1]; + op[2] = operands[2]; + op[3] = operands[3]; + + if (plen) + *plen = 0; + + if (CONST_INT_P (operands[2])) + { + bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL + && REG_P (operands[3])); + int count = INTVAL (operands[2]); + int max_len = 10; /* If larger than this, always use a loop. */ + + if (count <= 0) + return; + + if (count < 8 && !scratch) + use_zero_reg = true; + + if (optimize_size) + max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5)); + + if (t_len * count <= max_len) + { + /* Output shifts inline with no loop - faster. */ + + while (count-- > 0) + avr_asm_len (templ, op, plen, t_len); + + return; + } + + if (scratch) + { + avr_asm_len ("ldi %3,%2", op, plen, 1); + } + else if (use_zero_reg) + { + /* Hack to save one word: use __zero_reg__ as loop counter. + Set one bit, then shift in a loop until it is 0 again. */ + + op[3] = zero_reg_rtx; + + avr_asm_len ("set" CR_TAB + "bld %3,%2-1", op, plen, 2); + } + else + { + /* No scratch register available, use one from LD_REGS (saved in + __tmp_reg__) that doesn't overlap with registers to shift. */ + + op[3] = all_regs_rtx[((REGNO (op[0]) - 1) & 15) + 16]; + op[4] = tmp_reg_rtx; + saved_in_tmp = true; + + avr_asm_len ("mov %4,%3" CR_TAB + "ldi %3,%2", op, plen, 2); + } + + second_label = false; + } + else if (MEM_P (op[2])) + { + rtx op_mov[2]; + + op_mov[0] = op[3] = tmp_reg_rtx; + op_mov[1] = op[2]; + + out_movqi_r_mr (insn, op_mov, plen); + } + else if (register_operand (op[2], QImode)) + { + op[3] = op[2]; + + if (!reg_unused_after (insn, op[2]) + || reg_overlap_mentioned_p (op[0], op[2])) + { + op[3] = tmp_reg_rtx; + avr_asm_len ("mov %3,%2", op, plen, 1); + } + } + else + fatal_insn ("bad shift insn:", insn); + + if (second_label) + avr_asm_len ("rjmp 2f", op, plen, 1); + + avr_asm_len ("1:", op, plen, 0); + avr_asm_len (templ, op, plen, t_len); + + if (second_label) + avr_asm_len ("2:", op, plen, 0); + + avr_asm_len (use_zero_reg ? "lsr %3" : "dec %3", op, plen, 1); + avr_asm_len (second_label ? "brpl 1b" : "brne 1b", op, plen, 1); + + if (saved_in_tmp) + avr_asm_len ("mov %3,%4", op, plen, 1); +} + + +/* 8bit shift left ((char)x << i) */ + +const char * +ashlqi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 8) + break; + + *len = 1; + return "clr %0"; + + case 1: + *len = 1; + return "lsl %0"; + + case 2: + *len = 2; + return ("lsl %0" CR_TAB + "lsl %0"); + + case 3: + *len = 3; + return ("lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0"); + + case 4: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len = 2; + return ("swap %0" CR_TAB + "andi %0,0xf0"); + } + *len = 4; + return ("lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0"); + + case 5: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len = 3; + return ("swap %0" CR_TAB + "lsl %0" CR_TAB + "andi %0,0xe0"); + } + *len = 5; + return ("lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0"); + + case 6: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len = 4; + return ("swap %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "andi %0,0xc0"); + } + *len = 6; + return ("lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0" CR_TAB + "lsl %0"); + + case 7: + *len = 3; + return ("ror %0" CR_TAB + "clr %0" CR_TAB + "ror %0"); + } + } + else if (CONSTANT_P (operands[2])) + fatal_insn ("internal compiler error. Incorrect shift:", insn); + + out_shift_with_cnt ("lsl %0", + insn, operands, len, 1); + return ""; +} + + +/* 16bit shift left ((short)x << i) */ + +const char * +ashlhi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); + int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 16) + break; + + *len = 2; + return ("clr %B0" CR_TAB + "clr %A0"); + + case 4: + if (optimize_size && scratch) + break; /* 5 */ + if (ldi_ok) + { + *len = 6; + return ("swap %A0" CR_TAB + "swap %B0" CR_TAB + "andi %B0,0xf0" CR_TAB + "eor %B0,%A0" CR_TAB + "andi %A0,0xf0" CR_TAB + "eor %B0,%A0"); + } + if (scratch) + { + *len = 7; + return ("swap %A0" CR_TAB + "swap %B0" CR_TAB + "ldi %3,0xf0" CR_TAB + "and %B0,%3" CR_TAB + "eor %B0,%A0" CR_TAB + "and %A0,%3" CR_TAB + "eor %B0,%A0"); + } + break; /* optimize_size ? 6 : 8 */ + + case 5: + if (optimize_size) + break; /* scratch ? 5 : 6 */ + if (ldi_ok) + { + *len = 8; + return ("lsl %A0" CR_TAB + "rol %B0" CR_TAB + "swap %A0" CR_TAB + "swap %B0" CR_TAB + "andi %B0,0xf0" CR_TAB + "eor %B0,%A0" CR_TAB + "andi %A0,0xf0" CR_TAB + "eor %B0,%A0"); + } + if (scratch) + { + *len = 9; + return ("lsl %A0" CR_TAB + "rol %B0" CR_TAB + "swap %A0" CR_TAB + "swap %B0" CR_TAB + "ldi %3,0xf0" CR_TAB + "and %B0,%3" CR_TAB + "eor %B0,%A0" CR_TAB + "and %A0,%3" CR_TAB + "eor %B0,%A0"); + } + break; /* 10 */ + + case 6: + if (optimize_size) + break; /* scratch ? 5 : 6 */ + *len = 9; + return ("clr __tmp_reg__" CR_TAB + "lsr %B0" CR_TAB + "ror %A0" CR_TAB + "ror __tmp_reg__" CR_TAB + "lsr %B0" CR_TAB + "ror %A0" CR_TAB + "ror __tmp_reg__" CR_TAB + "mov %B0,%A0" CR_TAB + "mov %A0,__tmp_reg__"); + + case 7: + *len = 5; + return ("lsr %B0" CR_TAB + "mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "ror %B0" CR_TAB + "ror %A0"); + + case 8: + return *len = 2, ("mov %B0,%A1" CR_TAB + "clr %A0"); + + case 9: + *len = 3; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "lsl %B0"); + + case 10: + *len = 4; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0"); + + case 11: + *len = 5; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0"); + + case 12: + if (ldi_ok) + { + *len = 4; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "swap %B0" CR_TAB + "andi %B0,0xf0"); + } + if (scratch) + { + *len = 5; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "swap %B0" CR_TAB + "ldi %3,0xf0" CR_TAB + "and %B0,%3"); + } + *len = 6; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0"); + + case 13: + if (ldi_ok) + { + *len = 5; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "swap %B0" CR_TAB + "lsl %B0" CR_TAB + "andi %B0,0xe0"); + } + if (AVR_HAVE_MUL && scratch) + { + *len = 5; + return ("ldi %3,0x20" CR_TAB + "mul %A0,%3" CR_TAB + "mov %B0,r0" CR_TAB + "clr %A0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && scratch) + break; /* 5 */ + if (scratch) + { + *len = 6; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "swap %B0" CR_TAB + "lsl %B0" CR_TAB + "ldi %3,0xe0" CR_TAB + "and %B0,%3"); + } + if (AVR_HAVE_MUL) + { + *len = 6; + return ("set" CR_TAB + "bld r1,5" CR_TAB + "mul %A0,r1" CR_TAB + "mov %B0,r0" CR_TAB + "clr %A0" CR_TAB + "clr __zero_reg__"); + } + *len = 7; + return ("mov %B0,%A0" CR_TAB + "clr %A0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0" CR_TAB + "lsl %B0"); + + case 14: + if (AVR_HAVE_MUL && ldi_ok) + { + *len = 5; + return ("ldi %B0,0x40" CR_TAB + "mul %A0,%B0" CR_TAB + "mov %B0,r0" CR_TAB + "clr %A0" CR_TAB + "clr __zero_reg__"); + } + if (AVR_HAVE_MUL && scratch) + { + *len = 5; + return ("ldi %3,0x40" CR_TAB + "mul %A0,%3" CR_TAB + "mov %B0,r0" CR_TAB + "clr %A0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && ldi_ok) + { + *len = 5; + return ("mov %B0,%A0" CR_TAB + "ldi %A0,6" "\n1:\t" + "lsl %B0" CR_TAB + "dec %A0" CR_TAB + "brne 1b"); + } + if (optimize_size && scratch) + break; /* 5 */ + *len = 6; + return ("clr %B0" CR_TAB + "lsr %A0" CR_TAB + "ror %B0" CR_TAB + "lsr %A0" CR_TAB + "ror %B0" CR_TAB + "clr %A0"); + + case 15: + *len = 4; + return ("clr %B0" CR_TAB + "lsr %A0" CR_TAB + "ror %B0" CR_TAB + "clr %A0"); + } + len = t; + } + out_shift_with_cnt ("lsl %A0" CR_TAB + "rol %B0", insn, operands, len, 2); + return ""; +} + + +/* 24-bit shift left */ + +const char* +avr_out_ashlpsi3 (rtx insn, rtx *op, int *plen) +{ + if (plen) + *plen = 0; + + if (CONST_INT_P (op[2])) + { + switch (INTVAL (op[2])) + { + default: + if (INTVAL (op[2]) < 24) + break; + + return avr_asm_len ("clr %A0" CR_TAB + "clr %B0" CR_TAB + "clr %C0", op, plen, 3); + + case 8: + { + int reg0 = REGNO (op[0]); + int reg1 = REGNO (op[1]); + + if (reg0 >= reg1) + return avr_asm_len ("mov %C0,%B1" CR_TAB + "mov %B0,%A1" CR_TAB + "clr %A0", op, plen, 3); + else + return avr_asm_len ("clr %A0" CR_TAB + "mov %B0,%A1" CR_TAB + "mov %C0,%B1", op, plen, 3); + } + + case 16: + { + int reg0 = REGNO (op[0]); + int reg1 = REGNO (op[1]); + + if (reg0 + 2 != reg1) + avr_asm_len ("mov %C0,%A0", op, plen, 1); + + return avr_asm_len ("clr %B0" CR_TAB + "clr %A0", op, plen, 2); + } + + case 23: + return avr_asm_len ("clr %C0" CR_TAB + "lsr %A0" CR_TAB + "ror %C0" CR_TAB + "clr %B0" CR_TAB + "clr %A0", op, plen, 5); + } + } + + out_shift_with_cnt ("lsl %A0" CR_TAB + "rol %B0" CR_TAB + "rol %C0", insn, op, plen, 3); + return ""; +} + + +/* 32bit shift left ((long)x << i) */ + +const char * +ashlsi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 32) + break; + + if (AVR_HAVE_MOVW) + return *len = 3, ("clr %D0" CR_TAB + "clr %C0" CR_TAB + "movw %A0,%C0"); + *len = 4; + return ("clr %D0" CR_TAB + "clr %C0" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + + case 8: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + *len = 4; + if (reg0 >= reg1) + return ("mov %D0,%C1" CR_TAB + "mov %C0,%B1" CR_TAB + "mov %B0,%A1" CR_TAB + "clr %A0"); + else + return ("clr %A0" CR_TAB + "mov %B0,%A1" CR_TAB + "mov %C0,%B1" CR_TAB + "mov %D0,%C1"); + } + + case 16: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + if (reg0 + 2 == reg1) + return *len = 2, ("clr %B0" CR_TAB + "clr %A0"); + if (AVR_HAVE_MOVW) + return *len = 3, ("movw %C0,%A1" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + else + return *len = 4, ("mov %C0,%A1" CR_TAB + "mov %D0,%B1" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + } + + case 24: + *len = 4; + return ("mov %D0,%A1" CR_TAB + "clr %C0" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + + case 31: + *len = 6; + return ("clr %D0" CR_TAB + "lsr %A0" CR_TAB + "ror %D0" CR_TAB + "clr %C0" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + } + len = t; + } + out_shift_with_cnt ("lsl %A0" CR_TAB + "rol %B0" CR_TAB + "rol %C0" CR_TAB + "rol %D0", insn, operands, len, 4); + return ""; +} + +/* 8bit arithmetic shift right ((signed char)x >> i) */ + +const char * +ashrqi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + case 1: + *len = 1; + return "asr %0"; + + case 2: + *len = 2; + return ("asr %0" CR_TAB + "asr %0"); + + case 3: + *len = 3; + return ("asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0"); + + case 4: + *len = 4; + return ("asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0"); + + case 5: + *len = 5; + return ("asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0" CR_TAB + "asr %0"); + + case 6: + *len = 4; + return ("bst %0,6" CR_TAB + "lsl %0" CR_TAB + "sbc %0,%0" CR_TAB + "bld %0,0"); + + default: + if (INTVAL (operands[2]) < 8) + break; + + /* fall through */ + + case 7: + *len = 2; + return ("lsl %0" CR_TAB + "sbc %0,%0"); + } + } + else if (CONSTANT_P (operands[2])) + fatal_insn ("internal compiler error. Incorrect shift:", insn); + + out_shift_with_cnt ("asr %0", + insn, operands, len, 1); + return ""; +} + + +/* 16bit arithmetic shift right ((signed short)x >> i) */ + +const char * +ashrhi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); + int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + case 4: + case 5: + /* XXX try to optimize this too? */ + break; + + case 6: + if (optimize_size) + break; /* scratch ? 5 : 6 */ + *len = 8; + return ("mov __tmp_reg__,%A0" CR_TAB + "mov %A0,%B0" CR_TAB + "lsl __tmp_reg__" CR_TAB + "rol %A0" CR_TAB + "sbc %B0,%B0" CR_TAB + "lsl __tmp_reg__" CR_TAB + "rol %A0" CR_TAB + "rol %B0"); + + case 7: + *len = 4; + return ("lsl %A0" CR_TAB + "mov %A0,%B0" CR_TAB + "rol %A0" CR_TAB + "sbc %B0,%B0"); + + case 8: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + + if (reg0 == reg1) + return *len = 3, ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0"); + else + return *len = 4, ("mov %A0,%B1" CR_TAB + "clr %B0" CR_TAB + "sbrc %A0,7" CR_TAB + "dec %B0"); + } + + case 9: + *len = 4; + return ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0" CR_TAB + "asr %A0"); + + case 10: + *len = 5; + return ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0" CR_TAB + "asr %A0" CR_TAB + "asr %A0"); + + case 11: + if (AVR_HAVE_MUL && ldi_ok) + { + *len = 5; + return ("ldi %A0,0x20" CR_TAB + "muls %B0,%A0" CR_TAB + "mov %A0,r1" CR_TAB + "sbc %B0,%B0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && scratch) + break; /* 5 */ + *len = 6; + return ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0"); + + case 12: + if (AVR_HAVE_MUL && ldi_ok) + { + *len = 5; + return ("ldi %A0,0x10" CR_TAB + "muls %B0,%A0" CR_TAB + "mov %A0,r1" CR_TAB + "sbc %B0,%B0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && scratch) + break; /* 5 */ + *len = 7; + return ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0"); + + case 13: + if (AVR_HAVE_MUL && ldi_ok) + { + *len = 5; + return ("ldi %A0,0x08" CR_TAB + "muls %B0,%A0" CR_TAB + "mov %A0,r1" CR_TAB + "sbc %B0,%B0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size) + break; /* scratch ? 5 : 7 */ + *len = 8; + return ("mov %A0,%B0" CR_TAB + "lsl %B0" CR_TAB + "sbc %B0,%B0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0" CR_TAB + "asr %A0"); + + case 14: + *len = 5; + return ("lsl %B0" CR_TAB + "sbc %A0,%A0" CR_TAB + "lsl %B0" CR_TAB + "mov %B0,%A0" CR_TAB + "rol %A0"); + + default: + if (INTVAL (operands[2]) < 16) + break; + + /* fall through */ + + case 15: + return *len = 3, ("lsl %B0" CR_TAB + "sbc %A0,%A0" CR_TAB + "mov %B0,%A0"); + } + len = t; + } + out_shift_with_cnt ("asr %B0" CR_TAB + "ror %A0", insn, operands, len, 2); + return ""; +} + + +/* 24-bit arithmetic shift right */ + +const char* +avr_out_ashrpsi3 (rtx insn, rtx *op, int *plen) +{ + int dest = REGNO (op[0]); + int src = REGNO (op[1]); + + if (CONST_INT_P (op[2])) + { + if (plen) + *plen = 0; + + switch (INTVAL (op[2])) + { + case 8: + if (dest <= src) + return avr_asm_len ("mov %A0,%B1" CR_TAB + "mov %B0,%C1" CR_TAB + "clr %C0" CR_TAB + "sbrc %B0,7" CR_TAB + "dec %C0", op, plen, 5); + else + return avr_asm_len ("clr %C0" CR_TAB + "sbrc %C1,7" CR_TAB + "dec %C0" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %A0,%B1", op, plen, 5); + + case 16: + if (dest != src + 2) + avr_asm_len ("mov %A0,%C1", op, plen, 1); + + return avr_asm_len ("clr %B0" CR_TAB + "sbrc %A0,7" CR_TAB + "com %B0" CR_TAB + "mov %C0,%B0", op, plen, 4); + + default: + if (INTVAL (op[2]) < 24) + break; + + /* fall through */ + + case 23: + return avr_asm_len ("lsl %C0" CR_TAB + "sbc %A0,%A0" CR_TAB + "mov %B0,%A0" CR_TAB + "mov %C0,%A0", op, plen, 4); + } /* switch */ + } + + out_shift_with_cnt ("asr %C0" CR_TAB + "ror %B0" CR_TAB + "ror %A0", insn, op, plen, 3); + return ""; +} + + +/* 32-bit arithmetic shift right ((signed long)x >> i) */ + +const char * +ashrsi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + case 8: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + *len=6; + if (reg0 <= reg1) + return ("mov %A0,%B1" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %C0,%D1" CR_TAB + "clr %D0" CR_TAB + "sbrc %C0,7" CR_TAB + "dec %D0"); + else + return ("clr %D0" CR_TAB + "sbrc %D1,7" CR_TAB + "dec %D0" CR_TAB + "mov %C0,%D1" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %A0,%B1"); + } + + case 16: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + + if (reg0 == reg1 + 2) + return *len = 4, ("clr %D0" CR_TAB + "sbrc %B0,7" CR_TAB + "com %D0" CR_TAB + "mov %C0,%D0"); + if (AVR_HAVE_MOVW) + return *len = 5, ("movw %A0,%C1" CR_TAB + "clr %D0" CR_TAB + "sbrc %B0,7" CR_TAB + "com %D0" CR_TAB + "mov %C0,%D0"); + else + return *len = 6, ("mov %B0,%D1" CR_TAB + "mov %A0,%C1" CR_TAB + "clr %D0" CR_TAB + "sbrc %B0,7" CR_TAB + "com %D0" CR_TAB + "mov %C0,%D0"); + } + + case 24: + return *len = 6, ("mov %A0,%D1" CR_TAB + "clr %D0" CR_TAB + "sbrc %A0,7" CR_TAB + "com %D0" CR_TAB + "mov %B0,%D0" CR_TAB + "mov %C0,%D0"); + + default: + if (INTVAL (operands[2]) < 32) + break; + + /* fall through */ + + case 31: + if (AVR_HAVE_MOVW) + return *len = 4, ("lsl %D0" CR_TAB + "sbc %A0,%A0" CR_TAB + "mov %B0,%A0" CR_TAB + "movw %C0,%A0"); + else + return *len = 5, ("lsl %D0" CR_TAB + "sbc %A0,%A0" CR_TAB + "mov %B0,%A0" CR_TAB + "mov %C0,%A0" CR_TAB + "mov %D0,%A0"); + } + len = t; + } + out_shift_with_cnt ("asr %D0" CR_TAB + "ror %C0" CR_TAB + "ror %B0" CR_TAB + "ror %A0", insn, operands, len, 4); + return ""; +} + +/* 8-bit logic shift right ((unsigned char)x >> i) */ + +const char * +lshrqi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 8) + break; + + *len = 1; + return "clr %0"; + + case 1: + *len = 1; + return "lsr %0"; + + case 2: + *len = 2; + return ("lsr %0" CR_TAB + "lsr %0"); + case 3: + *len = 3; + return ("lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0"); + + case 4: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len=2; + return ("swap %0" CR_TAB + "andi %0,0x0f"); + } + *len = 4; + return ("lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0"); + + case 5: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len = 3; + return ("swap %0" CR_TAB + "lsr %0" CR_TAB + "andi %0,0x7"); + } + *len = 5; + return ("lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0"); + + case 6: + if (test_hard_reg_class (LD_REGS, operands[0])) + { + *len = 4; + return ("swap %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "andi %0,0x3"); + } + *len = 6; + return ("lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0" CR_TAB + "lsr %0"); + + case 7: + *len = 3; + return ("rol %0" CR_TAB + "clr %0" CR_TAB + "rol %0"); + } + } + else if (CONSTANT_P (operands[2])) + fatal_insn ("internal compiler error. Incorrect shift:", insn); + + out_shift_with_cnt ("lsr %0", + insn, operands, len, 1); + return ""; +} + +/* 16-bit logic shift right ((unsigned short)x >> i) */ + +const char * +lshrhi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); + int ldi_ok = test_hard_reg_class (LD_REGS, operands[0]); + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 16) + break; + + *len = 2; + return ("clr %B0" CR_TAB + "clr %A0"); + + case 4: + if (optimize_size && scratch) + break; /* 5 */ + if (ldi_ok) + { + *len = 6; + return ("swap %B0" CR_TAB + "swap %A0" CR_TAB + "andi %A0,0x0f" CR_TAB + "eor %A0,%B0" CR_TAB + "andi %B0,0x0f" CR_TAB + "eor %A0,%B0"); + } + if (scratch) + { + *len = 7; + return ("swap %B0" CR_TAB + "swap %A0" CR_TAB + "ldi %3,0x0f" CR_TAB + "and %A0,%3" CR_TAB + "eor %A0,%B0" CR_TAB + "and %B0,%3" CR_TAB + "eor %A0,%B0"); + } + break; /* optimize_size ? 6 : 8 */ + + case 5: + if (optimize_size) + break; /* scratch ? 5 : 6 */ + if (ldi_ok) + { + *len = 8; + return ("lsr %B0" CR_TAB + "ror %A0" CR_TAB + "swap %B0" CR_TAB + "swap %A0" CR_TAB + "andi %A0,0x0f" CR_TAB + "eor %A0,%B0" CR_TAB + "andi %B0,0x0f" CR_TAB + "eor %A0,%B0"); + } + if (scratch) + { + *len = 9; + return ("lsr %B0" CR_TAB + "ror %A0" CR_TAB + "swap %B0" CR_TAB + "swap %A0" CR_TAB + "ldi %3,0x0f" CR_TAB + "and %A0,%3" CR_TAB + "eor %A0,%B0" CR_TAB + "and %B0,%3" CR_TAB + "eor %A0,%B0"); + } + break; /* 10 */ + + case 6: + if (optimize_size) + break; /* scratch ? 5 : 6 */ + *len = 9; + return ("clr __tmp_reg__" CR_TAB + "lsl %A0" CR_TAB + "rol %B0" CR_TAB + "rol __tmp_reg__" CR_TAB + "lsl %A0" CR_TAB + "rol %B0" CR_TAB + "rol __tmp_reg__" CR_TAB + "mov %A0,%B0" CR_TAB + "mov %B0,__tmp_reg__"); + + case 7: + *len = 5; + return ("lsl %A0" CR_TAB + "mov %A0,%B0" CR_TAB + "rol %A0" CR_TAB + "sbc %B0,%B0" CR_TAB + "neg %B0"); + + case 8: + return *len = 2, ("mov %A0,%B1" CR_TAB + "clr %B0"); + + case 9: + *len = 3; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "lsr %A0"); + + case 10: + *len = 4; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0"); + + case 11: + *len = 5; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0"); + + case 12: + if (ldi_ok) + { + *len = 4; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "swap %A0" CR_TAB + "andi %A0,0x0f"); + } + if (scratch) + { + *len = 5; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "swap %A0" CR_TAB + "ldi %3,0x0f" CR_TAB + "and %A0,%3"); + } + *len = 6; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0"); + + case 13: + if (ldi_ok) + { + *len = 5; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "swap %A0" CR_TAB + "lsr %A0" CR_TAB + "andi %A0,0x07"); + } + if (AVR_HAVE_MUL && scratch) + { + *len = 5; + return ("ldi %3,0x08" CR_TAB + "mul %B0,%3" CR_TAB + "mov %A0,r1" CR_TAB + "clr %B0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && scratch) + break; /* 5 */ + if (scratch) + { + *len = 6; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "swap %A0" CR_TAB + "lsr %A0" CR_TAB + "ldi %3,0x07" CR_TAB + "and %A0,%3"); + } + if (AVR_HAVE_MUL) + { + *len = 6; + return ("set" CR_TAB + "bld r1,3" CR_TAB + "mul %B0,r1" CR_TAB + "mov %A0,r1" CR_TAB + "clr %B0" CR_TAB + "clr __zero_reg__"); + } + *len = 7; + return ("mov %A0,%B0" CR_TAB + "clr %B0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0" CR_TAB + "lsr %A0"); + + case 14: + if (AVR_HAVE_MUL && ldi_ok) + { + *len = 5; + return ("ldi %A0,0x04" CR_TAB + "mul %B0,%A0" CR_TAB + "mov %A0,r1" CR_TAB + "clr %B0" CR_TAB + "clr __zero_reg__"); + } + if (AVR_HAVE_MUL && scratch) + { + *len = 5; + return ("ldi %3,0x04" CR_TAB + "mul %B0,%3" CR_TAB + "mov %A0,r1" CR_TAB + "clr %B0" CR_TAB + "clr __zero_reg__"); + } + if (optimize_size && ldi_ok) + { + *len = 5; + return ("mov %A0,%B0" CR_TAB + "ldi %B0,6" "\n1:\t" + "lsr %A0" CR_TAB + "dec %B0" CR_TAB + "brne 1b"); + } + if (optimize_size && scratch) + break; /* 5 */ + *len = 6; + return ("clr %A0" CR_TAB + "lsl %B0" CR_TAB + "rol %A0" CR_TAB + "lsl %B0" CR_TAB + "rol %A0" CR_TAB + "clr %B0"); + + case 15: + *len = 4; + return ("clr %A0" CR_TAB + "lsl %B0" CR_TAB + "rol %A0" CR_TAB + "clr %B0"); + } + len = t; + } + out_shift_with_cnt ("lsr %B0" CR_TAB + "ror %A0", insn, operands, len, 2); + return ""; +} + + +/* 24-bit logic shift right */ + +const char* +avr_out_lshrpsi3 (rtx insn, rtx *op, int *plen) +{ + int dest = REGNO (op[0]); + int src = REGNO (op[1]); + + if (CONST_INT_P (op[2])) + { + if (plen) + *plen = 0; + + switch (INTVAL (op[2])) + { + case 8: + if (dest <= src) + return avr_asm_len ("mov %A0,%B1" CR_TAB + "mov %B0,%C1" CR_TAB + "clr %C0", op, plen, 3); + else + return avr_asm_len ("clr %C0" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %A0,%B1", op, plen, 3); + + case 16: + if (dest != src + 2) + avr_asm_len ("mov %A0,%C1", op, plen, 1); + + return avr_asm_len ("clr %B0" CR_TAB + "clr %C0", op, plen, 2); + + default: + if (INTVAL (op[2]) < 24) + break; + + /* fall through */ + + case 23: + return avr_asm_len ("clr %A0" CR_TAB + "sbrc %C0,7" CR_TAB + "inc %A0" CR_TAB + "clr %B0" CR_TAB + "clr %C0", op, plen, 5); + } /* switch */ + } + + out_shift_with_cnt ("lsr %C0" CR_TAB + "ror %B0" CR_TAB + "ror %A0", insn, op, plen, 3); + return ""; +} + + +/* 32-bit logic shift right ((unsigned int)x >> i) */ + +const char * +lshrsi3_out (rtx insn, rtx operands[], int *len) +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + int k; + int *t = len; + + if (!len) + len = &k; + + switch (INTVAL (operands[2])) + { + default: + if (INTVAL (operands[2]) < 32) + break; + + if (AVR_HAVE_MOVW) + return *len = 3, ("clr %D0" CR_TAB + "clr %C0" CR_TAB + "movw %A0,%C0"); + *len = 4; + return ("clr %D0" CR_TAB + "clr %C0" CR_TAB + "clr %B0" CR_TAB + "clr %A0"); + + case 8: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + *len = 4; + if (reg0 <= reg1) + return ("mov %A0,%B1" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %C0,%D1" CR_TAB + "clr %D0"); + else + return ("clr %D0" CR_TAB + "mov %C0,%D1" CR_TAB + "mov %B0,%C1" CR_TAB + "mov %A0,%B1"); + } + + case 16: + { + int reg0 = true_regnum (operands[0]); + int reg1 = true_regnum (operands[1]); + + if (reg0 == reg1 + 2) + return *len = 2, ("clr %C0" CR_TAB + "clr %D0"); + if (AVR_HAVE_MOVW) + return *len = 3, ("movw %A0,%C1" CR_TAB + "clr %C0" CR_TAB + "clr %D0"); + else + return *len = 4, ("mov %B0,%D1" CR_TAB + "mov %A0,%C1" CR_TAB + "clr %C0" CR_TAB + "clr %D0"); + } + + case 24: + return *len = 4, ("mov %A0,%D1" CR_TAB + "clr %B0" CR_TAB + "clr %C0" CR_TAB + "clr %D0"); + + case 31: + *len = 6; + return ("clr %A0" CR_TAB + "sbrc %D0,7" CR_TAB + "inc %A0" CR_TAB + "clr %B0" CR_TAB + "clr %C0" CR_TAB + "clr %D0"); + } + len = t; + } + out_shift_with_cnt ("lsr %D0" CR_TAB + "ror %C0" CR_TAB + "ror %B0" CR_TAB + "ror %A0", insn, operands, len, 4); + return ""; +} + + +/* Output addition of register XOP[0] and compile time constant XOP[2]. + CODE == PLUS: perform addition by using ADD instructions or + CODE == MINUS: perform addition by using SUB instructions: + + XOP[0] = XOP[0] + XOP[2] + + Or perform addition/subtraction with register XOP[2] depending on CODE: + + XOP[0] = XOP[0] +/- XOP[2] + + If PLEN == NULL, print assembler instructions to perform the operation; + otherwise, set *PLEN to the length of the instruction sequence (in words) + printed with PLEN == NULL. XOP[3] is an 8-bit scratch register or NULL_RTX. + Set *PCC to effect on cc0 according to respective CC_* insn attribute. + + CODE_SAT == UNKNOWN: Perform ordinary, non-saturating operation. + CODE_SAT != UNKNOWN: Perform operation and saturate according to CODE_SAT. + If CODE_SAT != UNKNOWN then SIGN contains the sign of the summand resp. + the subtrahend in the original insn, provided it is a compile time constant. + In all other cases, SIGN is 0. + + Return "". */ + +static void +avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc, + enum rtx_code code_sat = UNKNOWN, int sign = 0) +{ + /* MODE of the operation. */ + enum machine_mode mode = GET_MODE (xop[0]); + + /* INT_MODE of the same size. */ + enum machine_mode imode = int_mode_for_mode (mode); + + /* Number of bytes to operate on. */ + int i, n_bytes = GET_MODE_SIZE (mode); + + /* Value (0..0xff) held in clobber register op[3] or -1 if unknown. */ + int clobber_val = -1; + + /* op[0]: 8-bit destination register + op[1]: 8-bit const int + op[2]: 8-bit scratch register */ + rtx op[3]; + + /* Started the operation? Before starting the operation we may skip + adding 0. This is no more true after the operation started because + carry must be taken into account. */ + bool started = false; + + /* Value to add. There are two ways to add VAL: R += VAL and R -= -VAL. */ + rtx xval = xop[2]; + + /* Output a BRVC instruction. Only needed with saturation. */ + bool out_brvc = true; + + if (plen) + *plen = 0; + + if (REG_P (xop[2])) + { + *pcc = MINUS == code ? (int) CC_SET_CZN : (int) CC_SET_N; + + for (i = 0; i < n_bytes; i++) + { + /* We operate byte-wise on the destination. */ + op[0] = simplify_gen_subreg (QImode, xop[0], mode, i); + op[1] = simplify_gen_subreg (QImode, xop[2], mode, i); + + if (i == 0) + avr_asm_len (code == PLUS ? "add %0,%1" : "sub %0,%1", + op, plen, 1); + else + avr_asm_len (code == PLUS ? "adc %0,%1" : "sbc %0,%1", + op, plen, 1); + } + + if (reg_overlap_mentioned_p (xop[0], xop[2])) + { + gcc_assert (REGNO (xop[0]) == REGNO (xop[2])); + + if (MINUS == code) + return; + } + + goto saturate; + } + + /* Except in the case of ADIW with 16-bit register (see below) + addition does not set cc0 in a usable way. */ + + *pcc = (MINUS == code) ? CC_SET_CZN : CC_CLOBBER; + + if (CONST_FIXED_P (xval)) + xval = avr_to_int_mode (xval); + + /* Adding/Subtracting zero is a no-op. */ + + if (xval == const0_rtx) + { + *pcc = CC_NONE; + return; + } + + if (MINUS == code) + xval = simplify_unary_operation (NEG, imode, xval, imode); + + op[2] = xop[3]; + + if (SS_PLUS == code_sat && MINUS == code + && sign < 0 + && 0x80 == (INTVAL (simplify_gen_subreg (QImode, xval, imode, n_bytes-1)) + & GET_MODE_MASK (QImode))) + { + /* We compute x + 0x80 by means of SUB instructions. We negated the + constant subtrahend above and are left with x - (-128) so that we + need something like SUBI r,128 which does not exist because SUBI sets + V according to the sign of the subtrahend. Notice the only case + where this must be done is when NEG overflowed in case [2s] because + the V computation needs the right sign of the subtrahend. */ + + rtx msb = simplify_gen_subreg (QImode, xop[0], mode, n_bytes-1); + + avr_asm_len ("subi %0,128" CR_TAB + "brmi 0f", &msb, plen, 2); + out_brvc = false; + + goto saturate; + } + + for (i = 0; i < n_bytes; i++) + { + /* We operate byte-wise on the destination. */ + rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i); + rtx xval8 = simplify_gen_subreg (QImode, xval, imode, i); + + /* 8-bit value to operate with this byte. */ + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + /* Registers R16..R31 can operate with immediate. */ + bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8); + + op[0] = reg8; + op[1] = gen_int_mode (val8, QImode); + + /* To get usable cc0 no low-bytes must have been skipped. */ + + if (i && !started) + *pcc = CC_CLOBBER; + + if (!started + && i % 2 == 0 + && i + 2 <= n_bytes + && test_hard_reg_class (ADDW_REGS, reg8)) + { + rtx xval16 = simplify_gen_subreg (HImode, xval, imode, i); + unsigned int val16 = UINTVAL (xval16) & GET_MODE_MASK (HImode); + + /* Registers R24, X, Y, Z can use ADIW/SBIW with constants < 64 + i.e. operate word-wise. */ + + if (val16 < 64) + { + if (val16 != 0) + { + started = true; + avr_asm_len (code == PLUS ? "adiw %0,%1" : "sbiw %0,%1", + op, plen, 1); + + if (n_bytes == 2 && PLUS == code) + *pcc = CC_SET_ZN; + } + + i++; + continue; + } + } + + if (val8 == 0) + { + if (started) + avr_asm_len (code == PLUS + ? "adc %0,__zero_reg__" : "sbc %0,__zero_reg__", + op, plen, 1); + continue; + } + else if ((val8 == 1 || val8 == 0xff) + && UNKNOWN == code_sat + && !started + && i == n_bytes - 1) + { + avr_asm_len ((code == PLUS) ^ (val8 == 1) ? "dec %0" : "inc %0", + op, plen, 1); + break; + } + + switch (code) + { + case PLUS: + + gcc_assert (plen != NULL || (op[2] && REG_P (op[2]))); + + if (plen != NULL && UNKNOWN != code_sat) + { + /* This belongs to the x + 0x80 corner case. The code with + ADD instruction is not smaller, thus make this case + expensive so that the caller won't pick it. */ + + *plen += 10; + break; + } + + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len (started ? "adc %0,%2" : "add %0,%2", op, plen, 1); + + break; /* PLUS */ + + case MINUS: + + if (ld_reg_p) + avr_asm_len (started ? "sbci %0,%1" : "subi %0,%1", op, plen, 1); + else + { + gcc_assert (plen != NULL || REG_P (op[2])); + + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len (started ? "sbc %0,%2" : "sub %0,%2", op, plen, 1); + } + + break; /* MINUS */ + + default: + /* Unknown code */ + gcc_unreachable(); + } + + started = true; + + } /* for all sub-bytes */ + + saturate: + + if (UNKNOWN == code_sat) + return; + + *pcc = (int) CC_CLOBBER; + + /* Vanilla addition/subtraction is done. We are left with saturation. + + We have to compute A = A B where A is a register and + B is a register or a non-zero compile time constant CONST. + A is register class "r" if unsigned && B is REG. Otherwise, A is in "d". + B stands for the original operand $2 in INSN. In the case of B = CONST, + SIGN in { -1, 1 } is the sign of B. Otherwise, SIGN is 0. + + CODE is the instruction flavor we use in the asm sequence to perform . + + + unsigned + operation | code | sat if | b is | sat value | case + -----------------+-------+----------+--------------+-----------+------- + + as a + b | add | C == 1 | const, reg | u+ = 0xff | [1u] + + as a - (-b) | sub | C == 0 | const | u+ = 0xff | [2u] + - as a - b | sub | C == 1 | const, reg | u- = 0 | [3u] + - as a + (-b) | add | C == 0 | const | u- = 0 | [4u] + + + signed + operation | code | sat if | b is | sat value | case + -----------------+-------+----------+--------------+-----------+------- + + as a + b | add | V == 1 | const, reg | s+ | [1s] + + as a - (-b) | sub | V == 1 | const | s+ | [2s] + - as a - b | sub | V == 1 | const, reg | s- | [3s] + - as a + (-b) | add | V == 1 | const | s- | [4s] + + s+ = b < 0 ? -0x80 : 0x7f + s- = b < 0 ? 0x7f : -0x80 + + The cases a - b actually perform a - (-(-b)) if B is CONST. + */ + + op[0] = simplify_gen_subreg (QImode, xop[0], mode, n_bytes-1); + op[1] = n_bytes > 1 + ? simplify_gen_subreg (QImode, xop[0], mode, n_bytes-2) + : NULL_RTX; + + bool need_copy = true; + int len_call = 1 + AVR_HAVE_JMP_CALL; + + switch (code_sat) + { + default: + gcc_unreachable(); + + case SS_PLUS: + case SS_MINUS: + + if (out_brvc) + avr_asm_len ("brvc 0f", op, plen, 1); + + if (reg_overlap_mentioned_p (xop[0], xop[2])) + { + /* [1s,reg] */ + + if (n_bytes == 1) + avr_asm_len ("ldi %0,0x7f" CR_TAB + "adc %0,__zero_reg__", op, plen, 2); + else + avr_asm_len ("ldi %0,0x7f" CR_TAB + "ldi %1,0xff" CR_TAB + "adc %1,__zero_reg__" CR_TAB + "adc %0,__zero_reg__", op, plen, 4); + } + else if (sign == 0 && PLUS == code) + { + /* [1s,reg] */ + + op[2] = simplify_gen_subreg (QImode, xop[2], mode, n_bytes-1); + + if (n_bytes == 1) + avr_asm_len ("ldi %0,0x80" CR_TAB + "sbrs %2,7" CR_TAB + "dec %0", op, plen, 3); + else + avr_asm_len ("ldi %0,0x80" CR_TAB + "cp %2,%0" CR_TAB + "sbc %1,%1" CR_TAB + "sbci %0,0", op, plen, 4); + } + else if (sign == 0 && MINUS == code) + { + /* [3s,reg] */ + + op[2] = simplify_gen_subreg (QImode, xop[2], mode, n_bytes-1); + + if (n_bytes == 1) + avr_asm_len ("ldi %0,0x7f" CR_TAB + "sbrs %2,7" CR_TAB + "inc %0", op, plen, 3); + else + avr_asm_len ("ldi %0,0x7f" CR_TAB + "cp %0,%2" CR_TAB + "sbc %1,%1" CR_TAB + "sbci %0,-1", op, plen, 4); + } + else if ((sign < 0) ^ (SS_MINUS == code_sat)) + { + /* [1s,const,B < 0] [2s,B < 0] */ + /* [3s,const,B > 0] [4s,B > 0] */ + + if (n_bytes == 8) + { + avr_asm_len ("%~call __clr_8", op, plen, len_call); + need_copy = false; + } + + avr_asm_len ("ldi %0,0x80", op, plen, 1); + if (n_bytes > 1 && need_copy) + avr_asm_len ("clr %1", op, plen, 1); + } + else if ((sign > 0) ^ (SS_MINUS == code_sat)) + { + /* [1s,const,B > 0] [2s,B > 0] */ + /* [3s,const,B < 0] [4s,B < 0] */ + + if (n_bytes == 8) + { + avr_asm_len ("sec" CR_TAB + "%~call __sbc_8", op, plen, 1 + len_call); + need_copy = false; + } + + avr_asm_len ("ldi %0,0x7f", op, plen, 1); + if (n_bytes > 1 && need_copy) + avr_asm_len ("ldi %1,0xff", op, plen, 1); + } + else + gcc_unreachable(); + + break; + + case US_PLUS: + /* [1u] : [2u] */ + + avr_asm_len (PLUS == code ? "brcc 0f" : "brcs 0f", op, plen, 1); + + if (n_bytes == 8) + { + if (MINUS == code) + avr_asm_len ("sec", op, plen, 1); + avr_asm_len ("%~call __sbc_8", op, plen, len_call); + + need_copy = false; + } + else + { + if (MINUS == code && !test_hard_reg_class (LD_REGS, op[0])) + avr_asm_len ("sec" CR_TAB "sbc %0,%0", op, plen, 2); + else + avr_asm_len (PLUS == code ? "sbc %0,%0" : "ldi %0,0xff", + op, plen, 1); + } + break; /* US_PLUS */ + + case US_MINUS: + /* [4u] : [3u] */ + + avr_asm_len (PLUS == code ? "brcs 0f" : "brcc 0f", op, plen, 1); + + if (n_bytes == 8) + { + avr_asm_len ("%~call __clr_8", op, plen, len_call); + need_copy = false; + } + else + avr_asm_len ("clr %0", op, plen, 1); + + break; + } + + /* We set the MSB in the unsigned case and the 2 MSBs in the signed case. + Now copy the right value to the LSBs. */ + + if (need_copy && n_bytes > 1) + { + if (US_MINUS == code_sat || US_PLUS == code_sat) + { + avr_asm_len ("mov %1,%0", op, plen, 1); + + if (n_bytes > 2) + { + op[0] = xop[0]; + if (AVR_HAVE_MOVW) + avr_asm_len ("movw %0,%1", op, plen, 1); + else + avr_asm_len ("mov %A0,%1" CR_TAB + "mov %B0,%1", op, plen, 2); + } + } + else if (n_bytes > 2) + { + op[0] = xop[0]; + avr_asm_len ("mov %A0,%1" CR_TAB + "mov %B0,%1", op, plen, 2); + } + } + + if (need_copy && n_bytes == 8) + { + if (AVR_HAVE_MOVW) + avr_asm_len ("movw %r0+2,%0" CR_TAB + "movw %r0+4,%0", xop, plen, 2); + else + avr_asm_len ("mov %r0+2,%0" CR_TAB + "mov %r0+3,%0" CR_TAB + "mov %r0+4,%0" CR_TAB + "mov %r0+5,%0", xop, plen, 4); + } + + avr_asm_len ("0:", op, plen, 0); +} + + +/* Output addition/subtraction of register XOP[0] and a constant XOP[2] that + is ont a compile-time constant: + + XOP[0] = XOP[0] +/- XOP[2] + + This is a helper for the function below. The only insns that need this + are additions/subtraction for pointer modes, i.e. HImode and PSImode. */ + +static const char* +avr_out_plus_symbol (rtx *xop, enum rtx_code code, int *plen, int *pcc) +{ + enum machine_mode mode = GET_MODE (xop[0]); + + /* Only pointer modes want to add symbols. */ + + gcc_assert (mode == HImode || mode == PSImode); + + *pcc = MINUS == code ? (int) CC_SET_CZN : (int) CC_SET_N; + + avr_asm_len (PLUS == code + ? "subi %A0,lo8(-(%2))" CR_TAB "sbci %B0,hi8(-(%2))" + : "subi %A0,lo8(%2)" CR_TAB "sbci %B0,hi8(%2)", + xop, plen, -2); + + if (PSImode == mode) + avr_asm_len (PLUS == code + ? "sbci %C0,hlo8(-(%2))" + : "sbci %C0,hlo8(%2)", xop, plen, 1); + return ""; +} + + +/* Prepare operands of addition/subtraction to be used with avr_out_plus_1. + + INSN is a single_set insn with a binary operation as SET_SRC that is + one of: PLUS, SS_PLUS, US_PLUS, MINUS, SS_MINUS, US_MINUS. + + XOP are the operands of INSN. In the case of 64-bit operations with + constant XOP[] has just one element: The summand/subtrahend in XOP[0]. + The non-saturating insns up to 32 bits may or may not supply a "d" class + scratch as XOP[3]. + + If PLEN == NULL output the instructions. + If PLEN != NULL set *PLEN to the length of the sequence in words. + + PCC is a pointer to store the instructions' effect on cc0. + PCC may be NULL. + + PLEN and PCC default to NULL. + + Return "" */ + +const char* +avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) +{ + int cc_plus, cc_minus, cc_dummy; + int len_plus, len_minus; + rtx op[4]; + rtx xdest = SET_DEST (single_set (insn)); + enum machine_mode mode = GET_MODE (xdest); + enum machine_mode imode = int_mode_for_mode (mode); + int n_bytes = GET_MODE_SIZE (mode); + enum rtx_code code_sat = GET_CODE (SET_SRC (single_set (insn))); + enum rtx_code code + = (PLUS == code_sat || SS_PLUS == code_sat || US_PLUS == code_sat + ? PLUS : MINUS); + + if (!pcc) + pcc = &cc_dummy; + + /* PLUS and MINUS don't saturate: Use modular wrap-around. */ + + if (PLUS == code_sat || MINUS == code_sat) + code_sat = UNKNOWN; + + if (n_bytes <= 4 && REG_P (xop[2])) + { + avr_out_plus_1 (xop, plen, code, pcc, code_sat); + return ""; + } + + if (8 == n_bytes) + { + op[0] = gen_rtx_REG (DImode, ACC_A); + op[1] = gen_rtx_REG (DImode, ACC_A); + op[2] = avr_to_int_mode (xop[0]); + } + else + { + if (!REG_P (xop[2]) + && !CONST_INT_P (xop[2]) + && !CONST_FIXED_P (xop[2])) + { + return avr_out_plus_symbol (xop, code, plen, pcc); + } + + op[0] = avr_to_int_mode (xop[0]); + op[1] = avr_to_int_mode (xop[1]); + op[2] = avr_to_int_mode (xop[2]); + } + + /* Saturations and 64-bit operations don't have a clobber operand. + For the other cases, the caller will provide a proper XOP[3]. */ + + op[3] = PARALLEL == GET_CODE (PATTERN (insn)) ? xop[3] : NULL_RTX; + + /* Saturation will need the sign of the original operand. */ + + rtx xmsb = simplify_gen_subreg (QImode, op[2], imode, n_bytes-1); + int sign = INTVAL (xmsb) < 0 ? -1 : 1; + + /* If we subtract and the subtrahend is a constant, then negate it + so that avr_out_plus_1 can be used. */ + + if (MINUS == code) + op[2] = simplify_unary_operation (NEG, imode, op[2], imode); + + /* Work out the shortest sequence. */ + + avr_out_plus_1 (op, &len_minus, MINUS, &cc_plus, code_sat, sign); + avr_out_plus_1 (op, &len_plus, PLUS, &cc_minus, code_sat, sign); + + if (plen) + { + *plen = (len_minus <= len_plus) ? len_minus : len_plus; + *pcc = (len_minus <= len_plus) ? cc_minus : cc_plus; + } + else if (len_minus <= len_plus) + avr_out_plus_1 (op, NULL, MINUS, pcc, code_sat, sign); + else + avr_out_plus_1 (op, NULL, PLUS, pcc, code_sat, sign); + + return ""; +} + + +/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile + time constant XOP[2]: + + XOP[0] = XOP[0] XOP[2] + + and return "". If PLEN == NULL, print assembler instructions to perform the + operation; otherwise, set *PLEN to the length of the instruction sequence + (in words) printed with PLEN == NULL. XOP[3] is either an 8-bit clobber + register or SCRATCH if no clobber register is needed for the operation. */ + +const char* +avr_out_bitop (rtx insn, rtx *xop, int *plen) +{ + /* CODE and MODE of the operation. */ + enum rtx_code code = GET_CODE (SET_SRC (single_set (insn))); + enum machine_mode mode = GET_MODE (xop[0]); + + /* Number of bytes to operate on. */ + int i, n_bytes = GET_MODE_SIZE (mode); + + /* Value of T-flag (0 or 1) or -1 if unknow. */ + int set_t = -1; + + /* Value (0..0xff) held in clobber register op[3] or -1 if unknown. */ + int clobber_val = -1; + + /* op[0]: 8-bit destination register + op[1]: 8-bit const int + op[2]: 8-bit clobber register or SCRATCH + op[3]: 8-bit register containing 0xff or NULL_RTX */ + rtx op[4]; + + op[2] = xop[3]; + op[3] = NULL_RTX; + + if (plen) + *plen = 0; + + for (i = 0; i < n_bytes; i++) + { + /* We operate byte-wise on the destination. */ + rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i); + rtx xval8 = simplify_gen_subreg (QImode, xop[2], mode, i); + + /* 8-bit value to operate with this byte. */ + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + /* Number of bits set in the current byte of the constant. */ + int pop8 = avr_popcount (val8); + + /* Registers R16..R31 can operate with immediate. */ + bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8); + + op[0] = reg8; + op[1] = GEN_INT (val8); + + switch (code) + { + case IOR: + + if (0 == pop8) + continue; + else if (ld_reg_p) + avr_asm_len ("ori %0,%1", op, plen, 1); + else if (1 == pop8) + { + if (set_t != 1) + avr_asm_len ("set", op, plen, 1); + set_t = 1; + + op[1] = GEN_INT (exact_log2 (val8)); + avr_asm_len ("bld %0,%1", op, plen, 1); + } + else if (8 == pop8) + { + if (op[3] != NULL_RTX) + avr_asm_len ("mov %0,%3", op, plen, 1); + else + avr_asm_len ("clr %0" CR_TAB + "dec %0", op, plen, 2); + + op[3] = op[0]; + } + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("or %0,%2", op, plen, 1); + } + + continue; /* IOR */ + + case AND: + + if (8 == pop8) + continue; + else if (0 == pop8) + avr_asm_len ("clr %0", op, plen, 1); + else if (ld_reg_p) + avr_asm_len ("andi %0,%1", op, plen, 1); + else if (7 == pop8) + { + if (set_t != 0) + avr_asm_len ("clt", op, plen, 1); + set_t = 0; + + op[1] = GEN_INT (exact_log2 (GET_MODE_MASK (QImode) & ~val8)); + avr_asm_len ("bld %0,%1", op, plen, 1); + } + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("and %0,%2", op, plen, 1); + } + + continue; /* AND */ + + case XOR: + + if (0 == pop8) + continue; + else if (8 == pop8) + avr_asm_len ("com %0", op, plen, 1); + else if (ld_reg_p && val8 == (1 << 7)) + avr_asm_len ("subi %0,%1", op, plen, 1); + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("eor %0,%2", op, plen, 1); + } + + continue; /* XOR */ + + default: + /* Unknown rtx_code */ + gcc_unreachable(); + } + } /* for all sub-bytes */ + + return ""; +} + + +/* PLEN == NULL: Output code to add CONST_INT OP[0] to SP. + PLEN != NULL: Set *PLEN to the length of that sequence. + Return "". */ + +const char* +avr_out_addto_sp (rtx *op, int *plen) +{ + int pc_len = AVR_2_BYTE_PC ? 2 : 3; + int addend = INTVAL (op[0]); + + if (plen) + *plen = 0; + + if (addend < 0) + { + if (flag_verbose_asm || flag_print_asm_name) + avr_asm_len (ASM_COMMENT_START "SP -= %n0", op, plen, 0); + + while (addend <= -pc_len) + { + addend += pc_len; + avr_asm_len ("rcall .", op, plen, 1); + } + + while (addend++ < 0) + avr_asm_len ("push __zero_reg__", op, plen, 1); + } + else if (addend > 0) + { + if (flag_verbose_asm || flag_print_asm_name) + avr_asm_len (ASM_COMMENT_START "SP += %0", op, plen, 0); + + while (addend-- > 0) + avr_asm_len ("pop __tmp_reg__", op, plen, 1); + } + + return ""; +} + + +/* Outputs instructions needed for fixed point type conversion. + This includes converting between any fixed point type, as well + as converting to any integer type. Conversion between integer + types is not supported. + + Converting signed fractional types requires a bit shift if converting + to or from any unsigned fractional type because the decimal place is + shifted by 1 bit. When the destination is a signed fractional, the sign + is stored in either the carry or T bit. */ + +const char* +avr_out_fract (rtx insn, rtx operands[], bool intsigned, int *plen) +{ + size_t i; + rtx xop[6]; + RTX_CODE shift = UNKNOWN; + bool sign_in_carry = false; + bool msb_in_carry = false; + bool lsb_in_carry = false; + const char *code_ashift = "lsl %0"; + + +#define MAY_CLOBBER(RR) \ + /* Shorthand used below. */ \ + ((sign_bytes \ + && IN_RANGE (RR, dest.regno_msb - sign_bytes + 1, dest.regno_msb)) \ + || (reg_unused_after (insn, all_regs_rtx[RR]) \ + && !IN_RANGE (RR, dest.regno, dest.regno_msb))) + + struct + { + /* bytes : Length of operand in bytes. + ibyte : Length of integral part in bytes. + fbyte, fbit : Length of fractional part in bytes, bits. */ + + bool sbit; + unsigned fbit, bytes, ibyte, fbyte; + unsigned regno, regno_msb; + } dest, src, *val[2] = { &dest, &src }; + + if (plen) + *plen = 0; + + /* Step 0: Determine information on source and destination operand we + ====== will need in the remainder. */ + + for (i = 0; i < sizeof (val) / sizeof (*val); i++) + { + enum machine_mode mode; + + xop[i] = operands[i]; + + mode = GET_MODE (xop[i]); + + val[i]->bytes = GET_MODE_SIZE (mode); + val[i]->regno = REGNO (xop[i]); + val[i]->regno_msb = REGNO (xop[i]) + val[i]->bytes - 1; + + if (SCALAR_INT_MODE_P (mode)) + { + val[i]->sbit = intsigned; + val[i]->fbit = 0; + } + else if (ALL_SCALAR_FIXED_POINT_MODE_P (mode)) + { + val[i]->sbit = SIGNED_SCALAR_FIXED_POINT_MODE_P (mode); + val[i]->fbit = GET_MODE_FBIT (mode); + } + else + fatal_insn ("unsupported fixed-point conversion", insn); + + val[i]->fbyte = (1 + val[i]->fbit) / BITS_PER_UNIT; + val[i]->ibyte = val[i]->bytes - val[i]->fbyte; + } + + // Byte offset of the decimal point taking into account different place + // of the decimal point in input and output and different register numbers + // of input and output. + int offset = dest.regno - src.regno + dest.fbyte - src.fbyte; + + // Number of destination bytes that will come from sign / zero extension. + int sign_bytes = (dest.ibyte - src.ibyte) * (dest.ibyte > src.ibyte); + + // Number of bytes at the low end to be filled with zeros. + int zero_bytes = (dest.fbyte - src.fbyte) * (dest.fbyte > src.fbyte); + + // Do we have a 16-Bit register that is cleared? + rtx clrw = NULL_RTX; + + bool sign_extend = src.sbit && sign_bytes; + + if (0 == dest.fbit % 8 && 7 == src.fbit % 8) + shift = ASHIFT; + else if (7 == dest.fbit % 8 && 0 == src.fbit % 8) + shift = ASHIFTRT; + else if (dest.fbit % 8 == src.fbit % 8) + shift = UNKNOWN; + else + gcc_unreachable(); + + /* Step 1: Clear bytes at the low end and copy payload bits from source + ====== to destination. */ + + int step = offset < 0 ? 1 : -1; + unsigned d0 = offset < 0 ? dest.regno : dest.regno_msb; + + // We leared at least that number of registers. + int clr_n = 0; + + for (; d0 >= dest.regno && d0 <= dest.regno_msb; d0 += step) + { + // Next regno of destination is needed for MOVW + unsigned d1 = d0 + step; + + // Current and next regno of source + signed s0 = d0 - offset; + signed s1 = s0 + step; + + // Must current resp. next regno be CLRed? This applies to the low + // bytes of the destination that have no associated source bytes. + bool clr0 = s0 < (signed) src.regno; + bool clr1 = s1 < (signed) src.regno && d1 >= dest.regno; + + // First gather what code to emit (if any) and additional step to + // apply if a MOVW is in use. xop[2] is destination rtx and xop[3] + // is the source rtx for the current loop iteration. + const char *code = NULL; + int stepw = 0; + + if (clr0) + { + if (AVR_HAVE_MOVW && clr1 && clrw) + { + xop[2] = all_regs_rtx[d0 & ~1]; + xop[3] = clrw; + code = "movw %2,%3"; + stepw = step; + } + else + { + xop[2] = all_regs_rtx[d0]; + code = "clr %2"; + + if (++clr_n >= 2 + && !clrw + && d0 % 2 == (step > 0)) + { + clrw = all_regs_rtx[d0 & ~1]; + } + } + } + else if (offset && s0 <= (signed) src.regno_msb) + { + int movw = AVR_HAVE_MOVW && offset % 2 == 0 + && d0 % 2 == (offset > 0) + && d1 <= dest.regno_msb && d1 >= dest.regno + && s1 <= (signed) src.regno_msb && s1 >= (signed) src.regno; + + xop[2] = all_regs_rtx[d0 & ~movw]; + xop[3] = all_regs_rtx[s0 & ~movw]; + code = movw ? "movw %2,%3" : "mov %2,%3"; + stepw = step * movw; + } + + if (code) + { + if (sign_extend && shift != ASHIFT && !sign_in_carry + && (d0 == src.regno_msb || d0 + stepw == src.regno_msb)) + { + /* We are going to override the sign bit. If we sign-extend, + store the sign in the Carry flag. This is not needed if + the destination will be ASHIFT is the remainder because + the ASHIFT will set Carry without extra instruction. */ + + avr_asm_len ("lsl %0", &all_regs_rtx[src.regno_msb], plen, 1); + sign_in_carry = true; + } + + unsigned src_msb = dest.regno_msb - sign_bytes - offset + 1; + + if (!sign_extend && shift == ASHIFTRT && !msb_in_carry + && src.ibyte > dest.ibyte + && (d0 == src_msb || d0 + stepw == src_msb)) + { + /* We are going to override the MSB. If we shift right, + store the MSB in the Carry flag. This is only needed if + we don't sign-extend becaue with sign-extension the MSB + (the sign) will be produced by the sign extension. */ + + avr_asm_len ("lsr %0", &all_regs_rtx[src_msb], plen, 1); + msb_in_carry = true; + } + + unsigned src_lsb = dest.regno - offset -1; + + if (shift == ASHIFT && src.fbyte > dest.fbyte && !lsb_in_carry + && (d0 == src_lsb || d0 + stepw == src_lsb)) + { + /* We are going to override the new LSB; store it into carry. */ + + avr_asm_len ("lsl %0", &all_regs_rtx[src_lsb], plen, 1); + code_ashift = "rol %0"; + lsb_in_carry = true; + } + + avr_asm_len (code, xop, plen, 1); + d0 += stepw; + } + } + + /* Step 2: Shift destination left by 1 bit position. This might be needed + ====== for signed input and unsigned output. */ + + if (shift == ASHIFT && src.fbyte > dest.fbyte && !lsb_in_carry) + { + unsigned s0 = dest.regno - offset -1; + + if (MAY_CLOBBER (s0)) + avr_asm_len ("lsl %0", &all_regs_rtx[s0], plen, 1); + else + avr_asm_len ("mov __tmp_reg__,%0" CR_TAB + "lsl __tmp_reg__", &all_regs_rtx[s0], plen, 2); + + code_ashift = "rol %0"; + lsb_in_carry = true; + } + + if (shift == ASHIFT) + { + for (d0 = dest.regno + zero_bytes; + d0 <= dest.regno_msb - sign_bytes; d0++) + { + avr_asm_len (code_ashift, &all_regs_rtx[d0], plen, 1); + code_ashift = "rol %0"; + } + + lsb_in_carry = false; + sign_in_carry = true; + } + + /* Step 4a: Store MSB in carry if we don't already have it or will produce + ======= it in sign-extension below. */ + + if (!sign_extend && shift == ASHIFTRT && !msb_in_carry + && src.ibyte > dest.ibyte) + { + unsigned s0 = dest.regno_msb - sign_bytes - offset + 1; + + if (MAY_CLOBBER (s0)) + avr_asm_len ("lsr %0", &all_regs_rtx[s0], plen, 1); + else + avr_asm_len ("mov __tmp_reg__,%0" CR_TAB + "lsr __tmp_reg__", &all_regs_rtx[s0], plen, 2); + + msb_in_carry = true; + } + + /* Step 3: Sign-extend or zero-extend the destination as needed. + ====== */ + + if (sign_extend && !sign_in_carry) + { + unsigned s0 = src.regno_msb; + + if (MAY_CLOBBER (s0)) + avr_asm_len ("lsl %0", &all_regs_rtx[s0], plen, 1); + else + avr_asm_len ("mov __tmp_reg__,%0" CR_TAB + "lsl __tmp_reg__", &all_regs_rtx[s0], plen, 2); + + sign_in_carry = true; + } + + gcc_assert (sign_in_carry + msb_in_carry + lsb_in_carry <= 1); + + unsigned copies = 0; + rtx movw = sign_extend ? NULL_RTX : clrw; + + for (d0 = dest.regno_msb - sign_bytes + 1; d0 <= dest.regno_msb; d0++) + { + if (AVR_HAVE_MOVW && movw + && d0 % 2 == 0 && d0 + 1 <= dest.regno_msb) + { + xop[2] = all_regs_rtx[d0]; + xop[3] = movw; + avr_asm_len ("movw %2,%3", xop, plen, 1); + d0++; + } + else + { + avr_asm_len (sign_extend ? "sbc %0,%0" : "clr %0", + &all_regs_rtx[d0], plen, 1); + + if (++copies >= 2 && !movw && d0 % 2 == 1) + movw = all_regs_rtx[d0-1]; + } + } /* for */ + + + /* Step 4: Right shift the destination. This might be needed for + ====== conversions from unsigned to signed. */ + + if (shift == ASHIFTRT) + { + const char *code_ashiftrt = "lsr %0"; + + if (sign_extend || msb_in_carry) + code_ashiftrt = "ror %0"; + + if (src.sbit && src.ibyte == dest.ibyte) + code_ashiftrt = "asr %0"; + + for (d0 = dest.regno_msb - sign_bytes; + d0 >= dest.regno + zero_bytes - 1 && d0 >= dest.regno; d0--) + { + avr_asm_len (code_ashiftrt, &all_regs_rtx[d0], plen, 1); + code_ashiftrt = "ror %0"; + } + } + +#undef MAY_CLOBBER + + return ""; +} + + +/* Create RTL split patterns for byte sized rotate expressions. This + produces a series of move instructions and considers overlap situations. + Overlapping non-HImode operands need a scratch register. */ + +bool +avr_rotate_bytes (rtx operands[]) +{ + int i, j; + enum machine_mode mode = GET_MODE (operands[0]); + bool overlapped = reg_overlap_mentioned_p (operands[0], operands[1]); + bool same_reg = rtx_equal_p (operands[0], operands[1]); + int num = INTVAL (operands[2]); + rtx scratch = operands[3]; + /* Work out if byte or word move is needed. Odd byte rotates need QImode. + Word move if no scratch is needed, otherwise use size of scratch. */ + enum machine_mode move_mode = QImode; + int move_size, offset, size; + + if (num & 0xf) + move_mode = QImode; + else if ((mode == SImode && !same_reg) || !overlapped) + move_mode = HImode; + else + move_mode = GET_MODE (scratch); + + /* Force DI rotate to use QI moves since other DI moves are currently split + into QI moves so forward propagation works better. */ + if (mode == DImode) + move_mode = QImode; + /* Make scratch smaller if needed. */ + if (SCRATCH != GET_CODE (scratch) + && HImode == GET_MODE (scratch) + && QImode == move_mode) + scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0); + + move_size = GET_MODE_SIZE (move_mode); + /* Number of bytes/words to rotate. */ + offset = (num >> 3) / move_size; + /* Number of moves needed. */ + size = GET_MODE_SIZE (mode) / move_size; + /* Himode byte swap is special case to avoid a scratch register. */ + if (mode == HImode && same_reg) + { + /* HImode byte swap, using xor. This is as quick as using scratch. */ + rtx src, dst; + src = simplify_gen_subreg (move_mode, operands[1], mode, 0); + dst = simplify_gen_subreg (move_mode, operands[0], mode, 1); + if (!rtx_equal_p (dst, src)) + { + emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src)); + emit_move_insn (src, gen_rtx_XOR (QImode, src, dst)); + emit_move_insn (dst, gen_rtx_XOR (QImode, dst, src)); + } + } + else + { +#define MAX_SIZE 8 /* GET_MODE_SIZE (DImode) / GET_MODE_SIZE (QImode) */ + /* Create linked list of moves to determine move order. */ + struct { + rtx src, dst; + int links; + } move[MAX_SIZE + 8]; + int blocked, moves; + + gcc_assert (size <= MAX_SIZE); + /* Generate list of subreg moves. */ + for (i = 0; i < size; i++) + { + int from = i; + int to = (from + offset) % size; + move[i].src = simplify_gen_subreg (move_mode, operands[1], + mode, from * move_size); + move[i].dst = simplify_gen_subreg (move_mode, operands[0], + mode, to * move_size); + move[i].links = -1; + } + /* Mark dependence where a dst of one move is the src of another move. + The first move is a conflict as it must wait until second is + performed. We ignore moves to self - we catch this later. */ + if (overlapped) + for (i = 0; i < size; i++) + if (reg_overlap_mentioned_p (move[i].dst, operands[1])) + for (j = 0; j < size; j++) + if (j != i && rtx_equal_p (move[j].src, move[i].dst)) + { + /* The dst of move i is the src of move j. */ + move[i].links = j; + break; + } + + blocked = -1; + moves = 0; + /* Go through move list and perform non-conflicting moves. As each + non-overlapping move is made, it may remove other conflicts + so the process is repeated until no conflicts remain. */ + do + { + blocked = -1; + moves = 0; + /* Emit move where dst is not also a src or we have used that + src already. */ + for (i = 0; i < size; i++) + if (move[i].src != NULL_RTX) + { + if (move[i].links == -1 + || move[move[i].links].src == NULL_RTX) + { + moves++; + /* Ignore NOP moves to self. */ + if (!rtx_equal_p (move[i].dst, move[i].src)) + emit_move_insn (move[i].dst, move[i].src); + + /* Remove conflict from list. */ + move[i].src = NULL_RTX; + } + else + blocked = i; + } + + /* Check for deadlock. This is when no moves occurred and we have + at least one blocked move. */ + if (moves == 0 && blocked != -1) + { + /* Need to use scratch register to break deadlock. + Add move to put dst of blocked move into scratch. + When this move occurs, it will break chain deadlock. + The scratch register is substituted for real move. */ + + gcc_assert (SCRATCH != GET_CODE (scratch)); + + move[size].src = move[blocked].dst; + move[size].dst = scratch; + /* Scratch move is never blocked. */ + move[size].links = -1; + /* Make sure we have valid link. */ + gcc_assert (move[blocked].links != -1); + /* Replace src of blocking move with scratch reg. */ + move[move[blocked].links].src = scratch; + /* Make dependent on scratch move occuring. */ + move[blocked].links = size; + size=size+1; + } + } + while (blocked != -1); + } + return true; +} + + +/* Worker function for `ADJUST_INSN_LENGTH'. */ +/* Modifies the length assigned to instruction INSN + LEN is the initially computed length of the insn. */ + +int +avr_adjust_insn_length (rtx insn, int len) +{ + rtx *op = recog_data.operand; + enum attr_adjust_len adjust_len; + + /* Some complex insns don't need length adjustment and therefore + the length need not/must not be adjusted for these insns. + It is easier to state this in an insn attribute "adjust_len" than + to clutter up code here... */ + + if (-1 == recog_memoized (insn)) + { + return len; + } + + /* Read from insn attribute "adjust_len" if/how length is to be adjusted. */ + + adjust_len = get_attr_adjust_len (insn); + + if (adjust_len == ADJUST_LEN_NO) + { + /* Nothing to adjust: The length from attribute "length" is fine. + This is the default. */ + + return len; + } + + /* Extract insn's operands. */ + + extract_constrain_insn_cached (insn); + + /* Dispatch to right function. */ + + switch (adjust_len) + { + case ADJUST_LEN_RELOAD_IN16: output_reload_inhi (op, op[2], &len); break; + case ADJUST_LEN_RELOAD_IN24: avr_out_reload_inpsi (op, op[2], &len); break; + case ADJUST_LEN_RELOAD_IN32: output_reload_insisf (op, op[2], &len); break; + + case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break; + + case ADJUST_LEN_PLUS: avr_out_plus (insn, op, &len); break; + case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break; + + case ADJUST_LEN_MOV8: output_movqi (insn, op, &len); break; + case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break; + case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break; + case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break; + case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break; + case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break; + case ADJUST_LEN_LPM: avr_out_lpm (insn, op, &len); break; + + case ADJUST_LEN_SFRACT: avr_out_fract (insn, op, true, &len); break; + case ADJUST_LEN_UFRACT: avr_out_fract (insn, op, false, &len); break; + + case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break; + case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break; + case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break; + case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break; + case ADJUST_LEN_COMPARE64: avr_out_compare64 (insn, op, &len); break; + + case ADJUST_LEN_LSHRQI: lshrqi3_out (insn, op, &len); break; + case ADJUST_LEN_LSHRHI: lshrhi3_out (insn, op, &len); break; + case ADJUST_LEN_LSHRSI: lshrsi3_out (insn, op, &len); break; + + case ADJUST_LEN_ASHRQI: ashrqi3_out (insn, op, &len); break; + case ADJUST_LEN_ASHRHI: ashrhi3_out (insn, op, &len); break; + case ADJUST_LEN_ASHRSI: ashrsi3_out (insn, op, &len); break; + + case ADJUST_LEN_ASHLQI: ashlqi3_out (insn, op, &len); break; + case ADJUST_LEN_ASHLHI: ashlhi3_out (insn, op, &len); break; + case ADJUST_LEN_ASHLSI: ashlsi3_out (insn, op, &len); break; + + case ADJUST_LEN_ASHLPSI: avr_out_ashlpsi3 (insn, op, &len); break; + case ADJUST_LEN_ASHRPSI: avr_out_ashrpsi3 (insn, op, &len); break; + case ADJUST_LEN_LSHRPSI: avr_out_lshrpsi3 (insn, op, &len); break; + + case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break; + + case ADJUST_LEN_INSERT_BITS: avr_out_insert_bits (op, &len); break; + + default: + gcc_unreachable(); + } + + return len; +} + +/* Return nonzero if register REG dead after INSN. */ + +int +reg_unused_after (rtx insn, rtx reg) +{ + return (dead_or_set_p (insn, reg) + || (REG_P(reg) && _reg_unused_after (insn, reg))); +} + +/* Return nonzero if REG is not used after INSN. + We assume REG is a reload reg, and therefore does + not live past labels. It may live past calls or jumps though. */ + +int +_reg_unused_after (rtx insn, rtx reg) +{ + enum rtx_code code; + rtx set; + + /* If the reg is set by this instruction, then it is safe for our + case. Disregard the case where this is a store to memory, since + we are checking a register used in the store address. */ + set = single_set (insn); + if (set && GET_CODE (SET_DEST (set)) != MEM + && reg_overlap_mentioned_p (reg, SET_DEST (set))) + return 1; + + while ((insn = NEXT_INSN (insn))) + { + rtx set; + code = GET_CODE (insn); + +#if 0 + /* If this is a label that existed before reload, then the register + if dead here. However, if this is a label added by reorg, then + the register may still be live here. We can't tell the difference, + so we just ignore labels completely. */ + if (code == CODE_LABEL) + return 1; + /* else */ +#endif + + if (!INSN_P (insn)) + continue; + + if (code == JUMP_INSN) + return 0; + + /* If this is a sequence, we must handle them all at once. + We could have for instance a call that sets the target register, + and an insn in a delay slot that uses the register. In this case, + we must return 0. */ + else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) + { + int i; + int retval = 0; + + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx this_insn = XVECEXP (PATTERN (insn), 0, i); + rtx set = single_set (this_insn); + + if (GET_CODE (this_insn) == CALL_INSN) + code = CALL_INSN; + else if (GET_CODE (this_insn) == JUMP_INSN) + { + if (INSN_ANNULLED_BRANCH_P (this_insn)) + return 0; + code = JUMP_INSN; + } + + if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) + return 0; + if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) + { + if (GET_CODE (SET_DEST (set)) != MEM) + retval = 1; + else + return 0; + } + if (set == 0 + && reg_overlap_mentioned_p (reg, PATTERN (this_insn))) + return 0; + } + if (retval == 1) + return 1; + else if (code == JUMP_INSN) + return 0; + } + + if (code == CALL_INSN) + { + rtx tem; + for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) + if (GET_CODE (XEXP (tem, 0)) == USE + && REG_P (XEXP (XEXP (tem, 0), 0)) + && reg_overlap_mentioned_p (reg, XEXP (XEXP (tem, 0), 0))) + return 0; + if (call_used_regs[REGNO (reg)]) + return 1; + } + + set = single_set (insn); + + if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) + return 0; + if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) + return GET_CODE (SET_DEST (set)) != MEM; + if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn))) + return 0; + } + return 1; +} + + +/* Implement `TARGET_ASM_INTEGER'. */ +/* Target hook for assembling integer objects. The AVR version needs + special handling for references to certain labels. */ + +static bool +avr_assemble_integer (rtx x, unsigned int size, int aligned_p) +{ + if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p + && text_segment_operand (x, VOIDmode)) + { + fputs ("\t.word\tgs(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\n", asm_out_file); + + return true; + } + else if (GET_MODE (x) == PSImode) + { + /* This needs binutils 2.23+, see PR binutils/13503 */ + + fputs ("\t.byte\tlo8(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")" ASM_COMMENT_START "need binutils PR13503\n", asm_out_file); + + fputs ("\t.byte\thi8(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")" ASM_COMMENT_START "need binutils PR13503\n", asm_out_file); + + fputs ("\t.byte\thh8(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")" ASM_COMMENT_START "need binutils PR13503\n", asm_out_file); + + return true; + } + else if (CONST_FIXED_P (x)) + { + unsigned n; + + /* varasm fails to handle big fixed modes that don't fit in hwi. */ + + for (n = 0; n < size; n++) + { + rtx xn = simplify_gen_subreg (QImode, x, GET_MODE (x), n); + default_assemble_integer (xn, 1, aligned_p); + } + + return true; + } + + return default_assemble_integer (x, size, aligned_p); +} + + +/* Implement `TARGET_CLASS_LIKELY_SPILLED_P'. */ +/* Return value is nonzero if pseudos that have been + assigned to registers of class CLASS would likely be spilled + because registers of CLASS are needed for spill registers. */ + +static bool +avr_class_likely_spilled_p (reg_class_t c) +{ + return (c != ALL_REGS && c != ADDW_REGS); +} + + +/* Valid attributes: + progmem - Put data to program memory. + signal - Make a function to be hardware interrupt. + After function prologue interrupts remain disabled. + interrupt - Make a function to be hardware interrupt. Before function + prologue interrupts are enabled by means of SEI. + naked - Don't generate function prologue/epilogue and RET + instruction. */ + +/* Handle a "progmem" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +avr_handle_progmem_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (DECL_P (*node)) + { + if (TREE_CODE (*node) == TYPE_DECL) + { + /* This is really a decl attribute, not a type attribute, + but try to handle it for GCC 3.0 backwards compatibility. */ + + tree type = TREE_TYPE (*node); + tree attr = tree_cons (name, args, TYPE_ATTRIBUTES (type)); + tree newtype = build_type_attribute_variant (type, attr); + + TYPE_MAIN_VARIANT (newtype) = TYPE_MAIN_VARIANT (type); + TREE_TYPE (*node) = newtype; + *no_add_attrs = true; + } + else if (TREE_STATIC (*node) || DECL_EXTERNAL (*node)) + { + *no_add_attrs = false; + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", + name); + *no_add_attrs = true; + } + } + + return NULL_TREE; +} + +/* Handle an attribute requiring a FUNCTION_DECL; arguments as in + struct attribute_spec.handler. */ + +static tree +avr_handle_fndecl_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute only applies to functions", + name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +static tree +avr_handle_fntype_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_TYPE) + { + warning (OPT_Wattributes, "%qE attribute only applies to functions", + name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + + +/* AVR attributes. */ +static const struct attribute_spec +avr_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, + affects_type_identity } */ + { "progmem", 0, 0, false, false, false, avr_handle_progmem_attribute, + false }, + { "signal", 0, 0, true, false, false, avr_handle_fndecl_attribute, + false }, + { "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute, + false }, + { "naked", 0, 0, false, true, true, avr_handle_fntype_attribute, + false }, + { "OS_task", 0, 0, false, true, true, avr_handle_fntype_attribute, + false }, + { "OS_main", 0, 0, false, true, true, avr_handle_fntype_attribute, + false }, + { NULL, 0, 0, false, false, false, NULL, false } +}; + + +/* Look if DECL shall be placed in program memory space by + means of attribute `progmem' or some address-space qualifier. + Return non-zero if DECL is data that must end up in Flash and + zero if the data lives in RAM (.bss, .data, .rodata, ...). + + Return 2 if DECL is located in 24-bit flash address-space + Return 1 if DECL is located in 16-bit flash address-space + Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES + Return 0 otherwise */ + +int +avr_progmem_p (tree decl, tree attributes) +{ + tree a; + + if (TREE_CODE (decl) != VAR_DECL) + return 0; + + if (avr_decl_memx_p (decl)) + return 2; + + if (avr_decl_flash_p (decl)) + return 1; + + if (NULL_TREE + != lookup_attribute ("progmem", attributes)) + return -1; + + a = decl; + + do + a = TREE_TYPE(a); + while (TREE_CODE (a) == ARRAY_TYPE); + + if (a == error_mark_node) + return 0; + + if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a))) + return -1; + + return 0; +} + + +/* Scan type TYP for pointer references to address space ASn. + Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting + the AS are also declared to be CONST. + Otherwise, return the respective address space, i.e. a value != 0. */ + +static addr_space_t +avr_nonconst_pointer_addrspace (tree typ) +{ + while (ARRAY_TYPE == TREE_CODE (typ)) + typ = TREE_TYPE (typ); + + if (POINTER_TYPE_P (typ)) + { + addr_space_t as; + tree target = TREE_TYPE (typ); + + /* Pointer to function: Test the function's return type. */ + + if (FUNCTION_TYPE == TREE_CODE (target)) + return avr_nonconst_pointer_addrspace (TREE_TYPE (target)); + + /* "Ordinary" pointers... */ + + while (TREE_CODE (target) == ARRAY_TYPE) + target = TREE_TYPE (target); + + /* Pointers to non-generic address space must be const. + Refuse address spaces outside the device's flash. */ + + as = TYPE_ADDR_SPACE (target); + + if (!ADDR_SPACE_GENERIC_P (as) + && (!TYPE_READONLY (target) + || avr_addrspace[as].segment >= avr_current_device->n_flash)) + { + return as; + } + + /* Scan pointer's target type. */ + + return avr_nonconst_pointer_addrspace (target); + } + + return ADDR_SPACE_GENERIC; +} + + +/* Sanity check NODE so that all pointers targeting non-generic address spaces + go along with CONST qualifier. Writing to these address spaces should + be detected and complained about as early as possible. */ + +static bool +avr_pgm_check_var_decl (tree node) +{ + const char *reason = NULL; + + addr_space_t as = ADDR_SPACE_GENERIC; + + gcc_assert (as == 0); + + if (avr_log.progmem) + avr_edump ("%?: %t\n", node); + + switch (TREE_CODE (node)) + { + default: + break; + + case VAR_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "variable"; + break; + + case PARM_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "function parameter"; + break; + + case FIELD_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "structure field"; + break; + + case FUNCTION_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))), + as) + reason = "return type of function"; + break; + + case POINTER_TYPE: + if (as = avr_nonconst_pointer_addrspace (node), as) + reason = "pointer"; + break; + } + + if (reason) + { + if (avr_addrspace[as].segment >= avr_current_device->n_flash) + { + if (TYPE_P (node)) + error ("%qT uses address space %qs beyond flash of %qs", + node, avr_addrspace[as].name, avr_current_device->name); + else + error ("%s %q+D uses address space %qs beyond flash of %qs", + reason, node, avr_addrspace[as].name, + avr_current_device->name); + } + else + { + if (TYPE_P (node)) + error ("pointer targeting address space %qs must be const in %qT", + avr_addrspace[as].name, node); + else + error ("pointer targeting address space %qs must be const" + " in %s %q+D", + avr_addrspace[as].name, reason, node); + } + } + + return reason == NULL; +} + + +/* Add the section attribute if the variable is in progmem. */ + +static void +avr_insert_attributes (tree node, tree *attributes) +{ + avr_pgm_check_var_decl (node); + + if (TREE_CODE (node) == VAR_DECL + && (TREE_STATIC (node) || DECL_EXTERNAL (node)) + && avr_progmem_p (node, *attributes)) + { + addr_space_t as; + tree node0 = node; + + /* For C++, we have to peel arrays in order to get correct + determination of readonlyness. */ + + do + node0 = TREE_TYPE (node0); + while (TREE_CODE (node0) == ARRAY_TYPE); + + if (error_mark_node == node0) + return; + + as = TYPE_ADDR_SPACE (TREE_TYPE (node)); + + if (avr_addrspace[as].segment >= avr_current_device->n_flash) + { + error ("variable %q+D located in address space %qs" + " beyond flash of %qs", + node, avr_addrspace[as].name, avr_current_device->name); + } + + if (!TYPE_READONLY (node0) + && !TREE_READONLY (node)) + { + const char *reason = "__attribute__((progmem))"; + + if (!ADDR_SPACE_GENERIC_P (as)) + reason = avr_addrspace[as].name; + + if (avr_log.progmem) + avr_edump ("\n%?: %t\n%t\n", node, node0); + + error ("variable %q+D must be const in order to be put into" + " read-only section by means of %qs", node, reason); + } + } +} + + +/* Implement `ASM_OUTPUT_ALIGNED_DECL_LOCAL'. */ +/* Implement `ASM_OUTPUT_ALIGNED_DECL_COMMON'. */ +/* Track need of __do_clear_bss. */ + +void +avr_asm_output_aligned_decl_common (FILE * stream, + const_tree decl ATTRIBUTE_UNUSED, + const char *name, + unsigned HOST_WIDE_INT size, + unsigned int align, bool local_p) +{ + /* __gnu_lto_v1 etc. are just markers for the linker injected by toplev.c. + There is no need to trigger __do_clear_bss code for them. */ + + if (!STR_PREFIX_P (name, "__gnu_lto")) + avr_need_clear_bss_p = true; + + if (local_p) + ASM_OUTPUT_ALIGNED_LOCAL (stream, name, size, align); + else + ASM_OUTPUT_ALIGNED_COMMON (stream, name, size, align); +} + + +/* Unnamed section callback for data_section + to track need of __do_copy_data. */ + +static void +avr_output_data_section_asm_op (const void *data) +{ + avr_need_copy_data_p = true; + + /* Dispatch to default. */ + output_section_asm_op (data); +} + + +/* Unnamed section callback for bss_section + to track need of __do_clear_bss. */ + +static void +avr_output_bss_section_asm_op (const void *data) +{ + avr_need_clear_bss_p = true; + + /* Dispatch to default. */ + output_section_asm_op (data); +} + + +/* Unnamed section callback for progmem*.data sections. */ + +static void +avr_output_progmem_section_asm_op (const void *data) +{ + fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n", + (const char*) data); +} + + +/* Implement `TARGET_ASM_INIT_SECTIONS'. */ + +static void +avr_asm_init_sections (void) +{ + /* Set up a section for jump tables. Alignment is handled by + ASM_OUTPUT_BEFORE_CASE_LABEL. */ + + if (AVR_HAVE_JMP_CALL) + { + progmem_swtable_section + = get_unnamed_section (0, output_section_asm_op, + "\t.section\t.progmem.gcc_sw_table" + ",\"a\",@progbits"); + } + else + { + progmem_swtable_section + = get_unnamed_section (SECTION_CODE, output_section_asm_op, + "\t.section\t.progmem.gcc_sw_table" + ",\"ax\",@progbits"); + } + + /* Override section callbacks to keep track of `avr_need_clear_bss_p' + resp. `avr_need_copy_data_p'. */ + + readonly_data_section->unnamed.callback = avr_output_data_section_asm_op; + data_section->unnamed.callback = avr_output_data_section_asm_op; + bss_section->unnamed.callback = avr_output_bss_section_asm_op; +} + + +/* Implement `TARGET_ASM_FUNCTION_RODATA_SECTION'. */ + +static section* +avr_asm_function_rodata_section (tree decl) +{ + /* If a function is unused and optimized out by -ffunction-sections + and --gc-sections, ensure that the same will happen for its jump + tables by putting them into individual sections. */ + + unsigned int flags; + section * frodata; + + /* Get the frodata section from the default function in varasm.c + but treat function-associated data-like jump tables as code + rather than as user defined data. AVR has no constant pools. */ + { + int fdata = flag_data_sections; + + flag_data_sections = flag_function_sections; + frodata = default_function_rodata_section (decl); + flag_data_sections = fdata; + flags = frodata->common.flags; + } + + if (frodata != readonly_data_section + && flags & SECTION_NAMED) + { + /* Adjust section flags and replace section name prefix. */ + + unsigned int i; + + static const char* const prefix[] = + { + ".rodata", ".progmem.gcc_sw_table", + ".gnu.linkonce.r.", ".gnu.linkonce.t." + }; + + for (i = 0; i < sizeof (prefix) / sizeof (*prefix); i += 2) + { + const char * old_prefix = prefix[i]; + const char * new_prefix = prefix[i+1]; + const char * name = frodata->named.name; + + if (STR_PREFIX_P (name, old_prefix)) + { + const char *rname = ACONCAT ((new_prefix, + name + strlen (old_prefix), NULL)); + flags &= ~SECTION_CODE; + flags |= AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE; + + return get_section (rname, flags, frodata->named.decl); + } + } + } + + return progmem_swtable_section; +} + + +/* Implement `TARGET_ASM_NAMED_SECTION'. */ +/* Track need of __do_clear_bss, __do_copy_data for named sections. */ + +static void +avr_asm_named_section (const char *name, unsigned int flags, tree decl) +{ + if (flags & AVR_SECTION_PROGMEM) + { + addr_space_t as = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP; + const char *old_prefix = ".rodata"; + const char *new_prefix = avr_addrspace[as].section_name; + + if (STR_PREFIX_P (name, old_prefix)) + { + const char *sname = ACONCAT ((new_prefix, + name + strlen (old_prefix), NULL)); + default_elf_asm_named_section (sname, flags, decl); + return; + } + + default_elf_asm_named_section (new_prefix, flags, decl); + return; + } + + if (!avr_need_copy_data_p) + avr_need_copy_data_p = (STR_PREFIX_P (name, ".data") + || STR_PREFIX_P (name, ".rodata") + || STR_PREFIX_P (name, ".gnu.linkonce.d")); + + if (!avr_need_clear_bss_p) + avr_need_clear_bss_p = STR_PREFIX_P (name, ".bss"); + + default_elf_asm_named_section (name, flags, decl); +} + + +/* Implement `TARGET_SECTION_TYPE_FLAGS'. */ + +static unsigned int +avr_section_type_flags (tree decl, const char *name, int reloc) +{ + unsigned int flags = default_section_type_flags (decl, name, reloc); + + if (STR_PREFIX_P (name, ".noinit")) + { + if (decl && TREE_CODE (decl) == VAR_DECL + && DECL_INITIAL (decl) == NULL_TREE) + flags |= SECTION_BSS; /* @nobits */ + else + warning (0, "only uninitialized variables can be placed in the " + ".noinit section"); + } + + if (decl && DECL_P (decl) + && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + { + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); + + /* Attribute progmem puts data in generic address space. + Set section flags as if it was in __flash to get the right + section prefix in the remainder. */ + + if (ADDR_SPACE_GENERIC_P (as)) + as = ADDR_SPACE_FLASH; + + flags |= as * SECTION_MACH_DEP; + flags &= ~SECTION_WRITE; + flags &= ~SECTION_BSS; + } + + return flags; +} + + +/* Implement `TARGET_ENCODE_SECTION_INFO'. */ + +static void +avr_encode_section_info (tree decl, rtx rtl, int new_decl_p) +{ + /* In avr_handle_progmem_attribute, DECL_INITIAL is not yet + readily available, see PR34734. So we postpone the warning + about uninitialized data in program memory section until here. */ + + if (new_decl_p + && decl && DECL_P (decl) + && NULL_TREE == DECL_INITIAL (decl) + && !DECL_EXTERNAL (decl) + && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + { + warning (OPT_Wuninitialized, + "uninitialized variable %q+D put into " + "program memory area", decl); + } + + default_encode_section_info (decl, rtl, new_decl_p); + + if (decl && DECL_P (decl) + && TREE_CODE (decl) != FUNCTION_DECL + && MEM_P (rtl) + && SYMBOL_REF == GET_CODE (XEXP (rtl, 0))) + { + rtx sym = XEXP (rtl, 0); + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); + + /* PSTR strings are in generic space but located in flash: + patch address space. */ + + if (-1 == avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + as = ADDR_SPACE_FLASH; + + AVR_SYMBOL_SET_ADDR_SPACE (sym, as); + } +} + + +/* Implement `TARGET_ASM_SELECT_SECTION' */ + +static section * +avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) +{ + section * sect = default_elf_select_section (decl, reloc, align); + + if (decl && DECL_P (decl) + && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + { + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); + + /* __progmem__ goes in generic space but shall be allocated to + .progmem.data */ + + if (ADDR_SPACE_GENERIC_P (as)) + as = ADDR_SPACE_FLASH; + + if (sect->common.flags & SECTION_NAMED) + { + const char * name = sect->named.name; + const char * old_prefix = ".rodata"; + const char * new_prefix = avr_addrspace[as].section_name; + + if (STR_PREFIX_P (name, old_prefix)) + { + const char *sname = ACONCAT ((new_prefix, + name + strlen (old_prefix), NULL)); + return get_section (sname, sect->common.flags, sect->named.decl); + } + } + + if (!progmem_section[as]) + { + progmem_section[as] + = get_unnamed_section (0, avr_output_progmem_section_asm_op, + avr_addrspace[as].section_name); + } + + return progmem_section[as]; + } + + return sect; +} + +/* Implement `TARGET_ASM_FILE_START'. */ +/* Outputs some text at the start of each assembler file. */ + +static void +avr_file_start (void) +{ + int sfr_offset = avr_current_arch->sfr_offset; + + if (avr_current_arch->asm_only) + error ("MCU %qs supported for assembler only", avr_current_device->name); + + default_file_start (); + + /* Print I/O addresses of some SFRs used with IN and OUT. */ + + if (AVR_HAVE_SPH) + fprintf (asm_out_file, "__SP_H__ = 0x%02x\n", avr_addr.sp_h - sfr_offset); + + fprintf (asm_out_file, "__SP_L__ = 0x%02x\n", avr_addr.sp_l - sfr_offset); + fprintf (asm_out_file, "__SREG__ = 0x%02x\n", avr_addr.sreg - sfr_offset); + if (AVR_HAVE_RAMPZ) + fprintf (asm_out_file, "__RAMPZ__ = 0x%02x\n", avr_addr.rampz - sfr_offset); + if (AVR_HAVE_RAMPY) + fprintf (asm_out_file, "__RAMPY__ = 0x%02x\n", avr_addr.rampy - sfr_offset); + if (AVR_HAVE_RAMPX) + fprintf (asm_out_file, "__RAMPX__ = 0x%02x\n", avr_addr.rampx - sfr_offset); + if (AVR_HAVE_RAMPD) + fprintf (asm_out_file, "__RAMPD__ = 0x%02x\n", avr_addr.rampd - sfr_offset); + if (AVR_XMEGA) + fprintf (asm_out_file, "__CCP__ = 0x%02x\n", avr_addr.ccp - sfr_offset); + fprintf (asm_out_file, "__tmp_reg__ = %d\n", TMP_REGNO); + fprintf (asm_out_file, "__zero_reg__ = %d\n", ZERO_REGNO); +} + + +/* Implement `TARGET_ASM_FILE_END'. */ +/* Outputs to the stdio stream FILE some + appropriate text to go at the end of an assembler file. */ + +static void +avr_file_end (void) +{ + /* Output these only if there is anything in the + .data* / .rodata* / .gnu.linkonce.* resp. .bss* or COMMON + input section(s) - some code size can be saved by not + linking in the initialization code from libgcc if resp. + sections are empty, see PR18145. */ + + if (avr_need_copy_data_p) + fputs (".global __do_copy_data\n", asm_out_file); + + if (avr_need_clear_bss_p) + fputs (".global __do_clear_bss\n", asm_out_file); +} + + +/* Worker function for `ADJUST_REG_ALLOC_ORDER'. */ +/* Choose the order in which to allocate hard registers for + pseudo-registers local to a basic block. + + Store the desired register order in the array `reg_alloc_order'. + Element 0 should be the register to allocate first; element 1, the + next register; and so on. */ + +void +avr_adjust_reg_alloc_order (void) +{ + unsigned int i; + static const int order_0[] = + { + 24, 25, + 18, 19, 20, 21, 22, 23, + 30, 31, + 26, 27, 28, 29, + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 0, 1, + 32, 33, 34, 35 + }; + static const int order_1[] = + { + 18, 19, 20, 21, 22, 23, 24, 25, + 30, 31, + 26, 27, 28, 29, + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 0, 1, + 32, 33, 34, 35 + }; + static const int order_2[] = + { + 25, 24, 23, 22, 21, 20, 19, 18, + 30, 31, + 26, 27, 28, 29, + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0, + 32, 33, 34, 35 + }; + + const int *order = (TARGET_ORDER_1 ? order_1 : + TARGET_ORDER_2 ? order_2 : + order_0); + for (i = 0; i < ARRAY_SIZE (order_0); ++i) + reg_alloc_order[i] = order[i]; +} + + +/* Implement `TARGET_REGISTER_MOVE_COST' */ + +static int +avr_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t from, reg_class_t to) +{ + return (from == STACK_REG ? 6 + : to == STACK_REG ? 12 + : 2); +} + + +/* Implement `TARGET_MEMORY_MOVE_COST' */ + +static int +avr_memory_move_cost (enum machine_mode mode, + reg_class_t rclass ATTRIBUTE_UNUSED, + bool in ATTRIBUTE_UNUSED) +{ + return (mode == QImode ? 2 + : mode == HImode ? 4 + : mode == SImode ? 8 + : mode == SFmode ? 8 + : 16); +} + + +/* Mutually recursive subroutine of avr_rtx_cost for calculating the + cost of an RTX operand given its context. X is the rtx of the + operand, MODE is its mode, and OUTER is the rtx_code of this + operand's parent operator. */ + +static int +avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer, + int opno, bool speed) +{ + enum rtx_code code = GET_CODE (x); + int total; + + switch (code) + { + case REG: + case SUBREG: + return 0; + + case CONST_INT: + case CONST_FIXED: + case CONST_DOUBLE: + return COSTS_N_INSNS (GET_MODE_SIZE (mode)); + + default: + break; + } + + total = 0; + avr_rtx_costs (x, code, outer, opno, &total, speed); + return total; +} + +/* Worker function for AVR backend's rtx_cost function. + X is rtx expression whose cost is to be calculated. + Return true if the complete cost has been computed. + Return false if subexpressions should be scanned. + In either case, *TOTAL contains the cost result. */ + +static bool +avr_rtx_costs_1 (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, + int opno ATTRIBUTE_UNUSED, int *total, bool speed) +{ + enum rtx_code code = (enum rtx_code) codearg; + enum machine_mode mode = GET_MODE (x); + HOST_WIDE_INT val; + + switch (code) + { + case CONST_INT: + case CONST_FIXED: + case CONST_DOUBLE: + case SYMBOL_REF: + case CONST: + case LABEL_REF: + /* Immediate constants are as cheap as registers. */ + *total = 0; + return true; + + case MEM: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); + return true; + + case NEG: + switch (mode) + { + case QImode: + case SFmode: + *total = COSTS_N_INSNS (1); + break; + + case HImode: + case PSImode: + case SImode: + *total = COSTS_N_INSNS (2 * GET_MODE_SIZE (mode) - 1); + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case ABS: + switch (mode) + { + case QImode: + case SFmode: + *total = COSTS_N_INSNS (1); + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case NOT: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case ZERO_EXTEND: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) + - GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case SIGN_EXTEND: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) + 2 + - GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case PLUS: + switch (mode) + { + case QImode: + if (AVR_HAVE_MUL + && MULT == GET_CODE (XEXP (x, 0)) + && register_operand (XEXP (x, 1), QImode)) + { + /* multiply-add */ + *total = COSTS_N_INSNS (speed ? 4 : 3); + /* multiply-add with constant: will be split and load constant. */ + if (CONST_INT_P (XEXP (XEXP (x, 0), 1))) + *total = COSTS_N_INSNS (1) + *total; + return true; + } + *total = COSTS_N_INSNS (1); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + break; + + case HImode: + if (AVR_HAVE_MUL + && (MULT == GET_CODE (XEXP (x, 0)) + || ASHIFT == GET_CODE (XEXP (x, 0))) + && register_operand (XEXP (x, 1), HImode) + && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0)) + || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0)))) + { + /* multiply-add */ + *total = COSTS_N_INSNS (speed ? 5 : 4); + /* multiply-add with constant: will be split and load constant. */ + if (CONST_INT_P (XEXP (XEXP (x, 0), 1))) + *total = COSTS_N_INSNS (1) + *total; + return true; + } + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (2); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + break; + + case PSImode: + if (!CONST_INT_P (XEXP (x, 1))) + { + *total = COSTS_N_INSNS (3); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (3); + break; + + case SImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (4); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (4); + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case MINUS: + if (AVR_HAVE_MUL + && QImode == mode + && register_operand (XEXP (x, 0), QImode) + && MULT == GET_CODE (XEXP (x, 1))) + { + /* multiply-sub */ + *total = COSTS_N_INSNS (speed ? 4 : 3); + /* multiply-sub with constant: will be split and load constant. */ + if (CONST_INT_P (XEXP (XEXP (x, 1), 1))) + *total = COSTS_N_INSNS (1) + *total; + return true; + } + if (AVR_HAVE_MUL + && HImode == mode + && register_operand (XEXP (x, 0), HImode) + && (MULT == GET_CODE (XEXP (x, 1)) + || ASHIFT == GET_CODE (XEXP (x, 1))) + && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0)) + || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0)))) + { + /* multiply-sub */ + *total = COSTS_N_INSNS (speed ? 5 : 4); + /* multiply-sub with constant: will be split and load constant. */ + if (CONST_INT_P (XEXP (XEXP (x, 1), 1))) + *total = COSTS_N_INSNS (1) + *total; + return true; + } + /* FALLTHRU */ + case AND: + case IOR: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + return true; + + case XOR: + *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + return true; + + case MULT: + switch (mode) + { + case QImode: + if (AVR_HAVE_MUL) + *total = COSTS_N_INSNS (!speed ? 3 : 4); + else if (!speed) + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); + else + return false; + break; + + case HImode: + if (AVR_HAVE_MUL) + { + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); + bool ex0 = SIGN_EXTEND == code0 || ZERO_EXTEND == code0; + bool ex1 = SIGN_EXTEND == code1 || ZERO_EXTEND == code1; + + if (ex0 + && (u8_operand (op1, HImode) + || s8_operand (op1, HImode))) + { + *total = COSTS_N_INSNS (!speed ? 4 : 6); + return true; + } + if (ex0 + && register_operand (op1, HImode)) + { + *total = COSTS_N_INSNS (!speed ? 5 : 8); + return true; + } + else if (ex0 || ex1) + { + *total = COSTS_N_INSNS (!speed ? 3 : 5); + return true; + } + else if (register_operand (op0, HImode) + && (u8_operand (op1, HImode) + || s8_operand (op1, HImode))) + { + *total = COSTS_N_INSNS (!speed ? 6 : 9); + return true; + } + else + *total = COSTS_N_INSNS (!speed ? 7 : 10); + } + else if (!speed) + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); + else + return false; + break; + + case PSImode: + if (!speed) + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); + else + *total = 10; + break; + + case SImode: + if (AVR_HAVE_MUL) + { + if (!speed) + { + /* Add some additional costs besides CALL like moves etc. */ + + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4); + } + else + { + /* Just a rough estimate. Even with -O2 we don't want bulky + code expanded inline. */ + + *total = COSTS_N_INSNS (25); + } + } + else + { + if (speed) + *total = COSTS_N_INSNS (300); + else + /* Add some additional costs besides CALL like moves etc. */ + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4); + } + + return true; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + return true; + + case DIV: + case MOD: + case UDIV: + case UMOD: + if (!speed) + *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1); + else + *total = COSTS_N_INSNS (15 * GET_MODE_SIZE (mode)); + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + /* For div/mod with const-int divisor we have at least the cost of + loading the divisor. */ + if (CONST_INT_P (XEXP (x, 1))) + *total += COSTS_N_INSNS (GET_MODE_SIZE (mode)); + /* Add some overall penaly for clobbering and moving around registers */ + *total += COSTS_N_INSNS (2); + return true; + + case ROTATE: + switch (mode) + { + case QImode: + if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 4) + *total = COSTS_N_INSNS (1); + + break; + + case HImode: + if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 8) + *total = COSTS_N_INSNS (3); + + break; + + case SImode: + if (CONST_INT_P (XEXP (x, 1))) + switch (INTVAL (XEXP (x, 1))) + { + case 8: + case 24: + *total = COSTS_N_INSNS (5); + break; + case 16: + *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 6); + break; + } + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case ASHIFT: + switch (mode) + { + case QImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 4 : 17); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + { + val = INTVAL (XEXP (x, 1)); + if (val == 7) + *total = COSTS_N_INSNS (3); + else if (val >= 0 && val <= 7) + *total = COSTS_N_INSNS (val); + else + *total = COSTS_N_INSNS (1); + } + break; + + case HImode: + if (AVR_HAVE_MUL) + { + if (const_2_to_7_operand (XEXP (x, 1), HImode) + && (SIGN_EXTEND == GET_CODE (XEXP (x, 0)) + || ZERO_EXTEND == GET_CODE (XEXP (x, 0)))) + { + *total = COSTS_N_INSNS (!speed ? 4 : 6); + return true; + } + } + + if (const1_rtx == (XEXP (x, 1)) + && SIGN_EXTEND == GET_CODE (XEXP (x, 0))) + { + *total = COSTS_N_INSNS (2); + return true; + } + + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + case 8: + *total = COSTS_N_INSNS (2); + break; + case 9: + *total = COSTS_N_INSNS (3); + break; + case 2: + case 3: + case 10: + case 15: + *total = COSTS_N_INSNS (4); + break; + case 7: + case 11: + case 12: + *total = COSTS_N_INSNS (5); + break; + case 4: + *total = COSTS_N_INSNS (!speed ? 5 : 8); + break; + case 6: + *total = COSTS_N_INSNS (!speed ? 5 : 9); + break; + case 5: + *total = COSTS_N_INSNS (!speed ? 5 : 10); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + case PSImode: + if (!CONST_INT_P (XEXP (x, 1))) + { + *total = COSTS_N_INSNS (!speed ? 6 : 73); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + case 8: + case 16: + *total = COSTS_N_INSNS (3); + break; + case 23: + *total = COSTS_N_INSNS (5); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1))); + break; + } + break; + + case SImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 24: + *total = COSTS_N_INSNS (3); + break; + case 1: + case 8: + case 16: + *total = COSTS_N_INSNS (4); + break; + case 31: + *total = COSTS_N_INSNS (6); + break; + case 2: + *total = COSTS_N_INSNS (!speed ? 7 : 8); + break; + default: + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case ASHIFTRT: + switch (mode) + { + case QImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 4 : 17); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + { + val = INTVAL (XEXP (x, 1)); + if (val == 6) + *total = COSTS_N_INSNS (4); + else if (val == 7) + *total = COSTS_N_INSNS (2); + else if (val >= 0 && val <= 7) + *total = COSTS_N_INSNS (val); + else + *total = COSTS_N_INSNS (1); + } + break; + + case HImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + *total = COSTS_N_INSNS (2); + break; + case 15: + *total = COSTS_N_INSNS (3); + break; + case 2: + case 7: + case 8: + case 9: + *total = COSTS_N_INSNS (4); + break; + case 10: + case 14: + *total = COSTS_N_INSNS (5); + break; + case 11: + *total = COSTS_N_INSNS (!speed ? 5 : 6); + break; + case 12: + *total = COSTS_N_INSNS (!speed ? 5 : 7); + break; + case 6: + case 13: + *total = COSTS_N_INSNS (!speed ? 5 : 8); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + case PSImode: + if (!CONST_INT_P (XEXP (x, 1))) + { + *total = COSTS_N_INSNS (!speed ? 6 : 73); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + *total = COSTS_N_INSNS (3); + break; + case 16: + case 8: + *total = COSTS_N_INSNS (5); + break; + case 23: + *total = COSTS_N_INSNS (4); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1))); + break; + } + break; + + case SImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + *total = COSTS_N_INSNS (4); + break; + case 8: + case 16: + case 24: + *total = COSTS_N_INSNS (6); + break; + case 2: + *total = COSTS_N_INSNS (!speed ? 7 : 8); + break; + case 31: + *total = COSTS_N_INSNS (AVR_HAVE_MOVW ? 4 : 5); + break; + default: + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case LSHIFTRT: + switch (mode) + { + case QImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 4 : 17); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + { + val = INTVAL (XEXP (x, 1)); + if (val == 7) + *total = COSTS_N_INSNS (3); + else if (val >= 0 && val <= 7) + *total = COSTS_N_INSNS (val); + else + *total = COSTS_N_INSNS (1); + } + break; + + case HImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + case 8: + *total = COSTS_N_INSNS (2); + break; + case 9: + *total = COSTS_N_INSNS (3); + break; + case 2: + case 10: + case 15: + *total = COSTS_N_INSNS (4); + break; + case 7: + case 11: + *total = COSTS_N_INSNS (5); + break; + case 3: + case 12: + case 13: + case 14: + *total = COSTS_N_INSNS (!speed ? 5 : 6); + break; + case 4: + *total = COSTS_N_INSNS (!speed ? 5 : 7); + break; + case 5: + case 6: + *total = COSTS_N_INSNS (!speed ? 5 : 9); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 41); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + case PSImode: + if (!CONST_INT_P (XEXP (x, 1))) + { + *total = COSTS_N_INSNS (!speed ? 6 : 73); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + case 8: + case 16: + *total = COSTS_N_INSNS (3); + break; + case 23: + *total = COSTS_N_INSNS (5); + break; + default: + *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1))); + break; + } + break; + + case SImode: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + else + switch (INTVAL (XEXP (x, 1))) + { + case 0: + *total = 0; + break; + case 1: + *total = COSTS_N_INSNS (4); + break; + case 2: + *total = COSTS_N_INSNS (!speed ? 7 : 8); + break; + case 8: + case 16: + case 24: + *total = COSTS_N_INSNS (4); + break; + case 31: + *total = COSTS_N_INSNS (6); + break; + default: + *total = COSTS_N_INSNS (!speed ? 7 : 113); + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, + speed); + } + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case COMPARE: + switch (GET_MODE (XEXP (x, 0))) + { + case QImode: + *total = COSTS_N_INSNS (1); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + break; + + case HImode: + *total = COSTS_N_INSNS (2); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + else if (INTVAL (XEXP (x, 1)) != 0) + *total += COSTS_N_INSNS (1); + break; + + case PSImode: + *total = COSTS_N_INSNS (3); + if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) != 0) + *total += COSTS_N_INSNS (2); + break; + + case SImode: + *total = COSTS_N_INSNS (4); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed); + else if (INTVAL (XEXP (x, 1)) != 0) + *total += COSTS_N_INSNS (3); + break; + + default: + return false; + } + *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); + return true; + + case TRUNCATE: + if (AVR_HAVE_MUL + && LSHIFTRT == GET_CODE (XEXP (x, 0)) + && MULT == GET_CODE (XEXP (XEXP (x, 0), 0)) + && CONST_INT_P (XEXP (XEXP (x, 0), 1))) + { + if (QImode == mode || HImode == mode) + { + *total = COSTS_N_INSNS (2); + return true; + } + } + break; + + default: + break; + } + return false; +} + + +/* Implement `TARGET_RTX_COSTS'. */ + +static bool +avr_rtx_costs (rtx x, int codearg, int outer_code, + int opno, int *total, bool speed) +{ + bool done = avr_rtx_costs_1 (x, codearg, outer_code, + opno, total, speed); + + if (avr_log.rtx_costs) + { + avr_edump ("\n%?=%b (%s) total=%d, outer=%C:\n%r\n", + done, speed ? "speed" : "size", *total, outer_code, x); + } + + return done; +} + + +/* Implement `TARGET_ADDRESS_COST'. */ + +static int +avr_address_cost (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED, + addr_space_t as ATTRIBUTE_UNUSED, + bool speed ATTRIBUTE_UNUSED) +{ + int cost = 4; + + if (GET_CODE (x) == PLUS + && CONST_INT_P (XEXP (x, 1)) + && (REG_P (XEXP (x, 0)) + || GET_CODE (XEXP (x, 0)) == SUBREG)) + { + if (INTVAL (XEXP (x, 1)) >= 61) + cost = 18; + } + else if (CONSTANT_ADDRESS_P (x)) + { + if (optimize > 0 + && io_address_operand (x, QImode)) + cost = 2; + } + + if (avr_log.address_cost) + avr_edump ("\n%?: %d = %r\n", cost, x); + + return cost; +} + +/* Test for extra memory constraint 'Q'. + It's a memory address based on Y or Z pointer with valid displacement. */ + +int +extra_constraint_Q (rtx x) +{ + int ok = 0; + + if (GET_CODE (XEXP (x,0)) == PLUS + && REG_P (XEXP (XEXP (x,0), 0)) + && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT + && (INTVAL (XEXP (XEXP (x,0), 1)) + <= MAX_LD_OFFSET (GET_MODE (x)))) + { + rtx xx = XEXP (XEXP (x,0), 0); + int regno = REGNO (xx); + + ok = (/* allocate pseudos */ + regno >= FIRST_PSEUDO_REGISTER + /* strictly check */ + || regno == REG_Z || regno == REG_Y + /* XXX frame & arg pointer checks */ + || xx == frame_pointer_rtx + || xx == arg_pointer_rtx); + + if (avr_log.constraints) + avr_edump ("\n%?=%d reload_completed=%d reload_in_progress=%d\n %r\n", + ok, reload_completed, reload_in_progress, x); + } + + return ok; +} + +/* Convert condition code CONDITION to the valid AVR condition code. */ + +RTX_CODE +avr_normalize_condition (RTX_CODE condition) +{ + switch (condition) + { + case GT: + return GE; + case GTU: + return GEU; + case LE: + return LT; + case LEU: + return LTU; + default: + gcc_unreachable (); + } +} + +/* Helper function for `avr_reorg'. */ + +static rtx +avr_compare_pattern (rtx insn) +{ + rtx pattern = single_set (insn); + + if (pattern + && NONJUMP_INSN_P (insn) + && SET_DEST (pattern) == cc0_rtx + && GET_CODE (SET_SRC (pattern)) == COMPARE) + { + enum machine_mode mode0 = GET_MODE (XEXP (SET_SRC (pattern), 0)); + enum machine_mode mode1 = GET_MODE (XEXP (SET_SRC (pattern), 1)); + + /* The 64-bit comparisons have fixed operands ACC_A and ACC_B. + They must not be swapped, thus skip them. */ + + if ((mode0 == VOIDmode || GET_MODE_SIZE (mode0) <= 4) + && (mode1 == VOIDmode || GET_MODE_SIZE (mode1) <= 4)) + return pattern; + } + + return NULL_RTX; +} + +/* Helper function for `avr_reorg'. */ + +/* Expansion of switch/case decision trees leads to code like + + cc0 = compare (Reg, Num) + if (cc0 == 0) + goto L1 + + cc0 = compare (Reg, Num) + if (cc0 > 0) + goto L2 + + The second comparison is superfluous and can be deleted. + The second jump condition can be transformed from a + "difficult" one to a "simple" one because "cc0 > 0" and + "cc0 >= 0" will have the same effect here. + + This function relies on the way switch/case is being expaned + as binary decision tree. For example code see PR 49903. + + Return TRUE if optimization performed. + Return FALSE if nothing changed. + + INSN1 is a comparison, i.e. avr_compare_pattern != 0. + + We don't want to do this in text peephole because it is + tedious to work out jump offsets there and the second comparison + might have been transormed by `avr_reorg'. + + RTL peephole won't do because peephole2 does not scan across + basic blocks. */ + +static bool +avr_reorg_remove_redundant_compare (rtx insn1) +{ + rtx comp1, ifelse1, xcond1, branch1; + rtx comp2, ifelse2, xcond2, branch2, insn2; + enum rtx_code code; + rtx jump, target, cond; + + /* Look out for: compare1 - branch1 - compare2 - branch2 */ + + branch1 = next_nonnote_nondebug_insn (insn1); + if (!branch1 || !JUMP_P (branch1)) + return false; + + insn2 = next_nonnote_nondebug_insn (branch1); + if (!insn2 || !avr_compare_pattern (insn2)) + return false; + + branch2 = next_nonnote_nondebug_insn (insn2); + if (!branch2 || !JUMP_P (branch2)) + return false; + + comp1 = avr_compare_pattern (insn1); + comp2 = avr_compare_pattern (insn2); + xcond1 = single_set (branch1); + xcond2 = single_set (branch2); + + if (!comp1 || !comp2 + || !rtx_equal_p (comp1, comp2) + || !xcond1 || SET_DEST (xcond1) != pc_rtx + || !xcond2 || SET_DEST (xcond2) != pc_rtx + || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1)) + || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2))) + { + return false; + } + + comp1 = SET_SRC (comp1); + ifelse1 = SET_SRC (xcond1); + ifelse2 = SET_SRC (xcond2); + + /* comp is COMPARE now and ifelse is IF_THEN_ELSE. */ + + if (EQ != GET_CODE (XEXP (ifelse1, 0)) + || !REG_P (XEXP (comp1, 0)) + || !CONST_INT_P (XEXP (comp1, 1)) + || XEXP (ifelse1, 2) != pc_rtx + || XEXP (ifelse2, 2) != pc_rtx + || LABEL_REF != GET_CODE (XEXP (ifelse1, 1)) + || LABEL_REF != GET_CODE (XEXP (ifelse2, 1)) + || !COMPARISON_P (XEXP (ifelse2, 0)) + || cc0_rtx != XEXP (XEXP (ifelse1, 0), 0) + || cc0_rtx != XEXP (XEXP (ifelse2, 0), 0) + || const0_rtx != XEXP (XEXP (ifelse1, 0), 1) + || const0_rtx != XEXP (XEXP (ifelse2, 0), 1)) + { + return false; + } + + /* We filtered the insn sequence to look like + + (set (cc0) + (compare (reg:M N) + (const_int VAL))) + (set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref L1) + (pc))) + + (set (cc0) + (compare (reg:M N) + (const_int VAL))) + (set (pc) + (if_then_else (CODE (cc0) + (const_int 0)) + (label_ref L2) + (pc))) + */ + + code = GET_CODE (XEXP (ifelse2, 0)); + + /* Map GT/GTU to GE/GEU which is easier for AVR. + The first two instructions compare/branch on EQ + so we may replace the difficult + + if (x == VAL) goto L1; + if (x > VAL) goto L2; + + with easy + + if (x == VAL) goto L1; + if (x >= VAL) goto L2; + + Similarly, replace LE/LEU by LT/LTU. */ + + switch (code) + { + case EQ: + case LT: case LTU: + case GE: case GEU: + break; + + case LE: case LEU: + case GT: case GTU: + code = avr_normalize_condition (code); + break; + + default: + return false; + } + + /* Wrap the branches into UNSPECs so they won't be changed or + optimized in the remainder. */ + + target = XEXP (XEXP (ifelse1, 1), 0); + cond = XEXP (ifelse1, 0); + jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1); + + JUMP_LABEL (jump) = JUMP_LABEL (branch1); + + target = XEXP (XEXP (ifelse2, 1), 0); + cond = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx); + jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2); + + JUMP_LABEL (jump) = JUMP_LABEL (branch2); + + /* The comparisons in insn1 and insn2 are exactly the same; + insn2 is superfluous so delete it. */ + + delete_insn (insn2); + delete_insn (branch1); + delete_insn (branch2); + + return true; +} + + +/* Implement `TARGET_MACHINE_DEPENDENT_REORG'. */ +/* Optimize conditional jumps. */ + +static void +avr_reorg (void) +{ + rtx insn = get_insns(); + + for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn)) + { + rtx pattern = avr_compare_pattern (insn); + + if (!pattern) + continue; + + if (optimize + && avr_reorg_remove_redundant_compare (insn)) + { + continue; + } + + if (compare_diff_p (insn)) + { + /* Now we work under compare insn with difficult branch. */ + + rtx next = next_real_insn (insn); + rtx pat = PATTERN (next); + + pattern = SET_SRC (pattern); + + if (true_regnum (XEXP (pattern, 0)) >= 0 + && true_regnum (XEXP (pattern, 1)) >= 0) + { + rtx x = XEXP (pattern, 0); + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); + PUT_CODE (t, swap_condition (GET_CODE (t))); + XEXP (pattern, 0) = XEXP (pattern, 1); + XEXP (pattern, 1) = x; + INSN_CODE (next) = -1; + } + else if (true_regnum (XEXP (pattern, 0)) >= 0 + && XEXP (pattern, 1) == const0_rtx) + { + /* This is a tst insn, we can reverse it. */ + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); + + PUT_CODE (t, swap_condition (GET_CODE (t))); + XEXP (pattern, 1) = XEXP (pattern, 0); + XEXP (pattern, 0) = const0_rtx; + INSN_CODE (next) = -1; + INSN_CODE (insn) = -1; + } + else if (true_regnum (XEXP (pattern, 0)) >= 0 + && CONST_INT_P (XEXP (pattern, 1))) + { + rtx x = XEXP (pattern, 1); + rtx src = SET_SRC (pat); + rtx t = XEXP (src,0); + enum machine_mode mode = GET_MODE (XEXP (pattern, 0)); + + if (avr_simplify_comparison_p (mode, GET_CODE (t), x)) + { + XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode); + PUT_CODE (t, avr_normalize_condition (GET_CODE (t))); + INSN_CODE (next) = -1; + INSN_CODE (insn) = -1; + } + } + } + } +} + +/* Returns register number for function return value.*/ + +static inline unsigned int +avr_ret_register (void) +{ + return 24; +} + + +/* Implement `TARGET_FUNCTION_VALUE_REGNO_P'. */ + +static bool +avr_function_value_regno_p (const unsigned int regno) +{ + return (regno == avr_ret_register ()); +} + + +/* Implement `TARGET_LIBCALL_VALUE'. */ +/* Create an RTX representing the place where a + library function returns a value of mode MODE. */ + +static rtx +avr_libcall_value (enum machine_mode mode, + const_rtx func ATTRIBUTE_UNUSED) +{ + int offs = GET_MODE_SIZE (mode); + + if (offs <= 4) + offs = (offs + 1) & ~1; + + return gen_rtx_REG (mode, avr_ret_register () + 2 - offs); +} + + +/* Implement `TARGET_FUNCTION_VALUE'. */ +/* Create an RTX representing the place where a + function returns a value of data type VALTYPE. */ + +static rtx +avr_function_value (const_tree type, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + unsigned int offs; + + if (TYPE_MODE (type) != BLKmode) + return avr_libcall_value (TYPE_MODE (type), NULL_RTX); + + offs = int_size_in_bytes (type); + if (offs < 2) + offs = 2; + if (offs > 2 && offs < GET_MODE_SIZE (SImode)) + offs = GET_MODE_SIZE (SImode); + else if (offs > GET_MODE_SIZE (SImode) && offs < GET_MODE_SIZE (DImode)) + offs = GET_MODE_SIZE (DImode); + + return gen_rtx_REG (BLKmode, avr_ret_register () + 2 - offs); +} + +int +test_hard_reg_class (enum reg_class rclass, rtx x) +{ + int regno = true_regnum (x); + if (regno < 0) + return 0; + + if (TEST_HARD_REG_CLASS (rclass, regno)) + return 1; + + return 0; +} + + +/* Helper for jump_over_one_insn_p: Test if INSN is a 2-word instruction + and thus is suitable to be skipped by CPSE, SBRC, etc. */ + +static bool +avr_2word_insn_p (rtx insn) +{ + if (avr_current_device->errata_skip + || !insn + || 2 != get_attr_length (insn)) + { + return false; + } + + switch (INSN_CODE (insn)) + { + default: + return false; + + case CODE_FOR_movqi_insn: + case CODE_FOR_movuqq_insn: + case CODE_FOR_movqq_insn: + { + rtx set = single_set (insn); + rtx src = SET_SRC (set); + rtx dest = SET_DEST (set); + + /* Factor out LDS and STS from movqi_insn. */ + + if (MEM_P (dest) + && (REG_P (src) || src == CONST0_RTX (GET_MODE (dest)))) + { + return CONSTANT_ADDRESS_P (XEXP (dest, 0)); + } + else if (REG_P (dest) + && MEM_P (src)) + { + return CONSTANT_ADDRESS_P (XEXP (src, 0)); + } + + return false; + } + + case CODE_FOR_call_insn: + case CODE_FOR_call_value_insn: + return true; + } +} + + +int +jump_over_one_insn_p (rtx insn, rtx dest) +{ + int uid = INSN_UID (GET_CODE (dest) == LABEL_REF + ? XEXP (dest, 0) + : dest); + int jump_addr = INSN_ADDRESSES (INSN_UID (insn)); + int dest_addr = INSN_ADDRESSES (uid); + int jump_offset = dest_addr - jump_addr - get_attr_length (insn); + + return (jump_offset == 1 + || (jump_offset == 2 + && avr_2word_insn_p (next_active_insn (insn)))); +} + + +/* Worker function for `HARD_REGNO_MODE_OK'. */ +/* Returns 1 if a value of mode MODE can be stored starting with hard + register number REGNO. On the enhanced core, anything larger than + 1 byte must start in even numbered register for "movw" to work + (this way we don't have to check for odd registers everywhere). */ + +int +avr_hard_regno_mode_ok (int regno, enum machine_mode mode) +{ + /* NOTE: 8-bit values must not be disallowed for R28 or R29. + Disallowing QI et al. in these regs might lead to code like + (set (subreg:QI (reg:HI 28) n) ...) + which will result in wrong code because reload does not + handle SUBREGs of hard regsisters like this. + This could be fixed in reload. However, it appears + that fixing reload is not wanted by reload people. */ + + /* Any GENERAL_REGS register can hold 8-bit values. */ + + if (GET_MODE_SIZE (mode) == 1) + return 1; + + /* FIXME: Ideally, the following test is not needed. + However, it turned out that it can reduce the number + of spill fails. AVR and it's poor endowment with + address registers is extreme stress test for reload. */ + + if (GET_MODE_SIZE (mode) >= 4 + && regno >= REG_X) + return 0; + + /* All modes larger than 8 bits should start in an even register. */ + + return !(regno & 1); +} + + +/* Implement `HARD_REGNO_CALL_PART_CLOBBERED'. */ + +int +avr_hard_regno_call_part_clobbered (unsigned regno, enum machine_mode mode) +{ + /* FIXME: This hook gets called with MODE:REGNO combinations that don't + represent valid hard registers like, e.g. HI:29. Returning TRUE + for such registers can lead to performance degradation as mentioned + in PR53595. Thus, report invalid hard registers as FALSE. */ + + if (!avr_hard_regno_mode_ok (regno, mode)) + return 0; + + /* Return true if any of the following boundaries is crossed: + 17/18, 27/28 and 29/30. */ + + return ((regno < 18 && regno + GET_MODE_SIZE (mode) > 18) + || (regno < REG_Y && regno + GET_MODE_SIZE (mode) > REG_Y) + || (regno < REG_Z && regno + GET_MODE_SIZE (mode) > REG_Z)); +} + + +/* Implement `MODE_CODE_BASE_REG_CLASS'. */ + +enum reg_class +avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED, + addr_space_t as, RTX_CODE outer_code, + RTX_CODE index_code ATTRIBUTE_UNUSED) +{ + if (!ADDR_SPACE_GENERIC_P (as)) + { + return POINTER_Z_REGS; + } + + if (!avr_strict_X) + return reload_completed ? BASE_POINTER_REGS : POINTER_REGS; + + return PLUS == outer_code ? BASE_POINTER_REGS : POINTER_REGS; +} + + +/* Implement `REGNO_MODE_CODE_OK_FOR_BASE_P'. */ + +bool +avr_regno_mode_code_ok_for_base_p (int regno, + enum machine_mode mode ATTRIBUTE_UNUSED, + addr_space_t as ATTRIBUTE_UNUSED, + RTX_CODE outer_code, + RTX_CODE index_code ATTRIBUTE_UNUSED) +{ + bool ok = false; + + if (!ADDR_SPACE_GENERIC_P (as)) + { + if (regno < FIRST_PSEUDO_REGISTER + && regno == REG_Z) + { + return true; + } + + if (reg_renumber) + { + regno = reg_renumber[regno]; + + if (regno == REG_Z) + { + return true; + } + } + + return false; + } + + if (regno < FIRST_PSEUDO_REGISTER + && (regno == REG_X + || regno == REG_Y + || regno == REG_Z + || regno == ARG_POINTER_REGNUM)) + { + ok = true; + } + else if (reg_renumber) + { + regno = reg_renumber[regno]; + + if (regno == REG_X + || regno == REG_Y + || regno == REG_Z + || regno == ARG_POINTER_REGNUM) + { + ok = true; + } + } + + if (avr_strict_X + && PLUS == outer_code + && regno == REG_X) + { + ok = false; + } + + return ok; +} + + +/* A helper for `output_reload_insisf' and `output_reload_inhi'. */ +/* Set 32-bit register OP[0] to compile-time constant OP[1]. + CLOBBER_REG is a QI clobber register or NULL_RTX. + LEN == NULL: output instructions. + LEN != NULL: set *LEN to the length of the instruction sequence + (in words) printed with LEN = NULL. + If CLEAR_P is true, OP[0] had been cleard to Zero already. + If CLEAR_P is false, nothing is known about OP[0]. + + The effect on cc0 is as follows: + + Load 0 to any register except ZERO_REG : NONE + Load ld register with any value : NONE + Anything else: : CLOBBER */ + +static void +output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) +{ + rtx src = op[1]; + rtx dest = op[0]; + rtx xval, xdest[4]; + int ival[4]; + int clobber_val = 1234; + bool cooked_clobber_p = false; + bool set_p = false; + enum machine_mode mode = GET_MODE (dest); + int n, n_bytes = GET_MODE_SIZE (mode); + + gcc_assert (REG_P (dest) + && CONSTANT_P (src)); + + if (len) + *len = 0; + + /* (REG:SI 14) is special: It's neither in LD_REGS nor in NO_LD_REGS + but has some subregs that are in LD_REGS. Use the MSB (REG:QI 17). */ + + if (REGNO (dest) < 16 + && REGNO (dest) + GET_MODE_SIZE (mode) > 16) + { + clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1]; + } + + /* We might need a clobber reg but don't have one. Look at the value to + be loaded more closely. A clobber is only needed if it is a symbol + or contains a byte that is neither 0, -1 or a power of 2. */ + + if (NULL_RTX == clobber_reg + && !test_hard_reg_class (LD_REGS, dest) + && (! (CONST_INT_P (src) || CONST_FIXED_P (src) || CONST_DOUBLE_P (src)) + || !avr_popcount_each_byte (src, n_bytes, + (1 << 0) | (1 << 1) | (1 << 8)))) + { + /* We have no clobber register but need one. Cook one up. + That's cheaper than loading from constant pool. */ + + cooked_clobber_p = true; + clobber_reg = all_regs_rtx[REG_Z + 1]; + avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1); + } + + /* Now start filling DEST from LSB to MSB. */ + + for (n = 0; n < n_bytes; n++) + { + int ldreg_p; + bool done_byte = false; + int j; + rtx xop[3]; + + /* Crop the n-th destination byte. */ + + xdest[n] = simplify_gen_subreg (QImode, dest, mode, n); + ldreg_p = test_hard_reg_class (LD_REGS, xdest[n]); + + if (!CONST_INT_P (src) + && !CONST_FIXED_P (src) + && !CONST_DOUBLE_P (src)) + { + static const char* const asm_code[][2] = + { + { "ldi %2,lo8(%1)" CR_TAB "mov %0,%2", "ldi %0,lo8(%1)" }, + { "ldi %2,hi8(%1)" CR_TAB "mov %0,%2", "ldi %0,hi8(%1)" }, + { "ldi %2,hlo8(%1)" CR_TAB "mov %0,%2", "ldi %0,hlo8(%1)" }, + { "ldi %2,hhi8(%1)" CR_TAB "mov %0,%2", "ldi %0,hhi8(%1)" } + }; + + xop[0] = xdest[n]; + xop[1] = src; + xop[2] = clobber_reg; + + avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2); + + continue; + } + + /* Crop the n-th source byte. */ + + xval = simplify_gen_subreg (QImode, src, mode, n); + ival[n] = INTVAL (xval); + + /* Look if we can reuse the low word by means of MOVW. */ + + if (n == 2 + && n_bytes >= 4 + && AVR_HAVE_MOVW) + { + rtx lo16 = simplify_gen_subreg (HImode, src, mode, 0); + rtx hi16 = simplify_gen_subreg (HImode, src, mode, 2); + + if (INTVAL (lo16) == INTVAL (hi16)) + { + if (0 != INTVAL (lo16) + || !clear_p) + { + avr_asm_len ("movw %C0,%A0", &op[0], len, 1); + } + + break; + } + } + + /* Don't use CLR so that cc0 is set as expected. */ + + if (ival[n] == 0) + { + if (!clear_p) + avr_asm_len (ldreg_p ? "ldi %0,0" + : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0" + : "mov %0,__zero_reg__", + &xdest[n], len, 1); + continue; + } + + if (clobber_val == ival[n] + && REGNO (clobber_reg) == REGNO (xdest[n])) + { + continue; + } + + /* LD_REGS can use LDI to move a constant value */ + + if (ldreg_p) + { + xop[0] = xdest[n]; + xop[1] = xval; + avr_asm_len ("ldi %0,lo8(%1)", xop, len, 1); + continue; + } + + /* Try to reuse value already loaded in some lower byte. */ + + for (j = 0; j < n; j++) + if (ival[j] == ival[n]) + { + xop[0] = xdest[n]; + xop[1] = xdest[j]; + + avr_asm_len ("mov %0,%1", xop, len, 1); + done_byte = true; + break; + } + + if (done_byte) + continue; + + /* Need no clobber reg for -1: Use CLR/DEC */ + + if (-1 == ival[n]) + { + if (!clear_p) + avr_asm_len ("clr %0", &xdest[n], len, 1); + + avr_asm_len ("dec %0", &xdest[n], len, 1); + continue; + } + else if (1 == ival[n]) + { + if (!clear_p) + avr_asm_len ("clr %0", &xdest[n], len, 1); + + avr_asm_len ("inc %0", &xdest[n], len, 1); + continue; + } + + /* Use T flag or INC to manage powers of 2 if we have + no clobber reg. */ + + if (NULL_RTX == clobber_reg + && single_one_operand (xval, QImode)) + { + xop[0] = xdest[n]; + xop[1] = GEN_INT (exact_log2 (ival[n] & GET_MODE_MASK (QImode))); + + gcc_assert (constm1_rtx != xop[1]); + + if (!set_p) + { + set_p = true; + avr_asm_len ("set", xop, len, 1); + } + + if (!clear_p) + avr_asm_len ("clr %0", xop, len, 1); + + avr_asm_len ("bld %0,%1", xop, len, 1); + continue; + } + + /* We actually need the LD_REGS clobber reg. */ + + gcc_assert (NULL_RTX != clobber_reg); + + xop[0] = xdest[n]; + xop[1] = xval; + xop[2] = clobber_reg; + clobber_val = ival[n]; + + avr_asm_len ("ldi %2,lo8(%1)" CR_TAB + "mov %0,%2", xop, len, 2); + } + + /* If we cooked up a clobber reg above, restore it. */ + + if (cooked_clobber_p) + { + avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1); + } +} + + +/* Reload the constant OP[1] into the HI register OP[0]. + CLOBBER_REG is a QI clobber reg needed to move vast majority of consts + into a NO_LD_REGS register. If CLOBBER_REG is NULL_RTX we either don't + need a clobber reg or have to cook one up. + + PLEN == NULL: Output instructions. + PLEN != NULL: Output nothing. Set *PLEN to number of words occupied + by the insns printed. + + Return "". */ + +const char* +output_reload_inhi (rtx *op, rtx clobber_reg, int *plen) +{ + output_reload_in_const (op, clobber_reg, plen, false); + return ""; +} + + +/* Reload a SI or SF compile time constant OP[1] into the register OP[0]. + CLOBBER_REG is a QI clobber reg needed to move vast majority of consts + into a NO_LD_REGS register. If CLOBBER_REG is NULL_RTX we either don't + need a clobber reg or have to cook one up. + + LEN == NULL: Output instructions. + + LEN != NULL: Output nothing. Set *LEN to number of words occupied + by the insns printed. + + Return "". */ + +const char * +output_reload_insisf (rtx *op, rtx clobber_reg, int *len) +{ + if (AVR_HAVE_MOVW + && !test_hard_reg_class (LD_REGS, op[0]) + && (CONST_INT_P (op[1]) + || CONST_FIXED_P (op[1]) + || CONST_DOUBLE_P (op[1]))) + { + int len_clr, len_noclr; + + /* In some cases it is better to clear the destination beforehand, e.g. + + CLR R2 CLR R3 MOVW R4,R2 INC R2 + + is shorther than + + CLR R2 INC R2 CLR R3 CLR R4 CLR R5 + + We find it too tedious to work that out in the print function. + Instead, we call the print function twice to get the lengths of + both methods and use the shortest one. */ + + output_reload_in_const (op, clobber_reg, &len_clr, true); + output_reload_in_const (op, clobber_reg, &len_noclr, false); + + if (len_noclr - len_clr == 4) + { + /* Default needs 4 CLR instructions: clear register beforehand. */ + + avr_asm_len ("mov %A0,__zero_reg__" CR_TAB + "mov %B0,__zero_reg__" CR_TAB + "movw %C0,%A0", &op[0], len, 3); + + output_reload_in_const (op, clobber_reg, len, true); + + if (len) + *len += 3; + + return ""; + } + } + + /* Default: destination not pre-cleared. */ + + output_reload_in_const (op, clobber_reg, len, false); + return ""; +} + +const char* +avr_out_reload_inpsi (rtx *op, rtx clobber_reg, int *len) +{ + output_reload_in_const (op, clobber_reg, len, false); + return ""; +} + + +/* Worker function for `ASM_OUTPUT_ADDR_VEC_ELT'. */ + +void +avr_output_addr_vec_elt (FILE *stream, int value) +{ + if (AVR_HAVE_JMP_CALL) + fprintf (stream, "\t.word gs(.L%d)\n", value); + else + fprintf (stream, "\trjmp .L%d\n", value); +} + + +/* Implement `TARGET_HARD_REGNO_SCRATCH_OK'. */ +/* Returns true if SCRATCH are safe to be allocated as a scratch + registers (for a define_peephole2) in the current function. */ + +static bool +avr_hard_regno_scratch_ok (unsigned int regno) +{ + /* Interrupt functions can only use registers that have already been saved + by the prologue, even if they would normally be call-clobbered. */ + + if ((cfun->machine->is_interrupt || cfun->machine->is_signal) + && !df_regs_ever_live_p (regno)) + return false; + + /* Don't allow hard registers that might be part of the frame pointer. + Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM + and don't care for a frame pointer that spans more than one register. */ + + if ((!reload_completed || frame_pointer_needed) + && (regno == REG_Y || regno == REG_Y + 1)) + { + return false; + } + + return true; +} + + +/* Worker function for `HARD_REGNO_RENAME_OK'. */ +/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ + +int +avr_hard_regno_rename_ok (unsigned int old_reg, + unsigned int new_reg) +{ + /* Interrupt functions can only use registers that have already been + saved by the prologue, even if they would normally be + call-clobbered. */ + + if ((cfun->machine->is_interrupt || cfun->machine->is_signal) + && !df_regs_ever_live_p (new_reg)) + return 0; + + /* Don't allow hard registers that might be part of the frame pointer. + Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM + and don't care for a frame pointer that spans more than one register. */ + + if ((!reload_completed || frame_pointer_needed) + && (old_reg == REG_Y || old_reg == REG_Y + 1 + || new_reg == REG_Y || new_reg == REG_Y + 1)) + { + return 0; + } + + return 1; +} + +/* Output a branch that tests a single bit of a register (QI, HI, SI or DImode) + or memory location in the I/O space (QImode only). + + Operand 0: comparison operator (must be EQ or NE, compare bit to zero). + Operand 1: register operand to test, or CONST_INT memory address. + Operand 2: bit number. + Operand 3: label to jump to if the test is true. */ + +const char* +avr_out_sbxx_branch (rtx insn, rtx operands[]) +{ + enum rtx_code comp = GET_CODE (operands[0]); + bool long_jump = get_attr_length (insn) >= 4; + bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); + + if (comp == GE) + comp = EQ; + else if (comp == LT) + comp = NE; + + if (reverse) + comp = reverse_condition (comp); + + switch (GET_CODE (operands[1])) + { + default: + gcc_unreachable(); + + case CONST_INT: + + if (low_io_address_operand (operands[1], QImode)) + { + if (comp == EQ) + output_asm_insn ("sbis %i1,%2", operands); + else + output_asm_insn ("sbic %i1,%2", operands); + } + else + { + output_asm_insn ("in __tmp_reg__,%i1", operands); + if (comp == EQ) + output_asm_insn ("sbrs __tmp_reg__,%2", operands); + else + output_asm_insn ("sbrc __tmp_reg__,%2", operands); + } + + break; /* CONST_INT */ + + case REG: + + if (comp == EQ) + output_asm_insn ("sbrs %T1%T2", operands); + else + output_asm_insn ("sbrc %T1%T2", operands); + + break; /* REG */ + } /* switch */ + + if (long_jump) + return ("rjmp .+4" CR_TAB + "jmp %x3"); + + if (!reverse) + return "rjmp %x3"; + + return ""; +} + +/* Worker function for `TARGET_ASM_CONSTRUCTOR'. */ + +static void +avr_asm_out_ctor (rtx symbol, int priority) +{ + fputs ("\t.global __do_global_ctors\n", asm_out_file); + default_ctor_section_asm_out_constructor (symbol, priority); +} + + +/* Worker function for `TARGET_ASM_DESTRUCTOR'. */ + +static void +avr_asm_out_dtor (rtx symbol, int priority) +{ + fputs ("\t.global __do_global_dtors\n", asm_out_file); + default_dtor_section_asm_out_destructor (symbol, priority); +} + + +/* Worker function for `TARGET_RETURN_IN_MEMORY'. */ + +static bool +avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +{ + if (TYPE_MODE (type) == BLKmode) + { + HOST_WIDE_INT size = int_size_in_bytes (type); + return (size == -1 || size > 8); + } + else + return false; +} + + +/* Implement `CASE_VALUES_THRESHOLD'. */ +/* Supply the default for --param case-values-threshold=0 */ + +static unsigned int +avr_case_values_threshold (void) +{ + /* The exact break-even point between a jump table and an if-else tree + depends on several factors not available here like, e.g. if 8-bit + comparisons can be used in the if-else tree or not, on the + range of the case values, if the case value can be reused, on the + register allocation, etc. '7' appears to be a good choice. */ + + return 7; +} + + +/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */ + +static enum machine_mode +avr_addr_space_address_mode (addr_space_t as) +{ + return avr_addrspace[as].pointer_size == 3 ? PSImode : HImode; +} + + +/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */ + +static enum machine_mode +avr_addr_space_pointer_mode (addr_space_t as) +{ + return avr_addr_space_address_mode (as); +} + + +/* Helper for following function. */ + +static bool +avr_reg_ok_for_pgm_addr (rtx reg, bool strict) +{ + gcc_assert (REG_P (reg)); + + if (strict) + { + return REGNO (reg) == REG_Z; + } + + /* Avoid combine to propagate hard regs. */ + + if (can_create_pseudo_p() + && REGNO (reg) < REG_Z) + { + return false; + } + + return true; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */ + +static bool +avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, + bool strict, addr_space_t as) +{ + bool ok = false; + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + return avr_legitimate_address_p (mode, x, strict); + + case ADDR_SPACE_FLASH: + case ADDR_SPACE_FLASH1: + case ADDR_SPACE_FLASH2: + case ADDR_SPACE_FLASH3: + case ADDR_SPACE_FLASH4: + case ADDR_SPACE_FLASH5: + + switch (GET_CODE (x)) + { + case REG: + ok = avr_reg_ok_for_pgm_addr (x, strict); + break; + + case POST_INC: + ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict); + break; + + default: + break; + } + + break; /* FLASH */ + + case ADDR_SPACE_MEMX: + if (REG_P (x)) + ok = (!strict + && can_create_pseudo_p()); + + if (LO_SUM == GET_CODE (x)) + { + rtx hi = XEXP (x, 0); + rtx lo = XEXP (x, 1); + + ok = (REG_P (hi) + && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER) + && REG_P (lo) + && REGNO (lo) == REG_Z); + } + + break; /* MEMX */ + } + + if (avr_log.legitimate_address_p) + { + avr_edump ("\n%?: ret=%b, mode=%m strict=%d " + "reload_completed=%d reload_in_progress=%d %s:", + ok, mode, strict, reload_completed, reload_in_progress, + reg_renumber ? "(reg_renumber)" : ""); + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode)) + && reg_renumber) + { + avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)), + true_regnum (XEXP (x, 0))); + } + + avr_edump ("\n%r\n", x); + } + + return ok; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'. */ + +static rtx +avr_addr_space_legitimize_address (rtx x, rtx old_x, + enum machine_mode mode, addr_space_t as) +{ + if (ADDR_SPACE_GENERIC_P (as)) + return avr_legitimize_address (x, old_x, mode); + + if (avr_log.legitimize_address) + { + avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x); + } + + return old_x; +} + + +/* Implement `TARGET_ADDR_SPACE_CONVERT'. */ + +static rtx +avr_addr_space_convert (rtx src, tree type_from, tree type_to) +{ + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from)); + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to)); + + if (avr_log.progmem) + avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n", + src, type_from, type_to); + + /* Up-casting from 16-bit to 24-bit pointer. */ + + if (as_from != ADDR_SPACE_MEMX + && as_to == ADDR_SPACE_MEMX) + { + int msb; + rtx sym = src; + rtx reg = gen_reg_rtx (PSImode); + + while (CONST == GET_CODE (sym) || PLUS == GET_CODE (sym)) + sym = XEXP (sym, 0); + + /* Look at symbol flags: avr_encode_section_info set the flags + also if attribute progmem was seen so that we get the right + promotion for, e.g. PSTR-like strings that reside in generic space + but are located in flash. In that case we patch the incoming + address space. */ + + if (SYMBOL_REF == GET_CODE (sym) + && ADDR_SPACE_FLASH == AVR_SYMBOL_GET_ADDR_SPACE (sym)) + { + as_from = ADDR_SPACE_FLASH; + } + + /* Linearize memory: RAM has bit 23 set. */ + + msb = ADDR_SPACE_GENERIC_P (as_from) + ? 0x80 + : avr_addrspace[as_from].segment; + + src = force_reg (Pmode, src); + + emit_insn (msb == 0 + ? gen_zero_extendhipsi2 (reg, src) + : gen_n_extendhipsi2 (reg, gen_int_mode (msb, QImode), src)); + + return reg; + } + + /* Down-casting from 24-bit to 16-bit throws away the high byte. */ + + if (as_from == ADDR_SPACE_MEMX + && as_to != ADDR_SPACE_MEMX) + { + rtx new_src = gen_reg_rtx (Pmode); + + src = force_reg (PSImode, src); + + emit_move_insn (new_src, + simplify_gen_subreg (Pmode, src, PSImode, 0)); + return new_src; + } + + return src; +} + + +/* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */ + +static bool +avr_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED, + addr_space_t superset ATTRIBUTE_UNUSED) +{ + /* Allow any kind of pointer mess. */ + + return true; +} + + +/* Implement `TARGET_CONVERT_TO_TYPE'. */ + +static tree +avr_convert_to_type (tree type, tree expr) +{ + /* Print a diagnose for pointer conversion that changes the address + space of the pointer target to a non-enclosing address space, + provided -Waddr-space-convert is on. + + FIXME: Filter out cases where the target object is known to + be located in the right memory, like in + + (const __flash*) PSTR ("text") + + Also try to distinguish between explicit casts requested by + the user and implicit casts like + + void f (const __flash char*); + + void g (const char *p) + { + f ((const __flash*) p); + } + + under the assumption that an explicit casts means that the user + knows what he is doing, e.g. interface with PSTR or old style + code with progmem and pgm_read_xxx. + */ + + if (avr_warn_addr_space_convert + && expr != error_mark_node + && POINTER_TYPE_P (type) + && POINTER_TYPE_P (TREE_TYPE (expr))) + { + addr_space_t as_old = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr))); + addr_space_t as_new = TYPE_ADDR_SPACE (TREE_TYPE (type)); + + if (avr_log.progmem) + avr_edump ("%?: type = %t\nexpr = %t\n\n", type, expr); + + if (as_new != ADDR_SPACE_MEMX + && as_new != as_old) + { + location_t loc = EXPR_LOCATION (expr); + const char *name_old = avr_addrspace[as_old].name; + const char *name_new = avr_addrspace[as_new].name; + + warning (OPT_Waddr_space_convert, + "conversion from address space %qs to address space %qs", + ADDR_SPACE_GENERIC_P (as_old) ? "generic" : name_old, + ADDR_SPACE_GENERIC_P (as_new) ? "generic" : name_new); + + return fold_build1_loc (loc, ADDR_SPACE_CONVERT_EXPR, type, expr); + } + } + + return NULL_TREE; +} + + +/* Worker function for movmemhi expander. + XOP[0] Destination as MEM:BLK + XOP[1] Source " " + XOP[2] # Bytes to copy + + Return TRUE if the expansion is accomplished. + Return FALSE if the operand compination is not supported. */ + +bool +avr_emit_movmemhi (rtx *xop) +{ + HOST_WIDE_INT count; + enum machine_mode loop_mode; + addr_space_t as = MEM_ADDR_SPACE (xop[1]); + rtx loop_reg, addr1, a_src, a_dest, insn, xas; + rtx a_hi8 = NULL_RTX; + + if (avr_mem_flash_p (xop[0])) + return false; + + if (!CONST_INT_P (xop[2])) + return false; + + count = INTVAL (xop[2]); + if (count <= 0) + return false; + + a_src = XEXP (xop[1], 0); + a_dest = XEXP (xop[0], 0); + + if (PSImode == GET_MODE (a_src)) + { + gcc_assert (as == ADDR_SPACE_MEMX); + + loop_mode = (count < 0x100) ? QImode : HImode; + loop_reg = gen_rtx_REG (loop_mode, 24); + emit_move_insn (loop_reg, gen_int_mode (count, loop_mode)); + + addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0); + a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2); + } + else + { + int segment = avr_addrspace[as].segment; + + if (segment + && avr_current_device->n_flash > 1) + { + a_hi8 = GEN_INT (segment); + emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8)); + } + else if (!ADDR_SPACE_GENERIC_P (as)) + { + as = ADDR_SPACE_FLASH; + } + + addr1 = a_src; + + loop_mode = (count <= 0x100) ? QImode : HImode; + loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode)); + } + + xas = GEN_INT (as); + + /* FIXME: Register allocator might come up with spill fails if it is left + on its own. Thus, we allocate the pointer registers by hand: + Z = source address + X = destination address */ + + emit_move_insn (lpm_addr_reg_rtx, addr1); + emit_move_insn (gen_rtx_REG (HImode, REG_X), a_dest); + + /* FIXME: Register allocator does a bad job and might spill address + register(s) inside the loop leading to additional move instruction + to/from stack which could clobber tmp_reg. Thus, do *not* emit + load and store as separate insns. Instead, we perform the copy + by means of one monolithic insn. */ + + gcc_assert (TMP_REGNO == LPM_REGNO); + + if (as != ADDR_SPACE_MEMX) + { + /* Load instruction ([E]LPM or LD) is known at compile time: + Do the copy-loop inline. */ + + rtx (*fun) (rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; + + insn = fun (xas, loop_reg, loop_reg); + } + else + { + rtx (*fun) (rtx, rtx) + = QImode == loop_mode ? gen_movmemx_qi : gen_movmemx_hi; + + emit_move_insn (gen_rtx_REG (QImode, 23), a_hi8); + + insn = fun (xas, GEN_INT (avr_addr.rampz)); + } + + set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as); + emit_insn (insn); + + return true; +} + + +/* Print assembler for movmem_qi, movmem_hi insns... + $0 : Address Space + $1, $2 : Loop register + Z : Source address + X : Destination address +*/ + +const char* +avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen) +{ + addr_space_t as = (addr_space_t) INTVAL (op[0]); + enum machine_mode loop_mode = GET_MODE (op[1]); + bool sbiw_p = test_hard_reg_class (ADDW_REGS, op[1]); + rtx xop[3]; + + if (plen) + *plen = 0; + + xop[0] = op[0]; + xop[1] = op[1]; + xop[2] = tmp_reg_rtx; + + /* Loop label */ + + avr_asm_len ("0:", xop, plen, 0); + + /* Load with post-increment */ + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + + avr_asm_len ("ld %2,Z+", xop, plen, 1); + break; + + case ADDR_SPACE_FLASH: + + if (AVR_HAVE_LPMX) + avr_asm_len ("lpm %2,Z+", xop, plen, 1); + else + avr_asm_len ("lpm" CR_TAB + "adiw r30,1", xop, plen, 2); + break; + + case ADDR_SPACE_FLASH1: + case ADDR_SPACE_FLASH2: + case ADDR_SPACE_FLASH3: + case ADDR_SPACE_FLASH4: + case ADDR_SPACE_FLASH5: + + if (AVR_HAVE_ELPMX) + avr_asm_len ("elpm %2,Z+", xop, plen, 1); + else + avr_asm_len ("elpm" CR_TAB + "adiw r30,1", xop, plen, 2); + break; + } + + /* Store with post-increment */ + + avr_asm_len ("st X+,%2", xop, plen, 1); + + /* Decrement loop-counter and set Z-flag */ + + if (QImode == loop_mode) + { + avr_asm_len ("dec %1", xop, plen, 1); + } + else if (sbiw_p) + { + avr_asm_len ("sbiw %1,1", xop, plen, 1); + } + else + { + avr_asm_len ("subi %A1,1" CR_TAB + "sbci %B1,0", xop, plen, 2); + } + + /* Loop until zero */ + + return avr_asm_len ("brne 0b", xop, plen, 1); +} + + + +/* Helper for __builtin_avr_delay_cycles */ + +static rtx +avr_mem_clobber (void) +{ + rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (mem) = 1; + return mem; +} + +static void +avr_expand_delay_cycles (rtx operands0) +{ + unsigned HOST_WIDE_INT cycles = UINTVAL (operands0) & GET_MODE_MASK (SImode); + 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), + avr_mem_clobber())); + 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), + avr_mem_clobber())); + 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), + avr_mem_clobber())); + 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), + avr_mem_clobber())); + 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--; + } +} + + +/* Return VAL * BASE + DIGIT. BASE = 0 is shortcut for BASE = 2^{32} */ + +static double_int +avr_double_int_push_digit (double_int val, int base, + unsigned HOST_WIDE_INT digit) +{ + val = 0 == base + ? val.llshift (32, 64) + : val * double_int::from_uhwi (base); + + return val + double_int::from_uhwi (digit); +} + + +/* Compute the image of x under f, i.e. perform x --> f(x) */ + +static int +avr_map (double_int f, int x) +{ + return 0xf & f.lrshift (4*x, 64).to_uhwi (); +} + + +/* Return some metrics of map A. */ + +enum + { + /* Number of fixed points in { 0 ... 7 } */ + MAP_FIXED_0_7, + + /* Size of preimage of non-fixed points in { 0 ... 7 } */ + MAP_NONFIXED_0_7, + + /* Mask representing the fixed points in { 0 ... 7 } */ + MAP_MASK_FIXED_0_7, + + /* Size of the preimage of { 0 ... 7 } */ + MAP_PREIMAGE_0_7, + + /* Mask that represents the preimage of { f } */ + MAP_MASK_PREIMAGE_F + }; + +static unsigned +avr_map_metric (double_int a, int mode) +{ + unsigned i, metric = 0; + + for (i = 0; i < 8; i++) + { + unsigned ai = avr_map (a, i); + + if (mode == MAP_FIXED_0_7) + metric += ai == i; + else if (mode == MAP_NONFIXED_0_7) + metric += ai < 8 && ai != i; + else if (mode == MAP_MASK_FIXED_0_7) + metric |= ((unsigned) (ai == i)) << i; + else if (mode == MAP_PREIMAGE_0_7) + metric += ai < 8; + else if (mode == MAP_MASK_PREIMAGE_F) + metric |= ((unsigned) (ai == 0xf)) << i; + else + gcc_unreachable(); + } + + return metric; +} + + +/* Return true if IVAL has a 0xf in its hexadecimal representation + and false, otherwise. Only nibbles 0..7 are taken into account. + Used as constraint helper for C0f and Cxf. */ + +bool +avr_has_nibble_0xf (rtx ival) +{ + return 0 != avr_map_metric (rtx_to_double_int (ival), MAP_MASK_PREIMAGE_F); +} + + +/* We have a set of bits that are mapped by a function F. + Try to decompose F by means of a second function G so that + + F = F o G^-1 o G + + and + + cost (F o G^-1) + cost (G) < cost (F) + + Example: Suppose builtin insert_bits supplies us with the map + F = 0x3210ffff. Instead of doing 4 bit insertions to get the high + nibble of the result, we can just as well rotate the bits before inserting + them and use the map 0x7654ffff which is cheaper than the original map. + For this example G = G^-1 = 0x32107654 and F o G^-1 = 0x7654ffff. */ + +typedef struct +{ + /* tree code of binary function G */ + enum tree_code code; + + /* The constant second argument of G */ + int arg; + + /* G^-1, the inverse of G (*, arg) */ + unsigned ginv; + + /* The cost of appplying G (*, arg) */ + int cost; + + /* The composition F o G^-1 (*, arg) for some function F */ + double_int map; + + /* For debug purpose only */ + const char *str; +} avr_map_op_t; + +static const avr_map_op_t avr_map_op[] = + { + { LROTATE_EXPR, 0, 0x76543210, 0, { 0, 0 }, "id" }, + { LROTATE_EXPR, 1, 0x07654321, 2, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 2, 0x10765432, 4, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 3, 0x21076543, 4, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 4, 0x32107654, 1, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 5, 0x43210765, 3, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 6, 0x54321076, 5, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 7, 0x65432107, 3, { 0, 0 }, "<<<" }, + { RSHIFT_EXPR, 1, 0x6543210c, 1, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 1, 0x7543210c, 1, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x543210cc, 2, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x643210cc, 2, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x743210cc, 2, { 0, 0 }, ">>" }, + { LSHIFT_EXPR, 1, 0xc7654321, 1, { 0, 0 }, "<<" }, + { LSHIFT_EXPR, 2, 0xcc765432, 2, { 0, 0 }, "<<" } + }; + + +/* Try to decompose F as F = (F o G^-1) o G as described above. + The result is a struct representing F o G^-1 and G. + If result.cost < 0 then such a decomposition does not exist. */ + +static avr_map_op_t +avr_map_decompose (double_int f, const avr_map_op_t *g, bool val_const_p) +{ + int i; + bool val_used_p = 0 != avr_map_metric (f, MAP_MASK_PREIMAGE_F); + avr_map_op_t f_ginv = *g; + double_int ginv = double_int::from_uhwi (g->ginv); + + f_ginv.cost = -1; + + /* Step 1: Computing F o G^-1 */ + + for (i = 7; i >= 0; i--) + { + int x = avr_map (f, i); + + if (x <= 7) + { + x = avr_map (ginv, x); + + /* The bit is no element of the image of G: no avail (cost = -1) */ + + if (x > 7) + return f_ginv; + } + + f_ginv.map = avr_double_int_push_digit (f_ginv.map, 16, x); + } + + /* Step 2: Compute the cost of the operations. + The overall cost of doing an operation prior to the insertion is + the cost of the insertion plus the cost of the operation. */ + + /* Step 2a: Compute cost of F o G^-1 */ + + if (0 == avr_map_metric (f_ginv.map, MAP_NONFIXED_0_7)) + { + /* The mapping consists only of fixed points and can be folded + to AND/OR logic in the remainder. Reasonable cost is 3. */ + + f_ginv.cost = 2 + (val_used_p && !val_const_p); + } + else + { + rtx xop[4]; + + /* Get the cost of the insn by calling the output worker with some + fake values. Mimic effect of reloading xop[3]: Unused operands + are mapped to 0 and used operands are reloaded to xop[0]. */ + + xop[0] = all_regs_rtx[24]; + xop[1] = gen_int_mode (f_ginv.map.to_uhwi (), SImode); + xop[2] = all_regs_rtx[25]; + xop[3] = val_used_p ? xop[0] : const0_rtx; + + avr_out_insert_bits (xop, &f_ginv.cost); + + f_ginv.cost += val_const_p && val_used_p ? 1 : 0; + } + + /* Step 2b: Add cost of G */ + + f_ginv.cost += g->cost; + + if (avr_log.builtin) + avr_edump (" %s%d=%d", g->str, g->arg, f_ginv.cost); + + return f_ginv; +} + + +/* Insert bits from XOP[1] into XOP[0] according to MAP. + XOP[0] and XOP[1] don't overlap. + If FIXP_P = true: Move all bits according to MAP using BLD/BST sequences. + If FIXP_P = false: Just move the bit if its position in the destination + is different to its source position. */ + +static void +avr_move_bits (rtx *xop, double_int map, bool fixp_p, int *plen) +{ + int bit_dest, b; + + /* T-flag contains this bit of the source, i.e. of XOP[1] */ + int t_bit_src = -1; + + /* We order the operations according to the requested source bit b. */ + + for (b = 0; b < 8; b++) + for (bit_dest = 0; bit_dest < 8; bit_dest++) + { + int bit_src = avr_map (map, bit_dest); + + if (b != bit_src + || bit_src >= 8 + /* Same position: No need to copy as requested by FIXP_P. */ + || (bit_dest == bit_src && !fixp_p)) + continue; + + if (t_bit_src != bit_src) + { + /* Source bit is not yet in T: Store it to T. */ + + t_bit_src = bit_src; + + xop[3] = GEN_INT (bit_src); + avr_asm_len ("bst %T1%T3", xop, plen, 1); + } + + /* Load destination bit with T. */ + + xop[3] = GEN_INT (bit_dest); + avr_asm_len ("bld %T0%T3", xop, plen, 1); + } +} + + +/* PLEN == 0: Print assembler code for `insert_bits'. + PLEN != 0: Compute code length in bytes. + + OP[0]: Result + OP[1]: The mapping composed of nibbles. If nibble no. N is + 0: Bit N of result is copied from bit OP[2].0 + ... ... + 7: Bit N of result is copied from bit OP[2].7 + 0xf: Bit N of result is copied from bit OP[3].N + OP[2]: Bits to be inserted + OP[3]: Target value */ + +const char* +avr_out_insert_bits (rtx *op, int *plen) +{ + double_int map = rtx_to_double_int (op[1]); + unsigned mask_fixed; + bool fixp_p = true; + rtx xop[4]; + + xop[0] = op[0]; + xop[1] = op[2]; + xop[2] = op[3]; + + gcc_assert (REG_P (xop[2]) || CONST_INT_P (xop[2])); + + if (plen) + *plen = 0; + else if (flag_print_asm_name) + fprintf (asm_out_file, + ASM_COMMENT_START "map = 0x%08" HOST_LONG_FORMAT "x\n", + map.to_uhwi () & GET_MODE_MASK (SImode)); + + /* If MAP has fixed points it might be better to initialize the result + with the bits to be inserted instead of moving all bits by hand. */ + + mask_fixed = avr_map_metric (map, MAP_MASK_FIXED_0_7); + + if (REGNO (xop[0]) == REGNO (xop[1])) + { + /* Avoid early-clobber conflicts */ + + avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); + xop[1] = tmp_reg_rtx; + fixp_p = false; + } + + if (avr_map_metric (map, MAP_MASK_PREIMAGE_F)) + { + /* XOP[2] is used and reloaded to XOP[0] already */ + + int n_fix = 0, n_nofix = 0; + + gcc_assert (REG_P (xop[2])); + + /* Get the code size of the bit insertions; once with all bits + moved and once with fixed points omitted. */ + + avr_move_bits (xop, map, true, &n_fix); + avr_move_bits (xop, map, false, &n_nofix); + + if (fixp_p && n_fix - n_nofix > 3) + { + xop[3] = gen_int_mode (~mask_fixed, QImode); + + avr_asm_len ("eor %0,%1" CR_TAB + "andi %0,%3" CR_TAB + "eor %0,%1", xop, plen, 3); + fixp_p = false; + } + } + else + { + /* XOP[2] is unused */ + + if (fixp_p && mask_fixed) + { + avr_asm_len ("mov %0,%1", xop, plen, 1); + fixp_p = false; + } + } + + /* Move/insert remaining bits. */ + + avr_move_bits (xop, map, fixp_p, plen); + + return ""; +} + + +/* IDs for all the AVR builtins. */ + +enum avr_builtin_id + { +#define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME) \ + AVR_BUILTIN_ ## NAME, +#include "builtins.def" +#undef DEF_BUILTIN + + AVR_BUILTIN_COUNT + }; + +struct GTY(()) avr_builtin_description +{ + enum insn_code icode; + int n_args; + tree fndecl; +}; + + +/* Notice that avr_bdesc[] and avr_builtin_id are initialized in such a way + that a built-in's ID can be used to access the built-in by means of + avr_bdesc[ID] */ + +static GTY(()) struct avr_builtin_description +avr_bdesc[AVR_BUILTIN_COUNT] = + { +#define DEF_BUILTIN(NAME, N_ARGS, TYPE, ICODE, LIBNAME) \ + { (enum insn_code) CODE_FOR_ ## ICODE, N_ARGS, NULL_TREE }, +#include "builtins.def" +#undef DEF_BUILTIN + }; + + +/* Implement `TARGET_BUILTIN_DECL'. */ + +static tree +avr_builtin_decl (unsigned id, bool initialize_p ATTRIBUTE_UNUSED) +{ + if (id < AVR_BUILTIN_COUNT) + return avr_bdesc[id].fndecl; + + return error_mark_node; +} + + +static void +avr_init_builtin_int24 (void) +{ + tree int24_type = make_signed_type (GET_MODE_BITSIZE (PSImode)); + tree uint24_type = make_unsigned_type (GET_MODE_BITSIZE (PSImode)); + + lang_hooks.types.register_builtin_type (int24_type, "__int24"); + lang_hooks.types.register_builtin_type (uint24_type, "__uint24"); +} + + +/* 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_list (void_type_node, NULL_TREE); + 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); + + tree uchar_ftype_ulong_uchar_uchar + = build_function_type_list (unsigned_char_type_node, + long_unsigned_type_node, + unsigned_char_type_node, + unsigned_char_type_node, + NULL_TREE); + + tree const_memx_void_node + = build_qualified_type (void_type_node, + TYPE_QUAL_CONST + | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_MEMX)); + + tree const_memx_ptr_type_node + = build_pointer_type_for_mode (const_memx_void_node, PSImode, false); + + tree char_ftype_const_memx_ptr + = build_function_type_list (char_type_node, + const_memx_ptr_type_node, + NULL); + +#define ITYP(T) \ + lang_hooks.types.type_for_size (TYPE_PRECISION (T), TYPE_UNSIGNED (T)) + +#define FX_FTYPE_FX(fx) \ + tree fx##r_ftype_##fx##r \ + = build_function_type_list (node_##fx##r, node_##fx##r, NULL); \ + tree fx##k_ftype_##fx##k \ + = build_function_type_list (node_##fx##k, node_##fx##k, NULL) + +#define FX_FTYPE_FX_INT(fx) \ + tree fx##r_ftype_##fx##r_int \ + = build_function_type_list (node_##fx##r, node_##fx##r, \ + integer_type_node, NULL); \ + tree fx##k_ftype_##fx##k_int \ + = build_function_type_list (node_##fx##k, node_##fx##k, \ + integer_type_node, NULL) + +#define INT_FTYPE_FX(fx) \ + tree int_ftype_##fx##r \ + = build_function_type_list (integer_type_node, node_##fx##r, NULL); \ + tree int_ftype_##fx##k \ + = build_function_type_list (integer_type_node, node_##fx##k, NULL) + +#define INTX_FTYPE_FX(fx) \ + tree int##fx##r_ftype_##fx##r \ + = build_function_type_list (ITYP (node_##fx##r), node_##fx##r, NULL); \ + tree int##fx##k_ftype_##fx##k \ + = build_function_type_list (ITYP (node_##fx##k), node_##fx##k, NULL) + +#define FX_FTYPE_INTX(fx) \ + tree fx##r_ftype_int##fx##r \ + = build_function_type_list (node_##fx##r, ITYP (node_##fx##r), NULL); \ + tree fx##k_ftype_int##fx##k \ + = build_function_type_list (node_##fx##k, ITYP (node_##fx##k), NULL) + + tree node_hr = short_fract_type_node; + tree node_nr = fract_type_node; + tree node_lr = long_fract_type_node; + tree node_llr = long_long_fract_type_node; + + tree node_uhr = unsigned_short_fract_type_node; + tree node_unr = unsigned_fract_type_node; + tree node_ulr = unsigned_long_fract_type_node; + tree node_ullr = unsigned_long_long_fract_type_node; + + tree node_hk = short_accum_type_node; + tree node_nk = accum_type_node; + tree node_lk = long_accum_type_node; + tree node_llk = long_long_accum_type_node; + + tree node_uhk = unsigned_short_accum_type_node; + tree node_unk = unsigned_accum_type_node; + tree node_ulk = unsigned_long_accum_type_node; + tree node_ullk = unsigned_long_long_accum_type_node; + + + /* For absfx builtins. */ + + FX_FTYPE_FX (h); + FX_FTYPE_FX (n); + FX_FTYPE_FX (l); + FX_FTYPE_FX (ll); + + /* For roundfx builtins. */ + + FX_FTYPE_FX_INT (h); + FX_FTYPE_FX_INT (n); + FX_FTYPE_FX_INT (l); + FX_FTYPE_FX_INT (ll); + + FX_FTYPE_FX_INT (uh); + FX_FTYPE_FX_INT (un); + FX_FTYPE_FX_INT (ul); + FX_FTYPE_FX_INT (ull); + + /* For countlsfx builtins. */ + + INT_FTYPE_FX (h); + INT_FTYPE_FX (n); + INT_FTYPE_FX (l); + INT_FTYPE_FX (ll); + + INT_FTYPE_FX (uh); + INT_FTYPE_FX (un); + INT_FTYPE_FX (ul); + INT_FTYPE_FX (ull); + + /* For bitsfx builtins. */ + + INTX_FTYPE_FX (h); + INTX_FTYPE_FX (n); + INTX_FTYPE_FX (l); + INTX_FTYPE_FX (ll); + + INTX_FTYPE_FX (uh); + INTX_FTYPE_FX (un); + INTX_FTYPE_FX (ul); + INTX_FTYPE_FX (ull); + + /* For fxbits builtins. */ + + FX_FTYPE_INTX (h); + FX_FTYPE_INTX (n); + FX_FTYPE_INTX (l); + FX_FTYPE_INTX (ll); + + FX_FTYPE_INTX (uh); + FX_FTYPE_INTX (un); + FX_FTYPE_INTX (ul); + FX_FTYPE_INTX (ull); + + +#define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME) \ + { \ + int id = AVR_BUILTIN_ ## NAME; \ + const char *Name = "__builtin_avr_" #NAME; \ + char *name = (char*) alloca (1 + strlen (Name)); \ + \ + gcc_assert (id < AVR_BUILTIN_COUNT); \ + avr_bdesc[id].fndecl \ + = add_builtin_function (avr_tolower (name, Name), TYPE, id, \ + BUILT_IN_MD, LIBNAME, NULL_TREE); \ + } +#include "builtins.def" +#undef DEF_BUILTIN + + avr_init_builtin_int24 (); +} + + +/* Subroutine of avr_expand_builtin to expand vanilla builtins + with non-void result and 1 ... 3 arguments. */ + +static rtx +avr_default_expand_builtin (enum insn_code icode, tree exp, rtx target) +{ + rtx pat, xop[3]; + int n, n_args = call_expr_nargs (exp); + enum machine_mode tmode = insn_data[icode].operand[0].mode; + + gcc_assert (n_args >= 1 && n_args <= 3); + + if (target == NULL_RTX + || GET_MODE (target) != tmode + || !insn_data[icode].operand[0].predicate (target, tmode)) + { + target = gen_reg_rtx (tmode); + } + + for (n = 0; n < n_args; n++) + { + tree arg = CALL_EXPR_ARG (exp, n); + rtx op = expand_expr (arg, NULL_RTX, VOIDmode, EXPAND_NORMAL); + enum machine_mode opmode = GET_MODE (op); + enum machine_mode mode = insn_data[icode].operand[n+1].mode; + + if ((opmode == SImode || opmode == VOIDmode) && mode == HImode) + { + opmode = HImode; + op = gen_lowpart (HImode, op); + } + + /* In case the insn wants input operands in modes different from + the result, abort. */ + + gcc_assert (opmode == mode || opmode == VOIDmode); + + if (!insn_data[icode].operand[n+1].predicate (op, mode)) + op = copy_to_mode_reg (mode, op); + + xop[n] = op; + } + + switch (n_args) + { + case 1: pat = GEN_FCN (icode) (target, xop[0]); break; + case 2: pat = GEN_FCN (icode) (target, xop[0], xop[1]); break; + case 3: pat = GEN_FCN (icode) (target, xop[0], xop[1], xop[2]); break; + + default: + gcc_unreachable(); + } + + if (pat == NULL_RTX) + return NULL_RTX; + + emit_insn (pat); + + return target; +} + + +/* Implement `TARGET_EXPAND_BUILTIN'. */ +/* 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) +{ + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + const char *bname = IDENTIFIER_POINTER (DECL_NAME (fndecl)); + unsigned int id = DECL_FUNCTION_CODE (fndecl); + const struct avr_builtin_description *d = &avr_bdesc[id]; + tree arg0; + rtx op0; + + gcc_assert (id < AVR_BUILTIN_COUNT); + + switch (id) + { + case AVR_BUILTIN_NOP: + emit_insn (gen_nopv (GEN_INT(1))); + return 0; + + case AVR_BUILTIN_DELAY_CYCLES: + { + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); + + if (!CONST_INT_P (op0)) + error ("%s expects a compile time integer constant", bname); + else + avr_expand_delay_cycles (op0); + + return NULL_RTX; + } + + case AVR_BUILTIN_INSERT_BITS: + { + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); + + if (!CONST_INT_P (op0)) + { + error ("%s expects a compile time long integer constant" + " as first argument", bname); + return target; + } + + break; + } + + case AVR_BUILTIN_ROUNDHR: case AVR_BUILTIN_ROUNDUHR: + case AVR_BUILTIN_ROUNDR: case AVR_BUILTIN_ROUNDUR: + case AVR_BUILTIN_ROUNDLR: case AVR_BUILTIN_ROUNDULR: + case AVR_BUILTIN_ROUNDLLR: case AVR_BUILTIN_ROUNDULLR: + + case AVR_BUILTIN_ROUNDHK: case AVR_BUILTIN_ROUNDUHK: + case AVR_BUILTIN_ROUNDK: case AVR_BUILTIN_ROUNDUK: + case AVR_BUILTIN_ROUNDLK: case AVR_BUILTIN_ROUNDULK: + case AVR_BUILTIN_ROUNDLLK: case AVR_BUILTIN_ROUNDULLK: + + /* Warn about odd rounding. Rounding points >= FBIT will have + no effect. */ + + if (TREE_CODE (CALL_EXPR_ARG (exp, 1)) != INTEGER_CST) + break; + + int rbit = (int) TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1)); + + if (rbit >= (int) GET_MODE_FBIT (mode)) + { + warning (OPT_Wextra, "rounding to %d bits has no effect for " + "fixed-point value with %d fractional bits", + rbit, GET_MODE_FBIT (mode)); + + return expand_expr (CALL_EXPR_ARG (exp, 0), NULL_RTX, mode, + EXPAND_NORMAL); + } + else if (rbit <= - (int) GET_MODE_IBIT (mode)) + { + warning (0, "rounding result will always be 0"); + return CONST0_RTX (mode); + } + + /* The rounding points RP satisfies now: -IBIT < RP < FBIT. + + TR 18037 only specifies results for RP > 0. However, the + remaining cases of -IBIT < RP <= 0 can easily be supported + without any additional overhead. */ + + break; /* round */ + } + + /* No fold found and no insn: Call support function from libgcc. */ + + if (d->icode == CODE_FOR_nothing + && DECL_ASSEMBLER_NAME (get_callee_fndecl (exp)) != NULL_TREE) + { + return expand_call (exp, target, ignore); + } + + /* No special treatment needed: vanilla expand. */ + + gcc_assert (d->icode != CODE_FOR_nothing); + gcc_assert (d->n_args == call_expr_nargs (exp)); + + if (d->n_args == 0) + { + emit_insn ((GEN_FCN (d->icode)) (target)); + return NULL_RTX; + } + + return avr_default_expand_builtin (d->icode, exp, target); +} + + +/* Helper for `avr_fold_builtin' that folds absfx (FIXED_CST). */ + +static tree +avr_fold_absfx (tree tval) +{ + if (FIXED_CST != TREE_CODE (tval)) + return NULL_TREE; + + /* Our fixed-points have no padding: Use double_int payload directly. */ + + FIXED_VALUE_TYPE fval = TREE_FIXED_CST (tval); + unsigned int bits = GET_MODE_BITSIZE (fval.mode); + double_int ival = fval.data.sext (bits); + + if (!ival.is_negative()) + return tval; + + /* ISO/IEC TR 18037, 7.18a.6.2: The absfx functions are saturating. */ + + fval.data = (ival == double_int::min_value (bits, false).sext (bits)) + ? double_int::max_value (bits, false) + : -ival; + + return build_fixed (TREE_TYPE (tval), fval); +} + + +/* Implement `TARGET_FOLD_BUILTIN'. */ + +static tree +avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg, + bool ignore ATTRIBUTE_UNUSED) +{ + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + tree val_type = TREE_TYPE (TREE_TYPE (fndecl)); + + if (!optimize) + return NULL_TREE; + + switch (fcode) + { + default: + break; + + case AVR_BUILTIN_SWAP: + { + return fold_build2 (LROTATE_EXPR, val_type, arg[0], + build_int_cst (val_type, 4)); + } + + case AVR_BUILTIN_ABSHR: + case AVR_BUILTIN_ABSR: + case AVR_BUILTIN_ABSLR: + case AVR_BUILTIN_ABSLLR: + + case AVR_BUILTIN_ABSHK: + case AVR_BUILTIN_ABSK: + case AVR_BUILTIN_ABSLK: + case AVR_BUILTIN_ABSLLK: + /* GCC is not good with folding ABS for fixed-point. Do it by hand. */ + + return avr_fold_absfx (arg[0]); + + case AVR_BUILTIN_BITSHR: case AVR_BUILTIN_HRBITS: + case AVR_BUILTIN_BITSHK: case AVR_BUILTIN_HKBITS: + case AVR_BUILTIN_BITSUHR: case AVR_BUILTIN_UHRBITS: + case AVR_BUILTIN_BITSUHK: case AVR_BUILTIN_UHKBITS: + + case AVR_BUILTIN_BITSR: case AVR_BUILTIN_RBITS: + case AVR_BUILTIN_BITSK: case AVR_BUILTIN_KBITS: + case AVR_BUILTIN_BITSUR: case AVR_BUILTIN_URBITS: + case AVR_BUILTIN_BITSUK: case AVR_BUILTIN_UKBITS: + + case AVR_BUILTIN_BITSLR: case AVR_BUILTIN_LRBITS: + case AVR_BUILTIN_BITSLK: case AVR_BUILTIN_LKBITS: + case AVR_BUILTIN_BITSULR: case AVR_BUILTIN_ULRBITS: + case AVR_BUILTIN_BITSULK: case AVR_BUILTIN_ULKBITS: + + case AVR_BUILTIN_BITSLLR: case AVR_BUILTIN_LLRBITS: + case AVR_BUILTIN_BITSLLK: case AVR_BUILTIN_LLKBITS: + case AVR_BUILTIN_BITSULLR: case AVR_BUILTIN_ULLRBITS: + case AVR_BUILTIN_BITSULLK: case AVR_BUILTIN_ULLKBITS: + + gcc_assert (TYPE_PRECISION (val_type) + == TYPE_PRECISION (TREE_TYPE (arg[0]))); + + return build1 (VIEW_CONVERT_EXPR, val_type, arg[0]); + + case AVR_BUILTIN_INSERT_BITS: + { + tree tbits = arg[1]; + tree tval = arg[2]; + tree tmap; + tree map_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + double_int map; + bool changed = false; + unsigned i; + avr_map_op_t best_g; + + if (TREE_CODE (arg[0]) != INTEGER_CST) + { + /* No constant as first argument: Don't fold this and run into + error in avr_expand_builtin. */ + + break; + } + + map = tree_to_double_int (arg[0]); + tmap = double_int_to_tree (map_type, map); + + if (TREE_CODE (tval) != INTEGER_CST + && 0 == avr_map_metric (map, MAP_MASK_PREIMAGE_F)) + { + /* There are no F in the map, i.e. 3rd operand is unused. + Replace that argument with some constant to render + respective input unused. */ + + tval = build_int_cst (val_type, 0); + changed = true; + } + + if (TREE_CODE (tbits) != INTEGER_CST + && 0 == avr_map_metric (map, MAP_PREIMAGE_0_7)) + { + /* Similar for the bits to be inserted. If they are unused, + we can just as well pass 0. */ + + tbits = build_int_cst (val_type, 0); + } + + if (TREE_CODE (tbits) == INTEGER_CST) + { + /* Inserting bits known at compile time is easy and can be + performed by AND and OR with appropriate masks. */ + + int bits = TREE_INT_CST_LOW (tbits); + int mask_ior = 0, mask_and = 0xff; + + for (i = 0; i < 8; i++) + { + int mi = avr_map (map, i); + + if (mi < 8) + { + if (bits & (1 << mi)) mask_ior |= (1 << i); + else mask_and &= ~(1 << i); + } + } + + tval = fold_build2 (BIT_IOR_EXPR, val_type, tval, + build_int_cst (val_type, mask_ior)); + return fold_build2 (BIT_AND_EXPR, val_type, tval, + build_int_cst (val_type, mask_and)); + } + + if (changed) + return build_call_expr (fndecl, 3, tmap, tbits, tval); + + /* If bits don't change their position we can use vanilla logic + to merge the two arguments. */ + + if (0 == avr_map_metric (map, MAP_NONFIXED_0_7)) + { + int mask_f = avr_map_metric (map, MAP_MASK_PREIMAGE_F); + tree tres, tmask = build_int_cst (val_type, mask_f ^ 0xff); + + tres = fold_build2 (BIT_XOR_EXPR, val_type, tbits, tval); + tres = fold_build2 (BIT_AND_EXPR, val_type, tres, tmask); + return fold_build2 (BIT_XOR_EXPR, val_type, tres, tval); + } + + /* Try to decomposing map to reduce overall cost. */ + + if (avr_log.builtin) + avr_edump ("\n%?: %X\n%?: ROL cost: ", map); + + best_g = avr_map_op[0]; + best_g.cost = 1000; + + for (i = 0; i < sizeof (avr_map_op) / sizeof (*avr_map_op); i++) + { + avr_map_op_t g + = avr_map_decompose (map, avr_map_op + i, + TREE_CODE (tval) == INTEGER_CST); + + if (g.cost >= 0 && g.cost < best_g.cost) + best_g = g; + } + + if (avr_log.builtin) + avr_edump ("\n"); + + if (best_g.arg == 0) + /* No optimization found */ + break; + + /* Apply operation G to the 2nd argument. */ + + if (avr_log.builtin) + avr_edump ("%?: using OP(%s%d, %X) cost %d\n", + best_g.str, best_g.arg, best_g.map, best_g.cost); + + /* Do right-shifts arithmetically: They copy the MSB instead of + shifting in a non-usable value (0) as with logic right-shift. */ + + tbits = fold_convert (signed_char_type_node, tbits); + tbits = fold_build2 (best_g.code, signed_char_type_node, tbits, + build_int_cst (val_type, best_g.arg)); + tbits = fold_convert (val_type, tbits); + + /* Use map o G^-1 instead of original map to undo the effect of G. */ + + tmap = double_int_to_tree (map_type, best_g.map); + + return build_call_expr (fndecl, 3, tmap, tbits, tval); + } /* AVR_BUILTIN_INSERT_BITS */ + } + + return NULL_TREE; +} + + + +/* Initialize the GCC target structure. */ + +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP "\t.long\t" +#undef TARGET_ASM_UNALIGNED_HI_OP +#define TARGET_ASM_UNALIGNED_HI_OP "\t.word\t" +#undef TARGET_ASM_UNALIGNED_SI_OP +#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t" +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER avr_assemble_integer +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START avr_file_start +#undef TARGET_ASM_FILE_END +#define TARGET_ASM_FILE_END avr_file_end + +#undef TARGET_ASM_FUNCTION_END_PROLOGUE +#define TARGET_ASM_FUNCTION_END_PROLOGUE avr_asm_function_end_prologue +#undef TARGET_ASM_FUNCTION_BEGIN_EPILOGUE +#define TARGET_ASM_FUNCTION_BEGIN_EPILOGUE avr_asm_function_begin_epilogue + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE avr_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE avr_libcall_value +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P avr_function_value_regno_p + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE avr_attribute_table +#undef TARGET_INSERT_ATTRIBUTES +#define TARGET_INSERT_ATTRIBUTES avr_insert_attributes +#undef TARGET_SECTION_TYPE_FLAGS +#define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags + +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION avr_asm_named_section +#undef TARGET_ASM_INIT_SECTIONS +#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info +#undef TARGET_ASM_SELECT_SECTION +#define TARGET_ASM_SELECT_SECTION avr_asm_select_section + +#undef TARGET_REGISTER_MOVE_COST +#define TARGET_REGISTER_MOVE_COST avr_register_move_cost +#undef TARGET_MEMORY_MOVE_COST +#define TARGET_MEMORY_MOVE_COST avr_memory_move_cost +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS avr_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST avr_address_cost +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG avr_reorg +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG avr_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance + +#undef TARGET_SET_CURRENT_FUNCTION +#define TARGET_SET_CURRENT_FUNCTION avr_set_current_function + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY avr_return_in_memory + +#undef TARGET_STRICT_ARGUMENT_NAMING +#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true + +#undef TARGET_BUILTIN_SETJMP_FRAME_VALUE +#define TARGET_BUILTIN_SETJMP_FRAME_VALUE avr_builtin_setjmp_frame_value + +#undef TARGET_HARD_REGNO_SCRATCH_OK +#define TARGET_HARD_REGNO_SCRATCH_OK avr_hard_regno_scratch_ok +#undef TARGET_CASE_VALUES_THRESHOLD +#define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold + +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE avr_can_eliminate + +#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS +#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS avr_allocate_stack_slots_for_args + +#undef TARGET_WARN_FUNC_RETURN +#define TARGET_WARN_FUNC_RETURN avr_warn_func_return + +#undef TARGET_CLASS_LIKELY_SPILLED_P +#define TARGET_CLASS_LIKELY_SPILLED_P avr_class_likely_spilled_p + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE avr_option_override + +#undef TARGET_CANNOT_MODIFY_JUMPS_P +#define TARGET_CANNOT_MODIFY_JUMPS_P avr_cannot_modify_jumps_p + +#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_BUILTIN_DECL +#define TARGET_BUILTIN_DECL avr_builtin_decl + +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN avr_expand_builtin + +#undef TARGET_FOLD_BUILTIN +#define TARGET_FOLD_BUILTIN avr_fold_builtin + +#undef TARGET_ASM_FUNCTION_RODATA_SECTION +#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section + +#undef TARGET_SCALAR_MODE_SUPPORTED_P +#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p + +#undef TARGET_BUILD_BUILTIN_VA_LIST +#define TARGET_BUILD_BUILTIN_VA_LIST avr_build_builtin_va_list + +#undef TARGET_FIXED_POINT_SUPPORTED_P +#define TARGET_FIXED_POINT_SUPPORTED_P hook_bool_void_true + +#undef TARGET_CONVERT_TO_TYPE +#define TARGET_CONVERT_TO_TYPE avr_convert_to_type + +#undef TARGET_ADDR_SPACE_SUBSET_P +#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p + +#undef TARGET_ADDR_SPACE_CONVERT +#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert + +#undef TARGET_ADDR_SPACE_ADDRESS_MODE +#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode + +#undef TARGET_ADDR_SPACE_POINTER_MODE +#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode + +#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P +#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \ + avr_addr_space_legitimate_address_p + +#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS +#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address + +#undef TARGET_MODE_DEPENDENT_ADDRESS_P +#define TARGET_MODE_DEPENDENT_ADDRESS_P avr_mode_dependent_address_p + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD avr_secondary_reload + +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND avr_print_operand +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address +#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P +#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p + +struct gcc_target targetm = TARGET_INITIALIZER; + + +#include "gt-avr.h" diff --git a/gcc-4.8/gcc/config/avr/avr.h b/gcc-4.8/gcc/config/avr/avr.h new file mode 100644 index 000000000..f223a6148 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr.h @@ -0,0 +1,604 @@ +/* Definitions of target machine for GNU compiler, + for ATMEL AVR at90s8515, ATmega103/103L, ATmega603/603L microcontrollers. + Copyright (C) 1998-2013 Free Software Foundation, Inc. + Contributed by Denis Chertykov (chertykov@gmail.com) + +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 +. */ + +typedef struct +{ + /* Id of the address space as used in c_register_addr_space */ + unsigned char id; + + /* Flavour of memory: 0 = RAM, 1 = Flash */ + int memory_class; + + /* Width of pointer (in bytes) */ + int pointer_size; + + /* Name of the address space as visible to the user */ + const char *name; + + /* Segment (i.e. 64k memory chunk) number. */ + int segment; + + /* Section prefix, e.g. ".progmem1.data" */ + const char *section_name; +} avr_addrspace_t; + +extern const avr_addrspace_t avr_addrspace[]; + +/* Known address spaces */ + +enum + { + ADDR_SPACE_RAM, /* ADDR_SPACE_GENERIC */ + ADDR_SPACE_FLASH, + ADDR_SPACE_FLASH1, + ADDR_SPACE_FLASH2, + ADDR_SPACE_FLASH3, + ADDR_SPACE_FLASH4, + ADDR_SPACE_FLASH5, + ADDR_SPACE_MEMX, + /* Sentinel */ + ADDR_SPACE_COUNT + }; + +#define TARGET_CPU_CPP_BUILTINS() avr_cpu_cpp_builtins (pfile) + +#define AVR_HAVE_JMP_CALL (avr_current_arch->have_jmp_call) +#define AVR_HAVE_MUL (avr_current_arch->have_mul) +#define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx) +#define AVR_HAVE_LPMX (avr_current_arch->have_movw_lpmx) +#define AVR_HAVE_ELPM (avr_current_arch->have_elpm) +#define AVR_HAVE_ELPMX (avr_current_arch->have_elpmx) +#define AVR_HAVE_RAMPD (avr_current_arch->have_rampd) +#define AVR_HAVE_RAMPX (avr_current_arch->have_rampd) +#define AVR_HAVE_RAMPY (avr_current_arch->have_rampd) +#define AVR_HAVE_RAMPZ (avr_current_arch->have_elpm \ + || avr_current_arch->have_rampd) +#define AVR_HAVE_EIJMP_EICALL (avr_current_arch->have_eijmp_eicall) + +/* Handling of 8-bit SP versus 16-bit SP is as follows: + + -msp8 is used internally to select the right multilib for targets with + 8-bit SP. -msp8 is set automatically by DRIVER_SELF_SPECS for devices + with 8-bit SP or by multilib generation machinery. If a frame pointer is + needed and SP is only 8 bits wide, SP is zero-extended to get FP. + + TARGET_TINY_STACK is triggered by -mtiny-stack which is a user option. + This option has no effect on multilib selection. It serves to save some + bytes on 16-bit SP devices by only changing SP_L and leaving SP_H alone. + + These two properties are reflected by built-in macros __AVR_SP8__ resp. + __AVR_HAVE_8BIT_SP__ and __AVR_HAVE_16BIT_SP__. During multilib generation + there is always __AVR_SP8__ == __AVR_HAVE_8BIT_SP__. */ + +#define AVR_HAVE_8BIT_SP \ + (avr_current_device->short_sp || TARGET_TINY_STACK || avr_sp8) + +#define AVR_HAVE_SPH (!avr_sp8) + +#define AVR_2_BYTE_PC (!AVR_HAVE_EIJMP_EICALL) +#define AVR_3_BYTE_PC (AVR_HAVE_EIJMP_EICALL) + +#define AVR_XMEGA (avr_current_arch->xmega_p) + +#define BITS_BIG_ENDIAN 0 +#define BYTES_BIG_ENDIAN 0 +#define WORDS_BIG_ENDIAN 0 + +#ifdef IN_LIBGCC2 +/* This is to get correct SI and DI modes in libgcc2.c (32 and 64 bits). */ +#define UNITS_PER_WORD 4 +#else +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 1 +#endif + +#define POINTER_SIZE 16 + + +/* Maximum sized of reasonable data type + DImode or Dfmode ... */ +#define MAX_FIXED_MODE_SIZE 32 + +#define PARM_BOUNDARY 8 + +#define FUNCTION_BOUNDARY 8 + +#define EMPTY_FIELD_BOUNDARY 8 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 8 + +#define TARGET_VTABLE_ENTRY_ALIGN 8 + +#define STRICT_ALIGNMENT 0 + +#define INT_TYPE_SIZE (TARGET_INT8 ? 8 : 16) +#define SHORT_TYPE_SIZE (INT_TYPE_SIZE == 8 ? INT_TYPE_SIZE : 16) +#define LONG_TYPE_SIZE (INT_TYPE_SIZE == 8 ? 16 : 32) +#define LONG_LONG_TYPE_SIZE (INT_TYPE_SIZE == 8 ? 32 : 64) +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 32 +#define LONG_DOUBLE_TYPE_SIZE 32 +#define LONG_LONG_ACCUM_TYPE_SIZE 64 + +#define DEFAULT_SIGNED_CHAR 1 + +#define SIZE_TYPE (INT_TYPE_SIZE == 8 ? "long unsigned int" : "unsigned int") +#define PTRDIFF_TYPE (INT_TYPE_SIZE == 8 ? "long int" :"int") + +#define WCHAR_TYPE_SIZE 16 + +#define FIRST_PSEUDO_REGISTER 36 + +#define FIXED_REGISTERS {\ + 1,1,/* r0 r1 */\ + 0,0,/* r2 r3 */\ + 0,0,/* r4 r5 */\ + 0,0,/* r6 r7 */\ + 0,0,/* r8 r9 */\ + 0,0,/* r10 r11 */\ + 0,0,/* r12 r13 */\ + 0,0,/* r14 r15 */\ + 0,0,/* r16 r17 */\ + 0,0,/* r18 r19 */\ + 0,0,/* r20 r21 */\ + 0,0,/* r22 r23 */\ + 0,0,/* r24 r25 */\ + 0,0,/* r26 r27 */\ + 0,0,/* r28 r29 */\ + 0,0,/* r30 r31 */\ + 1,1,/* STACK */\ + 1,1 /* arg pointer */ } + +#define CALL_USED_REGISTERS { \ + 1,1,/* r0 r1 */ \ + 0,0,/* r2 r3 */ \ + 0,0,/* r4 r5 */ \ + 0,0,/* r6 r7 */ \ + 0,0,/* r8 r9 */ \ + 0,0,/* r10 r11 */ \ + 0,0,/* r12 r13 */ \ + 0,0,/* r14 r15 */ \ + 0,0,/* r16 r17 */ \ + 1,1,/* r18 r19 */ \ + 1,1,/* r20 r21 */ \ + 1,1,/* r22 r23 */ \ + 1,1,/* r24 r25 */ \ + 1,1,/* r26 r27 */ \ + 0,0,/* r28 r29 */ \ + 1,1,/* r30 r31 */ \ + 1,1,/* STACK */ \ + 1,1 /* arg pointer */ } + +#define REG_ALLOC_ORDER { \ + 24,25, \ + 18,19, \ + 20,21, \ + 22,23, \ + 30,31, \ + 26,27, \ + 28,29, \ + 17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, \ + 0,1, \ + 32,33,34,35 \ + } + +#define ADJUST_REG_ALLOC_ORDER avr_adjust_reg_alloc_order() + + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +#define HARD_REGNO_MODE_OK(REGNO, MODE) avr_hard_regno_mode_ok(REGNO, MODE) + +#define MODES_TIEABLE_P(MODE1, MODE2) 1 + +enum reg_class { + NO_REGS, + R0_REG, /* r0 */ + POINTER_X_REGS, /* r26 - r27 */ + POINTER_Y_REGS, /* r28 - r29 */ + POINTER_Z_REGS, /* r30 - r31 */ + STACK_REG, /* STACK */ + BASE_POINTER_REGS, /* r28 - r31 */ + POINTER_REGS, /* r26 - r31 */ + ADDW_REGS, /* r24 - r31 */ + SIMPLE_LD_REGS, /* r16 - r23 */ + LD_REGS, /* r16 - r31 */ + NO_LD_REGS, /* r0 - r15 */ + GENERAL_REGS, /* r0 - r31 */ + ALL_REGS, LIM_REG_CLASSES +}; + + +#define N_REG_CLASSES (int)LIM_REG_CLASSES + +#define REG_CLASS_NAMES { \ + "NO_REGS", \ + "R0_REG", /* r0 */ \ + "POINTER_X_REGS", /* r26 - r27 */ \ + "POINTER_Y_REGS", /* r28 - r29 */ \ + "POINTER_Z_REGS", /* r30 - r31 */ \ + "STACK_REG", /* STACK */ \ + "BASE_POINTER_REGS", /* r28 - r31 */ \ + "POINTER_REGS", /* r26 - r31 */ \ + "ADDW_REGS", /* r24 - r31 */ \ + "SIMPLE_LD_REGS", /* r16 - r23 */ \ + "LD_REGS", /* r16 - r31 */ \ + "NO_LD_REGS", /* r0 - r15 */ \ + "GENERAL_REGS", /* r0 - r31 */ \ + "ALL_REGS" } + +#define REG_CLASS_CONTENTS { \ + {0x00000000,0x00000000}, /* NO_REGS */ \ + {0x00000001,0x00000000}, /* R0_REG */ \ + {3 << REG_X,0x00000000}, /* POINTER_X_REGS, r26 - r27 */ \ + {3 << REG_Y,0x00000000}, /* POINTER_Y_REGS, r28 - r29 */ \ + {3 << REG_Z,0x00000000}, /* POINTER_Z_REGS, r30 - r31 */ \ + {0x00000000,0x00000003}, /* STACK_REG, STACK */ \ + {(3 << REG_Y) | (3 << REG_Z), \ + 0x00000000}, /* BASE_POINTER_REGS, r28 - r31 */ \ + {(3 << REG_X) | (3 << REG_Y) | (3 << REG_Z), \ + 0x00000000}, /* POINTER_REGS, r26 - r31 */ \ + {(3 << REG_X) | (3 << REG_Y) | (3 << REG_Z) | (3 << REG_W), \ + 0x00000000}, /* ADDW_REGS, r24 - r31 */ \ + {0x00ff0000,0x00000000}, /* SIMPLE_LD_REGS r16 - r23 */ \ + {(3 << REG_X)|(3 << REG_Y)|(3 << REG_Z)|(3 << REG_W)|(0xff << 16), \ + 0x00000000}, /* LD_REGS, r16 - r31 */ \ + {0x0000ffff,0x00000000}, /* NO_LD_REGS r0 - r15 */ \ + {0xffffffff,0x00000000}, /* GENERAL_REGS, r0 - r31 */ \ + {0xffffffff,0x00000003} /* ALL_REGS */ \ +} + +#define REGNO_REG_CLASS(R) avr_regno_reg_class(R) + +#define MODE_CODE_BASE_REG_CLASS(mode, as, outer_code, index_code) \ + avr_mode_code_base_reg_class (mode, as, outer_code, index_code) + +#define INDEX_REG_CLASS NO_REGS + +#define REGNO_MODE_CODE_OK_FOR_BASE_P(num, mode, as, outer_code, index_code) \ + avr_regno_mode_code_ok_for_base_p (num, mode, as, outer_code, index_code) + +#define REGNO_OK_FOR_INDEX_P(NUM) 0 + +#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) \ + avr_hard_regno_call_part_clobbered (REGNO, MODE) + +#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P hook_bool_mode_true + +#define STACK_PUSH_CODE POST_DEC + +#define STACK_GROWS_DOWNWARD + +#define STARTING_FRAME_OFFSET avr_starting_frame_offset() + +#define STACK_POINTER_OFFSET 1 + +#define FIRST_PARM_OFFSET(FUNDECL) 0 + +#define STACK_BOUNDARY 8 + +#define STACK_POINTER_REGNUM 32 + +#define FRAME_POINTER_REGNUM REG_Y + +#define ARG_POINTER_REGNUM 34 + +#define STATIC_CHAIN_REGNUM 2 + +#define ELIMINABLE_REGS { \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \ + ,{FRAME_POINTER_REGNUM+1,STACK_POINTER_REGNUM+1}} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + OFFSET = avr_initial_elimination_offset (FROM, TO) + +#define RETURN_ADDR_RTX(count, tem) avr_return_addr_rtx (count, tem) + +/* Don't use Push rounding. expr.c: emit_single_push_insn is broken + for POST_DEC targets (PR27386). */ +/*#define PUSH_ROUNDING(NPUSHED) (NPUSHED)*/ + +typedef struct avr_args +{ + /* # Registers available for passing */ + int nregs; + + /* Next available register number */ + int regno; +} CUMULATIVE_ARGS; + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ + avr_init_cumulative_args (&(CUM), FNTYPE, LIBNAME, FNDECL) + +#define FUNCTION_ARG_REGNO_P(r) avr_function_arg_regno_p(r) + +#define DEFAULT_PCC_STRUCT_RETURN 0 + +#define EPILOGUE_USES(REGNO) avr_epilogue_uses(REGNO) + +#define HAVE_POST_INCREMENT 1 +#define HAVE_PRE_DECREMENT 1 + +#define MAX_REGS_PER_ADDRESS 1 + +#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_L,WIN) \ + do { \ + rtx new_x = avr_legitimize_reload_address (&(X), MODE, OPNUM, TYPE, \ + ADDR_TYPE (TYPE), \ + IND_L, make_memloc); \ + if (new_x) \ + { \ + X = new_x; \ + goto WIN; \ + } \ + } while (0) + +#define BRANCH_COST(speed_p, predictable_p) avr_branch_cost + +#define SLOW_BYTE_ACCESS 0 + +#define NO_FUNCTION_CSE + +#define REGISTER_TARGET_PRAGMAS() \ + do { \ + avr_register_target_pragmas(); \ + } while (0) + +#define TEXT_SECTION_ASM_OP "\t.text" + +#define DATA_SECTION_ASM_OP "\t.data" + +#define BSS_SECTION_ASM_OP "\t.section .bss" + +/* Define the pseudo-ops used to switch to the .ctors and .dtors sections. + There are no shared libraries on this target, and these sections are + placed in the read-only program memory, so they are not writable. */ + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP "\t.section .ctors,\"a\",@progbits" + +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP "\t.section .dtors,\"a\",@progbits" + +#define TARGET_ASM_CONSTRUCTOR avr_asm_out_ctor + +#define TARGET_ASM_DESTRUCTOR avr_asm_out_dtor + +#define SUPPORTS_INIT_PRIORITY 0 + +#define JUMP_TABLES_IN_TEXT_SECTION 0 + +#define ASM_COMMENT_START " ; " + +#define ASM_APP_ON "/* #APP */\n" + +#define ASM_APP_OFF "/* #NOAPP */\n" + +#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) ((C) == '\n' || ((C) == '$')) + +#define ASM_OUTPUT_ALIGNED_DECL_COMMON(STREAM, DECL, NAME, SIZE, ALIGN) \ + avr_asm_output_aligned_decl_common (STREAM, DECL, NAME, SIZE, ALIGN, false) + +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) + +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(STREAM, DECL, NAME, SIZE, ALIGN) \ + avr_asm_output_aligned_decl_common (STREAM, DECL, NAME, SIZE, ALIGN, true) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP ".global\t" + +#define SUPPORTS_WEAK 1 + +#define HAS_INIT_SECTION 1 + +#define REGISTER_NAMES { \ + "r0","r1","r2","r3","r4","r5","r6","r7", \ + "r8","r9","r10","r11","r12","r13","r14","r15", \ + "r16","r17","r18","r19","r20","r21","r22","r23", \ + "r24","r25","r26","r27","r28","r29","r30","r31", \ + "__SP_L__","__SP_H__","argL","argH"} + +#define FINAL_PRESCAN_INSN(insn, operand, nop) \ + avr_final_prescan_insn (insn, operand,nop) + +#define ASM_OUTPUT_REG_PUSH(STREAM, REGNO) \ +{ \ + gcc_assert (REGNO < 32); \ + fprintf (STREAM, "\tpush\tr%d", REGNO); \ +} + +#define ASM_OUTPUT_REG_POP(STREAM, REGNO) \ +{ \ + gcc_assert (REGNO < 32); \ + fprintf (STREAM, "\tpop\tr%d", REGNO); \ +} + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + avr_output_addr_vec_elt (STREAM, VALUE) + +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + do { \ + if ((POWER) > 0) \ + fprintf (STREAM, "\t.p2align\t%d\n", POWER); \ + } while (0) + +#define CASE_VECTOR_MODE HImode + +#undef WORD_REGISTER_OPERATIONS + +#define MOVE_MAX 4 + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define Pmode HImode + +#define FUNCTION_MODE HImode + +#define DOLLARS_IN_IDENTIFIERS 0 + +#define TRAMPOLINE_SIZE 4 + +/* Store in cc_status the expressions + that the condition codes will describe + after execution of an instruction whose pattern is EXP. + Do not alter them if the instruction would not alter the cc's. */ + +#define NOTICE_UPDATE_CC(EXP, INSN) avr_notice_update_cc (EXP, INSN) + +/* The add insns don't set overflow in a usable way. */ +#define CC_OVERFLOW_UNUSABLE 01000 +/* The mov,and,or,xor insns don't set carry. That's ok though as the + Z bit is all we need when doing unsigned comparisons on the result of + these insns (since they're always with 0). However, conditions.h has + CC_NO_OVERFLOW defined for this purpose. Rename it to something more + understandable. */ +#define CC_NO_CARRY CC_NO_OVERFLOW + + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "/* profiler %d */", (LABELNO)) + +#define ADJUST_INSN_LENGTH(INSN, LENGTH) \ + (LENGTH = avr_adjust_insn_length (INSN, LENGTH)) + +extern const char *avr_device_to_as (int argc, const char **argv); +extern const char *avr_device_to_ld (int argc, const char **argv); +extern const char *avr_device_to_data_start (int argc, const char **argv); +extern const char *avr_device_to_startfiles (int argc, const char **argv); +extern const char *avr_device_to_devicelib (int argc, const char **argv); +extern const char *avr_device_to_sp8 (int argc, const char **argv); + +#define EXTRA_SPEC_FUNCTIONS \ + { "device_to_as", avr_device_to_as }, \ + { "device_to_ld", avr_device_to_ld }, \ + { "device_to_data_start", avr_device_to_data_start }, \ + { "device_to_startfile", avr_device_to_startfiles }, \ + { "device_to_devicelib", avr_device_to_devicelib }, \ + { "device_to_sp8", avr_device_to_sp8 }, + +#define DRIVER_SELF_SPECS " %:device_to_sp8(%{mmcu=*:%*}) " +#define CPP_SPEC "" + +#define CC1_SPEC "" + +#define CC1PLUS_SPEC "%{!frtti:-fno-rtti} \ + %{!fenforce-eh-specs:-fno-enforce-eh-specs} \ + %{!fexceptions:-fno-exceptions}" + +#define ASM_SPEC "%:device_to_as(%{mmcu=*:%*}) " + +#define LINK_SPEC "\ +%{mrelax:--relax\ + %{mpmem-wrap-around:%{mmcu=at90usb8*:--pmem-wrap-around=8k}\ + %{mmcu=atmega16*:--pmem-wrap-around=16k}\ + %{mmcu=atmega32*|\ + mmcu=at90can32*:--pmem-wrap-around=32k}\ + %{mmcu=atmega64*|\ + mmcu=at90can64*|\ + mmcu=at90usb64*:--pmem-wrap-around=64k}}}\ +%:device_to_ld(%{mmcu=*:%*})\ +%:device_to_data_start(%{mmcu=*:%*})" + +#define LIB_SPEC \ + "%{!mmcu=at90s1*:%{!mmcu=attiny11:%{!mmcu=attiny12:%{!mmcu=attiny15:%{!mmcu=attiny28: -lc }}}}}" + +#define LIBSTDCXX "gcc" +/* No libstdc++ for now. Empty string doesn't work. */ + +#define LIBGCC_SPEC \ + "%{!mmcu=at90s1*:%{!mmcu=attiny11:%{!mmcu=attiny12:%{!mmcu=attiny15:%{!mmcu=attiny28: -lgcc }}}}}" + +#define STARTFILE_SPEC "%:device_to_startfile(%{mmcu=*:%*})" + +#define ENDFILE_SPEC "" + +/* This is the default without any -mmcu=* option (AT90S*). */ +#define MULTILIB_DEFAULTS { "mmcu=avr2" } + +#define TEST_HARD_REG_CLASS(CLASS, REGNO) \ + TEST_HARD_REG_BIT (reg_class_contents[ (int) (CLASS)], REGNO) + +#define CR_TAB "\n\t" + +#define DWARF2_ADDR_SIZE 4 + +#define INCOMING_RETURN_ADDR_RTX avr_incoming_return_addr_rtx () +#define INCOMING_FRAME_SP_OFFSET (AVR_3_BYTE_PC ? 3 : 2) + +/* The caller's stack pointer value immediately before the call + is one byte below the first argument. */ +#define ARG_POINTER_CFA_OFFSET(FNDECL) -1 + +#define HARD_REGNO_RENAME_OK(OLD_REG, NEW_REG) \ + avr_hard_regno_rename_ok (OLD_REG, NEW_REG) + +/* A C structure for machine-specific, per-function data. + This is added to the cfun structure. */ +struct GTY(()) machine_function +{ + /* 'true' - if current function is a naked function. */ + int is_naked; + + /* 'true' - if current function is an interrupt function + as specified by the "interrupt" attribute. */ + int is_interrupt; + + /* 'true' - if current function is a signal function + as specified by the "signal" attribute. */ + int is_signal; + + /* 'true' - if current function is a 'task' function + as specified by the "OS_task" attribute. */ + int is_OS_task; + + /* 'true' - if current function is a 'main' function + as specified by the "OS_main" attribute. */ + int is_OS_main; + + /* Current function stack size. */ + int stack_usage; + + /* 'true' if a callee might be tail called */ + int sibcall_fails; + + /* 'true' if the above is_foo predicates are sanity-checked to avoid + multiple diagnose for the same function. */ + int attributes_checked_p; +}; + +/* AVR does not round pushes, but the existence of this macro is + required in order for pushes to be generated. */ +#define PUSH_ROUNDING(X) (X) + +/* Define prototype here to avoid build warning. Some files using + ACCUMULATE_OUTGOING_ARGS (directly or indirectly) include + tm.h but not tm_p.h. */ +extern int avr_accumulate_outgoing_args (void); +#define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args() + +#define INIT_EXPANDERS avr_init_expanders() diff --git a/gcc-4.8/gcc/config/avr/avr.md b/gcc-4.8/gcc/config/avr/avr.md new file mode 100644 index 000000000..e9f5d038f --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr.md @@ -0,0 +1,6357 @@ +;; Machine description for GNU compiler, +;; for ATMEL AVR micro controllers. +;; Copyright (C) 1998-2013 Free Software Foundation, Inc. +;; Contributed by Denis Chertykov (chertykov@gmail.com) + +;; 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 +;; . + +;; Special characters after '%': +;; A No effect (add 0). +;; B Add 1 to REG number, MEM address or CONST_INT. +;; C Add 2. +;; D Add 3. +;; j Branch condition. +;; k Reverse branch condition. +;;..m..Constant Direct Data memory address. +;; i Print the SFR address quivalent of a CONST_INT or a CONST_INT +;; RAM address. The resulting address is suitable to be used in IN/OUT. +;; o Displacement for (mem (plus (reg) (const_int))) operands. +;; p POST_INC or PRE_DEC address as a pointer (X, Y, Z) +;; r POST_INC or PRE_DEC address as a register (r26, r28, r30) +;; T/T Print operand suitable for BLD/BST instruction, i.e. register and +;; bit number. This gets 2 operands: The first %T gets a REG_P and +;; just cashes the operand for the next %T. The second %T gets +;; a CONST_INT that represents a bit position. +;; Example: With %0 = (reg:HI 18) and %1 = (const_int 13) +;; "%T0%T1" it will print "r19,5". +;; Notice that you must not write a comma between %T0 and %T1. +;; T/t Similar to above, but don't print the comma and the bit number. +;; Example: With %0 = (reg:HI 18) and %1 = (const_int 13) +;; "%T0%t1" it will print "r19". +;;..x..Constant Direct Program memory address. +;; ~ Output 'r' if not AVR_HAVE_JMP_CALL. +;; ! Output 'e' if AVR_HAVE_EIJMP_EICALL. + + +(define_constants + [(REG_X 26) + (REG_Y 28) + (REG_Z 30) + (REG_W 24) + (REG_SP 32) + (LPM_REGNO 0) ; implicit target register of LPM + (TMP_REGNO 0) ; temporary register r0 + (ZERO_REGNO 1) ; zero register r1 + ]) + +(define_c_enum "unspec" + [UNSPEC_STRLEN + UNSPEC_MOVMEM + UNSPEC_INDEX_JMP + UNSPEC_FMUL + UNSPEC_FMULS + UNSPEC_FMULSU + UNSPEC_COPYSIGN + UNSPEC_IDENTITY + UNSPEC_INSERT_BITS + UNSPEC_ROUND + ]) + +(define_c_enum "unspecv" + [UNSPECV_PROLOGUE_SAVES + UNSPECV_EPILOGUE_RESTORES + UNSPECV_WRITE_SP + UNSPECV_GOTO_RECEIVER + UNSPECV_ENABLE_IRQS + UNSPECV_MEMORY_BARRIER + UNSPECV_NOP + UNSPECV_SLEEP + UNSPECV_WDR + UNSPECV_DELAY_CYCLES + ]) + + +(include "predicates.md") +(include "constraints.md") + +;; Condition code settings. +(define_attr "cc" "none,set_czn,set_zn,set_n,compare,clobber, + plus,ldi" + (const_string "none")) + +(define_attr "type" "branch,branch1,arith,xcall" + (const_string "arith")) + +;; The size of instructions in bytes. +;; XXX may depend from "cc" + +(define_attr "length" "" + (cond [(eq_attr "type" "branch") + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -63)) + (le (minus (pc) (match_dup 0)) + (const_int 62))) + (const_int 1) + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -2045)) + (le (minus (pc) (match_dup 0)) + (const_int 2045))) + (const_int 2) + (const_int 3))) + (eq_attr "type" "branch1") + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -62)) + (le (minus (pc) (match_dup 0)) + (const_int 61))) + (const_int 2) + (if_then_else (and (ge (minus (pc) (match_dup 0)) + (const_int -2044)) + (le (minus (pc) (match_dup 0)) + (const_int 2043))) + (const_int 3) + (const_int 4))) + (eq_attr "type" "xcall") + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 1) + (const_int 2))] + (const_int 2))) + +;; Lengths of several insns are adjusted in avr.c:adjust_insn_length(). +;; Following insn attribute tells if and how the adjustment has to be +;; done: +;; no No adjustment needed; attribute "length" is fine. +;; Otherwise do special processing depending on the attribute. + +(define_attr "adjust_len" + "out_bitop, plus, addto_sp, + tsthi, tstpsi, tstsi, compare, compare64, call, + mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, + ufract, sfract, + xload, lpm, movmem, + ashlqi, ashrqi, lshrqi, + ashlhi, ashrhi, lshrhi, + ashlsi, ashrsi, lshrsi, + ashlpsi, ashrpsi, lshrpsi, + insert_bits, + no" + (const_string "no")) + +;; Flavours of instruction set architecture (ISA), used in enabled attribute + +;; mov : ISA has no MOVW movw : ISA has MOVW +;; rjmp : ISA has no CALL/JMP jmp : ISA has CALL/JMP +;; ijmp : ISA has no EICALL/EIJMP eijmp : ISA has EICALL/EIJMP +;; lpm : ISA has no LPMX lpmx : ISA has LPMX +;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX +;; no_xmega: non-XMEGA core xmega : XMEGA core + +(define_attr "isa" + "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, no_xmega,xmega, + standard" + (const_string "standard")) + +(define_attr "enabled" "" + (cond [(eq_attr "isa" "standard") + (const_int 1) + + (and (eq_attr "isa" "mov") + (match_test "!AVR_HAVE_MOVW")) + (const_int 1) + + (and (eq_attr "isa" "movw") + (match_test "AVR_HAVE_MOVW")) + (const_int 1) + + (and (eq_attr "isa" "rjmp") + (match_test "!AVR_HAVE_JMP_CALL")) + (const_int 1) + + (and (eq_attr "isa" "jmp") + (match_test "AVR_HAVE_JMP_CALL")) + (const_int 1) + + (and (eq_attr "isa" "ijmp") + (match_test "!AVR_HAVE_EIJMP_EICALL")) + (const_int 1) + + (and (eq_attr "isa" "eijmp") + (match_test "AVR_HAVE_EIJMP_EICALL")) + (const_int 1) + + (and (eq_attr "isa" "lpm") + (match_test "!AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "lpmx") + (match_test "AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpm") + (match_test "AVR_HAVE_ELPM && !AVR_HAVE_ELPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpmx") + (match_test "AVR_HAVE_ELPMX")) + (const_int 1) + + (and (eq_attr "isa" "xmega") + (match_test "AVR_XMEGA")) + (const_int 1) + + (and (eq_attr "isa" "no_xmega") + (match_test "!AVR_XMEGA")) + (const_int 1) + ] (const_int 0))) + + +;; Define mode iterators +(define_mode_iterator QIHI [QI HI]) +(define_mode_iterator QIHI2 [QI HI]) +(define_mode_iterator QISI [QI HI PSI SI]) +(define_mode_iterator QIDI [QI HI PSI SI DI]) +(define_mode_iterator HISI [HI PSI SI]) + +(define_mode_iterator ALL1 [QI QQ UQQ]) +(define_mode_iterator ALL2 [HI HQ UHQ HA UHA]) +(define_mode_iterator ALL4 [SI SQ USQ SA USA]) + +;; All supported move-modes +(define_mode_iterator MOVMODE [QI QQ UQQ + HI HQ UHQ HA UHA + SI SQ USQ SA USA + SF PSI]) + +;; Supported ordered modes that are 2, 3, 4 bytes wide +(define_mode_iterator ORDERED234 [HI SI PSI + HQ UHQ HA UHA + SQ USQ SA USA]) + +;; Define code iterators +;; Define two incarnations so that we can build the cross product. +(define_code_iterator any_extend [sign_extend zero_extend]) +(define_code_iterator any_extend2 [sign_extend zero_extend]) + +(define_code_iterator xior [xor ior]) +(define_code_iterator eqne [eq ne]) + +(define_code_iterator ss_addsub [ss_plus ss_minus]) +(define_code_iterator us_addsub [us_plus us_minus]) +(define_code_iterator ss_abs_neg [ss_abs ss_neg]) + +;; Define code attributes +(define_code_attr extend_su + [(sign_extend "s") + (zero_extend "u")]) + +(define_code_attr extend_u + [(sign_extend "") + (zero_extend "u")]) + +(define_code_attr extend_s + [(sign_extend "s") + (zero_extend "")]) + +;; Constrain input operand of widening multiply, i.e. MUL resp. MULS. +(define_code_attr mul_r_d + [(zero_extend "r") + (sign_extend "d")]) + +(define_code_attr abelian + [(ss_minus "") (us_minus "") + (ss_plus "%") (us_plus "%")]) + +;; Map RTX code to its standard insn name +(define_code_attr code_stdname + [(ashift "ashl") + (ashiftrt "ashr") + (lshiftrt "lshr") + (ior "ior") + (xor "xor") + (rotate "rotl") + (ss_plus "ssadd") (ss_minus "sssub") (ss_neg "ssneg") (ss_abs "ssabs") + (us_plus "usadd") (us_minus "ussub") (us_neg "usneg") + ]) + +;;======================================================================== +;; The following is used by nonlocal_goto and setjmp. +;; The receiver pattern will create no instructions since internally +;; virtual_stack_vars = hard_frame_pointer + 1 so the RTL become R28=R28 +;; This avoids creating add/sub offsets in frame_pointer save/resore. +;; The 'null' receiver also avoids problems with optimisation +;; not recognising incoming jmp and removing code that resets frame_pointer. +;; The code derived from builtins.c. + +(define_expand "nonlocal_goto_receiver" + [(set (reg:HI REG_Y) + (unspec_volatile:HI [(const_int 0)] UNSPECV_GOTO_RECEIVER))] + "" + { + emit_move_insn (virtual_stack_vars_rtx, + gen_rtx_PLUS (Pmode, hard_frame_pointer_rtx, + gen_int_mode (STARTING_FRAME_OFFSET, + Pmode))); + /* ; This might change the hard frame pointer in ways that aren't + ; apparent to early optimization passes, so force a clobber. */ + emit_clobber (hard_frame_pointer_rtx); + DONE; + }) + + +;; Defining nonlocal_goto_receiver means we must also define this. +;; even though its function is identical to that in builtins.c + +(define_expand "nonlocal_goto" + [(use (match_operand 0 "general_operand")) + (use (match_operand 1 "general_operand")) + (use (match_operand 2 "general_operand")) + (use (match_operand 3 "general_operand"))] + "" + { + rtx r_label = copy_to_reg (operands[1]); + rtx r_fp = operands[3]; + rtx r_sp = operands[2]; + + emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))); + + emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx)); + + emit_move_insn (hard_frame_pointer_rtx, r_fp); + emit_stack_restore (SAVE_NONLOCAL, r_sp); + + emit_use (hard_frame_pointer_rtx); + emit_use (stack_pointer_rtx); + + emit_indirect_jump (r_label); + + DONE; + }) + +;; "pushqi1" +;; "pushqq1" "pushuqq1" +(define_insn "push1" + [(set (mem:ALL1 (post_dec:HI (reg:HI REG_SP))) + (match_operand:ALL1 0 "reg_or_0_operand" "r,Y00"))] + "" + "@ + push %0 + push __zero_reg__" + [(set_attr "length" "1,1")]) + +;; All modes for a multi-byte push. We must include complex modes here too, +;; lest emit_single_push_insn "helpfully" create the auto-inc itself. +(define_mode_iterator MPUSH + [CQI + HI CHI HA UHA HQ UHQ + SI CSI SA USA SQ USQ + DI CDI DA UDA DQ UDQ + TA UTA + SF SC + PSI]) + +(define_expand "push1" + [(match_operand:MPUSH 0 "" "")] + "" + { + int i; + for (i = GET_MODE_SIZE (mode) - 1; i >= 0; --i) + { + rtx part = simplify_gen_subreg (QImode, operands[0], mode, i); + if (part != const0_rtx) + part = force_reg (QImode, part); + emit_insn (gen_pushqi1 (part)); + } + DONE; + }) + +;; Notice a special-case when adding N to SP where N results in a +;; zero REG_ARGS_SIZE. This is equivalent to a move from FP. +(define_split + [(set (reg:HI REG_SP) + (match_operand:HI 0 "register_operand" ""))] + "reload_completed + && frame_pointer_needed + && !cfun->calls_alloca + && find_reg_note (insn, REG_ARGS_SIZE, const0_rtx)" + [(set (reg:HI REG_SP) + (reg:HI REG_Y))]) + +;;======================================================================== +;; Move stuff around + +;; Secondary input reload from non-generic 16-bit address spaces +(define_insn "reload_in" + [(set (match_operand:MOVMODE 0 "register_operand" "=r") + (match_operand:MOVMODE 1 "flash_operand" "m")) + (clobber (match_operand:QI 2 "d_register_operand" "=d"))] + ;; Fixme: The insn condition must not test the address space. + ;; Because the gen tools refuse to generate insns for address spaces + ;; and will generate insn-codes.h to look like: + ;; #define CODE_FOR_reload_inhi CODE_FOR_nothing + "reload_completed || reload_in_progress" + { + return avr_out_lpm (insn, operands, NULL); + } + [(set_attr "adjust_len" "lpm") + (set_attr "cc" "clobber")]) + + +;; "loadqi_libgcc" +;; "loadhi_libgcc" +;; "loadpsi_libgcc" +;; "loadsi_libgcc" +;; "loadsf_libgcc" +(define_expand "load_libgcc" + [(set (match_dup 3) + (match_dup 2)) + (set (reg:MOVMODE 22) + (match_operand:MOVMODE 1 "memory_operand" "")) + (set (match_operand:MOVMODE 0 "register_operand" "") + (reg:MOVMODE 22))] + "avr_load_libgcc_p (operands[1])" + { + operands[3] = gen_rtx_REG (HImode, REG_Z); + operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX); + operands[1] = replace_equiv_address (operands[1], operands[3]); + set_mem_addr_space (operands[1], ADDR_SPACE_FLASH); + }) + +;; "load_qi_libgcc" +;; "load_hi_libgcc" +;; "load_psi_libgcc" +;; "load_si_libgcc" +;; "load_sf_libgcc" +(define_insn "load__libgcc" + [(set (reg:MOVMODE 22) + (match_operand:MOVMODE 0 "memory_operand" "m,m"))] + "avr_load_libgcc_p (operands[0]) + && REG_P (XEXP (operands[0], 0)) + && REG_Z == REGNO (XEXP (operands[0], 0))" + { + operands[0] = GEN_INT (GET_MODE_SIZE (mode)); + return "%~call __load_%0"; + } + [(set_attr "length" "1,2") + (set_attr "isa" "rjmp,jmp") + (set_attr "cc" "clobber")]) + + +;; "xload8qi_A" +;; "xload8qq_A" "xload8uqq_A" +(define_insn_and_split "xload8_A" + [(set (match_operand:ALL1 0 "register_operand" "=r") + (match_operand:ALL1 1 "memory_operand" "m")) + (clobber (reg:HI REG_Z))] + "can_create_pseudo_p() + && !avr_xload_libgcc_p (mode) + && avr_mem_memx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + /* ; Split away the high part of the address. GCC's register allocator + ; in not able to allocate segment registers and reload the resulting + ; expressions. Notice that no address register can hold a PSImode. */ + + rtx insn, addr = XEXP (operands[1], 0); + rtx hi8 = gen_reg_rtx (QImode); + rtx reg_z = gen_rtx_REG (HImode, REG_Z); + + emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0)); + emit_move_insn (hi8, simplify_gen_subreg (QImode, addr, PSImode, 2)); + + insn = emit_insn (gen_xload_8 (operands[0], hi8)); + set_mem_addr_space (SET_SRC (single_set (insn)), + MEM_ADDR_SPACE (operands[1])); + DONE; + }) + +;; "xloadqi_A" "xloadqq_A" "xloaduqq_A" +;; "xloadhi_A" "xloadhq_A" "xloaduhq_A" "xloadha_A" "xloaduha_A" +;; "xloadsi_A" "xloadsq_A" "xloadusq_A" "xloadsa_A" "xloadusa_A" +;; "xloadpsi_A" +;; "xloadsf_A" +(define_insn_and_split "xload_A" + [(set (match_operand:MOVMODE 0 "register_operand" "=r") + (match_operand:MOVMODE 1 "memory_operand" "m")) + (clobber (reg:MOVMODE 22)) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "can_create_pseudo_p() + && avr_mem_memx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + rtx addr = XEXP (operands[1], 0); + rtx reg_z = gen_rtx_REG (HImode, REG_Z); + rtx addr_hi8 = simplify_gen_subreg (QImode, addr, PSImode, 2); + addr_space_t as = MEM_ADDR_SPACE (operands[1]); + rtx insn; + + /* Split the address to R21:Z */ + emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0)); + emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8); + + /* Load with code from libgcc */ + insn = emit_insn (gen_xload__libgcc ()); + set_mem_addr_space (SET_SRC (single_set (insn)), as); + + /* Move to destination */ + emit_move_insn (operands[0], gen_rtx_REG (mode, 22)); + + DONE; + }) + +;; Move value from address space memx to a register +;; These insns must be prior to respective generic move insn. + +;; "xloadqi_8" +;; "xloadqq_8" "xloaduqq_8" +(define_insn "xload_8" + [(set (match_operand:ALL1 0 "register_operand" "=&r,r") + (mem:ALL1 (lo_sum:PSI (match_operand:QI 1 "register_operand" "r,r") + (reg:HI REG_Z))))] + "!avr_xload_libgcc_p (mode)" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "length" "4,4") + (set_attr "adjust_len" "*,xload") + (set_attr "isa" "lpmx,lpm") + (set_attr "cc" "none")]) + +;; R21:Z : 24-bit source address +;; R22 : 1-4 byte output + +;; "xload_qi_libgcc" "xload_qq_libgcc" "xload_uqq_libgcc" +;; "xload_hi_libgcc" "xload_hq_libgcc" "xload_uhq_libgcc" "xload_ha_libgcc" "xload_uha_libgcc" +;; "xload_si_libgcc" "xload_sq_libgcc" "xload_usq_libgcc" "xload_sa_libgcc" "xload_usa_libgcc" +;; "xload_sf_libgcc" +;; "xload_psi_libgcc" +(define_insn "xload__libgcc" + [(set (reg:MOVMODE 22) + (mem:MOVMODE (lo_sum:PSI (reg:QI 21) + (reg:HI REG_Z)))) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "avr_xload_libgcc_p (mode)" + { + rtx x_bytes = GEN_INT (GET_MODE_SIZE (mode)); + + output_asm_insn ("%~call __xload_%0", &x_bytes); + return ""; + } + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; General move expanders + +;; "movqi" "movqq" "movuqq" +;; "movhi" "movhq" "movuhq" "movha" "movuha" +;; "movsi" "movsq" "movusq" "movsa" "movusa" +;; "movsf" +;; "movpsi" +(define_expand "mov" + [(set (match_operand:MOVMODE 0 "nonimmediate_operand" "") + (match_operand:MOVMODE 1 "general_operand" ""))] + "" + { + rtx dest = operands[0]; + rtx src = operands[1]; + + if (avr_mem_flash_p (dest)) + DONE; + + /* One of the operands has to be in a register. */ + if (!register_operand (dest, mode) + && !reg_or_0_operand (src, mode)) + { + operands[1] = src = copy_to_mode_reg (mode, src); + } + + if (avr_mem_memx_p (src)) + { + rtx addr = XEXP (src, 0); + + if (!REG_P (addr)) + src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr)); + + if (!avr_xload_libgcc_p (mode)) + /* ; No here because gen_xload8_A only iterates over ALL1. + ; insn-emit does not depend on the mode, it's all about operands. */ + emit_insn (gen_xload8qi_A (dest, src)); + else + emit_insn (gen_xload_A (dest, src)); + + DONE; + } + + if (avr_load_libgcc_p (src)) + { + /* For the small devices, do loads per libgcc call. */ + emit_insn (gen_load_libgcc (dest, src)); + DONE; + } + }) + +;;======================================================================== +;; move byte +;; The last alternative (any immediate constant to any register) is +;; very expensive. It should be optimized by peephole2 if a scratch +;; register is available, but then that register could just as well be +;; allocated for the variable we are loading. But, most of NO_LD_REGS +;; are call-saved registers, and most of LD_REGS are call-used registers, +;; so this may still be a win for registers live across function calls. + +;; "movqi_insn" +;; "movqq_insn" "movuqq_insn" +(define_insn "mov_insn" + [(set (match_operand:ALL1 0 "nonimmediate_operand" "=r ,d ,Qm ,r ,q,r,*r") + (match_operand:ALL1 1 "nox_general_operand" "r Y00,n Ynn,r Y00,Qm,r,q,i"))] + "register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)" + { + return output_movqi (insn, operands, NULL); + } + [(set_attr "length" "1,1,5,5,1,1,4") + (set_attr "adjust_len" "mov8") + (set_attr "cc" "ldi,none,clobber,clobber,none,none,clobber")]) + +;; This is used in peephole2 to optimize loading immediate constants +;; if a scratch register from LD_REGS happens to be available. + +;; "*reload_inqi" +;; "*reload_inqq" "*reload_inuqq" +(define_insn "*reload_in" + [(set (match_operand:ALL1 0 "register_operand" "=l") + (match_operand:ALL1 1 "const_operand" "i")) + (clobber (match_operand:QI 2 "register_operand" "=&d"))] + "reload_completed" + "ldi %2,lo8(%1) + mov %0,%2" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +(define_peephole2 + [(match_scratch:QI 2 "d") + (set (match_operand:ALL1 0 "l_register_operand" "") + (match_operand:ALL1 1 "const_operand" ""))] + ; No need for a clobber reg for 0x0, 0x01 or 0xff + "!satisfies_constraint_Y00 (operands[1]) + && !satisfies_constraint_Y01 (operands[1]) + && !satisfies_constraint_Ym1 (operands[1])" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (match_dup 2))])]) + +;;============================================================================ +;; move word (16 bit) + +;; Move register $1 to the Stack Pointer register SP. +;; This insn is emit during function prologue/epilogue generation. +;; $2 = 0: We know that IRQs are off +;; $2 = 1: We know that IRQs are on +;; $2 = 2: SP has 8 bits only, IRQ state does not matter +;; $2 = -1: We don't know anything about IRQ on/off +;; Always write SP via unspec, see PR50063 + +(define_insn "movhi_sp_r" + [(set (match_operand:HI 0 "stack_register_operand" "=q,q,q,q,q") + (unspec_volatile:HI [(match_operand:HI 1 "register_operand" "r,r,r,r,r") + (match_operand:HI 2 "const_int_operand" "L,P,N,K,LPN")] + UNSPECV_WRITE_SP))] + "" + "@ + out %B0,%B1\;out %A0,%A1 + cli\;out %B0,%B1\;sei\;out %A0,%A1 + in __tmp_reg__,__SREG__\;cli\;out %B0,%B1\;out __SREG__,__tmp_reg__\;out %A0,%A1 + out %A0,%A1 + out %A0,%A1\;out %B0,%B1" + [(set_attr "length" "2,4,5,1,2") + (set_attr "isa" "no_xmega,no_xmega,no_xmega,*,xmega") + (set_attr "cc" "none")]) + +(define_peephole2 + [(match_scratch:QI 2 "d") + (set (match_operand:ALL2 0 "l_register_operand" "") + (match_operand:ALL2 1 "const_or_immediate_operand" ""))] + "operands[1] != CONST0_RTX (mode)" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (match_dup 2))])]) + +;; '*' because it is not used in rtl generation, only in above peephole +;; "*reload_inhi" +;; "*reload_inhq" "*reload_inuhq" +;; "*reload_inha" "*reload_inuha" +(define_insn "*reload_in" + [(set (match_operand:ALL2 0 "l_register_operand" "=l") + (match_operand:ALL2 1 "immediate_operand" "i")) + (clobber (match_operand:QI 2 "register_operand" "=&d"))] + "reload_completed" + { + return output_reload_inhi (operands, operands[2], NULL); + } + [(set_attr "length" "4") + (set_attr "adjust_len" "reload_in16") + (set_attr "cc" "clobber")]) + +;; "*movhi" +;; "*movhq" "*movuhq" +;; "*movha" "*movuha" +(define_insn "*mov" + [(set (match_operand:ALL2 0 "nonimmediate_operand" "=r,r ,r,m ,d,*r,q,r") + (match_operand:ALL2 1 "nox_general_operand" "r,Y00,m,r Y00,i,i ,r,q"))] + "register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)" + { + return output_movhi (insn, operands, NULL); + } + [(set_attr "length" "2,2,6,7,2,6,5,2") + (set_attr "adjust_len" "mov16") + (set_attr "cc" "none,none,clobber,clobber,none,clobber,none,none")]) + +(define_peephole2 ; movw + [(set (match_operand:ALL1 0 "even_register_operand" "") + (match_operand:ALL1 1 "even_register_operand" "")) + (set (match_operand:ALL1 2 "odd_register_operand" "") + (match_operand:ALL1 3 "odd_register_operand" ""))] + "AVR_HAVE_MOVW + && REGNO (operands[0]) == REGNO (operands[2]) - 1 + && REGNO (operands[1]) == REGNO (operands[3]) - 1" + [(set (match_dup 4) + (match_dup 5))] + { + operands[4] = gen_rtx_REG (HImode, REGNO (operands[0])); + operands[5] = gen_rtx_REG (HImode, REGNO (operands[1])); + }) + +(define_peephole2 ; movw_r + [(set (match_operand:ALL1 0 "odd_register_operand" "") + (match_operand:ALL1 1 "odd_register_operand" "")) + (set (match_operand:ALL1 2 "even_register_operand" "") + (match_operand:ALL1 3 "even_register_operand" ""))] + "AVR_HAVE_MOVW + && REGNO (operands[2]) == REGNO (operands[0]) - 1 + && REGNO (operands[3]) == REGNO (operands[1]) - 1" + [(set (match_dup 4) + (match_dup 5))] + { + operands[4] = gen_rtx_REG (HImode, REGNO (operands[2])); + operands[5] = gen_rtx_REG (HImode, REGNO (operands[3])); + }) + +;; For LPM loads from AS1 we split +;; R = *Z +;; to +;; R = *Z++ +;; Z = Z - sizeof (R) +;; +;; so that the second instruction can be optimized out. + +(define_split ; "split-lpmx" + [(set (match_operand:HISI 0 "register_operand" "") + (match_operand:HISI 1 "memory_operand" ""))] + "reload_completed + && AVR_HAVE_LPMX" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 3) + (plus:HI (match_dup 3) + (match_dup 4)))] + { + rtx addr = XEXP (operands[1], 0); + + if (!avr_mem_flash_p (operands[1]) + || !REG_P (addr) + || reg_overlap_mentioned_p (addr, operands[0])) + { + FAIL; + } + + operands[2] = replace_equiv_address (operands[1], + gen_rtx_POST_INC (Pmode, addr)); + operands[3] = addr; + operands[4] = gen_int_mode (-GET_MODE_SIZE (mode), HImode); + }) + +;;========================================================================== +;; xpointer move (24 bit) + +(define_peephole2 ; *reload_inpsi + [(match_scratch:QI 2 "d") + (set (match_operand:PSI 0 "l_register_operand" "") + (match_operand:PSI 1 "immediate_operand" "")) + (match_dup 2)] + "operands[1] != const0_rtx + && operands[1] != constm1_rtx" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (match_dup 2))])]) + +;; '*' because it is not used in rtl generation. +(define_insn "*reload_inpsi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (match_operand:PSI 1 "immediate_operand" "i")) + (clobber (match_operand:QI 2 "register_operand" "=&d"))] + "reload_completed" + { + return avr_out_reload_inpsi (operands, operands[2], NULL); + } + [(set_attr "length" "6") + (set_attr "adjust_len" "reload_in24") + (set_attr "cc" "clobber")]) + +(define_insn "*movpsi" + [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:PSI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))] + "register_operand (operands[0], PSImode) + || register_operand (operands[1], PSImode) + || const0_rtx == operands[1]" + { + return avr_out_movpsi (insn, operands, NULL); + } + [(set_attr "length" "3,3,8,9,4,10") + (set_attr "adjust_len" "mov24") + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) + +;;========================================================================== +;; move double word (32 bit) + +(define_peephole2 ; *reload_insi + [(match_scratch:QI 2 "d") + (set (match_operand:ALL4 0 "l_register_operand" "") + (match_operand:ALL4 1 "immediate_operand" "")) + (match_dup 2)] + "operands[1] != CONST0_RTX (mode)" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (match_dup 2))])]) + +;; '*' because it is not used in rtl generation. +;; "*reload_insi" +;; "*reload_insq" "*reload_inusq" +;; "*reload_insa" "*reload_inusa" +(define_insn "*reload_insi" + [(set (match_operand:ALL4 0 "register_operand" "=r") + (match_operand:ALL4 1 "immediate_operand" "n Ynn")) + (clobber (match_operand:QI 2 "register_operand" "=&d"))] + "reload_completed" + { + return output_reload_insisf (operands, operands[2], NULL); + } + [(set_attr "length" "8") + (set_attr "adjust_len" "reload_in32") + (set_attr "cc" "clobber")]) + + +;; "*movsi" +;; "*movsq" "*movusq" +;; "*movsa" "*movusa" +(define_insn "*mov" + [(set (match_operand:ALL4 0 "nonimmediate_operand" "=r,r ,r ,Qm ,!d,r") + (match_operand:ALL4 1 "nox_general_operand" "r,Y00,Qm,r Y00,i ,i"))] + "register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)" + { + return output_movsisf (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,9,4,10") + (set_attr "adjust_len" "mov32") + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) + +;; fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +;; move floating point numbers (32 bit) + +(define_insn "*movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))] + "register_operand (operands[0], SFmode) + || reg_or_0_operand (operands[1], SFmode)" + { + return output_movsisf (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,9,4,10") + (set_attr "adjust_len" "mov32") + (set_attr "cc" "none,none,clobber,clobber,none,clobber")]) + +(define_peephole2 ; *reload_insf + [(match_scratch:QI 2 "d") + (set (match_operand:SF 0 "l_register_operand" "") + (match_operand:SF 1 "const_double_operand" "")) + (match_dup 2)] + "operands[1] != CONST0_RTX (SFmode)" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (match_dup 2))])]) + +;; '*' because it is not used in rtl generation. +(define_insn "*reload_insf" + [(set (match_operand:SF 0 "register_operand" "=r") + (match_operand:SF 1 "const_double_operand" "F")) + (clobber (match_operand:QI 2 "register_operand" "=&d"))] + "reload_completed" + { + return output_reload_insisf (operands, operands[2], NULL); + } + [(set_attr "length" "8") + (set_attr "adjust_len" "reload_in32") + (set_attr "cc" "clobber")]) + +;;========================================================================= +;; move string (like memcpy) + +(define_expand "movmemhi" + [(parallel [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand:HI 2 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" ""))])] + "" + { + if (avr_emit_movmemhi (operands)) + DONE; + + FAIL; + }) + +(define_mode_attr MOVMEM_r_d [(QI "r") + (HI "wd")]) + +;; $0 : Address Space +;; $1, $2 : Loop register +;; R30 : source address +;; R26 : destination address + +;; "movmem_qi" +;; "movmem_hi" +(define_insn "movmem_" + [(set (mem:BLK (reg:HI REG_X)) + (mem:BLK (reg:HI REG_Z))) + (unspec [(match_operand:QI 0 "const_int_operand" "n")] + UNSPEC_MOVMEM) + (use (match_operand:QIHI 1 "register_operand" "")) + (clobber (reg:HI REG_X)) + (clobber (reg:HI REG_Z)) + (clobber (reg:QI LPM_REGNO)) + (clobber (match_operand:QIHI 2 "register_operand" "=1"))] + "" + { + return avr_out_movmem (insn, operands, NULL); + } + [(set_attr "adjust_len" "movmem") + (set_attr "cc" "clobber")]) + + +;; $0 : Address Space +;; $1 : RAMPZ RAM address +;; R24 : #bytes and loop register +;; R23:Z : 24-bit source address +;; R26 : 16-bit destination address + +;; "movmemx_qi" +;; "movmemx_hi" +(define_insn "movmemx_" + [(set (mem:BLK (reg:HI REG_X)) + (mem:BLK (lo_sum:PSI (reg:QI 23) + (reg:HI REG_Z)))) + (unspec [(match_operand:QI 0 "const_int_operand" "n")] + UNSPEC_MOVMEM) + (use (reg:QIHI 24)) + (clobber (reg:HI REG_X)) + (clobber (reg:HI REG_Z)) + (clobber (reg:QI LPM_REGNO)) + (clobber (reg:HI 24)) + (clobber (reg:QI 23)) + (clobber (mem:QI (match_operand:QI 1 "io_address_operand" "n")))] + "" + "%~call __movmemx_" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 +;; memset (%0, %2, %1) + +(define_expand "setmemhi" + [(parallel [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand 2 "const_int_operand" "")) + (use (match_operand:HI 1 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" "")) + (clobber (match_scratch:HI 4 "")) + (clobber (match_dup 5))])] + "" + { + rtx addr0; + enum machine_mode mode; + + /* If value to set is not zero, use the library routine. */ + if (operands[2] != const0_rtx) + FAIL; + + if (!CONST_INT_P (operands[1])) + FAIL; + + mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode; + operands[5] = gen_rtx_SCRATCH (mode); + operands[1] = copy_to_mode_reg (mode, + gen_int_mode (INTVAL (operands[1]), mode)); + addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); + operands[0] = gen_rtx_MEM (BLKmode, addr0); + }) + + +(define_insn "*clrmemqi" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "e")) + (const_int 0)) + (use (match_operand:QI 1 "register_operand" "r")) + (use (match_operand:QI 2 "const_int_operand" "n")) + (clobber (match_scratch:HI 3 "=0")) + (clobber (match_scratch:QI 4 "=&1"))] + "" + "0:\;st %a0+,__zero_reg__\;dec %1\;brne 0b" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + + +(define_insn "*clrmemhi" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "e,e")) + (const_int 0)) + (use (match_operand:HI 1 "register_operand" "!w,d")) + (use (match_operand:HI 2 "const_int_operand" "n,n")) + (clobber (match_scratch:HI 3 "=0,0")) + (clobber (match_scratch:HI 4 "=&1,&1"))] + "" + "@ + 0:\;st %a0+,__zero_reg__\;sbiw %A1,1\;brne 0b + 0:\;st %a0+,__zero_reg__\;subi %A1,1\;sbci %B1,0\;brne 0b" + [(set_attr "length" "3,4") + (set_attr "cc" "clobber,clobber")]) + +(define_expand "strlenhi" + [(set (match_dup 4) + (unspec:HI [(match_operand:BLK 1 "memory_operand" "") + (match_operand:QI 2 "const_int_operand" "") + (match_operand:HI 3 "immediate_operand" "")] + UNSPEC_STRLEN)) + (set (match_dup 4) + (plus:HI (match_dup 4) + (const_int -1))) + (parallel [(set (match_operand:HI 0 "register_operand" "") + (minus:HI (match_dup 4) + (match_dup 5))) + (clobber (scratch:QI))])] + "" + { + rtx addr; + if (operands[2] != const0_rtx) + FAIL; + addr = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); + operands[1] = gen_rtx_MEM (BLKmode, addr); + operands[5] = addr; + operands[4] = gen_reg_rtx (HImode); + }) + +(define_insn "*strlenhi" + [(set (match_operand:HI 0 "register_operand" "=e") + (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "0")) + (const_int 0) + (match_operand:HI 2 "immediate_operand" "i")] + UNSPEC_STRLEN))] + "" + "0:\;ld __tmp_reg__,%a0+\;tst __tmp_reg__\;brne 0b" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; add bytes + +;; "addqi3" +;; "addqq3" "adduqq3" +(define_insn "add3" + [(set (match_operand:ALL1 0 "register_operand" "=r,d ,r ,r ,r ,r") + (plus:ALL1 (match_operand:ALL1 1 "register_operand" "%0,0 ,0 ,0 ,0 ,0") + (match_operand:ALL1 2 "nonmemory_operand" "r,n Ynn,Y01,Ym1,Y02,Ym2")))] + "" + "@ + add %0,%2 + subi %0,lo8(-(%2)) + inc %0 + dec %0 + inc %0\;inc %0 + dec %0\;dec %0" + [(set_attr "length" "1,1,1,1,2,2") + (set_attr "cc" "set_czn,set_czn,set_zn,set_zn,set_zn,set_zn")]) + +;; "addhi3" +;; "addhq3" "adduhq3" +;; "addha3" "adduha3" +(define_expand "add3" + [(set (match_operand:ALL2 0 "register_operand" "") + (plus:ALL2 (match_operand:ALL2 1 "register_operand" "") + (match_operand:ALL2 2 "nonmemory_or_const_operand" "")))] + "" + { + if (CONST_INT_P (operands[2])) + { + operands[2] = gen_int_mode (INTVAL (operands[2]), HImode); + + if (can_create_pseudo_p() + && !stack_register_operand (operands[0], HImode) + && !stack_register_operand (operands[1], HImode) + && !d_register_operand (operands[0], HImode) + && !d_register_operand (operands[1], HImode)) + { + emit_insn (gen_addhi3_clobber (operands[0], operands[1], operands[2])); + DONE; + } + } + + if (CONST_FIXED_P (operands[2])) + { + emit_insn (gen_add3_clobber (operands[0], operands[1], operands[2])); + DONE; + } + }) + + +(define_insn "*addhi3_zero_extend" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HI 2 "register_operand" "0")))] + "" + "add %A0,%1\;adc %B0,__zero_reg__" + [(set_attr "length" "2") + (set_attr "cc" "set_n")]) + +(define_insn "*addhi3_zero_extend1" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (match_operand:HI 1 "register_operand" "0") + (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "" + "add %A0,%2\;adc %B0,__zero_reg__" + [(set_attr "length" "2") + (set_attr "cc" "set_n")]) + +(define_insn "*addhi3.sign_extend1" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HI 2 "register_operand" "0")))] + "" + { + return reg_overlap_mentioned_p (operands[0], operands[1]) + ? "mov __tmp_reg__,%1\;add %A0,%1\;adc %B0,__zero_reg__\;sbrc __tmp_reg__,7\;dec %B0" + : "add %A0,%1\;adc %B0,__zero_reg__\;sbrc %1,7\;dec %B0"; + } + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +(define_insn "*addhi3_sp" + [(set (match_operand:HI 1 "stack_register_operand" "=q") + (plus:HI (match_operand:HI 2 "stack_register_operand" "q") + (match_operand:HI 0 "avr_sp_immediate_operand" "Csp")))] + "" + { + return avr_out_addto_sp (operands, NULL); + } + [(set_attr "length" "6") + (set_attr "adjust_len" "addto_sp")]) + +;; "*addhi3" +;; "*addhq3" "*adduhq3" +;; "*addha3" "*adduha3" +(define_insn "*add3" + [(set (match_operand:ALL2 0 "register_operand" "=??r,d,!w ,d") + (plus:ALL2 (match_operand:ALL2 1 "register_operand" "%0,0,0 ,0") + (match_operand:ALL2 2 "nonmemory_or_const_operand" "r,s,IJ YIJ,n Ynn")))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "length" "2") + (set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + +;; Adding a constant to NO_LD_REGS might have lead to a reload of +;; that constant to LD_REGS. We don't add a scratch to *addhi3 +;; itself because that insn is special to reload. + +(define_peephole2 ; addhi3_clobber + [(set (match_operand:ALL2 0 "d_register_operand" "") + (match_operand:ALL2 1 "const_operand" "")) + (set (match_operand:ALL2 2 "l_register_operand" "") + (plus:ALL2 (match_dup 2) + (match_dup 0)))] + "peep2_reg_dead_p (2, operands[0])" + [(parallel [(set (match_dup 2) + (plus:ALL2 (match_dup 2) + (match_dup 1))) + (clobber (match_dup 3))])] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], mode, 0); + }) + +;; Same, but with reload to NO_LD_REGS +;; Combine *reload_inhi with *addhi3 + +(define_peephole2 ; addhi3_clobber + [(parallel [(set (match_operand:ALL2 0 "l_register_operand" "") + (match_operand:ALL2 1 "const_operand" "")) + (clobber (match_operand:QI 2 "d_register_operand" ""))]) + (set (match_operand:ALL2 3 "l_register_operand" "") + (plus:ALL2 (match_dup 3) + (match_dup 0)))] + "peep2_reg_dead_p (2, operands[0])" + [(parallel [(set (match_dup 3) + (plus:ALL2 (match_dup 3) + (match_dup 1))) + (clobber (match_dup 2))])]) + +;; "addhi3_clobber" +;; "addhq3_clobber" "adduhq3_clobber" +;; "addha3_clobber" "adduha3_clobber" +(define_insn "add3_clobber" + [(set (match_operand:ALL2 0 "register_operand" "=!w ,d ,r") + (plus:ALL2 (match_operand:ALL2 1 "register_operand" "%0 ,0 ,0") + (match_operand:ALL2 2 "const_operand" "IJ YIJ,n Ynn,n Ynn"))) + (clobber (match_scratch:QI 3 "=X ,X ,&d"))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "length" "4") + (set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + + +;; "addsi3" +;; "addsq3" "addusq3" +;; "addsa3" "addusa3" +(define_insn "add3" + [(set (match_operand:ALL4 0 "register_operand" "=??r,d ,r") + (plus:ALL4 (match_operand:ALL4 1 "register_operand" "%0,0 ,0") + (match_operand:ALL4 2 "nonmemory_operand" "r,i ,n Ynn"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "length" "4") + (set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + +(define_insn "*addpsi3_zero_extend.qi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (plus:PSI (zero_extend:PSI (match_operand:QI 1 "register_operand" "r")) + (match_operand:PSI 2 "register_operand" "0")))] + "" + "add %A0,%A1\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "set_n")]) + +(define_insn "*addpsi3_zero_extend.hi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (plus:PSI (zero_extend:PSI (match_operand:HI 1 "register_operand" "r")) + (match_operand:PSI 2 "register_operand" "0")))] + "" + "add %A0,%A1\;adc %B0,%B1\;adc %C0,__zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "set_n")]) + +(define_insn "*addpsi3_sign_extend.hi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (plus:PSI (sign_extend:PSI (match_operand:HI 1 "register_operand" "r")) + (match_operand:PSI 2 "register_operand" "0")))] + "" + "add %A0,%1\;adc %B0,%B1\;adc %C0,__zero_reg__\;sbrc %B1,7\;dec %C0" + [(set_attr "length" "5") + (set_attr "cc" "set_n")]) + +(define_insn "*addsi3_zero_extend" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "0")))] + "" + "add %A0,%1\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "set_n")]) + +(define_insn "*addsi3_zero_extend.hi" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "0")))] + "" + "add %A0,%1\;adc %B0,%B1\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "set_n")]) + +(define_insn "addpsi3" + [(set (match_operand:PSI 0 "register_operand" "=??r,d ,d,r") + (plus:PSI (match_operand:PSI 1 "register_operand" "%0,0 ,0,0") + (match_operand:PSI 2 "nonmemory_operand" "r,s ,n,n"))) + (clobber (match_scratch:QI 3 "=X,X ,X,&d"))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "length" "3") + (set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + +(define_insn "subpsi3" + [(set (match_operand:PSI 0 "register_operand" "=r") + (minus:PSI (match_operand:PSI 1 "register_operand" "0") + (match_operand:PSI 2 "register_operand" "r")))] + "" + "sub %0,%2\;sbc %B0,%B2\;sbc %C0,%C2" + [(set_attr "length" "3") + (set_attr "cc" "set_czn")]) + +(define_insn "*subpsi3_zero_extend.qi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (minus:PSI (match_operand:SI 1 "register_operand" "0") + (zero_extend:PSI (match_operand:QI 2 "register_operand" "r"))))] + "" + "sub %A0,%2\;sbc %B0,__zero_reg__\;sbc %C0,__zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "set_czn")]) + +(define_insn "*subpsi3_zero_extend.hi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (minus:PSI (match_operand:PSI 1 "register_operand" "0") + (zero_extend:PSI (match_operand:HI 2 "register_operand" "r"))))] + "" + "sub %A0,%2\;sbc %B0,%B2\;sbc %C0,__zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "set_czn")]) + +(define_insn "*subpsi3_sign_extend.hi" + [(set (match_operand:PSI 0 "register_operand" "=r") + (minus:PSI (match_operand:PSI 1 "register_operand" "0") + (sign_extend:PSI (match_operand:HI 2 "register_operand" "r"))))] + "" + "sub %A0,%A2\;sbc %B0,%B2\;sbc %C0,__zero_reg__\;sbrc %B2,7\;inc %C0" + [(set_attr "length" "5") + (set_attr "cc" "set_czn")]) + +;----------------------------------------------------------------------------- +; sub bytes + +;; "subqi3" +;; "subqq3" "subuqq3" +(define_insn "sub3" + [(set (match_operand:ALL1 0 "register_operand" "=??r,d ,r ,r ,r ,r") + (minus:ALL1 (match_operand:ALL1 1 "register_operand" "0,0 ,0 ,0 ,0 ,0") + (match_operand:ALL1 2 "nonmemory_or_const_operand" "r,n Ynn,Y01,Ym1,Y02,Ym2")))] + "" + "@ + sub %0,%2 + subi %0,lo8(%2) + dec %0 + inc %0 + dec %0\;dec %0 + inc %0\;inc %0" + [(set_attr "length" "1,1,1,1,2,2") + (set_attr "cc" "set_czn,set_czn,set_zn,set_zn,set_zn,set_zn")]) + +;; "subhi3" +;; "subhq3" "subuhq3" +;; "subha3" "subuha3" +(define_insn "sub3" + [(set (match_operand:ALL2 0 "register_operand" "=??r,d ,*r") + (minus:ALL2 (match_operand:ALL2 1 "register_operand" "0,0 ,0") + (match_operand:ALL2 2 "nonmemory_or_const_operand" "r,i Ynn,Ynn"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + +(define_insn "*subhi3_zero_extend1" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 1 "register_operand" "0") + (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "" + "sub %A0,%2\;sbc %B0,__zero_reg__" + [(set_attr "length" "2") + (set_attr "cc" "set_czn")]) + +(define_insn "*subhi3.sign_extend2" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 1 "register_operand" "0") + (sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "" + { + return reg_overlap_mentioned_p (operands[0], operands[2]) + ? "mov __tmp_reg__,%2\;sub %A0,%2\;sbc %B0,__zero_reg__\;sbrc __tmp_reg__,7\;inc %B0" + : "sub %A0,%2\;sbc %B0,__zero_reg__\;sbrc %2,7\;inc %B0"; + } + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +;; "subsi3" +;; "subsq3" "subusq3" +;; "subsa3" "subusa3" +(define_insn "sub3" + [(set (match_operand:ALL4 0 "register_operand" "=??r,d ,r") + (minus:ALL4 (match_operand:ALL4 1 "register_operand" "0,0 ,0") + (match_operand:ALL4 2 "nonmemory_or_const_operand" "r,n Ynn,Ynn"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + return avr_out_plus (insn, operands); + } + [(set_attr "adjust_len" "plus") + (set_attr "cc" "plus")]) + +(define_insn "*subsi3_zero_extend" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (zero_extend:SI (match_operand:QI 2 "register_operand" "r"))))] + "" + "sub %A0,%2\;sbc %B0,__zero_reg__\;sbc %C0,__zero_reg__\;sbc %D0,__zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "set_czn")]) + +(define_insn "*subsi3_zero_extend.hi" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "" + "sub %A0,%2\;sbc %B0,%B2\;sbc %C0,__zero_reg__\;sbc %D0,__zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "set_czn")]) + +;****************************************************************************** +; mul + +(define_expand "mulqi3" + [(set (match_operand:QI 0 "register_operand" "") + (mult:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "register_operand" "")))] + "" + { + if (!AVR_HAVE_MUL) + { + emit_insn (gen_mulqi3_call (operands[0], operands[1], operands[2])); + DONE; + } + }) + +(define_insn "*mulqi3_enh" + [(set (match_operand:QI 0 "register_operand" "=r") + (mult:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "register_operand" "r")))] + "AVR_HAVE_MUL" + "mul %1,%2 + mov %0,r0 + clr r1" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +(define_expand "mulqi3_call" + [(set (reg:QI 24) (match_operand:QI 1 "register_operand" "")) + (set (reg:QI 22) (match_operand:QI 2 "register_operand" "")) + (parallel [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 22))]) + (set (match_operand:QI 0 "register_operand" "") (reg:QI 24))]) + +(define_insn "*mulqi3_call" + [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 22))] + "!AVR_HAVE_MUL" + "%~call __mulqi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "umulqi3_highpart" +;; "smulqi3_highpart" +(define_insn "mulqi3_highpart" + [(set (match_operand:QI 0 "register_operand" "=r") + (truncate:QI + (lshiftrt:HI (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (any_extend:HI (match_operand:QI 2 "register_operand" ""))) + (const_int 8))))] + "AVR_HAVE_MUL" + "mul %1,%2 + mov %0,r1 + clr __zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + + +;; Used when expanding div or mod inline for some special values +(define_insn "*subqi3.ashiftrt7" + [(set (match_operand:QI 0 "register_operand" "=r") + (minus:QI (match_operand:QI 1 "register_operand" "0") + (ashiftrt:QI (match_operand:QI 2 "register_operand" "r") + (const_int 7))))] + "" + "sbrc %2,7\;inc %0" + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + +(define_insn "*addqi3.lt0" + [(set (match_operand:QI 0 "register_operand" "=r") + (plus:QI (lt:QI (match_operand:QI 1 "register_operand" "r") + (const_int 0)) + (match_operand:QI 2 "register_operand" "0")))] + "" + "sbrc %1,7\;inc %0" + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + +(define_insn "*addhi3.lt0" + [(set (match_operand:HI 0 "register_operand" "=w,r") + (plus:HI (lt:HI (match_operand:QI 1 "register_operand" "r,r") + (const_int 0)) + (match_operand:HI 2 "register_operand" "0,0"))) + (clobber (match_scratch:QI 3 "=X,&1"))] + "" + "@ + sbrc %1,7\;adiw %0,1 + lsl %1\;adc %A0,__zero_reg__\;adc %B0,__zero_reg__" + [(set_attr "length" "2,3") + (set_attr "cc" "clobber")]) + +(define_insn "*addpsi3.lt0" + [(set (match_operand:PSI 0 "register_operand" "=r") + (plus:PSI (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "r") + (const_int 23)) + (match_operand:PSI 2 "register_operand" "0")))] + "" + "mov __tmp_reg__,%C1\;lsl __tmp_reg__ + adc %A0,__zero_reg__\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__" + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +(define_insn "*addsi3.lt0" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") + (const_int 31)) + (match_operand:SI 2 "register_operand" "0")))] + "" + "mov __tmp_reg__,%D1\;lsl __tmp_reg__ + adc %A0,__zero_reg__\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +(define_insn "*umulqihi3.call" + [(set (reg:HI 24) + (mult:HI (zero_extend:HI (reg:QI 22)) + (zero_extend:HI (reg:QI 24)))) + (clobber (reg:QI 21)) + (clobber (reg:HI 22))] + "!AVR_HAVE_MUL" + "%~call __umulqihi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "umulqihi3" +;; "mulqihi3" +(define_insn "mulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (any_extend:HI (match_operand:QI 2 "register_operand" ""))))] + "AVR_HAVE_MUL" + "mul %1,%2 + movw %0,r0 + clr __zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +(define_insn "usmulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "a")) + (sign_extend:HI (match_operand:QI 2 "register_operand" "a"))))] + "AVR_HAVE_MUL" + "mulsu %2,%1 + movw %0,r0 + clr __zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +;; Above insn is not canonicalized by insn combine, so here is a version with +;; operands swapped. + +(define_insn "*sumulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (zero_extend:HI (match_operand:QI 2 "register_operand" "a"))))] + "AVR_HAVE_MUL" + "mulsu %1,%2 + movw %0,r0 + clr __zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +;; One-extend operand 1 + +(define_insn "*osmulqihi3" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (not:HI (zero_extend:HI (not:QI (match_operand:QI 1 "register_operand" "a")))) + (sign_extend:HI (match_operand:QI 2 "register_operand" "a"))))] + "AVR_HAVE_MUL" + "mulsu %2,%1 + movw %0,r0 + sub %B0,%2 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_insn "*oumulqihi3" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (not:HI (zero_extend:HI (not:QI (match_operand:QI 1 "register_operand" "r")))) + (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "AVR_HAVE_MUL" + "mul %2,%1 + movw %0,r0 + sub %B0,%2 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +;****************************************************************************** +; multiply-add/sub QI: $0 = $3 +/- $1*$2 +;****************************************************************************** + +(define_insn "*maddqi4" + [(set (match_operand:QI 0 "register_operand" "=r") + (plus:QI (mult:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "register_operand" "r")) + (match_operand:QI 3 "register_operand" "0")))] + + "AVR_HAVE_MUL" + "mul %1,%2 + add %A0,r0 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_insn "*msubqi4" + [(set (match_operand:QI 0 "register_operand" "=r") + (minus:QI (match_operand:QI 3 "register_operand" "0") + (mult:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "register_operand" "r"))))] + "AVR_HAVE_MUL" + "mul %1,%2 + sub %A0,r0 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "*maddqi4.const" + [(set (match_operand:QI 0 "register_operand" "=r") + (plus:QI (mult:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "const_int_operand" "n")) + (match_operand:QI 3 "register_operand" "0"))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *maddqi4 + (set (match_dup 0) + (plus:QI (mult:QI (match_dup 1) + (match_dup 4)) + (match_dup 3)))]) + +(define_insn_and_split "*msubqi4.const" + [(set (match_operand:QI 0 "register_operand" "=r") + (minus:QI (match_operand:QI 3 "register_operand" "0") + (mult:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "const_int_operand" "n")))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *msubqi4 + (set (match_dup 0) + (minus:QI (match_dup 3) + (mult:QI (match_dup 1) + (match_dup 4))))]) + + +;****************************************************************************** +; multiply-add/sub HI: $0 = $3 +/- $1*$2 with 8-bit values $1, $2 +;****************************************************************************** + +;; We don't use standard insns/expanders as they lead to cumbersome code for, +;; e.g, +;; +;; int foo (unsigned char z) +;; { +;; extern int aInt[]; +;; return aInt[3*z+2]; +;; } +;; +;; because the constant +4 then is added explicitely instead of consuming it +;; with the aInt symbol. Therefore, we rely on insn combine which takes costs +;; into account more accurately and doesn't do burte-force multiply-add/sub. +;; The implementational effort is the same so we are fine with that approach. + + +;; "*maddqihi4" +;; "*umaddqihi4" +(define_insn "*maddqihi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (any_extend:HI (match_operand:QI 2 "register_operand" ""))) + (match_operand:HI 3 "register_operand" "0")))] + + "AVR_HAVE_MUL" + "mul %1,%2 + add %A0,r0 + adc %B0,r1 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +;; "*msubqihi4" +;; "*umsubqihi4" +(define_insn "*msubqihi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (any_extend:HI (match_operand:QI 2 "register_operand" "")))))] + "AVR_HAVE_MUL" + "mul %1,%2 + sub %A0,r0 + sbc %B0,r1 + clr __zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +;; "*usmaddqihi4" +;; "*sumaddqihi4" +(define_insn "*msubqihi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "a")) + (any_extend2:HI (match_operand:QI 2 "register_operand" "a"))) + (match_operand:HI 3 "register_operand" "0")))] + "AVR_HAVE_MUL + && reload_completed + && != " + { + output_asm_insn ( == SIGN_EXTEND + ? "mulsu %1,%2" : "mulsu %2,%1", operands); + + return "add %A0,r0\;adc %B0,r1\;clr __zero_reg__"; + } + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +;; "*usmsubqihi4" +;; "*sumsubqihi4" +(define_insn "*msubqihi4" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "a")) + (any_extend2:HI (match_operand:QI 2 "register_operand" "a")))))] + "AVR_HAVE_MUL + && reload_completed + && != " + { + output_asm_insn ( == SIGN_EXTEND + ? "mulsu %1,%2" : "mulsu %2,%1", operands); + + return "sub %A0,r0\;sbc %B0,r1\;clr __zero_reg__"; + } + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +;; Handle small constants + +;; Special case of a += 2*b as frequently seen with accesses to int arrays. +;; This is shorter, faster than MUL and has lower register pressure. + +(define_insn_and_split "*umaddqihi4.2" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r")) + (const_int 2)) + (match_operand:HI 2 "register_operand" "r")))] + "!reload_completed + && !reg_overlap_mentioned_p (operands[0], operands[1])" + { gcc_unreachable(); } + "&& 1" + [(set (match_dup 0) + (match_dup 2)) + ; *addhi3_zero_extend + (set (match_dup 0) + (plus:HI (zero_extend:HI (match_dup 1)) + (match_dup 0))) + ; *addhi3_zero_extend + (set (match_dup 0) + (plus:HI (zero_extend:HI (match_dup 1)) + (match_dup 0)))]) + +;; "umaddqihi4.uconst" +;; "maddqihi4.sconst" +(define_insn_and_split "*maddqihi4.const" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (match_operand:HI 2 "8_operand" "n")) + (match_operand:HI 3 "register_operand" "0"))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *umaddqihi4 resp. *maddqihi4 + (set (match_dup 0) + (plus:HI (mult:HI (any_extend:HI (match_dup 1)) + (any_extend:HI (match_dup 4))) + (match_dup 3)))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +;; "*umsubqihi4.uconst" +;; "*msubqihi4.sconst" +(define_insn_and_split "*msubqihi4.const" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (match_operand:HI 2 "8_operand" "n")))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *umsubqihi4 resp. *msubqihi4 + (set (match_dup 0) + (minus:HI (match_dup 3) + (mult:HI (any_extend:HI (match_dup 1)) + (any_extend:HI (match_dup 4)))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +;; Same as the insn above, but combiner tries versions canonicalized to ASHIFT +;; for MULT with power of 2 and skips trying MULT insn above. + +(define_insn_and_split "*umsubqihi4.uconst.ashift" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (ashift:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HI 2 "const_2_to_7_operand" "n")))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *umsubqihi4 + (set (match_dup 0) + (minus:HI (match_dup 3) + (mult:HI (zero_extend:HI (match_dup 1)) + (zero_extend:HI (match_dup 4)))))] + { + operands[2] = gen_int_mode (1 << INTVAL (operands[2]), QImode); + }) + +;; Same as the insn above, but combiner tries versions canonicalized to ASHIFT +;; for MULT with power of 2 and skips trying MULT insn above. We omit 128 +;; because this would require an extra pattern for just one value. + +(define_insn_and_split "*msubqihi4.sconst.ashift" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (ashift:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "d")) + (match_operand:HI 2 "const_1_to_6_operand" "M")))) + (clobber (match_scratch:QI 4 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *smsubqihi4 + (set (match_dup 0) + (minus:HI (match_dup 3) + (mult:HI (sign_extend:HI (match_dup 1)) + (sign_extend:HI (match_dup 4)))))] + { + operands[2] = gen_int_mode (1 << INTVAL (operands[2]), QImode); + }) + +;; For signed/unsigned combinations that require narrow constraint "a" +;; just provide a pattern if signed/unsigned combination is actually needed. + +(define_insn_and_split "*sumaddqihi4.uconst" + [(set (match_operand:HI 0 "register_operand" "=r") + (plus:HI (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "u8_operand" "M")) + (match_operand:HI 3 "register_operand" "0"))) + (clobber (match_scratch:QI 4 "=&a"))] + "AVR_HAVE_MUL + && !s8_operand (operands[2], VOIDmode)" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *sumaddqihi4 + (set (match_dup 0) + (plus:HI (mult:HI (sign_extend:HI (match_dup 1)) + (zero_extend:HI (match_dup 4))) + (match_dup 3)))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +(define_insn_and_split "*sumsubqihi4.uconst" + [(set (match_operand:HI 0 "register_operand" "=r") + (minus:HI (match_operand:HI 3 "register_operand" "0") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "u8_operand" "M")))) + (clobber (match_scratch:QI 4 "=&a"))] + "AVR_HAVE_MUL + && !s8_operand (operands[2], VOIDmode)" + "#" + "&& reload_completed" + [(set (match_dup 4) + (match_dup 2)) + ; *sumsubqihi4 + (set (match_dup 0) + (minus:HI (match_dup 3) + (mult:HI (sign_extend:HI (match_dup 1)) + (zero_extend:HI (match_dup 4)))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +;****************************************************************************** +; mul HI: $1 = sign/zero-extend, $2 = small constant +;****************************************************************************** + +;; "*muluqihi3.uconst" +;; "*mulsqihi3.sconst" +(define_insn_and_split "*mulqihi3.const" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (any_extend:HI (match_operand:QI 1 "register_operand" "")) + (match_operand:HI 2 "8_operand" "n"))) + (clobber (match_scratch:QI 3 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; umulqihi3 resp. mulqihi3 + (set (match_dup 0) + (mult:HI (any_extend:HI (match_dup 1)) + (any_extend:HI (match_dup 3))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +(define_insn_and_split "*muluqihi3.sconst" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "s8_operand" "n"))) + (clobber (match_scratch:QI 3 "=&a"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; usmulqihi3 + (set (match_dup 0) + (mult:HI (zero_extend:HI (match_dup 1)) + (sign_extend:HI (match_dup 3))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +(define_insn_and_split "*mulsqihi3.uconst" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "u8_operand" "M"))) + (clobber (match_scratch:QI 3 "=&a"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; usmulqihi3 + (set (match_dup 0) + (mult:HI (zero_extend:HI (match_dup 3)) + (sign_extend:HI (match_dup 1))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +(define_insn_and_split "*mulsqihi3.oconst" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "o8_operand" "n"))) + (clobber (match_scratch:QI 3 "=&a"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; *osmulqihi3 + (set (match_dup 0) + (mult:HI (not:HI (zero_extend:HI (not:QI (match_dup 3)))) + (sign_extend:HI (match_dup 1))))] + { + operands[2] = gen_int_mode (INTVAL (operands[2]), QImode); + }) + +;; The EXTEND of $1 only appears in combine, we don't see it in expand so that +;; expand decides to use ASHIFT instead of MUL because ASHIFT costs are cheaper +;; at that time. Fix that. + +(define_insn "*ashiftqihi2.signx.1" + [(set (match_operand:HI 0 "register_operand" "=r,*r") + (ashift:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "0,r")) + (const_int 1)))] + "" + "@ + lsl %A0\;sbc %B0,%B0 + mov %A0,%1\;lsl %A0\;sbc %B0,%B0" + [(set_attr "length" "2,3") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "*ashifthi3.signx.const" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashift:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "d")) + (match_operand:HI 2 "const_2_to_6_operand" "I"))) + (clobber (match_scratch:QI 3 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; mulqihi3 + (set (match_dup 0) + (mult:HI (sign_extend:HI (match_dup 1)) + (sign_extend:HI (match_dup 3))))] + { + operands[2] = GEN_INT (1 << INTVAL (operands[2])); + }) + +(define_insn_and_split "*ashifthi3.signx.const7" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashift:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (const_int 7))) + (clobber (match_scratch:QI 2 "=&a"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 2) + (match_dup 3)) + ; usmulqihi3 + (set (match_dup 0) + (mult:HI (zero_extend:HI (match_dup 2)) + (sign_extend:HI (match_dup 1))))] + { + operands[3] = gen_int_mode (1 << 7, QImode); + }) + +(define_insn_and_split "*ashifthi3.zerox.const" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashift:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HI 2 "const_2_to_7_operand" "I"))) + (clobber (match_scratch:QI 3 "=&d"))] + "AVR_HAVE_MUL" + "#" + "&& reload_completed" + [(set (match_dup 3) + (match_dup 2)) + ; umulqihi3 + (set (match_dup 0) + (mult:HI (zero_extend:HI (match_dup 1)) + (zero_extend:HI (match_dup 3))))] + { + operands[2] = gen_int_mode (1 << INTVAL (operands[2]), QImode); + }) + +;****************************************************************************** +; mul HI: $1 = sign-/zero-/one-extend, $2 = reg +;****************************************************************************** + +(define_insn "mulsqihi3" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) + (match_operand:HI 2 "register_operand" "a")))] + "AVR_HAVE_MUL" + "mulsu %1,%A2 + movw %0,r0 + mul %1,%B2 + add %B0,r0 + clr __zero_reg__" + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +(define_insn "muluqihi3" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HI 2 "register_operand" "r")))] + "AVR_HAVE_MUL" + "mul %1,%A2 + movw %0,r0 + mul %1,%B2 + add %B0,r0 + clr __zero_reg__" + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +;; one-extend operand 1 + +(define_insn "muloqihi3" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (not:HI (zero_extend:HI (not:QI (match_operand:QI 1 "register_operand" "r")))) + (match_operand:HI 2 "register_operand" "r")))] + "AVR_HAVE_MUL" + "mul %1,%A2 + movw %0,r0 + mul %1,%B2 + add %B0,r0 + sub %B0,%A2 + clr __zero_reg__" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +;****************************************************************************** + +(define_expand "mulhi3" + [(set (match_operand:HI 0 "register_operand" "") + (mult:HI (match_operand:HI 1 "register_operand" "") + (match_operand:HI 2 "register_or_s9_operand" "")))] + "" + { + if (!AVR_HAVE_MUL) + { + if (!register_operand (operands[2], HImode)) + operands[2] = force_reg (HImode, operands[2]); + + emit_insn (gen_mulhi3_call (operands[0], operands[1], operands[2])); + DONE; + } + + /* For small constants we can do better by extending them on the fly. + The constant can be loaded in one instruction and the widening + multiplication is shorter. First try the unsigned variant because it + allows constraint "d" instead of "a" for the signed version. */ + + if (s9_operand (operands[2], HImode)) + { + rtx reg = force_reg (QImode, gen_int_mode (INTVAL (operands[2]), QImode)); + + if (u8_operand (operands[2], HImode)) + { + emit_insn (gen_muluqihi3 (operands[0], reg, operands[1])); + } + else if (s8_operand (operands[2], HImode)) + { + emit_insn (gen_mulsqihi3 (operands[0], reg, operands[1])); + } + else + { + emit_insn (gen_muloqihi3 (operands[0], reg, operands[1])); + } + + DONE; + } + + if (!register_operand (operands[2], HImode)) + operands[2] = force_reg (HImode, operands[2]); + }) + +(define_insn "*mulhi3_enh" + [(set (match_operand:HI 0 "register_operand" "=&r") + (mult:HI (match_operand:HI 1 "register_operand" "r") + (match_operand:HI 2 "register_operand" "r")))] + "AVR_HAVE_MUL" + { + return REGNO (operands[1]) == REGNO (operands[2]) + ? "mul %A1,%A1\;movw %0,r0\;mul %A1,%B1\;add %B0,r0\;add %B0,r0\;clr r1" + : "mul %A1,%A2\;movw %0,r0\;mul %A1,%B2\;add %B0,r0\;mul %B1,%A2\;add %B0,r0\;clr r1"; + } + [(set_attr "length" "7") + (set_attr "cc" "clobber")]) + +(define_expand "mulhi3_call" + [(set (reg:HI 24) (match_operand:HI 1 "register_operand" "")) + (set (reg:HI 22) (match_operand:HI 2 "register_operand" "")) + (parallel [(set (reg:HI 24) (mult:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 22)) + (clobber (reg:QI 21))]) + (set (match_operand:HI 0 "register_operand" "") (reg:HI 24))]) + +(define_insn "*mulhi3_call" + [(set (reg:HI 24) (mult:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 22)) + (clobber (reg:QI 21))] + "!AVR_HAVE_MUL" + "%~call __mulhi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; To support widening multiplication with constant we postpone +;; expanding to the implicit library call until post combine and +;; prior to register allocation. Clobber all hard registers that +;; might be used by the (widening) multiply until it is split and +;; it's final register footprint is worked out. + +(define_expand "mulsi3" + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (mult:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" ""))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))])] + "AVR_HAVE_MUL" + { + if (u16_operand (operands[2], SImode)) + { + operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + emit_insn (gen_muluhisi3 (operands[0], operands[2], operands[1])); + DONE; + } + + if (o16_operand (operands[2], SImode)) + { + operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + emit_insn (gen_mulohisi3 (operands[0], operands[2], operands[1])); + DONE; + } + }) + +(define_insn_and_split "*mulsi3" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (match_operand:SI 1 "pseudo_register_operand" "r") + (match_operand:SI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:SI 18) + (match_dup 1)) + (set (reg:SI 22) + (match_dup 2)) + (parallel [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:HI 26))]) + (set (match_dup 0) + (reg:SI 22))] + { + if (u16_operand (operands[2], SImode)) + { + operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + emit_insn (gen_muluhisi3 (operands[0], operands[2], operands[1])); + DONE; + } + + if (o16_operand (operands[2], SImode)) + { + operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + emit_insn (gen_mulohisi3 (operands[0], operands[2], operands[1])); + DONE; + } + }) + +;; "muluqisi3" +;; "muluhisi3" +(define_insn_and_split "mulusi3" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (zero_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" "r")) + (match_operand:SI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:HI 26) + (match_dup 1)) + (set (reg:SI 18) + (match_dup 2)) + (set (reg:SI 22) + (mult:SI (zero_extend:SI (reg:HI 26)) + (reg:SI 18))) + (set (match_dup 0) + (reg:SI 22))] + { + /* Do the QI -> HI extension explicitely before the multiplication. */ + /* Do the HI -> SI extension implicitely and after the multiplication. */ + + if (QImode == mode) + operands[1] = gen_rtx_ZERO_EXTEND (HImode, operands[1]); + + if (u16_operand (operands[2], SImode)) + { + operands[1] = force_reg (HImode, operands[1]); + operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + emit_insn (gen_umulhisi3 (operands[0], operands[1], operands[2])); + DONE; + } + }) + +;; "mulsqisi3" +;; "mulshisi3" +(define_insn_and_split "mulssi3" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (sign_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" "r")) + (match_operand:SI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:HI 26) + (match_dup 1)) + (set (reg:SI 18) + (match_dup 2)) + (set (reg:SI 22) + (mult:SI (sign_extend:SI (reg:HI 26)) + (reg:SI 18))) + (set (match_dup 0) + (reg:SI 22))] + { + /* Do the QI -> HI extension explicitely before the multiplication. */ + /* Do the HI -> SI extension implicitely and after the multiplication. */ + + if (QImode == mode) + operands[1] = gen_rtx_SIGN_EXTEND (HImode, operands[1]); + + if (u16_operand (operands[2], SImode) + || s16_operand (operands[2], SImode)) + { + rtx xop2 = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); + + operands[1] = force_reg (HImode, operands[1]); + + if (u16_operand (operands[2], SImode)) + emit_insn (gen_usmulhisi3 (operands[0], xop2, operands[1])); + else + emit_insn (gen_mulhisi3 (operands[0], operands[1], xop2)); + + DONE; + } + }) + +;; One-extend operand 1 + +(define_insn_and_split "mulohisi3" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (not:SI (zero_extend:SI + (not:HI (match_operand:HI 1 "pseudo_register_operand" "r")))) + (match_operand:SI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:HI 26) + (match_dup 1)) + (set (reg:SI 18) + (match_dup 2)) + (set (reg:SI 22) + (mult:SI (not:SI (zero_extend:SI (not:HI (reg:HI 26)))) + (reg:SI 18))) + (set (match_dup 0) + (reg:SI 22))]) + +;; "mulhisi3" +;; "umulhisi3" +(define_expand "mulhisi3" + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (mult:SI (any_extend:SI (match_operand:HI 1 "register_operand" "")) + (any_extend:SI (match_operand:HI 2 "register_operand" "")))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))])] + "AVR_HAVE_MUL") + +(define_expand "usmulhisi3" + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "")) + (sign_extend:SI (match_operand:HI 2 "register_operand" "")))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))])] + "AVR_HAVE_MUL") + +;; "*uumulqihisi3" "*uumulhiqisi3" "*uumulhihisi3" "*uumulqiqisi3" +;; "*usmulqihisi3" "*usmulhiqisi3" "*usmulhihisi3" "*usmulqiqisi3" +;; "*sumulqihisi3" "*sumulhiqisi3" "*sumulhihisi3" "*sumulqiqisi3" +;; "*ssmulqihisi3" "*ssmulhiqisi3" "*ssmulhihisi3" "*ssmulqiqisi3" +(define_insn_and_split + "*mulsi3" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (any_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" "r")) + (any_extend2:SI (match_operand:QIHI2 2 "pseudo_register_operand" "r")))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:HI 18) + (match_dup 1)) + (set (reg:HI 26) + (match_dup 2)) + (set (reg:SI 22) + (mult:SI (match_dup 3) + (match_dup 4))) + (set (match_dup 0) + (reg:SI 22))] + { + rtx xop1 = operands[1]; + rtx xop2 = operands[2]; + + /* Do the QI -> HI extension explicitely before the multiplication. */ + /* Do the HI -> SI extension implicitely and after the multiplication. */ + + if (QImode == mode) + xop1 = gen_rtx_fmt_e (, HImode, xop1); + + if (QImode == mode) + xop2 = gen_rtx_fmt_e (, HImode, xop2); + + if ( == + || == ZERO_EXTEND) + { + operands[1] = xop1; + operands[2] = xop2; + operands[3] = gen_rtx_fmt_e (, SImode, gen_rtx_REG (HImode, 18)); + operands[4] = gen_rtx_fmt_e (, SImode, gen_rtx_REG (HImode, 26)); + } + else + { + /* = SIGN_EXTEND */ + /* = ZERO_EXTEND */ + + operands[1] = xop2; + operands[2] = xop1; + operands[3] = gen_rtx_ZERO_EXTEND (SImode, gen_rtx_REG (HImode, 18)); + operands[4] = gen_rtx_SIGN_EXTEND (SImode, gen_rtx_REG (HImode, 26)); + } + }) + +;; "smulhi3_highpart" +;; "umulhi3_highpart" +(define_expand "mulhi3_highpart" + [(set (reg:HI 18) + (match_operand:HI 1 "nonmemory_operand" "")) + (set (reg:HI 26) + (match_operand:HI 2 "nonmemory_operand" "")) + (parallel [(set (reg:HI 24) + (truncate:HI (lshiftrt:SI (mult:SI (any_extend:SI (reg:HI 18)) + (any_extend:SI (reg:HI 26))) + (const_int 16)))) + (clobber (reg:HI 22))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 24))] + "AVR_HAVE_MUL") + + +(define_insn "*mulsi3_call" + [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:HI 26))] + "AVR_HAVE_MUL" + "%~call __mulsi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*mulhisi3_call" +;; "*umulhisi3_call" +(define_insn "*mulhisi3_call" + [(set (reg:SI 22) + (mult:SI (any_extend:SI (reg:HI 18)) + (any_extend:SI (reg:HI 26))))] + "AVR_HAVE_MUL" + "%~call __mulhisi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*umulhi3_highpart_call" +;; "*smulhi3_highpart_call" +(define_insn "*mulhi3_highpart_call" + [(set (reg:HI 24) + (truncate:HI (lshiftrt:SI (mult:SI (any_extend:SI (reg:HI 18)) + (any_extend:SI (reg:HI 26))) + (const_int 16)))) + (clobber (reg:HI 22))] + "AVR_HAVE_MUL" + "%~call __mulhisi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*usmulhisi3_call" + [(set (reg:SI 22) + (mult:SI (zero_extend:SI (reg:HI 18)) + (sign_extend:SI (reg:HI 26))))] + "AVR_HAVE_MUL" + "%~call __usmulhisi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*mulhisi3_call" + [(set (reg:SI 22) + (mult:SI (any_extend:SI (reg:HI 26)) + (reg:SI 18)))] + "AVR_HAVE_MUL" + "%~call __mulhisi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*mulohisi3_call" + [(set (reg:SI 22) + (mult:SI (not:SI (zero_extend:SI (not:HI (reg:HI 26)))) + (reg:SI 18)))] + "AVR_HAVE_MUL" + "%~call __mulohisi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +; / % / % / % / % / % / % / % / % / % / % / % / % / % / % / % / % / % / % / % +; divmod + +;; Generate lib1funcs.S calls ourselves, because: +;; - we know exactly which registers are clobbered (for QI and HI +;; modes, some of the call-used registers are preserved) +;; - we get both the quotient and the remainder at no extra cost +;; - we split the patterns only after the first CSE passes because +;; CSE has problems to operate on hard regs. +;; +(define_insn_and_split "divmodqi4" + [(parallel [(set (match_operand:QI 0 "pseudo_register_operand" "") + (div:QI (match_operand:QI 1 "pseudo_register_operand" "") + (match_operand:QI 2 "pseudo_register_operand" ""))) + (set (match_operand:QI 3 "pseudo_register_operand" "") + (mod:QI (match_dup 1) (match_dup 2))) + (clobber (reg:QI 22)) + (clobber (reg:QI 23)) + (clobber (reg:QI 24)) + (clobber (reg:QI 25))])] + "" + "this divmodqi4 pattern should have been splitted;" + "" + [(set (reg:QI 24) (match_dup 1)) + (set (reg:QI 22) (match_dup 2)) + (parallel [(set (reg:QI 24) (div:QI (reg:QI 24) (reg:QI 22))) + (set (reg:QI 25) (mod:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 22)) + (clobber (reg:QI 23))]) + (set (match_dup 0) (reg:QI 24)) + (set (match_dup 3) (reg:QI 25))]) + +(define_insn "*divmodqi4_call" + [(set (reg:QI 24) (div:QI (reg:QI 24) (reg:QI 22))) + (set (reg:QI 25) (mod:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 22)) + (clobber (reg:QI 23))] + "" + "%~call __divmodqi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "udivmodqi4" + [(parallel [(set (match_operand:QI 0 "pseudo_register_operand" "") + (udiv:QI (match_operand:QI 1 "pseudo_register_operand" "") + (match_operand:QI 2 "pseudo_register_operand" ""))) + (set (match_operand:QI 3 "pseudo_register_operand" "") + (umod:QI (match_dup 1) (match_dup 2))) + (clobber (reg:QI 22)) + (clobber (reg:QI 23)) + (clobber (reg:QI 24)) + (clobber (reg:QI 25))])] + "" + "this udivmodqi4 pattern should have been splitted;" + "" + [(set (reg:QI 24) (match_dup 1)) + (set (reg:QI 22) (match_dup 2)) + (parallel [(set (reg:QI 24) (udiv:QI (reg:QI 24) (reg:QI 22))) + (set (reg:QI 25) (umod:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 23))]) + (set (match_dup 0) (reg:QI 24)) + (set (match_dup 3) (reg:QI 25))]) + +(define_insn "*udivmodqi4_call" + [(set (reg:QI 24) (udiv:QI (reg:QI 24) (reg:QI 22))) + (set (reg:QI 25) (umod:QI (reg:QI 24) (reg:QI 22))) + (clobber (reg:QI 23))] + "" + "%~call __udivmodqi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "divmodhi4" + [(parallel [(set (match_operand:HI 0 "pseudo_register_operand" "") + (div:HI (match_operand:HI 1 "pseudo_register_operand" "") + (match_operand:HI 2 "pseudo_register_operand" ""))) + (set (match_operand:HI 3 "pseudo_register_operand" "") + (mod:HI (match_dup 1) (match_dup 2))) + (clobber (reg:QI 21)) + (clobber (reg:HI 22)) + (clobber (reg:HI 24)) + (clobber (reg:HI 26))])] + "" + "this should have been splitted;" + "" + [(set (reg:HI 24) (match_dup 1)) + (set (reg:HI 22) (match_dup 2)) + (parallel [(set (reg:HI 22) (div:HI (reg:HI 24) (reg:HI 22))) + (set (reg:HI 24) (mod:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))]) + (set (match_dup 0) (reg:HI 22)) + (set (match_dup 3) (reg:HI 24))]) + +(define_insn "*divmodhi4_call" + [(set (reg:HI 22) (div:HI (reg:HI 24) (reg:HI 22))) + (set (reg:HI 24) (mod:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))] + "" + "%~call __divmodhi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "udivmodhi4" + [(parallel [(set (match_operand:HI 0 "pseudo_register_operand" "") + (udiv:HI (match_operand:HI 1 "pseudo_register_operand" "") + (match_operand:HI 2 "pseudo_register_operand" ""))) + (set (match_operand:HI 3 "pseudo_register_operand" "") + (umod:HI (match_dup 1) (match_dup 2))) + (clobber (reg:QI 21)) + (clobber (reg:HI 22)) + (clobber (reg:HI 24)) + (clobber (reg:HI 26))])] + "" + "this udivmodhi4 pattern should have been splitted.;" + "" + [(set (reg:HI 24) (match_dup 1)) + (set (reg:HI 22) (match_dup 2)) + (parallel [(set (reg:HI 22) (udiv:HI (reg:HI 24) (reg:HI 22))) + (set (reg:HI 24) (umod:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))]) + (set (match_dup 0) (reg:HI 22)) + (set (match_dup 3) (reg:HI 24))]) + +(define_insn "*udivmodhi4_call" + [(set (reg:HI 22) (udiv:HI (reg:HI 24) (reg:HI 22))) + (set (reg:HI 24) (umod:HI (reg:HI 24) (reg:HI 22))) + (clobber (reg:HI 26)) + (clobber (reg:QI 21))] + "" + "%~call __udivmodhi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 24-bit multiply + +;; To support widening multiplication with constant we postpone +;; expanding to the implicit library call until post combine and +;; prior to register allocation. Clobber all hard registers that +;; might be used by the (widening) multiply until it is split and +;; it's final register footprint is worked out. + +(define_expand "mulpsi3" + [(parallel [(set (match_operand:PSI 0 "register_operand" "") + (mult:PSI (match_operand:PSI 1 "register_operand" "") + (match_operand:PSI 2 "nonmemory_operand" ""))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))])] + "AVR_HAVE_MUL" + { + if (s8_operand (operands[2], PSImode)) + { + rtx reg = force_reg (QImode, gen_int_mode (INTVAL (operands[2]), QImode)); + emit_insn (gen_mulsqipsi3 (operands[0], reg, operands[1])); + DONE; + } + }) + +(define_insn "*umulqihipsi3" + [(set (match_operand:PSI 0 "register_operand" "=&r") + (mult:PSI (zero_extend:PSI (match_operand:QI 1 "register_operand" "r")) + (zero_extend:PSI (match_operand:HI 2 "register_operand" "r"))))] + "AVR_HAVE_MUL" + "mul %1,%A2 + movw %A0,r0 + mul %1,%B2 + clr %C0 + add %B0,r0 + adc %C0,r1 + clr __zero_reg__" + [(set_attr "length" "7") + (set_attr "cc" "clobber")]) + +(define_insn "*umulhiqipsi3" + [(set (match_operand:PSI 0 "register_operand" "=&r") + (mult:PSI (zero_extend:PSI (match_operand:HI 2 "register_operand" "r")) + (zero_extend:PSI (match_operand:QI 1 "register_operand" "r"))))] + "AVR_HAVE_MUL" + "mul %1,%A2 + movw %A0,r0 + mul %1,%B2 + add %B0,r0 + mov %C0,r1 + clr __zero_reg__ + adc %C0,__zero_reg__" + [(set_attr "length" "7") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "mulsqipsi3" + [(set (match_operand:PSI 0 "pseudo_register_operand" "=r") + (mult:PSI (sign_extend:PSI (match_operand:QI 1 "pseudo_register_operand" "r")) + (match_operand:PSI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:QI 25) + (match_dup 1)) + (set (reg:PSI 22) + (match_dup 2)) + (set (reg:PSI 18) + (mult:PSI (sign_extend:PSI (reg:QI 25)) + (reg:PSI 22))) + (set (match_dup 0) + (reg:PSI 18))]) + +(define_insn_and_split "*mulpsi3" + [(set (match_operand:PSI 0 "pseudo_register_operand" "=r") + (mult:PSI (match_operand:PSI 1 "pseudo_register_operand" "r") + (match_operand:PSI 2 "pseudo_register_or_const_int_operand" "rn"))) + (clobber (reg:HI 26)) + (clobber (reg:DI 18))] + "AVR_HAVE_MUL && !reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:PSI 18) + (match_dup 1)) + (set (reg:PSI 22) + (match_dup 2)) + (parallel [(set (reg:PSI 22) + (mult:PSI (reg:PSI 22) + (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:HI 26))]) + (set (match_dup 0) + (reg:PSI 22))] + { + if (s8_operand (operands[2], PSImode)) + { + rtx reg = force_reg (QImode, gen_int_mode (INTVAL (operands[2]), QImode)); + emit_insn (gen_mulsqipsi3 (operands[0], reg, operands[1])); + DONE; + } + }) + +(define_insn "*mulsqipsi3.libgcc" + [(set (reg:PSI 18) + (mult:PSI (sign_extend:PSI (reg:QI 25)) + (reg:PSI 22)))] + "AVR_HAVE_MUL" + "%~call __mulsqipsi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*mulpsi3.libgcc" + [(set (reg:PSI 22) + (mult:PSI (reg:PSI 22) + (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:HI 26))] + "AVR_HAVE_MUL" + "%~call __mulpsi3" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 24-bit signed/unsigned division and modulo. +;; Notice that the libgcc implementation return the quotient in R22 +;; and the remainder in R18 whereas the 32-bit [u]divmodsi4 +;; implementation works the other way round. + +(define_insn_and_split "divmodpsi4" + [(parallel [(set (match_operand:PSI 0 "pseudo_register_operand" "") + (div:PSI (match_operand:PSI 1 "pseudo_register_operand" "") + (match_operand:PSI 2 "pseudo_register_operand" ""))) + (set (match_operand:PSI 3 "pseudo_register_operand" "") + (mod:PSI (match_dup 1) + (match_dup 2))) + (clobber (reg:DI 18)) + (clobber (reg:QI 26))])] + "" + { gcc_unreachable(); } + "" + [(set (reg:PSI 22) (match_dup 1)) + (set (reg:PSI 18) (match_dup 2)) + (parallel [(set (reg:PSI 22) (div:PSI (reg:PSI 22) (reg:PSI 18))) + (set (reg:PSI 18) (mod:PSI (reg:PSI 22) (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:QI 26))]) + (set (match_dup 0) (reg:PSI 22)) + (set (match_dup 3) (reg:PSI 18))]) + +(define_insn "*divmodpsi4_call" + [(set (reg:PSI 22) (div:PSI (reg:PSI 22) (reg:PSI 18))) + (set (reg:PSI 18) (mod:PSI (reg:PSI 22) (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:QI 26))] + "" + "%~call __divmodpsi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "udivmodpsi4" + [(parallel [(set (match_operand:PSI 0 "pseudo_register_operand" "") + (udiv:PSI (match_operand:PSI 1 "pseudo_register_operand" "") + (match_operand:PSI 2 "pseudo_register_operand" ""))) + (set (match_operand:PSI 3 "pseudo_register_operand" "") + (umod:PSI (match_dup 1) + (match_dup 2))) + (clobber (reg:DI 18)) + (clobber (reg:QI 26))])] + "" + { gcc_unreachable(); } + "" + [(set (reg:PSI 22) (match_dup 1)) + (set (reg:PSI 18) (match_dup 2)) + (parallel [(set (reg:PSI 22) (udiv:PSI (reg:PSI 22) (reg:PSI 18))) + (set (reg:PSI 18) (umod:PSI (reg:PSI 22) (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:QI 26))]) + (set (match_dup 0) (reg:PSI 22)) + (set (match_dup 3) (reg:PSI 18))]) + +(define_insn "*udivmodpsi4_call" + [(set (reg:PSI 22) (udiv:PSI (reg:PSI 22) (reg:PSI 18))) + (set (reg:PSI 18) (umod:PSI (reg:PSI 22) (reg:PSI 18))) + (clobber (reg:QI 21)) + (clobber (reg:QI 25)) + (clobber (reg:QI 26))] + "" + "%~call __udivmodpsi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define_insn_and_split "divmodsi4" + [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "") + (div:SI (match_operand:SI 1 "pseudo_register_operand" "") + (match_operand:SI 2 "pseudo_register_operand" ""))) + (set (match_operand:SI 3 "pseudo_register_operand" "") + (mod:SI (match_dup 1) (match_dup 2))) + (clobber (reg:SI 18)) + (clobber (reg:SI 22)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))])] + "" + "this divmodsi4 pattern should have been splitted;" + "" + [(set (reg:SI 22) (match_dup 1)) + (set (reg:SI 18) (match_dup 2)) + (parallel [(set (reg:SI 18) (div:SI (reg:SI 22) (reg:SI 18))) + (set (reg:SI 22) (mod:SI (reg:SI 22) (reg:SI 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))]) + (set (match_dup 0) (reg:SI 18)) + (set (match_dup 3) (reg:SI 22))]) + +(define_insn "*divmodsi4_call" + [(set (reg:SI 18) (div:SI (reg:SI 22) (reg:SI 18))) + (set (reg:SI 22) (mod:SI (reg:SI 22) (reg:SI 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] + "" + "%~call __divmodsi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "udivmodsi4" + [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "") + (udiv:SI (match_operand:SI 1 "pseudo_register_operand" "") + (match_operand:SI 2 "pseudo_register_operand" ""))) + (set (match_operand:SI 3 "pseudo_register_operand" "") + (umod:SI (match_dup 1) (match_dup 2))) + (clobber (reg:SI 18)) + (clobber (reg:SI 22)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))])] + "" + "this udivmodsi4 pattern should have been splitted;" + "" + [(set (reg:SI 22) (match_dup 1)) + (set (reg:SI 18) (match_dup 2)) + (parallel [(set (reg:SI 18) (udiv:SI (reg:SI 22) (reg:SI 18))) + (set (reg:SI 22) (umod:SI (reg:SI 22) (reg:SI 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))]) + (set (match_dup 0) (reg:SI 18)) + (set (match_dup 3) (reg:SI 22))]) + +(define_insn "*udivmodsi4_call" + [(set (reg:SI 18) (udiv:SI (reg:SI 22) (reg:SI 18))) + (set (reg:SI 22) (umod:SI (reg:SI 22) (reg:SI 18))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] + "" + "%~call __udivmodsi4" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& +; and + +(define_insn "andqi3" + [(set (match_operand:QI 0 "register_operand" "=??r,d") + (and:QI (match_operand:QI 1 "register_operand" "%0,0") + (match_operand:QI 2 "nonmemory_operand" "r,i")))] + "" + "@ + and %0,%2 + andi %0,lo8(%2)" + [(set_attr "length" "1,1") + (set_attr "cc" "set_zn,set_zn")]) + +(define_insn "andhi3" + [(set (match_operand:HI 0 "register_operand" "=??r,d,d,r ,r") + (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,s,n,Ca2,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "and %A0,%A2\;and %B0,%B2"; + else if (which_alternative == 1) + return "andi %A0,lo8(%2)\;andi %B0,hi8(%2)"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,2,4,4") + (set_attr "adjust_len" "*,*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,set_n,clobber,clobber,clobber")]) + +(define_insn "andpsi3" + [(set (match_operand:PSI 0 "register_operand" "=??r,d,r ,r") + (and:PSI (match_operand:PSI 1 "register_operand" "%0,0,0 ,0") + (match_operand:PSI 2 "nonmemory_operand" "r,n,Ca3,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "and %A0,%A2" CR_TAB + "and %B0,%B2" CR_TAB + "and %C0,%C2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "3,3,6,6") + (set_attr "adjust_len" "*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=??r,d,r ,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,n,Ca4,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "and %0,%2" CR_TAB + "and %B0,%B2" CR_TAB + "and %C0,%C2" CR_TAB + "and %D0,%D2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,8") + (set_attr "adjust_len" "*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) + +(define_peephole2 ; andi + [(set (match_operand:QI 0 "d_register_operand" "") + (and:QI (match_dup 0) + (match_operand:QI 1 "const_int_operand" ""))) + (set (match_dup 0) + (and:QI (match_dup 0) + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1)))] + { + operands[1] = GEN_INT (INTVAL (operands[1]) & INTVAL (operands[2])); + }) + +;;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +;; ior + +(define_insn "iorqi3" + [(set (match_operand:QI 0 "register_operand" "=??r,d") + (ior:QI (match_operand:QI 1 "register_operand" "%0,0") + (match_operand:QI 2 "nonmemory_operand" "r,i")))] + "" + "@ + or %0,%2 + ori %0,lo8(%2)" + [(set_attr "length" "1,1") + (set_attr "cc" "set_zn,set_zn")]) + +(define_insn "iorhi3" + [(set (match_operand:HI 0 "register_operand" "=??r,d,d,r ,r") + (ior:HI (match_operand:HI 1 "register_operand" "%0,0,0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,s,n,Co2,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "or %A0,%A2\;or %B0,%B2"; + else if (which_alternative == 1) + return "ori %A0,lo8(%2)\;ori %B0,hi8(%2)"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,2,4,4") + (set_attr "adjust_len" "*,*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,set_n,clobber,clobber,clobber")]) + +(define_insn "iorpsi3" + [(set (match_operand:PSI 0 "register_operand" "=??r,d,r ,r") + (ior:PSI (match_operand:PSI 1 "register_operand" "%0,0,0 ,0") + (match_operand:PSI 2 "nonmemory_operand" "r,n,Co3,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "or %A0,%A2" CR_TAB + "or %B0,%B2" CR_TAB + "or %C0,%C2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "3,3,6,6") + (set_attr "adjust_len" "*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=??r,d,r ,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,n,Co4,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "or %0,%2" CR_TAB + "or %B0,%B2" CR_TAB + "or %C0,%C2" CR_TAB + "or %D0,%D2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,8") + (set_attr "adjust_len" "*,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) + +;;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +;; xor + +(define_insn "xorqi3" + [(set (match_operand:QI 0 "register_operand" "=r") + (xor:QI (match_operand:QI 1 "register_operand" "%0") + (match_operand:QI 2 "register_operand" "r")))] + "" + "eor %0,%2" + [(set_attr "length" "1") + (set_attr "cc" "set_zn")]) + +(define_insn "xorhi3" + [(set (match_operand:HI 0 "register_operand" "=??r,r ,r") + (xor:HI (match_operand:HI 1 "register_operand" "%0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,Cx2,n"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "eor %A0,%A2\;eor %B0,%B2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,4") + (set_attr "adjust_len" "*,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber")]) + +(define_insn "xorpsi3" + [(set (match_operand:PSI 0 "register_operand" "=??r,r ,r") + (xor:PSI (match_operand:PSI 1 "register_operand" "%0,0 ,0") + (match_operand:PSI 2 "nonmemory_operand" "r,Cx3,n"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "eor %A0,%A2" CR_TAB + "eor %B0,%B2" CR_TAB + "eor %C0,%C2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "3,6,6") + (set_attr "adjust_len" "*,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber")]) + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=??r,r ,r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,Cx4,n"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "eor %0,%2" CR_TAB + "eor %B0,%B2" CR_TAB + "eor %C0,%C2" CR_TAB + "eor %D0,%D2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,8,8") + (set_attr "adjust_len" "*,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber")]) + +;; swap swap swap swap swap swap swap swap swap swap swap swap swap swap swap +;; swap + +(define_expand "rotlqi3" + [(set (match_operand:QI 0 "register_operand" "") + (rotate:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "const_0_to_7_operand" "")))] + "" + { + if (!CONST_INT_P (operands[2])) + FAIL; + + operands[2] = gen_int_mode (INTVAL (operands[2]) & 7, QImode); + }) + +;; Expander used by __builtin_avr_swap +(define_expand "rotlqi3_4" + [(set (match_operand:QI 0 "register_operand" "") + (rotate:QI (match_operand:QI 1 "register_operand" "") + (const_int 4)))]) + +(define_insn "*rotlqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r ,r ,r ,r ,r ,r") + (rotate:QI (match_operand:QI 1 "register_operand" "0,0,0 ,0 ,0 ,0 ,0 ,0") + (match_operand:QI 2 "const_0_to_7_operand" "P,K,C03,C04,C05,C06,C07,L")))] + "" + "@ + lsl %0\;adc %0,__zero_reg__ + lsl %0\;adc %0,__zero_reg__\;lsl %0\;adc %0,__zero_reg__ + swap %0\;bst %0,0\;ror %0\;bld %0,7 + swap %0 + swap %0\;lsl %0\;adc %0,__zero_reg__ + swap %0\;lsl %0\;adc %0,__zero_reg__\;lsl %0\;adc %0,__zero_reg__ + bst %0,0\;ror %0\;bld %0,7 + " + [(set_attr "length" "2,4,4,1,3,5,3,0") + (set_attr "cc" "set_n,set_n,clobber,none,set_n,set_n,clobber,none")]) + +;; Split all rotates of HI,SI and PSImode registers where rotation is by +;; a whole number of bytes. The split creates the appropriate moves and +;; considers all overlap situations. + +;; HImode does not need scratch. Use attribute for this constraint. + +(define_mode_attr rotx [(SI "&r,&r,X") (PSI "&r,&r,X") (HI "X,X,X")]) +(define_mode_attr rotsmode [(SI "HI") (PSI "QI") (HI "QI")]) + +;; "rotlhi3" +;; "rotlpsi3" +;; "rotlsi3" +(define_expand "rotl3" + [(parallel [(set (match_operand:HISI 0 "register_operand" "") + (rotate:HISI (match_operand:HISI 1 "register_operand" "") + (match_operand:VOID 2 "const_int_operand" ""))) + (clobber (match_dup 3))])] + "" + { + int offset; + + if (!CONST_INT_P (operands[2])) + FAIL; + + offset = INTVAL (operands[2]); + + if (0 == offset % 8) + { + if (AVR_HAVE_MOVW && 0 == offset % 16) + operands[3] = gen_rtx_SCRATCH (mode); + else + operands[3] = gen_rtx_SCRATCH (QImode); + } + else if (offset == 1 + || offset == GET_MODE_BITSIZE (mode) -1) + { + /*; Support rotate left/right by 1 */ + + emit_move_insn (operands[0], + gen_rtx_ROTATE (mode, operands[1], operands[2])); + DONE; + } + else + FAIL; + }) + +(define_insn "*rotlhi2.1" + [(set (match_operand:HI 0 "register_operand" "=r") + (rotate:HI (match_operand:HI 1 "register_operand" "0") + (const_int 1)))] + "" + "lsl %A0\;rol %B0\;adc %A0,__zero_reg__" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + +(define_insn "*rotlhi2.15" + [(set (match_operand:HI 0 "register_operand" "=r") + (rotate:HI (match_operand:HI 1 "register_operand" "0") + (const_int 15)))] + "" + "bst %A0,0\;ror %B0\;ror %A0\;bld %B0,7" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_insn "*rotlpsi2.1" + [(set (match_operand:PSI 0 "register_operand" "=r") + (rotate:PSI (match_operand:PSI 1 "register_operand" "0") + (const_int 1)))] + "" + "lsl %A0\;rol %B0\;rol %C0\;adc %A0,__zero_reg__" + [(set_attr "length" "4") + (set_attr "cc" "clobber")]) + +(define_insn "*rotlpsi2.23" + [(set (match_operand:PSI 0 "register_operand" "=r") + (rotate:PSI (match_operand:PSI 1 "register_operand" "0") + (const_int 23)))] + "" + "bst %A0,0\;ror %C0\;ror %B0\;ror %A0\;bld %C0,7" + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +(define_insn "*rotlsi2.1" + [(set (match_operand:SI 0 "register_operand" "=r") + (rotate:SI (match_operand:SI 1 "register_operand" "0") + (const_int 1)))] + "" + "lsl %A0\;rol %B0\;rol %C0\;rol %D0\;adc %A0,__zero_reg__" + [(set_attr "length" "5") + (set_attr "cc" "clobber")]) + +(define_insn "*rotlsi2.31" + [(set (match_operand:SI 0 "register_operand" "=r") + (rotate:SI (match_operand:SI 1 "register_operand" "0") + (const_int 31)))] + "" + "bst %A0,0\;ror %D0\;ror %C0\;ror %B0\;ror %A0\;bld %D0,7" + [(set_attr "length" "6") + (set_attr "cc" "clobber")]) + +;; Overlapping non-HImode registers often (but not always) need a scratch. +;; The best we can do is use early clobber alternative "#&r" so that +;; completely non-overlapping operands dont get a scratch but # so register +;; allocation does not prefer non-overlapping. + + +;; Split word aligned rotates using scratch that is mode dependent. + +;; "*rotwhi" +;; "*rotwsi" +(define_insn_and_split "*rotw" + [(set (match_operand:HISI 0 "register_operand" "=r,r,#&r") + (rotate:HISI (match_operand:HISI 1 "register_operand" "0,r,r") + (match_operand 2 "const_int_operand" "n,n,n"))) + (clobber (match_scratch: 3 "="))] + "AVR_HAVE_MOVW + && CONST_INT_P (operands[2]) + && GET_MODE_SIZE (mode) % 2 == 0 + && 0 == INTVAL (operands[2]) % 16" + "#" + "&& reload_completed" + [(const_int 0)] + { + avr_rotate_bytes (operands); + DONE; + }) + + +;; Split byte aligned rotates using scratch that is always QI mode. + +;; "*rotbhi" +;; "*rotbpsi" +;; "*rotbsi" +(define_insn_and_split "*rotb" + [(set (match_operand:HISI 0 "register_operand" "=r,r,#&r") + (rotate:HISI (match_operand:HISI 1 "register_operand" "0,r,r") + (match_operand 2 "const_int_operand" "n,n,n"))) + (clobber (match_scratch:QI 3 "="))] + "CONST_INT_P (operands[2]) + && (8 == INTVAL (operands[2]) % 16 + || ((!AVR_HAVE_MOVW + || GET_MODE_SIZE (mode) % 2 != 0) + && 0 == INTVAL (operands[2]) % 16))" + "#" + "&& reload_completed" + [(const_int 0)] + { + avr_rotate_bytes (operands); + DONE; + }) + + +;;<< << << << << << << << << << << << << << << << << << << << << << << << << << +;; arithmetic shift left + +;; "ashlqi3" +;; "ashlqq3" "ashluqq3" +(define_expand "ashl3" + [(set (match_operand:ALL1 0 "register_operand" "") + (ashift:ALL1 (match_operand:ALL1 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) + +(define_split ; ashlqi3_const4 + [(set (match_operand:ALL1 0 "d_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 4)))] + "" + [(set (match_dup 1) + (rotate:QI (match_dup 1) + (const_int 4))) + (set (match_dup 1) + (and:QI (match_dup 1) + (const_int -16)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +(define_split ; ashlqi3_const5 + [(set (match_operand:ALL1 0 "d_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 5)))] + "" + [(set (match_dup 1) (rotate:QI (match_dup 1) (const_int 4))) + (set (match_dup 1) (ashift:QI (match_dup 1) (const_int 1))) + (set (match_dup 1) (and:QI (match_dup 1) (const_int -32)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +(define_split ; ashlqi3_const6 + [(set (match_operand:ALL1 0 "d_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 6)))] + "" + [(set (match_dup 1) (rotate:QI (match_dup 1) (const_int 4))) + (set (match_dup 1) (ashift:QI (match_dup 1) (const_int 2))) + (set (match_dup 1) (and:QI (match_dup 1) (const_int -64)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +;; "*ashlqi3" +;; "*ashlqq3" "*ashluqq3" +(define_insn "*ashl3" + [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,!d,r,r") + (ashift:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] + "" + { + return ashlqi3_out (insn, operands, NULL); + } + [(set_attr "length" "5,0,1,2,4,6,9") + (set_attr "adjust_len" "ashlqi") + (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) + +(define_insn "ashl3" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return ashlhi3_out (insn, operands, NULL); + } + [(set_attr "length" "6,0,2,2,4,10,10") + (set_attr "adjust_len" "ashlhi") + (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) + + +;; Insns like the following are generated when (implicitly) extending 8-bit shifts +;; like char1 = char2 << char3. Only the low-byte is needed in that situation. + +;; "*ashluqihiqi3" +;; "*ashlsqihiqi3" +(define_insn_and_split "*ashlqihiqi3" + [(set (match_operand:QI 0 "register_operand" "=r") + (subreg:QI (ashift:HI (any_extend:HI (match_operand:QI 1 "register_operand" "0")) + (match_operand:QI 2 "register_operand" "r")) + 0))] + "" + "#" + "" + [(set (match_dup 0) + (ashift:QI (match_dup 1) + (match_dup 2)))]) + +;; ??? Combiner does not recognize that it could split the following insn; +;; presumably because he has no register handy? + +;; "*ashluqihiqi3.mem" +;; "*ashlsqihiqi3.mem" +(define_insn_and_split "*ashlqihiqi3.mem" + [(set (match_operand:QI 0 "memory_operand" "=m") + (subreg:QI (ashift:HI (any_extend:HI (match_operand:QI 1 "register_operand" "r")) + (match_operand:QI 2 "register_operand" "r")) + 0))] + "!reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (match_dup 3) + (ashift:QI (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (match_dup 3))] + { + operands[3] = gen_reg_rtx (QImode); + }) + +;; Similar. + +(define_insn_and_split "*ashlhiqi3" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r") + (subreg:QI (ashift:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:QI 2 "register_operand" "r")) 0))] + "!reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (match_dup 4) + (ashift:QI (match_dup 3) + (match_dup 2))) + (set (match_dup 0) + (match_dup 4))] + { + operands[3] = simplify_gen_subreg (QImode, operands[1], HImode, 0); + operands[4] = gen_reg_rtx (QImode); + }) + +;; High part of 16-bit shift is unused after the instruction: +;; No need to compute it, map to 8-bit shift. + +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") + (ashift:HI (match_dup 0) + (match_operand:QI 1 "register_operand" "")))] + "" + [(set (match_dup 2) + (ashift:QI (match_dup 2) + (match_dup 1))) + (clobber (match_dup 3))] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], HImode, 1); + + if (!peep2_reg_dead_p (1, operands[3])) + FAIL; + + operands[2] = simplify_gen_subreg (QImode, operands[0], HImode, 0); + }) + + +;; "ashlsi3" +;; "ashlsq3" "ashlusq3" +;; "ashlsa3" "ashlusa3" +(define_insn "ashl3" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return ashlsi3_out (insn, operands, NULL); + } + [(set_attr "length" "8,0,4,4,8,10,12") + (set_attr "adjust_len" "ashlsi") + (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) + +;; Optimize if a scratch register from LD_REGS happens to be available. + +(define_peephole2 ; ashlqi3_l_const4 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 4))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 1) (const_int -16)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 ; ashlqi3_l_const5 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 5))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 2) (ashift:QI (match_dup 2) (const_int 1))) + (set (match_dup 1) (const_int -32)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 ; ashlqi3_l_const6 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (ashift:ALL1 (match_dup 0) + (const_int 6))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 2) (ashift:QI (match_dup 2) (const_int 2))) + (set (match_dup 1) (const_int -64)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL2 0 "register_operand" "") + (ashift:ALL2 (match_operand:ALL2 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (ashift:ALL2 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*ashlhi3_const" +;; "*ashlhq3_const" "*ashluhq3_const" +;; "*ashlha3_const" "*ashluha3_const" +(define_insn "*ashl3_const" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r") + (ashift:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + "reload_completed" + { + return ashlhi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,2,2,4,10") + (set_attr "adjust_len" "ashlhi") + (set_attr "cc" "none,set_n,clobber,set_n,clobber")]) + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL4 0 "register_operand" "") + (ashift:ALL4 (match_operand:ALL4 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (ashift:ALL4 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*ashlsi3_const" +;; "*ashlsq3_const" "*ashlusq3_const" +;; "*ashlsa3_const" "*ashlusa3_const" +(define_insn "*ashl3_const" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r") + (ashift:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "reload_completed" + { + return ashlsi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,4,4,10") + (set_attr "adjust_len" "ashlsi") + (set_attr "cc" "none,set_n,clobber,clobber")]) + +(define_expand "ashlpsi3" + [(parallel [(set (match_operand:PSI 0 "register_operand" "") + (ashift:PSI (match_operand:PSI 1 "register_operand" "") + (match_operand:QI 2 "nonmemory_operand" ""))) + (clobber (scratch:QI))])] + "" + { + if (AVR_HAVE_MUL + && CONST_INT_P (operands[2])) + { + if (IN_RANGE (INTVAL (operands[2]), 3, 6)) + { + rtx xoffset = force_reg (QImode, gen_int_mode (1 << INTVAL (operands[2]), QImode)); + emit_insn (gen_mulsqipsi3 (operands[0], xoffset, operands[1])); + DONE; + } + else if (optimize_insn_for_speed_p () + && INTVAL (operands[2]) != 16 + && IN_RANGE (INTVAL (operands[2]), 9, 22)) + { + rtx xoffset = force_reg (PSImode, gen_int_mode (1 << INTVAL (operands[2]), PSImode)); + emit_insn (gen_mulpsi3 (operands[0], operands[1], xoffset)); + DONE; + } + } + }) + +(define_insn "*ashlpsi3" + [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r") + (ashift:PSI (match_operand:PSI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "nonmemory_operand" "r,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "" + { + return avr_out_ashlpsi3 (insn, operands, NULL); + } + [(set_attr "adjust_len" "ashlpsi") + (set_attr "cc" "clobber")]) + +;; >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> +;; arithmetic shift right + +;; "ashrqi3" +;; "ashrqq3" "ashruqq3" +(define_insn "ashr3" + [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,r ,r ,r") + (ashiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,0 ,0 ,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] + "" + { + return ashrqi3_out (insn, operands, NULL); + } + [(set_attr "length" "5,0,1,2,5,4,9") + (set_attr "adjust_len" "ashrqi") + (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")]) + +;; "ashrhi3" +;; "ashrhq3" "ashruhq3" +;; "ashrha3" "ashruha3" +(define_insn "ashr3" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return ashrhi3_out (insn, operands, NULL); + } + [(set_attr "length" "6,0,2,4,4,10,10") + (set_attr "adjust_len" "ashrhi") + (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) + +(define_insn "ashrpsi3" + [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r,r") + (ashiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,0,r,0") + (match_operand:QI 2 "nonmemory_operand" "r,P,K,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + "" + { + return avr_out_ashrpsi3 (insn, operands, NULL); + } + [(set_attr "adjust_len" "ashrpsi") + (set_attr "cc" "clobber")]) + +;; "ashrsi3" +;; "ashrsq3" "ashrusq3" +;; "ashrsa3" "ashrusa3" +(define_insn "ashr3" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return ashrsi3_out (insn, operands, NULL); + } + [(set_attr "length" "8,0,4,6,8,10,12") + (set_attr "adjust_len" "ashrsi") + (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) + +;; Optimize if a scratch register from LD_REGS happens to be available. + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL2 0 "register_operand" "") + (ashiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (ashiftrt:ALL2 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*ashrhi3_const" +;; "*ashrhq3_const" "*ashruhq3_const" +;; "*ashrha3_const" "*ashruha3_const" +(define_insn "*ashr3_const" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r") + (ashiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + "reload_completed" + { + return ashrhi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,2,4,4,10") + (set_attr "adjust_len" "ashrhi") + (set_attr "cc" "none,clobber,set_n,clobber,clobber")]) + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL4 0 "register_operand" "") + (ashiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (ashiftrt:ALL4 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*ashrsi3_const" +;; "*ashrsq3_const" "*ashrusq3_const" +;; "*ashrsa3_const" "*ashrusa3_const" +(define_insn "*ashr3_const" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r") + (ashiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "reload_completed" + { + return ashrsi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,4,4,10") + (set_attr "adjust_len" "ashrsi") + (set_attr "cc" "none,clobber,set_n,clobber")]) + +;; >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> +;; logical shift right + +;; "lshrqi3" +;; "lshrqq3 "lshruqq3" +(define_expand "lshr3" + [(set (match_operand:ALL1 0 "register_operand" "") + (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) + +(define_split ; lshrqi3_const4 + [(set (match_operand:ALL1 0 "d_register_operand" "") + (lshiftrt:ALL1 (match_dup 0) + (const_int 4)))] + "" + [(set (match_dup 1) + (rotate:QI (match_dup 1) + (const_int 4))) + (set (match_dup 1) + (and:QI (match_dup 1) + (const_int 15)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +(define_split ; lshrqi3_const5 + [(set (match_operand:ALL1 0 "d_register_operand" "") + (lshiftrt:ALL1 (match_dup 0) + (const_int 5)))] + "" + [(set (match_dup 1) (rotate:QI (match_dup 1) (const_int 4))) + (set (match_dup 1) (lshiftrt:QI (match_dup 1) (const_int 1))) + (set (match_dup 1) (and:QI (match_dup 1) (const_int 7)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +(define_split ; lshrqi3_const6 + [(set (match_operand:QI 0 "d_register_operand" "") + (lshiftrt:QI (match_dup 0) + (const_int 6)))] + "" + [(set (match_dup 1) (rotate:QI (match_dup 1) (const_int 4))) + (set (match_dup 1) (lshiftrt:QI (match_dup 1) (const_int 2))) + (set (match_dup 1) (and:QI (match_dup 1) (const_int 3)))] + { + operands[1] = avr_to_int_mode (operands[0]); + }) + +;; "*lshrqi3" +;; "*lshrqq3" +;; "*lshruqq3" +(define_insn "*lshr3" + [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,!d,r,r") + (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] + "" + { + return lshrqi3_out (insn, operands, NULL); + } + [(set_attr "length" "5,0,1,2,4,6,9") + (set_attr "adjust_len" "lshrqi") + (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) + +;; "lshrhi3" +;; "lshrhq3" "lshruhq3" +;; "lshrha3" "lshruha3" +(define_insn "lshr3" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return lshrhi3_out (insn, operands, NULL); + } + [(set_attr "length" "6,0,2,2,4,10,10") + (set_attr "adjust_len" "lshrhi") + (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) + +(define_insn "lshrpsi3" + [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r,r") + (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "nonmemory_operand" "r,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + "" + { + return avr_out_lshrpsi3 (insn, operands, NULL); + } + [(set_attr "adjust_len" "lshrpsi") + (set_attr "cc" "clobber")]) + +;; "lshrsi3" +;; "lshrsq3" "lshrusq3" +;; "lshrsa3" "lshrusa3" +(define_insn "lshr3" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + "" + { + return lshrsi3_out (insn, operands, NULL); + } + [(set_attr "length" "8,0,4,4,8,10,12") + (set_attr "adjust_len" "lshrsi") + (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) + +;; Optimize if a scratch register from LD_REGS happens to be available. + +(define_peephole2 ; lshrqi3_l_const4 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (lshiftrt:ALL1 (match_dup 0) + (const_int 4))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 1) (const_int 15)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 ; lshrqi3_l_const5 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (lshiftrt:ALL1 (match_dup 0) + (const_int 5))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 2) (lshiftrt:QI (match_dup 2) (const_int 1))) + (set (match_dup 1) (const_int 7)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 ; lshrqi3_l_const6 + [(set (match_operand:ALL1 0 "l_register_operand" "") + (lshiftrt:ALL1 (match_dup 0) + (const_int 6))) + (match_scratch:QI 1 "d")] + "" + [(set (match_dup 2) (rotate:QI (match_dup 2) (const_int 4))) + (set (match_dup 2) (lshiftrt:QI (match_dup 2) (const_int 2))) + (set (match_dup 1) (const_int 3)) + (set (match_dup 2) (and:QI (match_dup 2) (match_dup 1)))] + { + operands[2] = avr_to_int_mode (operands[0]); + }) + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL2 0 "register_operand" "") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (lshiftrt:ALL2 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*lshrhi3_const" +;; "*lshrhq3_const" "*lshruhq3_const" +;; "*lshrha3_const" "*lshruha3_const" +(define_insn "*lshr3_const" + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + "reload_completed" + { + return lshrhi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,2,2,4,10") + (set_attr "adjust_len" "lshrhi") + (set_attr "cc" "none,clobber,clobber,clobber,clobber")]) + +(define_peephole2 + [(match_scratch:QI 3 "d") + (set (match_operand:ALL4 0 "register_operand" "") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "") + (match_operand:QI 2 "const_int_operand" "")))] + "" + [(parallel [(set (match_dup 0) + (lshiftrt:ALL4 (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3))])]) + +;; "*lshrsi3_const" +;; "*lshrsq3_const" "*lshrusq3_const" +;; "*lshrsa3_const" "*lshrusa3_const" +(define_insn "*lshr3_const" + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "reload_completed" + { + return lshrsi3_out (insn, operands, NULL); + } + [(set_attr "length" "0,4,4,10") + (set_attr "adjust_len" "lshrsi") + (set_attr "cc" "none,clobber,clobber,clobber")]) + +;; abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) +;; abs + +(define_insn "absqi2" + [(set (match_operand:QI 0 "register_operand" "=r") + (abs:QI (match_operand:QI 1 "register_operand" "0")))] + "" + "sbrc %0,7 + neg %0" + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=d,r") + (abs:SF (match_operand:SF 1 "register_operand" "0,0")))] + "" + "@ + andi %D0,0x7f + clt\;bld %D0,7" + [(set_attr "length" "1,2") + (set_attr "cc" "set_n,clobber")]) + +;; 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x 0 - x +;; neg + +(define_insn "negqi2" + [(set (match_operand:QI 0 "register_operand" "=r") + (neg:QI (match_operand:QI 1 "register_operand" "0")))] + "" + "neg %0" + [(set_attr "length" "1") + (set_attr "cc" "set_zn")]) + +(define_insn "*negqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (neg:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "0"))))] + "" + "clr %B0\;neg %A0\;brge .+2\;com %B0" + [(set_attr "length" "4") + (set_attr "cc" "set_n")]) + +(define_insn "neghi2" + [(set (match_operand:HI 0 "register_operand" "=r,&r") + (neg:HI (match_operand:HI 1 "register_operand" "0,r")))] + "" + "@ + neg %B0\;neg %A0\;sbc %B0,__zero_reg__ + clr %A0\;clr %B0\;sub %A0,%A1\;sbc %B0,%B1" + [(set_attr "length" "3,4") + (set_attr "cc" "set_czn")]) + +(define_insn "negpsi2" + [(set (match_operand:PSI 0 "register_operand" "=!d,r,&r") + (neg:PSI (match_operand:PSI 1 "register_operand" "0,0,r")))] + "" + "@ + com %C0\;com %B0\;neg %A0\;sbci %B0,-1\;sbci %C0,-1 + com %C0\;com %B0\;com %A0\;adc %A0,__zero_reg__\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__ + clr %A0\;clr %B0\;clr %C0\;sub %A0,%A1\;sbc %B0,%B1\;sbc %C0,%C1" + [(set_attr "length" "5,6,6") + (set_attr "cc" "set_czn,set_n,set_czn")]) + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=!d,r,&r,&r") + (neg:SI (match_operand:SI 1 "register_operand" "0,0,r ,r")))] + "" + "@ + com %D0\;com %C0\;com %B0\;neg %A0\;sbci %B0,lo8(-1)\;sbci %C0,lo8(-1)\;sbci %D0,lo8(-1) + com %D0\;com %C0\;com %B0\;com %A0\;adc %A0,__zero_reg__\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__ + clr %A0\;clr %B0\;clr %C0\;clr %D0\;sub %A0,%A1\;sbc %B0,%B1\;sbc %C0,%C1\;sbc %D0,%D1 + clr %A0\;clr %B0\;movw %C0,%A0\;sub %A0,%A1\;sbc %B0,%B1\;sbc %C0,%C1\;sbc %D0,%D1" + [(set_attr "length" "7,8,8,7") + (set_attr "isa" "*,*,mov,movw") + (set_attr "cc" "set_czn,set_n,set_czn,set_czn")]) + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=d,r") + (neg:SF (match_operand:SF 1 "register_operand" "0,0")))] + "" + "@ + subi %D0,0x80 + bst %D0,7\;com %D0\;bld %D0,7\;com %D0" + [(set_attr "length" "1,4") + (set_attr "cc" "set_n,set_n")]) + +;; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +;; not + +(define_insn "one_cmplqi2" + [(set (match_operand:QI 0 "register_operand" "=r") + (not:QI (match_operand:QI 1 "register_operand" "0")))] + "" + "com %0" + [(set_attr "length" "1") + (set_attr "cc" "set_czn")]) + +(define_insn "one_cmplhi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (not:HI (match_operand:HI 1 "register_operand" "0")))] + "" + "com %0 + com %B0" + [(set_attr "length" "2") + (set_attr "cc" "set_n")]) + +(define_insn "one_cmplpsi2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (not:PSI (match_operand:PSI 1 "register_operand" "0")))] + "" + "com %0\;com %B0\;com %C0" + [(set_attr "length" "3") + (set_attr "cc" "set_n")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "0")))] + "" + "com %0 + com %B0 + com %C0 + com %D0" + [(set_attr "length" "4") + (set_attr "cc" "set_n")]) + +;; xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x +;; sign extend + +;; We keep combiner from inserting hard registers into the input of sign- and +;; zero-extends. A hard register in the input operand is not wanted because +;; 32-bit multiply patterns clobber some hard registers and extends with a +;; hard register that overlaps these clobbers won't be combined to a widening +;; multiplication. There is no need for combine to propagate hard registers, +;; register allocation can do it just as well. + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (sign_extend:HI (match_operand:QI 1 "combine_pseudo_register_operand" "0,*r")))] + "" + "@ + clr %B0\;sbrc %0,7\;com %B0 + mov %A0,%A1\;clr %B0\;sbrc %A0,7\;com %B0" + [(set_attr "length" "3,4") + (set_attr "cc" "set_n,set_n")]) + +(define_insn "extendqipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r,r") + (sign_extend:PSI (match_operand:QI 1 "combine_pseudo_register_operand" "0,*r")))] + "" + "@ + clr %B0\;sbrc %A0,7\;com %B0\;mov %C0,%B0 + mov %A0,%A1\;clr %B0\;sbrc %A0,7\;com %B0\;mov %C0,%B0" + [(set_attr "length" "4,5") + (set_attr "cc" "set_n,set_n")]) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (sign_extend:SI (match_operand:QI 1 "combine_pseudo_register_operand" "0,*r")))] + "" + "@ + clr %B0\;sbrc %A0,7\;com %B0\;mov %C0,%B0\;mov %D0,%B0 + mov %A0,%A1\;clr %B0\;sbrc %A0,7\;com %B0\;mov %C0,%B0\;mov %D0,%B0" + [(set_attr "length" "5,6") + (set_attr "cc" "set_n,set_n")]) + +(define_insn "extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r,r ,r") + (sign_extend:PSI (match_operand:HI 1 "combine_pseudo_register_operand" "0,*r,*r")))] + "" + "@ + clr %C0\;sbrc %B0,7\;com %C0 + mov %A0,%A1\;mov %B0,%B1\;clr %C0\;sbrc %B0,7\;com %C0 + movw %A0,%A1\;clr %C0\;sbrc %B0,7\;com %C0" + [(set_attr "length" "3,5,4") + (set_attr "isa" "*,mov,movw") + (set_attr "cc" "set_n")]) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r ,r") + (sign_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "0,*r,*r")))] + "" + "@ + clr %C0\;sbrc %B0,7\;com %C0\;mov %D0,%C0 + mov %A0,%A1\;mov %B0,%B1\;clr %C0\;sbrc %B0,7\;com %C0\;mov %D0,%C0 + movw %A0,%A1\;clr %C0\;sbrc %B0,7\;com %C0\;mov %D0,%C0" + [(set_attr "length" "4,6,5") + (set_attr "isa" "*,mov,movw") + (set_attr "cc" "set_n")]) + +(define_insn "extendpsisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:PSI 1 "combine_pseudo_register_operand" "0")))] + "" + "clr %D0\;sbrc %C0,7\;com %D0" + [(set_attr "length" "3") + (set_attr "cc" "set_n")]) + +;; xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x xx<---x +;; zero extend + +(define_insn_and_split "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (match_operand:QI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (QImode, HImode); + unsigned int high_off = subreg_highpart_offset (QImode, HImode); + + operands[2] = simplify_gen_subreg (QImode, operands[0], HImode, low_off); + operands[3] = simplify_gen_subreg (QImode, operands[0], HImode, high_off); + }) + +(define_insn_and_split "zero_extendqipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (zero_extend:PSI (match_operand:QI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0)) + (set (match_dup 4) (const_int 0))] + { + operands[2] = simplify_gen_subreg (QImode, operands[0], PSImode, 0); + operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 1); + operands[4] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + }) + +(define_insn_and_split "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:QI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (zero_extend:HI (match_dup 1))) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (HImode, SImode); + unsigned int high_off = subreg_highpart_offset (HImode, SImode); + + operands[2] = simplify_gen_subreg (HImode, operands[0], SImode, low_off); + operands[3] = simplify_gen_subreg (HImode, operands[0], SImode, high_off); + }) + +(define_insn_and_split "zero_extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (zero_extend:PSI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0))] + { + operands[2] = simplify_gen_subreg (HImode, operands[0], PSImode, 0); + operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + }) + +(define_insn_and_split "n_extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r,r,d,r") + (lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n") + (match_operand:HI 2 "register_operand" "r,r,r,r"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "" + "#" + "reload_completed" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 3) (match_dup 6)) + ; no-op move in the case where no scratch is needed + (set (match_dup 5) (match_dup 3))] + { + operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0); + operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + operands[6] = operands[1]; + + if (GET_CODE (operands[3]) == SCRATCH) + operands[3] = operands[5]; + }) + +(define_insn_and_split "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (HImode, SImode); + unsigned int high_off = subreg_highpart_offset (HImode, SImode); + + operands[2] = simplify_gen_subreg (HImode, operands[0], SImode, low_off); + operands[3] = simplify_gen_subreg (HImode, operands[0], SImode, high_off); + }) + +(define_insn_and_split "zero_extendpsisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:PSI 1 "combine_pseudo_register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0))] + { + operands[2] = simplify_gen_subreg (PSImode, operands[0], SImode, 0); + operands[3] = simplify_gen_subreg (QImode, operands[0], SImode, 3); + }) + +(define_insn_and_split "zero_extendqidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:QI 1 "register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (zero_extend:SI (match_dup 1))) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (SImode, DImode); + unsigned int high_off = subreg_highpart_offset (SImode, DImode); + + operands[2] = simplify_gen_subreg (SImode, operands[0], DImode, low_off); + operands[3] = simplify_gen_subreg (SImode, operands[0], DImode, high_off); + }) + +(define_insn_and_split "zero_extendhidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:HI 1 "register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (zero_extend:SI (match_dup 1))) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (SImode, DImode); + unsigned int high_off = subreg_highpart_offset (SImode, DImode); + + operands[2] = simplify_gen_subreg (SImode, operands[0], DImode, low_off); + operands[3] = simplify_gen_subreg (SImode, operands[0], DImode, high_off); + }) + +(define_insn_and_split "zero_extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:SI 1 "register_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (const_int 0))] + { + unsigned int low_off = subreg_lowpart_offset (SImode, DImode); + unsigned int high_off = subreg_highpart_offset (SImode, DImode); + + operands[2] = simplify_gen_subreg (SImode, operands[0], DImode, low_off); + operands[3] = simplify_gen_subreg (SImode, operands[0], DImode, high_off); + }) + +;;<=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=> +;; compare + +; Optimize negated tests into reverse compare if overflow is undefined. +(define_insn "*negated_tstqi" + [(set (cc0) + (compare (neg:QI (match_operand:QI 0 "register_operand" "r")) + (const_int 0)))] + "!flag_wrapv && !flag_trapv && flag_strict_overflow" + "cp __zero_reg__,%0" + [(set_attr "cc" "compare") + (set_attr "length" "1")]) + +(define_insn "*reversed_tstqi" + [(set (cc0) + (compare (const_int 0) + (match_operand:QI 0 "register_operand" "r")))] + "" + "cp __zero_reg__,%0" +[(set_attr "cc" "compare") + (set_attr "length" "2")]) + +(define_insn "*negated_tsthi" + [(set (cc0) + (compare (neg:HI (match_operand:HI 0 "register_operand" "r")) + (const_int 0)))] + "!flag_wrapv && !flag_trapv && flag_strict_overflow" + "cp __zero_reg__,%A0 + cpc __zero_reg__,%B0" +[(set_attr "cc" "compare") + (set_attr "length" "2")]) + +;; Leave here the clobber used by the cmphi pattern for simplicity, even +;; though it is unused, because this pattern is synthesized by avr_reorg. +(define_insn "*reversed_tsthi" + [(set (cc0) + (compare (const_int 0) + (match_operand:HI 0 "register_operand" "r"))) + (clobber (match_scratch:QI 1 "=X"))] + "" + "cp __zero_reg__,%A0 + cpc __zero_reg__,%B0" +[(set_attr "cc" "compare") + (set_attr "length" "2")]) + +(define_insn "*negated_tstpsi" + [(set (cc0) + (compare (neg:PSI (match_operand:PSI 0 "register_operand" "r")) + (const_int 0)))] + "!flag_wrapv && !flag_trapv && flag_strict_overflow" + "cp __zero_reg__,%A0\;cpc __zero_reg__,%B0\;cpc __zero_reg__,%C0" + [(set_attr "cc" "compare") + (set_attr "length" "3")]) + +(define_insn "*reversed_tstpsi" + [(set (cc0) + (compare (const_int 0) + (match_operand:PSI 0 "register_operand" "r"))) + (clobber (match_scratch:QI 1 "=X"))] + "" + "cp __zero_reg__,%A0\;cpc __zero_reg__,%B0\;cpc __zero_reg__,%C0" + [(set_attr "cc" "compare") + (set_attr "length" "3")]) + +(define_insn "*negated_tstsi" + [(set (cc0) + (compare (neg:SI (match_operand:SI 0 "register_operand" "r")) + (const_int 0)))] + "!flag_wrapv && !flag_trapv && flag_strict_overflow" + "cp __zero_reg__,%A0 + cpc __zero_reg__,%B0 + cpc __zero_reg__,%C0 + cpc __zero_reg__,%D0" + [(set_attr "cc" "compare") + (set_attr "length" "4")]) + +;; "*reversed_tstsi" +;; "*reversed_tstsq" "*reversed_tstusq" +;; "*reversed_tstsa" "*reversed_tstusa" +(define_insn "*reversed_tst" + [(set (cc0) + (compare (match_operand:ALL4 0 "const0_operand" "Y00") + (match_operand:ALL4 1 "register_operand" "r"))) + (clobber (match_scratch:QI 2 "=X"))] + "" + "cp __zero_reg__,%A1 + cpc __zero_reg__,%B1 + cpc __zero_reg__,%C1 + cpc __zero_reg__,%D1" + [(set_attr "cc" "compare") + (set_attr "length" "4")]) + + +;; "*cmpqi" +;; "*cmpqq" "*cmpuqq" +(define_insn "*cmp" + [(set (cc0) + (compare (match_operand:ALL1 0 "register_operand" "r ,r,d") + (match_operand:ALL1 1 "nonmemory_operand" "Y00,r,i")))] + "" + "@ + tst %0 + cp %0,%1 + cpi %0,lo8(%1)" + [(set_attr "cc" "compare,compare,compare") + (set_attr "length" "1,1,1")]) + +(define_insn "*cmpqi_sign_extend" + [(set (cc0) + (compare (sign_extend:HI (match_operand:QI 0 "register_operand" "d")) + (match_operand:HI 1 "s8_operand" "n")))] + "" + "cpi %0,lo8(%1)" + [(set_attr "cc" "compare") + (set_attr "length" "1")]) + +;; "*cmphi" +;; "*cmphq" "*cmpuhq" +;; "*cmpha" "*cmpuha" +(define_insn "*cmp" + [(set (cc0) + (compare (match_operand:ALL2 0 "register_operand" "!w ,r ,r,d ,r ,d,r") + (match_operand:ALL2 1 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn"))) + (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d ,X,&d"))] + "" + { + switch (which_alternative) + { + case 0: + case 1: + return avr_out_tsthi (insn, operands, NULL); + + case 2: + return "cp %A0,%A1\;cpc %B0,%B1"; + + case 3: + if (mode != HImode) + break; + return reg_unused_after (insn, operands[0]) + ? "subi %A0,lo8(%1)\;sbci %B0,hi8(%1)" + : "ldi %2,hi8(%1)\;cpi %A0,lo8(%1)\;cpc %B0,%2"; + + case 4: + if (mode != HImode) + break; + return "ldi %2,lo8(%1)\;cp %A0,%2\;ldi %2,hi8(%1)\;cpc %B0,%2"; + } + + return avr_out_compare (insn, operands, NULL); + } + [(set_attr "cc" "compare") + (set_attr "length" "1,2,2,3,4,2,4") + (set_attr "adjust_len" "tsthi,tsthi,*,*,*,compare,compare")]) + +(define_insn "*cmppsi" + [(set (cc0) + (compare (match_operand:PSI 0 "register_operand" "r,r,d ,r ,d,r") + (match_operand:PSI 1 "nonmemory_operand" "L,r,s ,s ,M,n"))) + (clobber (match_scratch:QI 2 "=X,X,&d,&d ,X,&d"))] + "" + { + switch (which_alternative) + { + case 0: + return avr_out_tstpsi (insn, operands, NULL); + + case 1: + return "cp %A0,%A1\;cpc %B0,%B1\;cpc %C0,%C1"; + + case 2: + return reg_unused_after (insn, operands[0]) + ? "subi %A0,lo8(%1)\;sbci %B0,hi8(%1)\;sbci %C0,hh8(%1)" + : "cpi %A0,lo8(%1)\;ldi %2,hi8(%1)\;cpc %B0,%2\;ldi %2,hh8(%1)\;cpc %C0,%2"; + + case 3: + return "ldi %2,lo8(%1)\;cp %A0,%2\;ldi %2,hi8(%1)\;cpc %B0,%2\;ldi %2,hh8(%1)\;cpc %C0,%2"; + } + + return avr_out_compare (insn, operands, NULL); + } + [(set_attr "cc" "compare") + (set_attr "length" "3,3,5,6,3,7") + (set_attr "adjust_len" "tstpsi,*,*,*,compare,compare")]) + +;; "*cmpsi" +;; "*cmpsq" "*cmpusq" +;; "*cmpsa" "*cmpusa" +(define_insn "*cmp" + [(set (cc0) + (compare (match_operand:ALL4 0 "register_operand" "r ,r ,d,r ,r") + (match_operand:ALL4 1 "nonmemory_operand" "Y00,r ,M,M ,n Ynn"))) + (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d"))] + "" + { + if (0 == which_alternative) + return avr_out_tstsi (insn, operands, NULL); + else if (1 == which_alternative) + return "cp %A0,%A1\;cpc %B0,%B1\;cpc %C0,%C1\;cpc %D0,%D1"; + + return avr_out_compare (insn, operands, NULL); + } + [(set_attr "cc" "compare") + (set_attr "length" "4,4,4,5,8") + (set_attr "adjust_len" "tstsi,*,compare,compare,compare")]) + + +;; ---------------------------------------------------------------------- +;; JUMP INSTRUCTIONS +;; ---------------------------------------------------------------------- +;; Conditional jump instructions + +;; "cbranchqi4" +;; "cbranchqq4" "cbranchuqq4" +(define_expand "cbranch4" + [(set (cc0) + (compare (match_operand:ALL1 1 "register_operand" "") + (match_operand:ALL1 2 "nonmemory_operand" ""))) + (set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" [(cc0) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))]) + +;; "cbranchhi4" "cbranchhq4" "cbranchuhq4" "cbranchha4" "cbranchuha4" +;; "cbranchsi4" "cbranchsq4" "cbranchusq4" "cbranchsa4" "cbranchusa4" +;; "cbranchpsi4" +(define_expand "cbranch4" + [(parallel [(set (cc0) + (compare (match_operand:ORDERED234 1 "register_operand" "") + (match_operand:ORDERED234 2 "nonmemory_operand" ""))) + (clobber (match_scratch:QI 4 ""))]) + (set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" [(cc0) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))]) + + +;; Test a single bit in a QI/HI/SImode register. +;; Combine will create zero extract patterns for single bit tests. +;; permit any mode in source pattern by using VOIDmode. + +(define_insn "*sbrx_branch" + [(set (pc) + (if_then_else + (match_operator 0 "eqne_operator" + [(zero_extract:QIDI + (match_operand:VOID 1 "register_operand" "r") + (const_int 1) + (match_operand 2 "const_int_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + { + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 3)) (const_int -2046)) + (le (minus (pc) (match_dup 3)) (const_int 2046))) + (const_int 2) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 2) + (const_int 4)))) + (set_attr "cc" "clobber")]) + +;; Same test based on bitwise AND. Keep this in case gcc changes patterns. +;; or for old peepholes. +;; Fixme - bitwise Mask will not work for DImode + +(define_insn "*sbrx_and_branch" + [(set (pc) + (if_then_else + (match_operator 0 "eqne_operator" + [(and:QISI + (match_operand:QISI 1 "register_operand" "r") + (match_operand:QISI 2 "single_one_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + { + HOST_WIDE_INT bitnumber; + bitnumber = exact_log2 (GET_MODE_MASK (mode) & INTVAL (operands[2])); + operands[2] = GEN_INT (bitnumber); + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 3)) (const_int -2046)) + (le (minus (pc) (match_dup 3)) (const_int 2046))) + (const_int 2) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 2) + (const_int 4)))) + (set_attr "cc" "clobber")]) + +;; Convert sign tests to bit 7/15/31 tests that match the above insns. +(define_peephole2 + [(set (cc0) (compare (match_operand:QI 0 "register_operand" "") + (const_int 0))) + (set (pc) (if_then_else (ge (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (eq (zero_extract:HI (match_dup 0) + (const_int 1) + (const_int 7)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))]) + +(define_peephole2 + [(set (cc0) (compare (match_operand:QI 0 "register_operand" "") + (const_int 0))) + (set (pc) (if_then_else (lt (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (ne (zero_extract:HI (match_dup 0) + (const_int 1) + (const_int 7)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))]) + +(define_peephole2 + [(parallel [(set (cc0) (compare (match_operand:HI 0 "register_operand" "") + (const_int 0))) + (clobber (match_operand:HI 2 ""))]) + (set (pc) (if_then_else (ge (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (eq (and:HI (match_dup 0) (const_int -32768)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))]) + +(define_peephole2 + [(parallel [(set (cc0) (compare (match_operand:HI 0 "register_operand" "") + (const_int 0))) + (clobber (match_operand:HI 2 ""))]) + (set (pc) (if_then_else (lt (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (ne (and:HI (match_dup 0) (const_int -32768)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))]) + +(define_peephole2 + [(parallel [(set (cc0) (compare (match_operand:SI 0 "register_operand" "") + (const_int 0))) + (clobber (match_operand:SI 2 ""))]) + (set (pc) (if_then_else (ge (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (eq (and:SI (match_dup 0) (match_dup 2)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))] + "operands[2] = gen_int_mode (-2147483647 - 1, SImode);") + +(define_peephole2 + [(parallel [(set (cc0) (compare (match_operand:SI 0 "register_operand" "") + (const_int 0))) + (clobber (match_operand:SI 2 ""))]) + (set (pc) (if_then_else (lt (cc0) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + [(set (pc) (if_then_else (ne (and:SI (match_dup 0) (match_dup 2)) + (const_int 0)) + (label_ref (match_dup 1)) + (pc)))] + "operands[2] = gen_int_mode (-2147483647 - 1, SImode);") + +;; ************************************************************************ +;; Implementation of conditional jumps here. +;; Compare with 0 (test) jumps +;; ************************************************************************ + +(define_insn "branch" + [(set (pc) + (if_then_else (match_operator 1 "simple_comparison_operator" + [(cc0) + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); + } + [(set_attr "type" "branch") + (set_attr "cc" "clobber")]) + + +;; Same as above but wrap SET_SRC so that this branch won't be transformed +;; or optimized in the remainder. + +(define_insn "branch_unspec" + [(set (pc) + (unspec [(if_then_else (match_operator 1 "simple_comparison_operator" + [(cc0) + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)) + ] UNSPEC_IDENTITY))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); + } + [(set_attr "type" "branch") + (set_attr "cc" "none")]) + +;; **************************************************************** +;; AVR does not have following conditional jumps: LE,LEU,GT,GTU. +;; Convert them all to proper jumps. +;; ****************************************************************/ + +(define_insn "difficult_branch" + [(set (pc) + (if_then_else (match_operator 1 "difficult_comparison_operator" + [(cc0) + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); + } + [(set_attr "type" "branch1") + (set_attr "cc" "clobber")]) + +;; revers branch + +(define_insn "rvbranch" + [(set (pc) + (if_then_else (match_operator 1 "simple_comparison_operator" + [(cc0) + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 1); + } + [(set_attr "type" "branch1") + (set_attr "cc" "clobber")]) + +(define_insn "difficult_rvbranch" + [(set (pc) + (if_then_else (match_operator 1 "difficult_comparison_operator" + [(cc0) + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + { + return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 1); + } + [(set_attr "type" "branch") + (set_attr "cc" "clobber")]) + +;; ************************************************************************** +;; Unconditional and other jump instructions. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + { + return AVR_HAVE_JMP_CALL && get_attr_length (insn) != 1 + ? "jmp %x0" + : "rjmp %x0"; + } + [(set (attr "length") + (if_then_else (match_operand 0 "symbol_ref_operand" "") + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 1) + (const_int 2)) + (if_then_else (and (ge (minus (pc) (match_dup 0)) (const_int -2047)) + (le (minus (pc) (match_dup 0)) (const_int 2047))) + (const_int 1) + (const_int 2)))) + (set_attr "cc" "none")]) + +;; call + +;; Operand 1 not used on the AVR. +;; Operand 2 is 1 for tail-call, 0 otherwise. +(define_expand "call" + [(parallel[(call (match_operand:HI 0 "call_insn_operand" "") + (match_operand:HI 1 "general_operand" "")) + (use (const_int 0))])]) + +;; Operand 1 not used on the AVR. +;; Operand 2 is 1 for tail-call, 0 otherwise. +(define_expand "sibcall" + [(parallel[(call (match_operand:HI 0 "call_insn_operand" "") + (match_operand:HI 1 "general_operand" "")) + (use (const_int 1))])]) + +;; call value + +;; Operand 2 not used on the AVR. +;; Operand 3 is 1 for tail-call, 0 otherwise. +(define_expand "call_value" + [(parallel[(set (match_operand 0 "register_operand" "") + (call (match_operand:HI 1 "call_insn_operand" "") + (match_operand:HI 2 "general_operand" ""))) + (use (const_int 0))])]) + +;; Operand 2 not used on the AVR. +;; Operand 3 is 1 for tail-call, 0 otherwise. +(define_expand "sibcall_value" + [(parallel[(set (match_operand 0 "register_operand" "") + (call (match_operand:HI 1 "call_insn_operand" "") + (match_operand:HI 2 "general_operand" ""))) + (use (const_int 1))])]) + +(define_insn "call_insn" + [(parallel[(call (mem:HI (match_operand:HI 0 "nonmemory_operand" "z,s,z,s")) + (match_operand:HI 1 "general_operand" "X,X,X,X")) + (use (match_operand:HI 2 "const_int_operand" "L,L,P,P"))])] + ;; Operand 1 not used on the AVR. + ;; Operand 2 is 1 for tail-call, 0 otherwise. + "" + "@ + %!icall + %~call %x0 + %!ijmp + %~jmp %x0" + [(set_attr "cc" "clobber") + (set_attr "length" "1,*,1,*") + (set_attr "adjust_len" "*,call,*,call")]) + +(define_insn "call_value_insn" + [(parallel[(set (match_operand 0 "register_operand" "=r,r,r,r") + (call (mem:HI (match_operand:HI 1 "nonmemory_operand" "z,s,z,s")) + (match_operand:HI 2 "general_operand" "X,X,X,X"))) + (use (match_operand:HI 3 "const_int_operand" "L,L,P,P"))])] + ;; Operand 2 not used on the AVR. + ;; Operand 3 is 1 for tail-call, 0 otherwise. + "" + "@ + %!icall + %~call %x1 + %!ijmp + %~jmp %x1" + [(set_attr "cc" "clobber") + (set_attr "length" "1,*,1,*") + (set_attr "adjust_len" "*,call,*,call")]) + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "cc" "none") + (set_attr "length" "1")]) + +; indirect jump + +(define_expand "indirect_jump" + [(set (pc) + (match_operand:HI 0 "nonmemory_operand" ""))] + "" + { + if (!AVR_HAVE_JMP_CALL && !register_operand (operands[0], HImode)) + { + operands[0] = copy_to_mode_reg (HImode, operands[0]); + } + }) + +; indirect jump +(define_insn "*indirect_jump" + [(set (pc) + (match_operand:HI 0 "nonmemory_operand" "i,i,!z,*r,z"))] + "" + "@ + rjmp %x0 + jmp %x0 + ijmp + push %A0\;push %B0\;ret + eijmp" + [(set_attr "length" "1,2,1,3,1") + (set_attr "isa" "rjmp,jmp,ijmp,ijmp,eijmp") + (set_attr "cc" "none")]) + +;; table jump +;; For entries in jump table see avr_output_addr_vec_elt. + +;; Table made from +;; "rjmp .L" instructions for <= 8K devices +;; ".word gs(.L)" addresses for > 8K devices +(define_insn "*tablejump" + [(set (pc) + (unspec:HI [(match_operand:HI 0 "register_operand" "!z,*r,z")] + UNSPEC_INDEX_JMP)) + (use (label_ref (match_operand 1 "" ""))) + (clobber (match_dup 0))] + "" + "@ + ijmp + push %A0\;push %B0\;ret + jmp __tablejump2__" + [(set_attr "length" "1,3,2") + (set_attr "isa" "rjmp,rjmp,jmp") + (set_attr "cc" "none,none,clobber")]) + + +(define_expand "casesi" + [(parallel [(set (match_dup 6) + (minus:HI (subreg:HI (match_operand:SI 0 "register_operand" "") 0) + (match_operand:HI 1 "register_operand" ""))) + (clobber (scratch:QI))]) + (parallel [(set (cc0) + (compare (match_dup 6) + (match_operand:HI 2 "register_operand" ""))) + (clobber (match_scratch:QI 9 ""))]) + + (set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (label_ref (match_operand 4 "" "")) + (pc))) + + (set (match_dup 6) + (plus:HI (match_dup 6) (label_ref (match_operand:HI 3 "" "")))) + + (parallel [(set (pc) (unspec:HI [(match_dup 6)] UNSPEC_INDEX_JMP)) + (use (label_ref (match_dup 3))) + (clobber (match_dup 6))])] + "" + { + operands[6] = gen_reg_rtx (HImode); + }) + + +;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +;; This instruction sets Z flag + +(define_insn "sez" + [(set (cc0) (const_int 0))] + "" + "sez" + [(set_attr "length" "1") + (set_attr "cc" "compare")]) + +;; Clear/set/test a single bit in I/O address space. + +(define_insn "*cbi" + [(set (mem:QI (match_operand 0 "low_io_address_operand" "n")) + (and:QI (mem:QI (match_dup 0)) + (match_operand:QI 1 "single_zero_operand" "n")))] + "" + { + operands[2] = GEN_INT (exact_log2 (~INTVAL (operands[1]) & 0xff)); + return "cbi %i0,%2"; + } + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +(define_insn "*sbi" + [(set (mem:QI (match_operand 0 "low_io_address_operand" "n")) + (ior:QI (mem:QI (match_dup 0)) + (match_operand:QI 1 "single_one_operand" "n")))] + "" + { + operands[2] = GEN_INT (exact_log2 (INTVAL (operands[1]) & 0xff)); + return "sbi %i0,%2"; + } + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +;; Lower half of the I/O space - use sbic/sbis directly. +(define_insn "*sbix_branch" + [(set (pc) + (if_then_else + (match_operator 0 "eqne_operator" + [(zero_extract:QIHI + (mem:QI (match_operand 1 "low_io_address_operand" "n")) + (const_int 1) + (match_operand 2 "const_int_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + { + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 3)) (const_int -2046)) + (le (minus (pc) (match_dup 3)) (const_int 2046))) + (const_int 2) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 2) + (const_int 4)))) + (set_attr "cc" "clobber")]) + +;; Tests of bit 7 are pessimized to sign tests, so we need this too... +(define_insn "*sbix_branch_bit7" + [(set (pc) + (if_then_else + (match_operator 0 "gelt_operator" + [(mem:QI (match_operand 1 "low_io_address_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + operands[3] = operands[2]; + operands[2] = GEN_INT (7); + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 2)) (const_int -2046)) + (le (minus (pc) (match_dup 2)) (const_int 2046))) + (const_int 2) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 2) + (const_int 4)))) + (set_attr "cc" "clobber")]) + +;; Upper half of the I/O space - read port to __tmp_reg__ and use sbrc/sbrs. +(define_insn "*sbix_branch_tmp" + [(set (pc) + (if_then_else + (match_operator 0 "eqne_operator" + [(zero_extract:QIHI + (mem:QI (match_operand 1 "high_io_address_operand" "n")) + (const_int 1) + (match_operand 2 "const_int_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + { + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 3)) (const_int -2046)) + (le (minus (pc) (match_dup 3)) (const_int 2045))) + (const_int 3) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 3) + (const_int 5)))) + (set_attr "cc" "clobber")]) + +(define_insn "*sbix_branch_tmp_bit7" + [(set (pc) + (if_then_else + (match_operator 0 "gelt_operator" + [(mem:QI (match_operand 1 "high_io_address_operand" "n")) + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + operands[3] = operands[2]; + operands[2] = GEN_INT (7); + return avr_out_sbxx_branch (insn, operands); + } + [(set (attr "length") + (if_then_else (and (ge (minus (pc) (match_dup 2)) (const_int -2046)) + (le (minus (pc) (match_dup 2)) (const_int 2045))) + (const_int 3) + (if_then_else (match_test "!AVR_HAVE_JMP_CALL") + (const_int 3) + (const_int 5)))) + (set_attr "cc" "clobber")]) + +;; ************************* Peepholes ******************************** + +(define_peephole ; "*dec-and-branchsi!=-1.d.clobber" + [(parallel [(set (match_operand:SI 0 "d_register_operand" "") + (plus:SI (match_dup 0) + (const_int -1))) + (clobber (scratch:QI))]) + (parallel [(set (cc0) + (compare (match_dup 0) + (const_int -1))) + (clobber (match_operand:QI 1 "d_register_operand" ""))]) + (set (pc) + (if_then_else (eqne (cc0) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + const char *op; + int jump_mode; + CC_STATUS_INIT; + if (test_hard_reg_class (ADDW_REGS, operands[0])) + output_asm_insn ("sbiw %0,1" CR_TAB + "sbc %C0,__zero_reg__" CR_TAB + "sbc %D0,__zero_reg__", operands); + else + output_asm_insn ("subi %A0,1" CR_TAB + "sbc %B0,__zero_reg__" CR_TAB + "sbc %C0,__zero_reg__" CR_TAB + "sbc %D0,__zero_reg__", operands); + + jump_mode = avr_jump_mode (operands[2], insn); + op = ((EQ == ) ^ (jump_mode == 1)) ? "brcc" : "brcs"; + operands[1] = gen_rtx_CONST_STRING (VOIDmode, op); + + switch (jump_mode) + { + case 1: return "%1 %2"; + case 2: return "%1 .+2\;rjmp %2"; + case 3: return "%1 .+4\;jmp %2"; + } + + gcc_unreachable(); + return ""; + }) + +(define_peephole ; "*dec-and-branchhi!=-1" + [(set (match_operand:HI 0 "d_register_operand" "") + (plus:HI (match_dup 0) + (const_int -1))) + (parallel [(set (cc0) + (compare (match_dup 0) + (const_int -1))) + (clobber (match_operand:QI 1 "d_register_operand" ""))]) + (set (pc) + (if_then_else (eqne (cc0) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + const char *op; + int jump_mode; + CC_STATUS_INIT; + if (test_hard_reg_class (ADDW_REGS, operands[0])) + output_asm_insn ("sbiw %0,1", operands); + else + output_asm_insn ("subi %A0,1" CR_TAB + "sbc %B0,__zero_reg__", operands); + + jump_mode = avr_jump_mode (operands[2], insn); + op = ((EQ == ) ^ (jump_mode == 1)) ? "brcc" : "brcs"; + operands[1] = gen_rtx_CONST_STRING (VOIDmode, op); + + switch (jump_mode) + { + case 1: return "%1 %2"; + case 2: return "%1 .+2\;rjmp %2"; + case 3: return "%1 .+4\;jmp %2"; + } + + gcc_unreachable(); + return ""; + }) + +;; Same as above but with clobber flavour of addhi3 +(define_peephole ; "*dec-and-branchhi!=-1.d.clobber" + [(parallel [(set (match_operand:HI 0 "d_register_operand" "") + (plus:HI (match_dup 0) + (const_int -1))) + (clobber (scratch:QI))]) + (parallel [(set (cc0) + (compare (match_dup 0) + (const_int -1))) + (clobber (match_operand:QI 1 "d_register_operand" ""))]) + (set (pc) + (if_then_else (eqne (cc0) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + const char *op; + int jump_mode; + CC_STATUS_INIT; + if (test_hard_reg_class (ADDW_REGS, operands[0])) + output_asm_insn ("sbiw %0,1", operands); + else + output_asm_insn ("subi %A0,1" CR_TAB + "sbc %B0,__zero_reg__", operands); + + jump_mode = avr_jump_mode (operands[2], insn); + op = ((EQ == ) ^ (jump_mode == 1)) ? "brcc" : "brcs"; + operands[1] = gen_rtx_CONST_STRING (VOIDmode, op); + + switch (jump_mode) + { + case 1: return "%1 %2"; + case 2: return "%1 .+2\;rjmp %2"; + case 3: return "%1 .+4\;jmp %2"; + } + + gcc_unreachable(); + return ""; + }) + +;; Same as above but with clobber flavour of addhi3 +(define_peephole ; "*dec-and-branchhi!=-1.l.clobber" + [(parallel [(set (match_operand:HI 0 "l_register_operand" "") + (plus:HI (match_dup 0) + (const_int -1))) + (clobber (match_operand:QI 3 "d_register_operand" ""))]) + (parallel [(set (cc0) + (compare (match_dup 0) + (const_int -1))) + (clobber (match_operand:QI 1 "d_register_operand" ""))]) + (set (pc) + (if_then_else (eqne (cc0) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + { + const char *op; + int jump_mode; + CC_STATUS_INIT; + output_asm_insn ("ldi %3,1" CR_TAB + "sub %A0,%3" CR_TAB + "sbc %B0,__zero_reg__", operands); + + jump_mode = avr_jump_mode (operands[2], insn); + op = ((EQ == ) ^ (jump_mode == 1)) ? "brcc" : "brcs"; + operands[1] = gen_rtx_CONST_STRING (VOIDmode, op); + + switch (jump_mode) + { + case 1: return "%1 %2"; + case 2: return "%1 .+2\;rjmp %2"; + case 3: return "%1 .+4\;jmp %2"; + } + + gcc_unreachable(); + return ""; + }) + +(define_peephole ; "*dec-and-branchqi!=-1" + [(set (match_operand:QI 0 "d_register_operand" "") + (plus:QI (match_dup 0) + (const_int -1))) + (set (cc0) + (compare (match_dup 0) + (const_int -1))) + (set (pc) + (if_then_else (eqne (cc0) + (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + { + const char *op; + int jump_mode; + CC_STATUS_INIT; + cc_status.value1 = operands[0]; + cc_status.flags |= CC_OVERFLOW_UNUSABLE; + + output_asm_insn ("subi %A0,1", operands); + + jump_mode = avr_jump_mode (operands[1], insn); + op = ((EQ == ) ^ (jump_mode == 1)) ? "brcc" : "brcs"; + operands[0] = gen_rtx_CONST_STRING (VOIDmode, op); + + switch (jump_mode) + { + case 1: return "%0 %1"; + case 2: return "%0 .+2\;rjmp %1"; + case 3: return "%0 .+4\;jmp %1"; + } + + gcc_unreachable(); + return ""; + }) + + +(define_peephole ; "*cpse.eq" + [(set (cc0) + (compare (match_operand:ALL1 1 "register_operand" "r,r") + (match_operand:ALL1 2 "reg_or_0_operand" "r,Y00"))) + (set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "jump_over_one_insn_p (insn, operands[0])" + "@ + cpse %1,%2 + cpse %1,__zero_reg__") + +;; This peephole avoids code like +;; +;; TST Rn ; *cmpqi +;; BREQ .+2 ; branch +;; RJMP .Lm +;; +;; Notice that the peephole is always shorter than cmpqi + branch. +;; The reason to write it as peephole is that sequences like +;; +;; AND Rm, Rn +;; BRNE .La +;; +;; shall not be superseeded. With a respective combine pattern +;; the latter sequence would be +;; +;; AND Rm, Rn +;; CPSE Rm, __zero_reg__ +;; RJMP .La +;; +;; and thus longer and slower and not easy to be rolled back. + +(define_peephole ; "*cpse.ne" + [(set (cc0) + (compare (match_operand:ALL1 1 "register_operand" "") + (match_operand:ALL1 2 "reg_or_0_operand" ""))) + (set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "!AVR_HAVE_JMP_CALL + || !avr_current_device->errata_skip" + { + if (operands[2] == CONST0_RTX (mode)) + operands[2] = zero_reg_rtx; + + return 3 == avr_jump_mode (operands[0], insn) + ? "cpse %1,%2\;jmp %0" + : "cpse %1,%2\;rjmp %0"; + }) + +;;pppppppppppppppppppppppppppppppppppppppppppppppppppp +;;prologue/epilogue support instructions + +(define_insn "popqi" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (pre_inc:HI (reg:HI REG_SP))))] + "" + "pop %0" + [(set_attr "cc" "none") + (set_attr "length" "1")]) + +;; Enable Interrupts +(define_expand "enable_interrupt" + [(clobber (const_int 0))] + "" + { + rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (mem) = 1; + emit_insn (gen_cli_sei (const1_rtx, mem)); + DONE; + }) + +;; Disable Interrupts +(define_expand "disable_interrupt" + [(clobber (const_int 0))] + "" + { + rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (mem) = 1; + emit_insn (gen_cli_sei (const0_rtx, mem)); + DONE; + }) + +(define_insn "cli_sei" + [(unspec_volatile [(match_operand:QI 0 "const_int_operand" "L,P")] + UNSPECV_ENABLE_IRQS) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))] + "" + "@ + cli + sei" + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +;; Library prologue saves +(define_insn "call_prologue_saves" + [(unspec_volatile:HI [(const_int 0)] UNSPECV_PROLOGUE_SAVES) + (match_operand:HI 0 "immediate_operand" "i,i") + (set (reg:HI REG_SP) + (minus:HI (reg:HI REG_SP) + (match_operand:HI 1 "immediate_operand" "i,i"))) + (use (reg:HI REG_X)) + (clobber (reg:HI REG_Z))] + "" + "ldi r30,lo8(gs(1f)) + ldi r31,hi8(gs(1f)) + %~jmp __prologue_saves__+((18 - %0) * 2) +1:" + [(set_attr "length" "5,6") + (set_attr "cc" "clobber") + (set_attr "isa" "rjmp,jmp")]) + +; epilogue restores using library +(define_insn "epilogue_restores" + [(unspec_volatile:QI [(const_int 0)] UNSPECV_EPILOGUE_RESTORES) + (set (reg:HI REG_Y) + (plus:HI (reg:HI REG_Y) + (match_operand:HI 0 "immediate_operand" "i,i"))) + (set (reg:HI REG_SP) + (plus:HI (reg:HI REG_Y) + (match_dup 0))) + (clobber (reg:QI REG_Z))] + "" + "ldi r30, lo8(%0) + %~jmp __epilogue_restores__ + ((18 - %0) * 2)" + [(set_attr "length" "2,3") + (set_attr "cc" "clobber") + (set_attr "isa" "rjmp,jmp")]) + +; return +(define_insn "return" + [(return)] + "reload_completed && avr_simple_epilogue ()" + "ret" + [(set_attr "cc" "none") + (set_attr "length" "1")]) + +(define_insn "return_from_epilogue" + [(return)] + "reload_completed + && cfun->machine + && !(cfun->machine->is_interrupt || cfun->machine->is_signal) + && !cfun->machine->is_naked" + "ret" + [(set_attr "cc" "none") + (set_attr "length" "1")]) + +(define_insn "return_from_interrupt_epilogue" + [(return)] + "reload_completed + && cfun->machine + && (cfun->machine->is_interrupt || cfun->machine->is_signal) + && !cfun->machine->is_naked" + "reti" + [(set_attr "cc" "none") + (set_attr "length" "1")]) + +(define_insn "return_from_naked_epilogue" + [(return)] + "reload_completed + && cfun->machine + && cfun->machine->is_naked" + "" + [(set_attr "cc" "none") + (set_attr "length" "0")]) + +(define_expand "prologue" + [(const_int 0)] + "" + { + avr_expand_prologue (); + DONE; + }) + +(define_expand "epilogue" + [(const_int 0)] + "" + { + avr_expand_epilogue (false /* sibcall_p */); + DONE; + }) + +(define_expand "sibcall_epilogue" + [(const_int 0)] + "" + { + avr_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) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (clobber (match_scratch:QI 2 "=&d"))] + "" + "ldi %2,lo8(%0) + 1: dec %2 + 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) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (clobber (match_scratch:HI 2 "=&w"))] + "" + "ldi %A2,lo8(%0) + ldi %B2,hi8(%0) + 1: sbiw %A2,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) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (clobber (match_scratch:QI 2 "=&d")) + (clobber (match_scratch:QI 3 "=&d")) + (clobber (match_scratch:QI 4 "=&d"))] + "" + "ldi %2,lo8(%0) + ldi %3,hi8(%0) + ldi %4,hlo8(%0) + 1: subi %2,1 + sbci %3,0 + sbci %4,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) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (clobber (match_scratch:QI 2 "=&d")) + (clobber (match_scratch:QI 3 "=&d")) + (clobber (match_scratch:QI 4 "=&d")) + (clobber (match_scratch:QI 5 "=&d"))] + "" + "ldi %2,lo8(%0) + ldi %3,hi8(%0) + ldi %4,hlo8(%0) + ldi %5,hhi8(%0) + 1: subi %2,1 + sbci %3,0 + sbci %4,0 + sbci %5,0 + brne 1b" + [(set_attr "length" "9") + (set_attr "cc" "clobber")]) + + +;; __builtin_avr_insert_bits + +(define_insn "insert_bits" + [(set (match_operand:QI 0 "register_operand" "=r ,d ,r") + (unspec:QI [(match_operand:SI 1 "const_int_operand" "C0f,Cxf,C0f") + (match_operand:QI 2 "register_operand" "r ,r ,r") + (match_operand:QI 3 "nonmemory_operand" "n ,0 ,0")] + UNSPEC_INSERT_BITS))] + "" + { + return avr_out_insert_bits (operands, NULL); + } + [(set_attr "adjust_len" "insert_bits") + (set_attr "cc" "clobber")]) + + +;; __builtin_avr_flash_segment + +;; Just a helper for the next "official" expander. + +(define_expand "flash_segment1" + [(set (match_operand:QI 0 "register_operand" "") + (subreg:QI (match_operand:PSI 1 "register_operand" "") + 2)) + (set (cc0) + (compare (match_dup 0) + (const_int 0))) + (set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (set (match_dup 0) + (const_int -1))]) + +(define_expand "flash_segment" + [(parallel [(match_operand:QI 0 "register_operand" "") + (match_operand:PSI 1 "register_operand" "")])] + "" + { + rtx label = gen_label_rtx (); + emit (gen_flash_segment1 (operands[0], operands[1], label)); + emit_label (label); + DONE; + }) + +;; Actually, it's too late now to work out address spaces known at compiletime. +;; Best place would be to fold ADDR_SPACE_CONVERT_EXPR in avr_fold_builtin. +;; However, avr_addr_space_convert can add some built-in knowledge for PSTR +;; so that ADDR_SPACE_CONVERT_EXPR in the built-in must not be resolved. + +(define_insn_and_split "*split.flash_segment" + [(set (match_operand:QI 0 "register_operand" "=d") + (subreg:QI (lo_sum:PSI (match_operand:QI 1 "nonmemory_operand" "ri") + (match_operand:HI 2 "register_operand" "r")) + 2))] + "" + { gcc_unreachable(); } + "" + [(set (match_dup 0) + (match_dup 1))]) + + +;; Parity + +;; Postpone expansion of 16-bit parity to libgcc call until after combine for +;; better 8-bit parity recognition. + +(define_expand "parityhi2" + [(parallel [(set (match_operand:HI 0 "register_operand" "") + (parity:HI (match_operand:HI 1 "register_operand" ""))) + (clobber (reg:HI 24))])]) + +(define_insn_and_split "*parityhi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (parity:HI (match_operand:HI 1 "register_operand" "r"))) + (clobber (reg:HI 24))] + "!reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:HI 24) + (match_dup 1)) + (set (reg:HI 24) + (parity:HI (reg:HI 24))) + (set (match_dup 0) + (reg:HI 24))]) + +(define_insn_and_split "*parityqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (parity:HI (match_operand:QI 1 "register_operand" "r"))) + (clobber (reg:HI 24))] + "!reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:QI 24) + (match_dup 1)) + (set (reg:HI 24) + (zero_extend:HI (parity:QI (reg:QI 24)))) + (set (match_dup 0) + (reg:HI 24))]) + +(define_expand "paritysi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (set (reg:HI 24) + (truncate:HI (parity:SI (reg:SI 22)))) + (set (match_dup 2) + (reg:HI 24)) + (set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_dup 2)))] + "" + { + operands[2] = gen_reg_rtx (HImode); + }) + +(define_insn "*parityhi2.libgcc" + [(set (reg:HI 24) + (parity:HI (reg:HI 24)))] + "" + "%~call __parityhi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*parityqihi2.libgcc" + [(set (reg:HI 24) + (zero_extend:HI (parity:QI (reg:QI 24))))] + "" + "%~call __parityqi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*paritysihi2.libgcc" + [(set (reg:HI 24) + (truncate:HI (parity:SI (reg:SI 22))))] + "" + "%~call __paritysi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; Popcount + +(define_expand "popcounthi2" + [(set (reg:HI 24) + (match_operand:HI 1 "register_operand" "")) + (set (reg:HI 24) + (popcount:HI (reg:HI 24))) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 24))] + "" + "") + +(define_expand "popcountsi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (set (reg:HI 24) + (truncate:HI (popcount:SI (reg:SI 22)))) + (set (match_dup 2) + (reg:HI 24)) + (set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_dup 2)))] + "" + { + operands[2] = gen_reg_rtx (HImode); + }) + +(define_insn "*popcounthi2.libgcc" + [(set (reg:HI 24) + (popcount:HI (reg:HI 24)))] + "" + "%~call __popcounthi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*popcountsi2.libgcc" + [(set (reg:HI 24) + (truncate:HI (popcount:SI (reg:SI 22))))] + "" + "%~call __popcountsi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*popcountqi2.libgcc" + [(set (reg:QI 24) + (popcount:QI (reg:QI 24)))] + "" + "%~call __popcountqi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn_and_split "*popcountqihi2.libgcc" + [(set (reg:HI 24) + (zero_extend:HI (popcount:QI (reg:QI 24))))] + "" + "#" + "" + [(set (reg:QI 24) + (popcount:QI (reg:QI 24))) + (set (reg:QI 25) + (const_int 0))]) + +;; Count Leading Zeros + +(define_expand "clzhi2" + [(set (reg:HI 24) + (match_operand:HI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (clz:HI (reg:HI 24))) + (clobber (reg:QI 26))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 24))]) + +(define_expand "clzsi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (truncate:HI (clz:SI (reg:SI 22)))) + (clobber (reg:QI 26))]) + (set (match_dup 2) + (reg:HI 24)) + (set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_dup 2)))] + "" + { + operands[2] = gen_reg_rtx (HImode); + }) + +(define_insn "*clzhi2.libgcc" + [(set (reg:HI 24) + (clz:HI (reg:HI 24))) + (clobber (reg:QI 26))] + "" + "%~call __clzhi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*clzsihi2.libgcc" + [(set (reg:HI 24) + (truncate:HI (clz:SI (reg:SI 22)))) + (clobber (reg:QI 26))] + "" + "%~call __clzsi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; Count Trailing Zeros + +(define_expand "ctzhi2" + [(set (reg:HI 24) + (match_operand:HI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (ctz:HI (reg:HI 24))) + (clobber (reg:QI 26))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 24))]) + +(define_expand "ctzsi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (truncate:HI (ctz:SI (reg:SI 22)))) + (clobber (reg:QI 22)) + (clobber (reg:QI 26))]) + (set (match_dup 2) + (reg:HI 24)) + (set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_dup 2)))] + "" + { + operands[2] = gen_reg_rtx (HImode); + }) + +(define_insn "*ctzhi2.libgcc" + [(set (reg:HI 24) + (ctz:HI (reg:HI 24))) + (clobber (reg:QI 26))] + "" + "%~call __ctzhi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*ctzsihi2.libgcc" + [(set (reg:HI 24) + (truncate:HI (ctz:SI (reg:SI 22)))) + (clobber (reg:QI 22)) + (clobber (reg:QI 26))] + "" + "%~call __ctzsi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; Find First Set + +(define_expand "ffshi2" + [(set (reg:HI 24) + (match_operand:HI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (ffs:HI (reg:HI 24))) + (clobber (reg:QI 26))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 24))]) + +(define_expand "ffssi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (parallel [(set (reg:HI 24) + (truncate:HI (ffs:SI (reg:SI 22)))) + (clobber (reg:QI 22)) + (clobber (reg:QI 26))]) + (set (match_dup 2) + (reg:HI 24)) + (set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_dup 2)))] + "" + { + operands[2] = gen_reg_rtx (HImode); + }) + +(define_insn "*ffshi2.libgcc" + [(set (reg:HI 24) + (ffs:HI (reg:HI 24))) + (clobber (reg:QI 26))] + "" + "%~call __ffshi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +(define_insn "*ffssihi2.libgcc" + [(set (reg:HI 24) + (truncate:HI (ffs:SI (reg:SI 22)))) + (clobber (reg:QI 22)) + (clobber (reg:QI 26))] + "" + "%~call __ffssi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; Copysign + +(define_insn "copysignsf3" + [(set (match_operand:SF 0 "register_operand" "=r") + (unspec:SF [(match_operand:SF 1 "register_operand" "0") + (match_operand:SF 2 "register_operand" "r")] + UNSPEC_COPYSIGN))] + "" + "bst %D2,7\;bld %D0,7" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +;; Swap Bytes (change byte-endianess) + +(define_expand "bswapsi2" + [(set (reg:SI 22) + (match_operand:SI 1 "register_operand" "")) + (set (reg:SI 22) + (bswap:SI (reg:SI 22))) + (set (match_operand:SI 0 "register_operand" "") + (reg:SI 22))]) + +(define_insn "*bswapsi2.libgcc" + [(set (reg:SI 22) + (bswap:SI (reg:SI 22)))] + "" + "%~call __bswapsi2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; CPU instructions + +;; NOP taking 1 or 2 Ticks +(define_expand "nopv" + [(parallel [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "")] + UNSPECV_NOP) + (set (match_dup 1) + (unspec_volatile:BLK [(match_dup 1)] + UNSPECV_MEMORY_BARRIER))])] + "" + { + operands[1] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[1]) = 1; + }) + +(define_insn "*nopv" + [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "P,K")] + UNSPECV_NOP) + (set (match_operand:BLK 1 "" "") + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))] + "" + "@ + nop + rjmp ." + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +;; SLEEP +(define_expand "sleep" + [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP) + (set (match_dup 0) + (unspec_volatile:BLK [(match_dup 0)] + UNSPECV_MEMORY_BARRIER))])] + "" + { + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; + }) + +(define_insn "*sleep" + [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP) + (set (match_operand:BLK 0 "" "") + (unspec_volatile:BLK [(match_dup 0)] UNSPECV_MEMORY_BARRIER))] + "" + "sleep" + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +;; WDR +(define_expand "wdr" + [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_WDR) + (set (match_dup 0) + (unspec_volatile:BLK [(match_dup 0)] + UNSPECV_MEMORY_BARRIER))])] + "" + { + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; + }) + +(define_insn "*wdr" + [(unspec_volatile [(const_int 0)] UNSPECV_WDR) + (set (match_operand:BLK 0 "" "") + (unspec_volatile:BLK [(match_dup 0)] UNSPECV_MEMORY_BARRIER))] + "" + "wdr" + [(set_attr "length" "1") + (set_attr "cc" "none")]) + +;; FMUL +(define_expand "fmul" + [(set (reg:QI 24) + (match_operand:QI 1 "register_operand" "")) + (set (reg:QI 25) + (match_operand:QI 2 "register_operand" "")) + (parallel [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMUL)) + (clobber (reg:HI 24))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 22))] + "" + { + if (AVR_HAVE_MUL) + { + emit_insn (gen_fmul_insn (operand0, operand1, operand2)); + DONE; + } + }) + +(define_insn "fmul_insn" + [(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")]) + +(define_insn "*fmul.call" + [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMUL)) + (clobber (reg:HI 24))] + "!AVR_HAVE_MUL" + "%~call __fmul" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; FMULS +(define_expand "fmuls" + [(set (reg:QI 24) + (match_operand:QI 1 "register_operand" "")) + (set (reg:QI 25) + (match_operand:QI 2 "register_operand" "")) + (parallel [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMULS)) + (clobber (reg:HI 24))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 22))] + "" + { + if (AVR_HAVE_MUL) + { + emit_insn (gen_fmuls_insn (operand0, operand1, operand2)); + DONE; + } + }) + +(define_insn "fmuls_insn" + [(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")]) + +(define_insn "*fmuls.call" + [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMULS)) + (clobber (reg:HI 24))] + "!AVR_HAVE_MUL" + "%~call __fmuls" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; FMULSU +(define_expand "fmulsu" + [(set (reg:QI 24) + (match_operand:QI 1 "register_operand" "")) + (set (reg:QI 25) + (match_operand:QI 2 "register_operand" "")) + (parallel [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMULSU)) + (clobber (reg:HI 24))]) + (set (match_operand:HI 0 "register_operand" "") + (reg:HI 22))] + "" + { + if (AVR_HAVE_MUL) + { + emit_insn (gen_fmulsu_insn (operand0, operand1, operand2)); + DONE; + } + }) + +(define_insn "fmulsu_insn" + [(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")]) + +(define_insn "*fmulsu.call" + [(set (reg:HI 22) + (unspec:HI [(reg:QI 24) + (reg:QI 25)] UNSPEC_FMULSU)) + (clobber (reg:HI 24))] + "!AVR_HAVE_MUL" + "%~call __fmulsu" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + + +;; Some combiner patterns dealing with bits. +;; See PR42210 + +;; Move bit $3.0 into bit $0.$4 +(define_insn "*movbitqi.1-6.a" + [(set (match_operand:QI 0 "register_operand" "=r") + (ior:QI (and:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "single_zero_operand" "n")) + (and:QI (ashift:QI (match_operand:QI 3 "register_operand" "r") + (match_operand:QI 4 "const_0_to_7_operand" "n")) + (match_operand:QI 5 "single_one_operand" "n"))))] + "INTVAL(operands[4]) == exact_log2 (~INTVAL(operands[2]) & GET_MODE_MASK (QImode)) + && INTVAL(operands[4]) == exact_log2 (INTVAL(operands[5]) & GET_MODE_MASK (QImode))" + "bst %3,0\;bld %0,%4" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +;; Move bit $3.0 into bit $0.$4 +;; Variation of above. Unfortunately, there is no canonicalized representation +;; of moving around bits. So what we see here depends on how user writes down +;; bit manipulations. +(define_insn "*movbitqi.1-6.b" + [(set (match_operand:QI 0 "register_operand" "=r") + (ior:QI (and:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "single_zero_operand" "n")) + (ashift:QI (and:QI (match_operand:QI 3 "register_operand" "r") + (const_int 1)) + (match_operand:QI 4 "const_0_to_7_operand" "n"))))] + "INTVAL(operands[4]) == exact_log2 (~INTVAL(operands[2]) & GET_MODE_MASK (QImode))" + "bst %3,0\;bld %0,%4" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +;; Move bit $3.0 into bit $0.0. +;; For bit 0, combiner generates slightly different pattern. +(define_insn "*movbitqi.0" + [(set (match_operand:QI 0 "register_operand" "=r") + (ior:QI (and:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "single_zero_operand" "n")) + (and:QI (match_operand:QI 3 "register_operand" "r") + (const_int 1))))] + "0 == exact_log2 (~INTVAL(operands[2]) & GET_MODE_MASK (QImode))" + "bst %3,0\;bld %0,0" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +;; Move bit $2.0 into bit $0.7. +;; For bit 7, combiner generates slightly different pattern +(define_insn "*movbitqi.7" + [(set (match_operand:QI 0 "register_operand" "=r") + (ior:QI (and:QI (match_operand:QI 1 "register_operand" "0") + (const_int 127)) + (ashift:QI (match_operand:QI 2 "register_operand" "r") + (const_int 7))))] + "" + "bst %2,0\;bld %0,7" + [(set_attr "length" "2") + (set_attr "cc" "none")]) + +;; Combiner transforms above four pattern into ZERO_EXTRACT if it sees MEM +;; and input/output match. We provide a special pattern for this, because +;; in contrast to a IN/BST/BLD/OUT sequence we need less registers and the +;; operation on I/O is atomic. +(define_insn "*insv.io" + [(set (zero_extract:QI (mem:QI (match_operand 0 "low_io_address_operand" "n,n,n")) + (const_int 1) + (match_operand:QI 1 "const_0_to_7_operand" "n,n,n")) + (match_operand:QI 2 "nonmemory_operand" "L,P,r"))] + "" + "@ + cbi %i0,%1 + sbi %i0,%1 + sbrc %2,0\;sbi %i0,%1\;sbrs %2,0\;cbi %i0,%1" + [(set_attr "length" "1,1,4") + (set_attr "cc" "none")]) + +(define_insn "*insv.not.io" + [(set (zero_extract:QI (mem:QI (match_operand 0 "low_io_address_operand" "n")) + (const_int 1) + (match_operand:QI 1 "const_0_to_7_operand" "n")) + (not:QI (match_operand:QI 2 "register_operand" "r")))] + "" + "sbrs %2,0\;sbi %i0,%1\;sbrc %2,0\;cbi %i0,%1" + [(set_attr "length" "4") + (set_attr "cc" "none")]) + +;; The insv expander. +;; We only support 1-bit inserts +(define_expand "insv" + [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "const1_operand" "") ; width + (match_operand:QI 2 "const_0_to_7_operand" "")) ; pos + (match_operand:QI 3 "nonmemory_operand" ""))] + "optimize") + +;; Insert bit $2.0 into $0.$1 +(define_insn "*insv.reg" + [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r,d,d,l,l") + (const_int 1) + (match_operand:QI 1 "const_0_to_7_operand" "n,n,n,n,n")) + (match_operand:QI 2 "nonmemory_operand" "r,L,P,L,P"))] + "" + "@ + bst %2,0\;bld %0,%1 + andi %0,lo8(~(1<<%1)) + ori %0,lo8(1<<%1) + clt\;bld %0,%1 + set\;bld %0,%1" + [(set_attr "length" "2,1,1,2,2") + (set_attr "cc" "none,set_zn,set_zn,none,none")]) + + +;; Some combine patterns that try to fix bad code when a value is composed +;; from byte parts like in PR27663. +;; The patterns give some release but the code still is not optimal, +;; in particular when subreg lowering (-fsplit-wide-types) is turned on. +;; That switch obfuscates things here and in many other places. + +;; "*iorhiqi.byte0" "*iorpsiqi.byte0" "*iorsiqi.byte0" +;; "*xorhiqi.byte0" "*xorpsiqi.byte0" "*xorsiqi.byte0" +(define_insn_and_split "*qi.byte0" + [(set (match_operand:HISI 0 "register_operand" "=r") + (xior:HISI + (zero_extend:HISI (match_operand:QI 1 "register_operand" "r")) + (match_operand:HISI 2 "register_operand" "0")))] + "" + "#" + "reload_completed" + [(set (match_dup 3) + (xior:QI (match_dup 3) + (match_dup 1)))] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], mode, 0); + }) + +;; "*iorhiqi.byte1-3" "*iorpsiqi.byte1-3" "*iorsiqi.byte1-3" +;; "*xorhiqi.byte1-3" "*xorpsiqi.byte1-3" "*xorsiqi.byte1-3" +(define_insn_and_split "*qi.byte1-3" + [(set (match_operand:HISI 0 "register_operand" "=r") + (xior:HISI + (ashift:HISI (zero_extend:HISI (match_operand:QI 1 "register_operand" "r")) + (match_operand:QI 2 "const_8_16_24_operand" "n")) + (match_operand:HISI 3 "register_operand" "0")))] + "INTVAL(operands[2]) < GET_MODE_BITSIZE (mode)" + "#" + "&& reload_completed" + [(set (match_dup 4) + (xior:QI (match_dup 4) + (match_dup 1)))] + { + int byteno = INTVAL(operands[2]) / BITS_PER_UNIT; + operands[4] = simplify_gen_subreg (QImode, operands[0], mode, byteno); + }) + +(define_expand "extzv" + [(set (match_operand:QI 0 "register_operand" "") + (zero_extract:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "const1_operand" "") + (match_operand:QI 3 "const_0_to_7_operand" "")))]) + +(define_insn "*extzv" + [(set (match_operand:QI 0 "register_operand" "=*d,*d,*d,*d,r") + (zero_extract:QI (match_operand:QI 1 "register_operand" "0,r,0,0,r") + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "L,L,P,C04,n")))] + "" + "@ + andi %0,1 + mov %0,%1\;andi %0,1 + lsr %0\;andi %0,1 + swap %0\;andi %0,1 + bst %1,%2\;clr %0\;bld %0,0" + [(set_attr "length" "1,2,2,2,3") + (set_attr "cc" "set_zn,set_zn,set_zn,set_zn,clobber")]) + +(define_insn_and_split "*extzv.qihi1" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extract:HI (match_operand:QI 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n")))] + "" + "#" + "" + [(set (match_dup 3) + (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 2))) + (set (match_dup 4) + (const_int 0))] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], HImode, 0); + operands[4] = simplify_gen_subreg (QImode, operands[0], HImode, 1); + }) + +(define_insn_and_split "*extzv.qihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI + (zero_extract:QI (match_operand:QI 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n"))))] + "" + "#" + "" + [(set (match_dup 3) + (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 2))) + (set (match_dup 4) + (const_int 0))] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], HImode, 0); + operands[4] = simplify_gen_subreg (QImode, operands[0], HImode, 1); + }) + + +;; Fixed-point instructions +(include "avr-fixed.md") + +;; Operations on 64-bit registers +(include "avr-dimode.md") diff --git a/gcc-4.8/gcc/config/avr/avr.opt b/gcc-4.8/gcc/config/avr/avr.opt new file mode 100644 index 000000000..6b2e755ee --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avr.opt @@ -0,0 +1,80 @@ +; Options for the ATMEL AVR port of the compiler. + +; Copyright (C) 2005-2013 Free Software Foundation, Inc. +; +; 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 +; . + +mcall-prologues +Target Report Mask(CALL_PROLOGUES) +Use subroutines for function prologues and epilogues + +mmcu= +Target RejectNegative Joined Var(avr_mcu_index) Init(0) Enum(avr_mcu) +-mmcu=MCU Select the target MCU + +mdeb +Target Report Undocumented Mask(ALL_DEBUG) + +mlog= +Target RejectNegative Joined Undocumented Var(avr_log_details) + +mint8 +Target Report Mask(INT8) +Use an 8-bit 'int' type + +mno-interrupts +Target Report RejectNegative Mask(NO_INTERRUPTS) +Change the stack pointer without disabling interrupts + +mbranch-cost= +Target Report Joined RejectNegative UInteger Var(avr_branch_cost) Init(0) +Set the branch costs for conditional branch instructions. Reasonable values are small, non-negative integers. The default branch cost is 0. + +morder1 +Target Report Undocumented Mask(ORDER_1) + +morder2 +Target Report Undocumented Mask(ORDER_2) + +mtiny-stack +Target Report Mask(TINY_STACK) +Change only the low 8 bits of the stack pointer + +mrelax +Target Report +Relax branches + +mpmem-wrap-around +Target Report +Make the linker relaxation machine assume that a program counter wrap-around occurs. + +maccumulate-args +Target Report Mask(ACCUMULATE_OUTGOING_ARGS) +Accumulate outgoing function arguments and acquire/release the needed stack space for outpoing function arguments in function prologue/epilogue. Without this option, outgoing arguments are pushed before calling a function and popped afterwards. This option can lead to reduced code size for functions that call many functions that get their arguments on the stack like, for example printf. + +mstrict-X +Target Report Var(avr_strict_X) Init(0) +When accessing RAM, use X as imposed by the hardware, i.e. just use pre-decrement, post-increment and indirect addressing with the X register. Without this option, the compiler may assume that there is an addressing mode X+const similar to Y+const and Z+const and emit instructions to emulate such an addressing mode for X. + +;; For rationale behind -msp8 see explanation in avr.h. +msp8 +Target Report RejectNegative Var(avr_sp8) Init(0) +The device has no SPH special function register. This option will be overridden by the compiler driver with the correct setting if presence/absence of SPH can be deduced from -mmcu=MCU. + +Waddr-space-convert +Warning C Report Var(avr_warn_addr_space_convert) Init(0) +Warn if the address space of an address is change. diff --git a/gcc-4.8/gcc/config/avr/avrlibc.h b/gcc-4.8/gcc/config/avr/avrlibc.h new file mode 100644 index 000000000..550913460 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/avrlibc.h @@ -0,0 +1,30 @@ +/* Definitions of target machine for the GNU compiler collection + for Atmel AVR micro controller if configured for AVR-Libc. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Georg-Johann Lay (avr@gjlay.de) + +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 +. */ + +/* AVR-Libc implements functions from libgcc.a in libm.a, see PR54461. */ + +#undef LIBGCC_SPEC +#define LIBGCC_SPEC \ + "%{!mmcu=at90s1*:%{!mmcu=attiny11:%{!mmcu=attiny12:%{!mmcu=attiny15:%{!mmcu=attiny28: -lgcc -lm }}}}}" + +#undef LINK_GCC_C_SEQUENCE_SPEC +#define LINK_GCC_C_SEQUENCE_SPEC \ + "--start-group %G %L --end-group" diff --git a/gcc-4.8/gcc/config/avr/builtins.def b/gcc-4.8/gcc/config/avr/builtins.def new file mode 100644 index 000000000..3562c6932 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/builtins.def @@ -0,0 +1,169 @@ +/* Copyright (C) 2012-2013 Free Software Foundation, Inc. + + 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 + . */ + +/* This file contains the definitions and documentation for the + builtins defined in the AVR part of the GNU compiler. + Befor including this file, define a macro + + DEF_BUILTIN(NAME, N_ARGS, TYPE, ICODE, LIBNAME) + + NAME: `__builtin_avr_name' will be the user-level name of the builtin. + `AVR_BUILTIN_NAME' will be the internal builtin's id. + N_ARGS: Number of input arguments. If special treatment is needed, + set to -1 and handle it by hand, see avr.c:avr_expand_builtin(). + TYPE: A tree node describing the prototype of the built-in. + ICODE: Name of attached insn or expander. If special treatment in avr.c + is needed to expand the built-in, use `nothing'. + LIBNAME: Name of the attached implementation in libgcc which is used if + the builtin cannot be folded away and there is no insn. */ + +/* Mapped to respective instruction. */ + +DEF_BUILTIN (NOP, -1, void_ftype_void, nothing, NULL) +DEF_BUILTIN (SEI, 0, void_ftype_void, enable_interrupt, NULL) +DEF_BUILTIN (CLI, 0, void_ftype_void, disable_interrupt, NULL) +DEF_BUILTIN (WDR, 0, void_ftype_void, wdr, NULL) +DEF_BUILTIN (SLEEP, 0, void_ftype_void, sleep, NULL) + +/* Mapped to respective instruction but might also be folded away + or emit as libgcc call if ISA does not provide the instruction. */ + +DEF_BUILTIN (SWAP, 1, uchar_ftype_uchar, rotlqi3_4, NULL) +DEF_BUILTIN (FMUL, 2, uint_ftype_uchar_uchar, fmul, NULL) +DEF_BUILTIN (FMULS, 2, int_ftype_char_char, fmuls, NULL) +DEF_BUILTIN (FMULSU, 2, int_ftype_char_uchar, fmulsu, NULL) + +/* More complex stuff that cannot be mapped 1:1 to an instruction. */ + +DEF_BUILTIN (DELAY_CYCLES, -1, void_ftype_ulong, nothing, NULL) +DEF_BUILTIN (INSERT_BITS, 3, uchar_ftype_ulong_uchar_uchar, insert_bits, NULL) +DEF_BUILTIN (FLASH_SEGMENT, 1, char_ftype_const_memx_ptr, flash_segment, NULL) + +/* ISO/IEC TR 18037 "Embedded C" + The following builtins are undocumented and used by stdfix.h. */ + +/* 7.18a.6 The fixed-point intrinsic functions. */ + +/* 7.18a.6.2 The fixed-point absolute value functions. */ + +DEF_BUILTIN (ABSHR, 1, hr_ftype_hr, ssabsqq2, "__ssabs_1") +DEF_BUILTIN (ABSR, 1, nr_ftype_nr, ssabshq2, "__ssabs_2") +DEF_BUILTIN (ABSLR, 1, lr_ftype_lr, ssabssq2, "__ssabs_4") +DEF_BUILTIN (ABSLLR, -1, llr_ftype_llr, nothing, "__ssabsdq2") // GCC extension + +DEF_BUILTIN (ABSHK, 1, hk_ftype_hk, ssabsha2, "__ssabs_2") +DEF_BUILTIN (ABSK, 1, nk_ftype_nk, ssabssa2, "__ssabs_4") +DEF_BUILTIN (ABSLK, -1, lk_ftype_lk, nothing, "__ssabsda2") +DEF_BUILTIN (ABSLLK, -1, llk_ftype_llk, nothing, "__ssabsta2") // GCC extension + +/* 7.18a.6.3 The fixed-point round functions. */ + +DEF_BUILTIN (ROUNDHR, 2, hr_ftype_hr_int, roundqq3, "__roundhr") +DEF_BUILTIN (ROUNDR, 2, nr_ftype_nr_int, roundhq3, "__roundr") +DEF_BUILTIN (ROUNDLR, 2, lr_ftype_lr_int, roundsq3, "__roundlr") +DEF_BUILTIN (ROUNDLLR, -1, llr_ftype_llr_int, nothing, "__rounddq3") // GCC extension + +DEF_BUILTIN (ROUNDUHR, 2, uhr_ftype_uhr_int, rounduqq3, "__rounduhr") +DEF_BUILTIN (ROUNDUR, 2, unr_ftype_unr_int, rounduhq3, "__roundur") +DEF_BUILTIN (ROUNDULR, 2, ulr_ftype_ulr_int, roundusq3, "__roundulr") +DEF_BUILTIN (ROUNDULLR, -1, ullr_ftype_ullr_int, nothing, "__roundudq3") // GCC extension + +DEF_BUILTIN (ROUNDHK, 2, hk_ftype_hk_int, roundha3, "__roundhk") +DEF_BUILTIN (ROUNDK, 2, nk_ftype_nk_int, roundsa3, "__roundk") +DEF_BUILTIN (ROUNDLK, -1, lk_ftype_lk_int, nothing, "__roundda3") +DEF_BUILTIN (ROUNDLLK, -1, llk_ftype_llk_int, nothing, "__roundta3") // GCC extension + +DEF_BUILTIN (ROUNDUHK, 2, uhk_ftype_uhk_int, rounduha3, "__rounduhk") +DEF_BUILTIN (ROUNDUK, 2, unk_ftype_unk_int, roundusa3, "__rounduk") +DEF_BUILTIN (ROUNDULK, -1, ulk_ftype_ulk_int, nothing, "__rounduda3") +DEF_BUILTIN (ROUNDULLK, -1, ullk_ftype_ullk_int, nothing, "__rounduta3") // GCC extension + +/* 7.18a.6.4 The fixed-point bit countls functions. */ + +DEF_BUILTIN (COUNTLSHR, -1, int_ftype_hr, nothing, "__countlsqi2") +DEF_BUILTIN (COUNTLSR, -1, int_ftype_nr, nothing, "__countlshi2") +DEF_BUILTIN (COUNTLSLR, -1, int_ftype_lr, nothing, "__countlssi2") +DEF_BUILTIN (COUNTLSLLR, -1, int_ftype_llr, nothing, "__countlsdi2") // GCC extension + +DEF_BUILTIN (COUNTLSUHR, -1, int_ftype_uhr, nothing, "__countlsuqi2") +DEF_BUILTIN (COUNTLSUR, -1, int_ftype_unr, nothing, "__countlsuhi2") +DEF_BUILTIN (COUNTLSULR, -1, int_ftype_ulr, nothing, "__countlsusi2") +DEF_BUILTIN (COUNTLSULLR, -1, int_ftype_ullr, nothing, "__countlsudi2") // GCC extension + +DEF_BUILTIN (COUNTLSHK, -1, int_ftype_hk, nothing, "__countlshi2") +DEF_BUILTIN (COUNTLSK, -1, int_ftype_nk, nothing, "__countlssi2") +DEF_BUILTIN (COUNTLSLK, -1, int_ftype_lk, nothing, "__countlsdi2") +DEF_BUILTIN (COUNTLSLLK, -1, int_ftype_llk, nothing, "__countlsdi2") // GCC extension + +DEF_BUILTIN (COUNTLSUHK, -1, int_ftype_uhk, nothing, "__countlsuhi2") +DEF_BUILTIN (COUNTLSUK, -1, int_ftype_unk, nothing, "__countlsusi2") +DEF_BUILTIN (COUNTLSULK, -1, int_ftype_ulk, nothing, "__countlsudi2") +DEF_BUILTIN (COUNTLSULLK, -1, int_ftype_ullk, nothing, "__countlsudi2") // GCC extension + +/* 7.18a.6.5 The bitwise fixed-point to integer conversion functions. */ + +DEF_BUILTIN (BITSHR, -1, inthr_ftype_hr, nothing, "__ret") +DEF_BUILTIN (BITSR, -1, intnr_ftype_nr, nothing, "__ret") +DEF_BUILTIN (BITSLR, -1, intlr_ftype_lr, nothing, "__ret") +DEF_BUILTIN (BITSLLR, -1, intllr_ftype_llr, nothing, "__ret") // GCC extension + +DEF_BUILTIN (BITSUHR, -1, intuhr_ftype_uhr, nothing, "__ret") +DEF_BUILTIN (BITSUR, -1, intunr_ftype_unr, nothing, "__ret") +DEF_BUILTIN (BITSULR, -1, intulr_ftype_ulr, nothing, "__ret") +DEF_BUILTIN (BITSULLR, -1, intullr_ftype_ullr, nothing, "__ret") // GCC extension + +DEF_BUILTIN (BITSHK, -1, inthk_ftype_hk, nothing, "__ret") +DEF_BUILTIN (BITSK, -1, intnk_ftype_nk, nothing, "__ret") +DEF_BUILTIN (BITSLK, -1, intlk_ftype_lk, nothing, "__ret") +DEF_BUILTIN (BITSLLK, -1, intllk_ftype_llk, nothing, "__ret") // GCC extension + +DEF_BUILTIN (BITSUHK, -1, intuhk_ftype_uhk, nothing, "__ret") +DEF_BUILTIN (BITSUK, -1, intunk_ftype_unk, nothing, "__ret") +DEF_BUILTIN (BITSULK, -1, intulk_ftype_ulk, nothing, "__ret") +DEF_BUILTIN (BITSULLK, -1, intullk_ftype_ullk, nothing, "__ret") // GCC extension + + +/* 7.18a.6.6 The bitwise integer to fixed-point conversion functions. */ + +DEF_BUILTIN ( HRBITS, -1, hr_ftype_inthr, nothing, "__ret") +DEF_BUILTIN ( RBITS, -1, nr_ftype_intnr, nothing, "__ret") +DEF_BUILTIN ( LRBITS, -1, lr_ftype_intlr, nothing, "__ret") +DEF_BUILTIN ( LLRBITS, -1, llr_ftype_intllr, nothing, "__ret") // GCC extension + +DEF_BUILTIN ( UHRBITS, -1, uhr_ftype_intuhr, nothing, "__ret") +DEF_BUILTIN ( URBITS, -1, unr_ftype_intunr, nothing, "__ret") +DEF_BUILTIN ( ULRBITS, -1, ulr_ftype_intulr, nothing, "__ret") +DEF_BUILTIN (ULLRBITS, -1, ullr_ftype_intullr, nothing, "__ret") // GCC extension + +DEF_BUILTIN ( HKBITS, -1, hk_ftype_inthk, nothing, "__ret") +DEF_BUILTIN ( KBITS, -1, nk_ftype_intnk, nothing, "__ret") +DEF_BUILTIN ( LKBITS, -1, lk_ftype_intlk, nothing, "__ret") +DEF_BUILTIN ( LLKBITS, -1, llk_ftype_intllk, nothing, "__ret") // GCC extension + +DEF_BUILTIN ( UHKBITS, -1, uhk_ftype_intuhk, nothing, "__ret") +DEF_BUILTIN ( UKBITS, -1, unk_ftype_intunk, nothing, "__ret") +DEF_BUILTIN ( ULKBITS, -1, ulk_ftype_intulk, nothing, "__ret") +DEF_BUILTIN (ULLKBITS, -1, ullk_ftype_intullk, nothing, "__ret") // GCC extension + +/* Overloaded */ + +/* 7.18a.6.7 Type-generic fixed-point functions. */ + +DEF_BUILTIN (ABSFX, -1, void_ftype_void /* dummy */, nothing, NULL) +DEF_BUILTIN (ROUNDFX, -1, void_ftype_void /* dummy */, nothing, NULL) +DEF_BUILTIN (COUNTLSFX, -1, void_ftype_void /* dummy */, nothing, NULL) diff --git a/gcc-4.8/gcc/config/avr/constraints.md b/gcc-4.8/gcc/config/avr/constraints.md new file mode 100644 index 000000000..76a8ef190 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/constraints.md @@ -0,0 +1,238 @@ +;; Constraint definitions for ATMEL AVR micro controllers. +;; Copyright (C) 2006-2013 Free Software Foundation, Inc. +;; +;; 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 +;; . + +;; Register constraints + +(define_register_constraint "t" "R0_REG" + "Temporary register r0") + +(define_register_constraint "b" "BASE_POINTER_REGS" + "Base pointer registers (r28--r31)") + +(define_register_constraint "e" "POINTER_REGS" + "Pointer registers (r26--r31)") + +(define_register_constraint "w" "ADDW_REGS" + "Registers from r24 to r31. These registers + can be used in @samp{adiw} command.") + +(define_register_constraint "d" "LD_REGS" + "Registers from r16 to r31.") + +(define_register_constraint "l" "NO_LD_REGS" + "Registers from r0 to r15.") + +(define_register_constraint "a" "SIMPLE_LD_REGS" + "Registers from r16 to r23.") + +(define_register_constraint "x" "POINTER_X_REGS" + "Register pair X (r27:r26).") + +(define_register_constraint "y" "POINTER_Y_REGS" + "Register pair Y (r29:r28).") + +(define_register_constraint "z" "POINTER_Z_REGS" + "Register pair Z (r31:r30).") + +(define_register_constraint "q" "STACK_REG" + "Stack pointer register (SPH:SPL).") + +(define_constraint "I" + "Integer constant in the range 0 @dots{} 63." + (and (match_code "const_int") + (match_test "ival >= 0 && ival <= 63"))) + +(define_constraint "J" + "Integer constant in the range -63 @dots{} 0." + (and (match_code "const_int") + (match_test "ival <= 0 && ival >= -63"))) + +(define_constraint "K" + "Integer constant 2." + (and (match_code "const_int") + (match_test "ival == 2"))) + +(define_constraint "L" + "Zero." + (and (match_code "const_int") + (match_test "ival == 0"))) + +(define_constraint "M" + "Integer constant in the range 0 @dots{} 0xff." + (and (match_code "const_int") + (match_test "ival >= 0 && ival <= 0xff"))) + +(define_constraint "N" + "Constant integer @minus{}1." + (and (match_code "const_int") + (match_test "ival == -1"))) + +(define_constraint "O" + "Constant integer 8, 16, or 24." + (and (match_code "const_int") + (match_test "ival == 8 || ival == 16 || ival == 24"))) + +(define_constraint "P" + "Constant integer 1." + (and (match_code "const_int") + (match_test "ival == 1"))) + +(define_constraint "G" + "Constant float 0." + (and (match_code "const_double") + (match_test "op == CONST0_RTX (SFmode)"))) + +(define_memory_constraint "Q" + "A memory address based on Y or Z pointer with displacement." + (and (match_code "mem") + (match_test "extra_constraint_Q (op)"))) + +(define_constraint "Cm2" + "Constant integer @minus{}2." + (and (match_code "const_int") + (match_test "ival == -2"))) + +(define_constraint "C03" + "Constant integer 3." + (and (match_code "const_int") + (match_test "ival == 3"))) + +(define_constraint "C04" + "Constant integer 4." + (and (match_code "const_int") + (match_test "ival == 4"))) + +(define_constraint "C05" + "Constant integer 5." + (and (match_code "const_int") + (match_test "ival == 5"))) + +(define_constraint "C06" + "Constant integer 6." + (and (match_code "const_int") + (match_test "ival == 6"))) + +(define_constraint "C07" + "Constant integer 7." + (and (match_code "const_int") + (match_test "ival == 7"))) + +(define_constraint "Ca2" + "Constant 2-byte integer that allows AND without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<7) | (1<<8))"))) + +(define_constraint "Ca3" + "Constant 3-byte integer that allows AND without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 3, (1<<0) | (1<<7) | (1<<8))"))) + +(define_constraint "Ca4" + "Constant 4-byte integer that allows AND without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<7) | (1<<8))"))) + +(define_constraint "Co2" + "Constant 2-byte integer that allows OR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<1) | (1<<8))"))) + +(define_constraint "Co3" + "Constant 3-byte integer that allows OR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 3, (1<<0) | (1<<1) | (1<<8))"))) + +(define_constraint "Co4" + "Constant 4-byte integer that allows OR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<1) | (1<<8))"))) + +(define_constraint "Cx2" + "Constant 2-byte integer that allows XOR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<8))"))) + +(define_constraint "Cx3" + "Constant 3-byte integer that allows XOR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 3, (1<<0) | (1<<8))"))) + +(define_constraint "Cx4" + "Constant 4-byte integer that allows XOR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<8))"))) + +(define_constraint "Csp" + "Integer constant in the range -6 @dots{} 6." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -6, 6)"))) + +(define_constraint "Cxf" + "32-bit integer constant where at least one nibble is 0xf." + (and (match_code "const_int") + (match_test "avr_has_nibble_0xf (op)"))) + +(define_constraint "C0f" + "32-bit integer constant where no nibble equals 0xf." + (and (match_code "const_int") + (match_test "!avr_has_nibble_0xf (op)"))) + +;; CONST_FIXED is no element of 'n' so cook our own. +;; "i" or "s" would match but because the insn uses iterators that cover +;; INT_MODE, "i" or "s" is not always possible. + +(define_constraint "Ynn" + "Fixed-point constant known at compile time." + (match_code "const_fixed")) + +(define_constraint "Y00" + "Fixed-point or integer constant with bit representation 0x0" + (and (match_code "const_fixed,const_int") + (match_test "op == CONST0_RTX (GET_MODE (op))"))) + +(define_constraint "Y01" + "Fixed-point or integer constant with bit representation 0x1" + (ior (and (match_code "const_fixed") + (match_test "1 == INTVAL (avr_to_int_mode (op))")) + (match_test "satisfies_constraint_P (op)"))) + +(define_constraint "Ym1" + "Fixed-point or integer constant with bit representation -0x1" + (ior (and (match_code "const_fixed") + (match_test "-1 == INTVAL (avr_to_int_mode (op))")) + (match_test "satisfies_constraint_N (op)"))) + +(define_constraint "Y02" + "Fixed-point or integer constant with bit representation 0x2" + (ior (and (match_code "const_fixed") + (match_test "2 == INTVAL (avr_to_int_mode (op))")) + (match_test "satisfies_constraint_K (op)"))) + +(define_constraint "Ym2" + "Fixed-point or integer constant with bit representation -0x2" + (ior (and (match_code "const_fixed") + (match_test "-2 == INTVAL (avr_to_int_mode (op))")) + (match_test "satisfies_constraint_Cm2 (op)"))) + +;; Similar to "IJ" used with ADIW/SBIW, but for CONST_FIXED. + +(define_constraint "YIJ" + "Fixed-point constant from @minus{}0x003f to 0x003f." + (and (match_code "const_fixed") + (match_test "IN_RANGE (INTVAL (avr_to_int_mode (op)), -63, 63)"))) diff --git a/gcc-4.8/gcc/config/avr/driver-avr.c b/gcc-4.8/gcc/config/avr/driver-avr.c new file mode 100644 index 000000000..e859f5f65 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/driver-avr.c @@ -0,0 +1,150 @@ +/* Subroutines for the gcc driver. + Copyright (C) 2009-2013 Free Software Foundation, Inc. + Contributed by Anatoly Sokolov + +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" + +/* Current architecture. */ +const avr_arch_t *avr_current_arch = NULL; + +/* Current device. */ +const avr_mcu_t *avr_current_device = NULL; + +/* Initialize avr_current_arch and avr_current_device variables. */ + +static void +avr_set_current_device (const char *name) +{ + + if (NULL != avr_current_arch) + return; + + for (avr_current_device = avr_mcu_types; avr_current_device->name; + avr_current_device++) + { + if (strcmp (avr_current_device->name, name) == 0) + break; + } + + avr_current_arch = &avr_arch_types[avr_current_device->arch]; +} + +/* Returns command line parameters to pass to as. */ + +const char* +avr_device_to_as (int argc, const char **argv) +{ + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + return concat ("-mmcu=", avr_current_arch->arch_name, + avr_current_device->errata_skip ? "" : " -mno-skip-bug", + NULL); +} + +/* Returns command line parameters to pass to ld. */ + +const char* +avr_device_to_ld (int argc, const char **argv) +{ + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + return concat ("-m ", avr_current_arch->arch_name, NULL); +} + +/* Returns command line parameters that describe start of date section. */ + +const char * +avr_device_to_data_start (int argc, const char **argv) +{ + unsigned long data_section_start; + char data_section_start_str[16]; + + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + if (avr_current_device->data_section_start + == avr_current_arch->default_data_section_start) + return NULL; + + data_section_start = 0x800000 + avr_current_device->data_section_start; + + snprintf (data_section_start_str, sizeof(data_section_start_str) - 1, + "0x%lX", data_section_start); + + return concat ("-Tdata ", data_section_start_str, NULL); +} + +/* Returns command line parameters that describe the device startfile. */ + +const char * +avr_device_to_startfiles (int argc, const char **argv) +{ + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + return concat ("crt", avr_current_device->library_name, ".o%s", NULL); +} + +/* Returns command line parameters that describe the device library. */ + +const char * +avr_device_to_devicelib (int argc, const char **argv) +{ + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + return concat ("-l", avr_current_device->library_name, NULL); +} + +const char* +avr_device_to_sp8 (int argc, const char **argv) +{ + if (0 == argc) + return NULL; + + avr_set_current_device (argv[0]); + + /* Leave "avr2" and "avr25" alone. These two architectures are + the only ones that mix devices with 8-bit SP and 16-bit SP. + -msp8 is set by mmultilib machinery. */ + + if (avr_current_device->macro == NULL + && (avr_current_device->arch == ARCH_AVR2 + || avr_current_device->arch == ARCH_AVR25)) + return ""; + + return avr_current_device->short_sp + ? "-msp8" + : "%. */ + + +/* Overriding some definitions from elfos.h for AVR. */ + +#undef PCC_BITFIELD_TYPE_MATTERS + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#undef MAX_OFILE_ALIGNMENT +#define MAX_OFILE_ALIGNMENT (32768 * 8) + +#undef STRING_LIMIT +#define STRING_LIMIT ((unsigned) 64) + +/* Output alignment 2**1 for jump tables. */ +#undef ASM_OUTPUT_BEFORE_CASE_LABEL +#define ASM_OUTPUT_BEFORE_CASE_LABEL(FILE, PREFIX, NUM, TABLE) \ + ASM_OUTPUT_ALIGN (FILE, 1); + +/* Be conservative in crtstuff.c. */ +#undef INIT_SECTION_ASM_OP +#undef FINI_SECTION_ASM_OP diff --git a/gcc-4.8/gcc/config/avr/gen-avr-mmcu-texi.c b/gcc-4.8/gcc/config/avr/gen-avr-mmcu-texi.c new file mode 100644 index 000000000..55335f98d --- /dev/null +++ b/gcc-4.8/gcc/config/avr/gen-avr-mmcu-texi.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Georg-Johann Lay (avr@gjlay.de) + + 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 +#include + +#define IN_GEN_AVR_MMCU_TEXI + +#include "avr-arch.h" +#include "avr-devices.c" + +static const char* +mcu_name[sizeof avr_mcu_types / sizeof avr_mcu_types[0]]; + +static int letter (char c) +{ + return c >= 'a' && c <= 'z'; +} + +static int digit (char c) +{ + return c >= '0' && c <= '9'; +} + +static int +comparator (const void *va, const void *vb) +{ + const char *a = *(const char* const*) va; + const char *b = *(const char* const*) vb; + + while (*a && *b) + { + /* Make letters smaller than digits so that `atmega16a' follows + `atmega16' without `atmega161' etc. between them. */ + + if (letter (*a) && digit (*b)) + return -1; + + if (digit (*a) && letter (*b)) + return 1; + + if (*a != *b) + return *a - *b; + + a++; + b++; + } + + return *a - *b; +} + +static void +print_mcus (size_t n_mcus) +{ + size_t i; + + if (!n_mcus) + return; + + qsort (mcu_name, n_mcus, sizeof (char*), comparator); + + printf ("@*@var{mcu}@tie{}="); + + for (i = 0; i < n_mcus; i++) + printf (" @code{%s}%s", mcu_name[i], i == n_mcus-1 ? ".\n\n" : ","); +} + +int main (void) +{ + enum avr_arch arch = ARCH_UNKNOWN; + size_t i, n_mcus = 0; + const avr_mcu_t *mcu; + + printf ("@c Copyright (C) 2012-2013 Free Software Foundation, Inc.\n"); + printf ("@c This is part of the GCC manual.\n"); + printf ("@c For copying conditions, see the file " + "gcc/doc/include/fdl.texi.\n\n"); + + printf ("@c This file is generated automatically using\n"); + printf ("@c gcc/config/avr/gen-avr-mmcu-texi.c from:\n"); + printf ("@c gcc/config/avr/avr-arch.h\n"); + printf ("@c gcc/config/avr/avr-devices.c\n"); + printf ("@c gcc/config/avr/avr-mcus.def\n\n"); + + printf ("@c Please do not edit manually.\n\n"); + + printf ("@table @code\n\n"); + + for (mcu = avr_mcu_types; mcu->name; mcu++) + { + if (mcu->macro == NULL) + { + arch = mcu->arch; + + /* Start a new architecture: Flush the MCUs collected so far. */ + + print_mcus (n_mcus); + n_mcus = 0; + + for (i = 0; i < sizeof (avr_texinfo) / sizeof (*avr_texinfo); i++) + if (arch == avr_texinfo[i].arch) + printf ("@item %s\n%s\n", mcu->name, avr_texinfo[i].texinfo); + } + else if (arch == (enum avr_arch) mcu->arch) + { + mcu_name[n_mcus++] = mcu->name; + } + } + + print_mcus (n_mcus); + printf ("@end table\n"); + + return EXIT_SUCCESS; +} diff --git a/gcc-4.8/gcc/config/avr/genmultilib.awk b/gcc-4.8/gcc/config/avr/genmultilib.awk new file mode 100644 index 000000000..14ed6586a --- /dev/null +++ b/gcc-4.8/gcc/config/avr/genmultilib.awk @@ -0,0 +1,216 @@ +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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 +# . + +################################################################## +# +# Transform Core/Device Information from avr-mcus.def to a +# Representation that is understood by GCC's multilib Machinery. +# +# The Script works as a Filter from STDIN to STDOUT. +# +# FORMAT = "Makefile": Generate Makefile Snipet that sets some +# MULTILIB_* Variables as needed. +# +################################################################## + +BEGIN { + FS ="[(, \t]+" + option[""] = "" + tiny_stack[""] = 1 + comment = 1 + n_mcu = 0 + n_cores = 0 + + mtiny[0] = "" + mtiny[1] = "tiny-stack" + option["tiny-stack"] = "msp8" +} + +################################################################## +# Add some Comments to the generated Files and copy-paste +# Copyright Notice from above. +################################################################## + +/^#/ { + if (!comment) + next + else if (comment == 1) + { + if (FORMAT == "Makefile") + { + print "# Auto-generated Makefile Snip" + print "# Generated by : ./gcc/config/avr/genmultilib.awk" + print "# Generated from : ./gcc/config/avr/avr-mcus.def" + print "# Used by : tmake_file from Makefile and genmultilib" + print "" + } + } + + comment = 2; + + print +} + +/^$/ { + # The first empty line stops copy-pasting the GPL comments + # from this file to the generated file. + + comment = 0 +} + +################################################################## +# Run over all AVR_MCU Lines and gather Information: +# cores[] : Enumerates the Cores (avr2, avr25, ...) +# mcu[] : Enumerates the Devices +# tiny_stack[]: Maps Core/Device to 0 (2-byte SP) or 1 (1-byte SP) +# option[] : Maps Core/Device to the mmcu= option to get it +# toCore[] : Maps Device to its Core +################################################################## + +/^AVR_MCU/ { + name = $2 + gsub ("\"", "", name) + + if ($4 == "NULL") + { + core = name + + # avr1 is supported for Assembler only: It gets no multilib + if (core == "avr1") + next + + cores[n_cores] = core + n_cores++ + tiny_stack[core] = 0 + option[core] = "mmcu=" core + + next + } + + # avr1 is supported for Assembler only: Its Devices are ignored + if (core == "avr1") + next + + tiny_stack[name] = $5 + mcu[n_mcu] = name + n_mcu++ + option[name] = "mmcu=" name + toCore[name] = core + + if (tiny_stack[name] == 1) + tiny_stack[core] = 1 +} + +################################################################## +# +# We gathered all the Information, now build/output the following: +# +# awk Variable target Variable FORMAT +# ----------------------------------------------------------- +# m_options <-> MULTILIB_OPTIONS Makefile +# m_dirnames <-> MULTILIB_DIRNAMES " +# m_exceptions <-> MULTILIB_EXCEPTIONS " +# m_matches <-> MULTILIB_MATCHES " +# +################################################################## + +END { + m_options = "\nMULTILIB_OPTIONS = " + m_dirnames = "\nMULTILIB_DIRNAMES =" + m_exceptions = "\nMULTILIB_EXCEPTIONS =" + m_matches = "\nMULTILIB_MATCHES =" + + ############################################################## + # Compose MULTILIB_OPTIONS. This represents the Cross-Product + # (avr2, avr25, ...) x msp8 + + sep = "" + for (c = 0; c < n_cores; c++) + { + m_options = m_options sep option[cores[c]] + sep = "/" + } + + # The ... x msp8 + m_options = m_options " " option[mtiny[1]] + + ############################################################## + # Map Device to its multilib + + for (t = 0; t < n_mcu; t++) + { + core = toCore[mcu[t]] + + line = option[core] ":" option[mcu[t]] + gsub ("=", "?", line) + gsub (":", "=", line) + + m_matches = m_matches " \\\n\t" line + } + + #################################################################### + # Compose MULTILIB_DIRNAMES and MULTILIB_EXEPTIONS + + n_mtiny = 2 + for (t = 0; t < n_mtiny; t++) + for (c = -1; c < n_cores; c++) + { + if (c == -1) + core = "" + else + core = cores[c] + + # The Directory Name for this multilib + + if (core != "" && mtiny[t] != "") + { + mdir = core "/" mtiny[t] + mopt = option[core] "/" option[mtiny[t]] + } + else + { + mdir = core mtiny[t] + mopt = option[core] option[mtiny[t]] + } + + if (core != "" && tiny_stack[core] == 0 && mtiny[t] != "") + { + # There's not a single SP = 8 Devices for this Core: + # Don't build respective multilib + m_exceptions = m_exceptions " \\\n\t" mopt + continue + } + + if (core != "avr2" || mtiny[t] == "") + m_dirnames = m_dirnames " " mdir + } + + ############################################################ + # Output that Stuff + ############################################################ + + if (FORMAT == "Makefile") + { + # Intended Target: ./gcc/config/avr/t-multilib + + print m_options + print m_dirnames + print m_exceptions + print m_matches + } +} diff --git a/gcc-4.8/gcc/config/avr/genopt.sh b/gcc-4.8/gcc/config/avr/genopt.sh new file mode 100755 index 000000000..ef07ad005 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/genopt.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# Generate avr-tables.opt from the list in avr-mcus.def. +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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 +# . + +cat <. + +Enum +Name(avr_mcu) Type(int) +Known MCU names: + +EOF + +awk -F'[(, ]+' 'BEGIN { + value = 0 +} +/^AVR_MCU/ { + name = $2 + gsub("\"", "", name) + print "EnumValue" + print "Enum(avr_mcu) String(" name ") Value(" value ")" + print "" + value++ +}' $1 diff --git a/gcc-4.8/gcc/config/avr/predicates.md b/gcc-4.8/gcc/config/avr/predicates.md new file mode 100644 index 000000000..4983c963b --- /dev/null +++ b/gcc-4.8/gcc/config/avr/predicates.md @@ -0,0 +1,275 @@ +;; Predicate definitions for ATMEL AVR micro controllers. +;; Copyright (C) 2006-2013 Free Software Foundation, Inc. +;; +;; 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 +;; . + +;; Registers from r0 to r15. +(define_predicate "l_register_operand" + (and (match_code "reg") + (match_test "REGNO (op) <= 15"))) + +;; Registers from r16 to r31. +(define_predicate "d_register_operand" + (and (match_code "reg") + (match_test "REGNO (op) >= 16 && REGNO (op) <= 31"))) + +(define_predicate "even_register_operand" + (and (match_code "reg") + (and (match_test "REGNO (op) <= 31") + (match_test "(REGNO (op) & 1) == 0")))) + +(define_predicate "odd_register_operand" + (and (match_code "reg") + (and (match_test "REGNO (op) <= 31") + (match_test "(REGNO (op) & 1) != 0")))) + +;; SP register. +(define_predicate "stack_register_operand" + (and (match_code "reg") + (match_test "REGNO (op) == REG_SP"))) + +;; Return true if OP is a valid address for lower half of I/O space. +(define_predicate "low_io_address_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op) - avr_current_arch->sfr_offset, + 0, 0x1f)"))) + +;; Return true if OP is a valid address for high half of I/O space. +(define_predicate "high_io_address_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op) - avr_current_arch->sfr_offset, + 0x20, 0x3F)"))) + +;; Return true if OP is a valid address of I/O space. +(define_predicate "io_address_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op) - avr_current_arch->sfr_offset, + 0, 0x40 - GET_MODE_SIZE (mode))"))) + +;; Return 1 if OP is a general operand not in flash memory +(define_predicate "nop_general_operand" + (and (match_operand 0 "general_operand") + (match_test "!avr_mem_flash_p (op)"))) + +;; Return 1 if OP is an "ordinary" general operand, i.e. a general +;; operand whose load is not handled by a libgcc call or ELPM. +(define_predicate "nox_general_operand" + (and (match_operand 0 "general_operand") + (not (match_test "avr_load_libgcc_p (op)")) + (not (match_test "avr_mem_memx_p (op)")))) + +;; Return 1 if OP is a memory operand in one of the __flash* address spaces +(define_predicate "flash_operand" + (and (match_operand 0 "memory_operand") + (match_test "Pmode == mode") + (ior (match_test "!MEM_P (op)") + (match_test "avr_mem_flash_p (op)")))) + +;; Return 1 if OP is the zero constant for MODE. +(define_predicate "const0_operand" + (and (match_code "const_int,const_fixed,const_double") + (match_test "op == CONST0_RTX (mode)"))) + +;; Return 1 if OP is the one constant integer for MODE. +(define_predicate "const1_operand" + (and (match_code "const_int") + (match_test "op == CONST1_RTX (mode)"))) + + +;; Return 1 if OP is constant integer 0..7 for MODE. +(define_predicate "const_0_to_7_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 7)"))) + +;; Return 1 if OP is constant integer 2..7 for MODE. +(define_predicate "const_2_to_7_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 2, 7)"))) + +;; Return 1 if OP is constant integer 1..6 for MODE. +(define_predicate "const_1_to_6_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 1, 6)"))) + +;; Return 1 if OP is constant integer 2..6 for MODE. +(define_predicate "const_2_to_6_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 2, 6)"))) + +;; Returns true if OP is either the constant zero or a register. +(define_predicate "reg_or_0_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "const0_operand"))) + +;; Returns 1 if OP is a SYMBOL_REF. +(define_predicate "symbol_ref_operand" + (match_code "symbol_ref")) + +;; Return true if OP is a text segment reference. +;; This is needed for program memory address expressions. +(define_predicate "text_segment_operand" + (match_code "code_label,label_ref,symbol_ref,plus,const") +{ + switch (GET_CODE (op)) + { + case CODE_LABEL: + return true; + case LABEL_REF : + return true; + case SYMBOL_REF : + return SYMBOL_REF_FUNCTION_P (op); + case PLUS : + /* Assume canonical format of symbol + constant. + Fall through. */ + case CONST : + return text_segment_operand (XEXP (op, 0), VOIDmode); + default : + return false; + } +}) + +;; Return true if OP is a constant that contains only one 1 in its +;; binary representation. +(define_predicate "single_one_operand" + (and (match_code "const_int") + (match_test "exact_log2(INTVAL (op) & GET_MODE_MASK (mode)) >= 0"))) + +;; Return true if OP is a constant that contains only one 0 in its +;; binary representation. +(define_predicate "single_zero_operand" + (and (match_code "const_int") + (match_test "exact_log2(~INTVAL (op) & GET_MODE_MASK (mode)) >= 0"))) + +;; +(define_predicate "avr_sp_immediate_operand" + (and (match_code "const_int") + (match_test "satisfies_constraint_Csp (op)"))) + +;; True for EQ & NE +(define_predicate "eqne_operator" + (match_code "eq,ne")) + +;; True for GE & LT +(define_predicate "gelt_operator" + (match_code "ge,lt")) + +;; True for GT, GTU, LE & LEU +(define_predicate "difficult_comparison_operator" + (match_code "gt,gtu,le,leu")) + +;; False for GT, GTU, LE & LEU +(define_predicate "simple_comparison_operator" + (and (match_operand 0 "comparison_operator") + (not (match_code "gt,gtu,le,leu")))) + +;; Return true if OP is a valid call operand. +(define_predicate "call_insn_operand" + (and (match_code "mem") + (ior (match_test "register_operand (XEXP (op, 0), mode)") + (match_test "CONSTANT_ADDRESS_P (XEXP (op, 0))")))) + +;; For some insns we must ensure that no hard register is inserted +;; into their operands because the insns are split and the split +;; involves hard registers. An example are divmod insn that are +;; split to insns that represent implicit library calls. + +;; True for register that is pseudo register. +(define_predicate "pseudo_register_operand" + (and (match_operand 0 "register_operand") + (not (and (match_code "reg") + (match_test "HARD_REGISTER_P (op)"))))) + +;; True for operand that is pseudo register or CONST_INT. +(define_predicate "pseudo_register_or_const_int_operand" + (ior (match_operand 0 "const_int_operand") + (match_operand 0 "pseudo_register_operand"))) + +;; We keep combiner from inserting hard registers into the input of sign- and +;; zero-extends. A hard register in the input operand is not wanted because +;; 32-bit multiply patterns clobber some hard registers and extends with a +;; hard register that overlaps these clobbers won't combine to a widening +;; multiplication. There is no need for combine to propagate or insert +;; hard registers, register allocation can do it just as well. + +;; True for operand that is pseudo register at combine time. +(define_predicate "combine_pseudo_register_operand" + (ior (match_operand 0 "pseudo_register_operand") + (and (match_operand 0 "register_operand") + (match_test "reload_completed || reload_in_progress")))) + +;; Return true if OP is a constant integer that is either +;; 8 or 16 or 24. +(define_predicate "const_8_16_24_operand" + (and (match_code "const_int") + (match_test "8 == INTVAL(op) || 16 == INTVAL(op) || 24 == INTVAL(op)"))) + +;; Unsigned CONST_INT that fits in 8 bits, i.e. 0..255. +(define_predicate "u8_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 255)"))) + +;; Signed CONST_INT that fits in 8 bits, i.e. -128..127. +(define_predicate "s8_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -128, 127)"))) + +;; One-extended CONST_INT that fits in 8 bits, i.e. -256..-1. +(define_predicate "o8_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -256, -1)"))) + +;; Signed CONST_INT that fits in 9 bits, i.e. -256..255. +(define_predicate "s9_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -256, 255)"))) + +(define_predicate "register_or_s9_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "s9_operand"))) + +;; Unsigned CONST_INT that fits in 16 bits, i.e. 0..65536. +(define_predicate "u16_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, (1<<16)-1)"))) + +;; Signed CONST_INT that fits in 16 bits, i.e. -32768..32767. +(define_predicate "s16_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -(1<<15), (1<<15)-1)"))) + +;; One-extended CONST_INT that fits in 16 bits, i.e. -65536..-1. +(define_predicate "o16_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -(1<<16), -1)"))) + +;; Const int, fixed, or double operand +(define_predicate "const_operand" + (ior (match_code "const_fixed") + (match_code "const_double") + (match_operand 0 "const_int_operand"))) + +;; Const int, const fixed, or const double operand +(define_predicate "nonmemory_or_const_operand" + (ior (match_code "const_fixed") + (match_code "const_double") + (match_operand 0 "nonmemory_operand"))) + +;; Immediate, const fixed, or const double operand +(define_predicate "const_or_immediate_operand" + (ior (match_code "const_fixed") + (match_code "const_double") + (match_operand 0 "immediate_operand"))) diff --git a/gcc-4.8/gcc/config/avr/rtems.h b/gcc-4.8/gcc/config/avr/rtems.h new file mode 100644 index 000000000..6621d7950 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/rtems.h @@ -0,0 +1,27 @@ +/* Definitions for rtems targeting a AVR using ELF. + Copyright (C) 2004-2013 Free Software Foundation, Inc. + Contributed by Ralf Corsepius (ralf.corsepius@rtems.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 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 +. */ + +/* Specify predefined symbols in preprocessor. */ + +#define TARGET_OS_CPP_BUILTINS() \ +do { \ + builtin_define ("__rtems__"); \ + builtin_assert ("system=rtems"); \ +} while (0) diff --git a/gcc-4.8/gcc/config/avr/stdfix.h b/gcc-4.8/gcc/config/avr/stdfix.h new file mode 100644 index 000000000..afcacdfb0 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/stdfix.h @@ -0,0 +1,236 @@ +/* Copyright (C) 2007-2013 Free Software Foundation, Inc. + + 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. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* ISO/IEC JTC1 SC22 WG14 N1169 + * Date: 2006-04-04 + * ISO/IEC TR 18037 + * Programming languages - C - Extensions to support embedded processors + */ + +#ifndef _AVRGCC_STDFIX_H +#define _AVRGCC_STDFIX_H + +/* 7.18a.1 Introduction. */ +/* 7.18a.3 Precision macros. */ + +#include + + +#if __SIZEOF_INT__ == 2 + +typedef signed char int_hr_t; +typedef unsigned char uint_uhr_t; + +typedef short int int_r_t; +typedef short unsigned int uint_ur_t; + +typedef short int int_hk_t; +typedef short unsigned int uint_uhk_t; + +typedef long int int_lr_t; +typedef long unsigned int uint_ulr_t; + +typedef long int int_k_t; +typedef long unsigned int uint_uk_t; + +typedef long long int int_llr_t; +typedef long long unsigned int uint_ullr_t; + +typedef long long int int_lk_t; +typedef long long unsigned int uint_ulk_t; + +typedef long long int int_llk_t; +typedef long long unsigned int uint_ullk_t; + +#elif __SIZEOF_INT__ == 1 /* -mint8 */ + +typedef signed char int_hr_t; +typedef unsigned char uint_uhr_t; + +typedef long int int_r_t; +typedef long unsigned int uint_ur_t; + +typedef long int int_hk_t; +typedef long unsigned int uint_uhk_t; + +typedef long long int int_lr_t; +typedef long long unsigned int uint_ulr_t; + +typedef long long int int_k_t; +typedef long long unsigned int uint_uk_t; + +#endif /* __SIZEOF_INT__ == 1, 2 */ + + +/* 7.18a.6 The fixed-point intrinsic functions. */ + + +/* 7.18a.6.2 The fixed-point absolute value functions. */ + +#define abshr __builtin_avr_abshr +#define absr __builtin_avr_absr +#define abslr __builtin_avr_abslr + +#define abshk __builtin_avr_abshk +#define absk __builtin_avr_absk + +#if __SIZEOF_INT__ == 2 + +#define abslk __builtin_avr_abslk +#define absllr __builtin_avr_absllr /* GCC Extension */ +#define absllk __builtin_avr_absllk /* GCC Extension */ + +#endif /* sizeof (int) == 2 */ + + +/* 7.18a.6.3 The fixed-point round functions. */ + +/* The Embedded-C paper specifies results only for rounding points + + 0 < RP < FBIT + + As an extension, the following functions work as expected + with rounding points + + -IBIT < RP < FBIT + + For example, rounding an accum with a rounding point of -1 will + result in an even integer value. */ + +#define roundhr __builtin_avr_roundhr +#define roundr __builtin_avr_roundr +#define roundlr __builtin_avr_roundlr + +#define rounduhr __builtin_avr_rounduhr +#define roundur __builtin_avr_roundur +#define roundulr __builtin_avr_roundulr + +#define roundhk __builtin_avr_roundhk +#define roundk __builtin_avr_roundk + +#define rounduhk __builtin_avr_rounduhk +#define rounduk __builtin_avr_rounduk + +#if __SIZEOF_INT__ == 2 + +#define roundlk __builtin_avr_roundlk +#define roundulk __builtin_avr_roundulk +#define roundllr __builtin_avr_roundllr /* GCC Extension */ +#define roundullr __builtin_avr_roundullr /* GCC Extension */ +#define roundllk __builtin_avr_roundllk /* GCC Extension */ +#define roundullk __builtin_avr_roundullk /* GCC Extension */ + +#endif /* sizeof (int) == 2 */ + + +/* 7.18a.6.4 The fixed-point bit countls functions. */ + +#define countlshr __builtin_avr_countlshr +#define countlsr __builtin_avr_countlsr +#define countlslr __builtin_avr_countlslr + +#define countlsuhr __builtin_avr_countlsuhr +#define countlsur __builtin_avr_countlsur +#define countlsulr __builtin_avr_countlsulr + +#define countlshk __builtin_avr_countlshk +#define countlsk __builtin_avr_countlsk + +#define countlsuhk __builtin_avr_countlsuhk +#define countlsuk __builtin_avr_countlsuk + +#if __SIZEOF_INT__ == 2 + +#define countlslk __builtin_avr_countlslk +#define countlsulk __builtin_avr_countlsulk +#define countlsllr __builtin_avr_countlsllr /* GCC Extension */ +#define countlsullr __builtin_avr_countlsullr /* GCC Extension */ +#define countlsllk __builtin_avr_countlsllk /* GCC Extension */ +#define countlsullk __builtin_avr_countlsullk /* GCC Extension */ + +#endif /* sizeof (int) == 2 */ + + +/* 7.18a.6.5 The bitwise fixed-point to integer conversion functions. */ + +#define bitshr __builtin_avr_bitshr +#define bitsr __builtin_avr_bitsr +#define bitslr __builtin_avr_bitslr + +#define bitsuhr __builtin_avr_bitsuhr +#define bitsur __builtin_avr_bitsur +#define bitsulr __builtin_avr_bitsulr + +#define bitshk __builtin_avr_bitshk +#define bitsk __builtin_avr_bitsk + +#define bitsuhk __builtin_avr_bitsuhk +#define bitsuk __builtin_avr_bitsuk + +#if __SIZEOF_INT__ == 2 + +#define bitslk __builtin_avr_bitslk +#define bitsulk __builtin_avr_bitsulk +#define bitsllr __builtin_avr_bitsllr /* GCC Extension */ +#define bitsullr __builtin_avr_bitsullr /* GCC Extension */ +#define bitsllk __builtin_avr_bitsllk /* GCC Extension */ +#define bitsullk __builtin_avr_bitsullk /* GCC Extension */ + +#endif /* sizeof (int) == 2 */ + + +/* 7.18a.6.6 The bitwise integer to fixed-point conversion functions. */ + +#define hrbits __builtin_avr_hrbits +#define rbits __builtin_avr_rbits +#define lrbits __builtin_avr_lrbits + +#define uhrbits __builtin_avr_uhrbits +#define urbits __builtin_avr_urbits +#define ulrbits __builtin_avr_ulrbits + +#define hkbits __builtin_avr_hkbits +#define kbits __builtin_avr_kbits + +#define uhkbits __builtin_avr_uhkbits +#define ukbits __builtin_avr_ukbits + +#if __SIZEOF_INT__ == 2 + +#define lkbits __builtin_avr_lkbits +#define ulkbits __builtin_avr_ulkbits +#define llrbits __builtin_avr_llrbits /* GCC Extension */ +#define ullrbits __builtin_avr_ullrbits /* GCC Extension */ +#define llkbits __builtin_avr_llkbits /* GCC Extension */ +#define ullkbits __builtin_avr_ullkbits /* GCC Extension */ + +#endif /* sizeof (int) == 2 */ + + +/* 7.18a.6.7 Type-generic fixed-point functions. */ + +#define absfx __builtin_avr_absfx +#define roundfx __builtin_avr_roundfx +#define countlsfx __builtin_avr_countlsfx + +#endif /* _AVRGCC_STDFIX_H */ diff --git a/gcc-4.8/gcc/config/avr/t-avr b/gcc-4.8/gcc/config/avr/t-avr new file mode 100644 index 000000000..e76a7606d --- /dev/null +++ b/gcc-4.8/gcc/config/avr/t-avr @@ -0,0 +1,83 @@ +# Copyright (C) 2000-2013 Free Software Foundation, Inc. +# +# 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 +# . + +driver-avr.o: $(srcdir)/config/avr/driver-avr.c \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +avr-devices.o: $(srcdir)/config/avr/avr-devices.c \ + $(srcdir)/config/avr/avr-mcus.def \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +avr-c.o: $(srcdir)/config/avr/avr-c.c \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(C_COMMON_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +avr-log.o: $(srcdir)/config/avr/avr-log.c \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(INPUT_H) dumpfile.h + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +avr.o avr-c.o: $(srcdir)/config/avr/builtins.def + +# This overrides stdfix.h from USER_H which we supply and include +# in our own stdint.h as stdint-gcc.h. + +EXTRA_HEADERS = $(srcdir)/config/avr/stdfix.h \ + stdfix-gcc.h + +stdfix-gcc.h: $(srcdir)/ginclude/stdfix.h + -cp $< $@ + +# Files and Variables auto-generated from avr-mcus.def + +AVR_MCUS = $(srcdir)/config/avr/avr-mcus.def + +# Run `avr-mcus' after you changed or added devices in avr-mcus.def + +.PHONY: avr-mcus + +avr-mcus: $(srcdir)/config/avr/t-multilib \ + $(srcdir)/config/avr/avr-tables.opt \ + $(srcdir)/doc/avr-mmcu.texi ; @true + +# Make sure that -mmcu= is supported for devices from avr-mcus.def and +# all -mmcu= values are displayed on the help screen +$(srcdir)/config/avr/avr-tables.opt: $(srcdir)/config/avr/genopt.sh $(AVR_MCUS) + $(SHELL) $< $(AVR_MCUS) > $@ + +# Make sure that -mmcu= support is in sync with -mmcu= documentation. +gen-avr-mmcu-texi$(build_exeext): $(srcdir)/config/avr/gen-avr-mmcu-texi.c \ + $(AVR_MCUS) $(srcdir)/config/avr/avr-devices.c \ + $(srcdir)/config/avr/avr-arch.h + $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $< -o $@ + +$(srcdir)/doc/avr-mmcu.texi: gen-avr-mmcu-texi$(build_exeext) + $(RUN_GEN) ./$< > $@ + +# Map -mmcu= to the right multilib variant +# MULTILIB_OPTIONS +# MULTILIB_DIRNAMES +# MULTILIB_EXCEPTIONS +# MULTILIB_MATCHES + +s-mlib: $(srcdir)/config/avr/t-multilib + +$(srcdir)/config/avr/t-multilib: $(srcdir)/config/avr/genmultilib.awk \ + $(AVR_MCUS) + $(AWK) -f $< -v FORMAT=Makefile $< $(AVR_MCUS) > $@ diff --git a/gcc-4.8/gcc/config/avr/t-multilib b/gcc-4.8/gcc/config/avr/t-multilib new file mode 100644 index 000000000..6b1db6092 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/t-multilib @@ -0,0 +1,283 @@ +# Auto-generated Makefile Snip +# Generated by : ./gcc/config/avr/genmultilib.awk +# Generated from : ./gcc/config/avr/avr-mcus.def +# Used by : tmake_file from Makefile and genmultilib + +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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 +# . + +MULTILIB_OPTIONS = mmcu=avr2/mmcu=avr25/mmcu=avr3/mmcu=avr31/mmcu=avr35/mmcu=avr4/mmcu=avr5/mmcu=avr51/mmcu=avr6/mmcu=avrxmega2/mmcu=avrxmega4/mmcu=avrxmega5/mmcu=avrxmega6/mmcu=avrxmega7 msp8 + +MULTILIB_DIRNAMES = avr2 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack avr25/tiny-stack + +MULTILIB_EXCEPTIONS = \ + mmcu=avr3/msp8 \ + mmcu=avr31/msp8 \ + mmcu=avr35/msp8 \ + mmcu=avr4/msp8 \ + mmcu=avr5/msp8 \ + mmcu=avr51/msp8 \ + mmcu=avr6/msp8 \ + mmcu=avrxmega2/msp8 \ + mmcu=avrxmega4/msp8 \ + mmcu=avrxmega5/msp8 \ + mmcu=avrxmega6/msp8 \ + mmcu=avrxmega7/msp8 + +MULTILIB_MATCHES = \ + mmcu?avr2=mmcu?at90s2313 \ + mmcu?avr2=mmcu?at90s2323 \ + mmcu?avr2=mmcu?at90s2333 \ + mmcu?avr2=mmcu?at90s2343 \ + mmcu?avr2=mmcu?attiny22 \ + mmcu?avr2=mmcu?attiny26 \ + mmcu?avr2=mmcu?at90s4414 \ + mmcu?avr2=mmcu?at90s4433 \ + mmcu?avr2=mmcu?at90s4434 \ + mmcu?avr2=mmcu?at90s8515 \ + mmcu?avr2=mmcu?at90c8534 \ + mmcu?avr2=mmcu?at90s8535 \ + mmcu?avr25=mmcu?ata6289 \ + mmcu?avr25=mmcu?ata5272 \ + mmcu?avr25=mmcu?attiny13 \ + mmcu?avr25=mmcu?attiny13a \ + mmcu?avr25=mmcu?attiny2313 \ + mmcu?avr25=mmcu?attiny2313a \ + mmcu?avr25=mmcu?attiny24 \ + mmcu?avr25=mmcu?attiny24a \ + mmcu?avr25=mmcu?attiny4313 \ + mmcu?avr25=mmcu?attiny44 \ + mmcu?avr25=mmcu?attiny44a \ + mmcu?avr25=mmcu?attiny84 \ + mmcu?avr25=mmcu?attiny84a \ + mmcu?avr25=mmcu?attiny25 \ + mmcu?avr25=mmcu?attiny45 \ + mmcu?avr25=mmcu?attiny85 \ + mmcu?avr25=mmcu?attiny261 \ + mmcu?avr25=mmcu?attiny261a \ + mmcu?avr25=mmcu?attiny461 \ + mmcu?avr25=mmcu?attiny461a \ + mmcu?avr25=mmcu?attiny861 \ + mmcu?avr25=mmcu?attiny861a \ + mmcu?avr25=mmcu?attiny43u \ + mmcu?avr25=mmcu?attiny87 \ + mmcu?avr25=mmcu?attiny48 \ + mmcu?avr25=mmcu?attiny88 \ + mmcu?avr25=mmcu?at86rf401 \ + mmcu?avr3=mmcu?at43usb355 \ + mmcu?avr3=mmcu?at76c711 \ + mmcu?avr31=mmcu?atmega103 \ + mmcu?avr31=mmcu?at43usb320 \ + mmcu?avr35=mmcu?ata5505 \ + mmcu?avr35=mmcu?at90usb82 \ + mmcu?avr35=mmcu?at90usb162 \ + mmcu?avr35=mmcu?atmega8u2 \ + mmcu?avr35=mmcu?atmega16u2 \ + mmcu?avr35=mmcu?atmega32u2 \ + mmcu?avr35=mmcu?attiny167 \ + mmcu?avr35=mmcu?attiny1634 \ + mmcu?avr4=mmcu?ata6285 \ + mmcu?avr4=mmcu?ata6286 \ + mmcu?avr4=mmcu?atmega8 \ + mmcu?avr4=mmcu?atmega8a \ + mmcu?avr4=mmcu?atmega48 \ + mmcu?avr4=mmcu?atmega48a \ + mmcu?avr4=mmcu?atmega48p \ + mmcu?avr4=mmcu?atmega48pa \ + mmcu?avr4=mmcu?atmega88 \ + mmcu?avr4=mmcu?atmega88a \ + mmcu?avr4=mmcu?atmega88p \ + mmcu?avr4=mmcu?atmega88pa \ + mmcu?avr4=mmcu?atmega8515 \ + mmcu?avr4=mmcu?atmega8535 \ + mmcu?avr4=mmcu?atmega8hva \ + mmcu?avr4=mmcu?at90pwm1 \ + mmcu?avr4=mmcu?at90pwm2 \ + mmcu?avr4=mmcu?at90pwm2b \ + mmcu?avr4=mmcu?at90pwm3 \ + mmcu?avr4=mmcu?at90pwm3b \ + mmcu?avr4=mmcu?at90pwm81 \ + mmcu?avr5=mmcu?ata5790 \ + mmcu?avr5=mmcu?ata5790n \ + mmcu?avr5=mmcu?ata5795 \ + mmcu?avr5=mmcu?atmega16 \ + mmcu?avr5=mmcu?atmega16a \ + mmcu?avr5=mmcu?atmega161 \ + mmcu?avr5=mmcu?atmega162 \ + mmcu?avr5=mmcu?atmega163 \ + mmcu?avr5=mmcu?atmega164a \ + mmcu?avr5=mmcu?atmega164p \ + mmcu?avr5=mmcu?atmega164pa \ + mmcu?avr5=mmcu?atmega165 \ + mmcu?avr5=mmcu?atmega165a \ + mmcu?avr5=mmcu?atmega165p \ + mmcu?avr5=mmcu?atmega165pa \ + mmcu?avr5=mmcu?atmega168 \ + mmcu?avr5=mmcu?atmega168a \ + mmcu?avr5=mmcu?atmega168p \ + mmcu?avr5=mmcu?atmega168pa \ + mmcu?avr5=mmcu?atmega169 \ + mmcu?avr5=mmcu?atmega169a \ + mmcu?avr5=mmcu?atmega169p \ + mmcu?avr5=mmcu?atmega169pa \ + mmcu?avr5=mmcu?atmega16hva \ + mmcu?avr5=mmcu?atmega16hvb \ + mmcu?avr5=mmcu?atmega16hvbrevb \ + mmcu?avr5=mmcu?atmega16m1 \ + mmcu?avr5=mmcu?atmega16u4 \ + mmcu?avr5=mmcu?atmega26hvg \ + mmcu?avr5=mmcu?atmega32a \ + mmcu?avr5=mmcu?atmega32 \ + mmcu?avr5=mmcu?atmega32a \ + mmcu?avr5=mmcu?atmega323 \ + mmcu?avr5=mmcu?atmega324a \ + mmcu?avr5=mmcu?atmega324p \ + mmcu?avr5=mmcu?atmega324pa \ + mmcu?avr5=mmcu?atmega325 \ + mmcu?avr5=mmcu?atmega325a \ + mmcu?avr5=mmcu?atmega325p \ + mmcu?avr5=mmcu?atmega3250 \ + mmcu?avr5=mmcu?atmega3250a \ + mmcu?avr5=mmcu?atmega3250p \ + mmcu?avr5=mmcu?atmega3250pa \ + mmcu?avr5=mmcu?atmega328 \ + mmcu?avr5=mmcu?atmega328p \ + mmcu?avr5=mmcu?atmega329 \ + mmcu?avr5=mmcu?atmega329a \ + mmcu?avr5=mmcu?atmega329p \ + mmcu?avr5=mmcu?atmega329pa \ + mmcu?avr5=mmcu?atmega3290 \ + mmcu?avr5=mmcu?atmega3290a \ + mmcu?avr5=mmcu?atmega3290p \ + mmcu?avr5=mmcu?atmega3290pa \ + mmcu?avr5=mmcu?atmega32c1 \ + mmcu?avr5=mmcu?atmega32m1 \ + mmcu?avr5=mmcu?atmega32u4 \ + mmcu?avr5=mmcu?atmega32u6 \ + mmcu?avr5=mmcu?atmega406 \ + mmcu?avr5=mmcu?atmega64 \ + mmcu?avr5=mmcu?atmega64a \ + mmcu?avr5=mmcu?atmega640 \ + mmcu?avr5=mmcu?atmega644 \ + mmcu?avr5=mmcu?atmega644a \ + mmcu?avr5=mmcu?atmega644p \ + mmcu?avr5=mmcu?atmega644pa \ + mmcu?avr5=mmcu?atmega645 \ + mmcu?avr5=mmcu?atmega645a \ + mmcu?avr5=mmcu?atmega645p \ + mmcu?avr5=mmcu?atmega6450 \ + mmcu?avr5=mmcu?atmega6450a \ + mmcu?avr5=mmcu?atmega6450p \ + mmcu?avr5=mmcu?atmega649 \ + mmcu?avr5=mmcu?atmega649a \ + mmcu?avr5=mmcu?atmega649p \ + mmcu?avr5=mmcu?atmega6490 \ + mmcu?avr5=mmcu?atmega16hva \ + mmcu?avr5=mmcu?atmega16hva2 \ + mmcu?avr5=mmcu?atmega16hvb \ + mmcu?avr5=mmcu?atmega32hvb \ + mmcu?avr5=mmcu?atmega6490a \ + mmcu?avr5=mmcu?atmega6490p \ + mmcu?avr5=mmcu?atmega64c1 \ + mmcu?avr5=mmcu?atmega64m1 \ + mmcu?avr5=mmcu?atmega64hve \ + mmcu?avr5=mmcu?atmega64rfa2 \ + mmcu?avr5=mmcu?atmega64rfr2 \ + mmcu?avr5=mmcu?atmega32hvb \ + mmcu?avr5=mmcu?atmega32hvbrevb \ + mmcu?avr5=mmcu?atmega16hva2 \ + mmcu?avr5=mmcu?atmega48hvf \ + mmcu?avr5=mmcu?at90can32 \ + mmcu?avr5=mmcu?at90can64 \ + mmcu?avr5=mmcu?at90pwm161 \ + mmcu?avr5=mmcu?at90pwm216 \ + mmcu?avr5=mmcu?at90pwm316 \ + mmcu?avr5=mmcu?atmega32c1 \ + mmcu?avr5=mmcu?atmega64c1 \ + mmcu?avr5=mmcu?atmega16m1 \ + mmcu?avr5=mmcu?atmega32m1 \ + mmcu?avr5=mmcu?atmega64m1 \ + mmcu?avr5=mmcu?atmega16u4 \ + mmcu?avr5=mmcu?atmega32u4 \ + mmcu?avr5=mmcu?atmega32u6 \ + mmcu?avr5=mmcu?at90scr100 \ + mmcu?avr5=mmcu?at90usb646 \ + mmcu?avr5=mmcu?at90usb647 \ + mmcu?avr5=mmcu?at94k \ + mmcu?avr5=mmcu?m3000 \ + mmcu?avr51=mmcu?atmega128 \ + mmcu?avr51=mmcu?atmega128a \ + mmcu?avr51=mmcu?atmega1280 \ + mmcu?avr51=mmcu?atmega1281 \ + mmcu?avr51=mmcu?atmega1284 \ + mmcu?avr51=mmcu?atmega1284p \ + mmcu?avr51=mmcu?atmega128rfa1 \ + mmcu?avr51=mmcu?at90can128 \ + mmcu?avr51=mmcu?at90usb1286 \ + mmcu?avr51=mmcu?at90usb1287 \ + mmcu?avr6=mmcu?atmega2560 \ + mmcu?avr6=mmcu?atmega2561 \ + mmcu?avrxmega2=mmcu?atxmega16a4 \ + mmcu?avrxmega2=mmcu?atxmega16d4 \ + mmcu?avrxmega2=mmcu?atxmega16x1 \ + mmcu?avrxmega2=mmcu?atxmega32a4 \ + mmcu?avrxmega2=mmcu?atxmega32d4 \ + mmcu?avrxmega2=mmcu?atxmega32x1 \ + mmcu?avrxmega2=mmcu?atmxt112sl \ + mmcu?avrxmega2=mmcu?atmxt224 \ + mmcu?avrxmega2=mmcu?atmxt224e \ + mmcu?avrxmega2=mmcu?atmxt336s \ + mmcu?avrxmega2=mmcu?atxmega16a4u \ + mmcu?avrxmega2=mmcu?atxmega16c4 \ + mmcu?avrxmega2=mmcu?atxmega32a4u \ + mmcu?avrxmega2=mmcu?atxmega32c4 \ + mmcu?avrxmega2=mmcu?atxmega32e5 \ + mmcu?avrxmega4=mmcu?atxmega64a3 \ + mmcu?avrxmega4=mmcu?atxmega64d3 \ + mmcu?avrxmega4=mmcu?atxmega64a3u \ + mmcu?avrxmega4=mmcu?atxmega64a4u \ + mmcu?avrxmega4=mmcu?atxmega64b1 \ + mmcu?avrxmega4=mmcu?atxmega64b3 \ + mmcu?avrxmega4=mmcu?atxmega64c3 \ + mmcu?avrxmega4=mmcu?atxmega64d4 \ + mmcu?avrxmega5=mmcu?atxmega64a1 \ + mmcu?avrxmega5=mmcu?atxmega64a1u \ + mmcu?avrxmega6=mmcu?atxmega128a3 \ + mmcu?avrxmega6=mmcu?atxmega128d3 \ + mmcu?avrxmega6=mmcu?atxmega192a3 \ + mmcu?avrxmega6=mmcu?atxmega192d3 \ + mmcu?avrxmega6=mmcu?atxmega256a3 \ + mmcu?avrxmega6=mmcu?atxmega256a3b \ + mmcu?avrxmega6=mmcu?atxmega256a3bu \ + mmcu?avrxmega6=mmcu?atxmega256d3 \ + mmcu?avrxmega6=mmcu?atxmega128a3u \ + mmcu?avrxmega6=mmcu?atxmega128b1 \ + mmcu?avrxmega6=mmcu?atxmega128b3 \ + mmcu?avrxmega6=mmcu?atxmega128c3 \ + mmcu?avrxmega6=mmcu?atxmega128d4 \ + mmcu?avrxmega6=mmcu?atmxt540s \ + mmcu?avrxmega6=mmcu?atmxt540sreva \ + mmcu?avrxmega6=mmcu?atxmega192a3u \ + mmcu?avrxmega6=mmcu?atxmega192c3 \ + mmcu?avrxmega6=mmcu?atxmega256a3u \ + mmcu?avrxmega6=mmcu?atxmega256c3 \ + mmcu?avrxmega6=mmcu?atxmega384c3 \ + mmcu?avrxmega6=mmcu?atxmega384d3 \ + mmcu?avrxmega7=mmcu?atxmega128a1 \ + mmcu?avrxmega7=mmcu?atxmega128a1u \ + mmcu?avrxmega7=mmcu?atxmega128a4u diff --git a/gcc-4.8/gcc/config/avr/t-rtems b/gcc-4.8/gcc/config/avr/t-rtems new file mode 100644 index 000000000..a3ef8bd80 --- /dev/null +++ b/gcc-4.8/gcc/config/avr/t-rtems @@ -0,0 +1,3 @@ +# Multilibs for avr RTEMS targets. + +# ATM, this is just a stub -- cgit v1.2.3