diff --git a/.cproject b/.cproject new file mode 100755 index 000000000000..6db4cbe2447e --- /dev/null +++ .cproject @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 000000000000..500c9ee08dca --- /dev/null +++ .project @@ -0,0 +1,34 @@ + + + gcc-6 + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + build-gcc + 2 + D:/develop/workspaces/c1/amigaos-cross-toolchain/.build-m68k/build/gcc-6 + + + diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml new file mode 100755 index 000000000000..caef162d88d1 --- /dev/null +++ .settings/language.settings.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.cdt.codan.core.prefs b/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100755 index 000000000000..b5248c620107 --- /dev/null +++ .settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,71 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.codan.checkers.errnoreturn=Warning +org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false} +org.eclipse.cdt.codan.checkers.errreturnvalue=Error +org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"} +org.eclipse.cdt.codan.checkers.nocommentinside=-Error +org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"} +org.eclipse.cdt.codan.checkers.nolinecomment=-Error +org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"} +org.eclipse.cdt.codan.checkers.noreturn=Error +org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false} +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"} +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"} +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"} +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"} +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} +org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning +org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"} +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true} +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"} +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"} +org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error +org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"} +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"} +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"} +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"} +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"} +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"} +org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error +org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"} +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"} +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"} +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"} +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"} +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false} +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false} +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"} +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")} +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"} diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs new file mode 100755 index 000000000000..8ec9fe72ca59 --- /dev/null +++ .settings/org.eclipse.cdt.core.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.452878522/PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.452878522/PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.452878522/PATH/value=C\:\\WINDOWS\\system32;C\:\\WINDOWS;C\:\\Program Files\\SlikSvn\\bin;C\:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0;c\:\\cygwin\\bin;D\:\\develop\\workspaces\\c1\\amigaos-cross-toolchain\\m68k-amigaos\\bin +environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.452878522/append=true +environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.452878522/appendContributed=true diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs new file mode 100755 index 000000000000..12511e62a174 --- /dev/null +++ .settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,5 @@ +content-types/enabled=true +content-types/org.eclipse.cdt.core.cHeader/file-extensions=def +content-types/org.eclipse.cdt.core.cxxHeader/file-extensions=h +content-types/org.eclipse.cdt.core.cxxSource/file-extensions=c +eclipse.preferences.version=1 diff --git a/config.sub b/config.sub index 41146e11c6c9..35247fe0c474 100755 --- config.sub +++ config.sub @@ -2,7 +2,7 @@ # Configuration validation subroutine script. # Copyright 1992-2016 Free Software Foundation, Inc. -timestamp='2016-01-01' +timestamp='2017-04-21' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -500,7 +500,7 @@ case $basic_machine in amiga | amiga-*) basic_machine=m68k-unknown ;; - amigaos | amigados) + amigaos | amigaosvasm | amigados) basic_machine=m68k-unknown os=-amigaos ;; @@ -1380,7 +1380,7 @@ case $os in | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rt* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 51e2bc86e9a4..4aedf54bab12 100644 --- gcc/Makefile.in +++ gcc/Makefile.in @@ -1199,6 +1199,7 @@ OBJS = \ auto-inc-dec.o \ auto-profile.o \ bb-reorder.o \ + bbb-opts.o \ bitmap.o \ bt-load.o \ builtins.o \ @@ -1986,7 +1987,7 @@ gcc-nm.c: gcc-ar.c cp $^ $@ COLLECT2_OBJS = collect2.o collect2-aix.o tlink.o vec.o ggc-none.o \ - collect-utils.o file-find.o hash-table.o + collect-utils.o file-find.o hash-table.o $(EXTRA_COLLECT2_OBJS) COLLECT2_LIBS = @COLLECT2_LIBS@ collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS) # Don't try modifying collect2 (aka ld) in place--it might be linking this. @@ -3270,7 +3271,7 @@ endif install-strip: install # Handle cpp installation. -install-cpp: installdirs cpp$(exeext) +install-cpp: installdirs cpp$(exeext) all.cross -if test "$(enable_as_accelerator)" != "yes" ; then \ rm -f $(DESTDIR)$(bindir)/$(CPP_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) -m 755 cpp$(exeext) $(DESTDIR)$(bindir)/$(CPP_INSTALL_NAME)$(exeext); \ diff --git a/gcc/amigacollect2.c b/gcc/amigacollect2.c new file mode 100755 index 000000000000..941ea0248fbe --- /dev/null +++ gcc/amigacollect2.c @@ -0,0 +1,348 @@ +/* GG-local whole file: dynamic libraries */ +/* Supplimentary functions that get compiled and linked to collect2 for + AmigaOS target. + Copyright (C) 1996 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 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +/* From collect2.c: */ + +void maybe_unlink(const char *); +void fatal_error(location_t, const char *, ...); +void fork_execute(const char *, char **, bool); + +extern char *c_file_name; +extern int debug; + +/* Local functions. */ + +static void safename (char *); +static void add_lib (const char *); +static void cat (const char *, FILE *); + + /* Names of temporary files we create. */ +#define XLIBS_C_NAME "xlibs.c" +#define XLIBS_O_NAME "xlibs.o" +#define SHARED_X_NAME "shared.x" + +/* Suffix which is prepended to "-l" options for dynamic libraries. */ +#define DYNAMIC_LIB_SUFFIX "_ixlibrary" + +/* Structure that holds library names. */ +struct liblist +{ + struct liblist *next; + char *name; + char *cname; +}; + +/* Not zero if "-static" was specified on GCC command line or if all the + libraries are static. */ +static int flag_static=0; + +/* Not zero if linking a base relative executable. This is recognized by + presence of "-m amiga_bss" on the linker's commandline. */ +static int flag_baserel=0; + +/* Not zero if some of the specified libraries are dynamic. */ +static int found_dynamic_libs=0; + +/* List of linker libraries. */ +struct liblist *head = NULL; + + /* Return 1 if collect2 should do something more apart from tlink. We want it + to call "postlink" and "strip" if linking with dynamic libraries. */ + +int +amigaos_do_collecting (void) +{ + return !flag_static; +} + +/* Check for presence of "-static" on the GCC command line. We should not do + collecting if this flag was specified. */ + +void +amigaos_gccopts_hook (const char *arg) +{ + if (strncmp(arg, "-static", strlen("-static"))==0) + flag_static=1; +} + +/* Replace unprintable characters with underscores. Used by "add_lib()". */ + +static void +safename (char *p) +{ + if (!ISALPHA(*p)) + *p = '_'; + p++; + while (*p) + { + if (!ISALNUM(*p)) + *p = '_'; + p++; + } +} + +/* Add a library to the list of dynamic libraries. First make sure that the + library is actually dynamic. Used by "amigaos_libname_hook()". */ + +static void +add_lib (const char *name) +{ + struct liblist *lib; + static char buf[256]; + + for (lib = head; lib; lib = lib->next) + if (!strcmp(lib->name, name)) + return; + + /* A2IXDIR_PREFIX is passed by "make". */ + sprintf(buf, A2IXDIR_PREFIX "/ldscripts/%s.x", name); + if (access(buf, R_OK)) + return; + + lib = (struct liblist*)xmalloc(sizeof(struct liblist)); + lib->name = xstrdup(name); + lib->cname = xstrdup(name); + safename(lib->cname); + lib->next = head; + head = lib; + + if (debug) + fprintf(stderr, "found dynamic library, name: %s, cname: %s\n", lib->name, + lib->cname); + + found_dynamic_libs=1; +} + +/* Check if the argument is a linker library. Call "add_lib()" if yes. */ + +void +amigaos_libname_hook (const char *arg) +{ + int len = strlen(arg); + if (flag_static) + return; + + if (len > 2 && !memcmp(arg, "-l", 2)) + add_lib(arg + 2); + else if (len > 2 && !strcmp(arg + len - 2, ".a")) + { + const char *lib; + + ((char*)arg)[len - 2] = '\0'; + lib = strrchr(arg, '/'); + if (lib == NULL) + lib = strrchr(arg, ':'); + if (lib == NULL) + lib = arg - 1; + if (!strncmp(lib + 1, "lib", 3)) + add_lib(lib + 4); + ((char *)arg)[len - 2] = '.'; + } +} + +/* Delete temporary files. */ + +void +amigaos_collect2_cleanup (void) +{ + if (flag_static) + return; + maybe_unlink(XLIBS_C_NAME); + maybe_unlink(XLIBS_O_NAME); + maybe_unlink(SHARED_X_NAME); +} + +/* Copy file named by FNAME to X. */ + +static void +cat (const char *fname, FILE *x) +{ +#define BUFSIZE 16384 + FILE *in; + static char buf[BUFSIZE]; + int bytes; + + in = fopen(fname, "r"); + if (in == NULL) + fatal_error (input_location, "%s", fname); + while (!feof(in) && (bytes = fread(buf, 1, BUFSIZE, in))) + fwrite(buf, 1, bytes, x); + fclose(in); +} + +/* If no dynamic libraries were found, perform like "-static". Otherwise, + create "xlibs.c", "shared.x" and invoke "gcc" to create "xlibs.o". We also + have to adjust the linker commandline. */ + +void +amigaos_prelink_hook (const char **ld1_argv, int *strip_flag) +{ + if (flag_static) + return; + + if (!found_dynamic_libs) + { + flag_static=1; + /* If the user has not requested "-static", but has requested "-s", + collect2 removes "-s" from the "ld1_argv", and calls "strip" after + linking. However, this would not be efficient if we linked the + executable without any dynamic library. In this case, we put "-s" + back. */ + if (*strip_flag) + { + /* Add "-s" as the last argument on the command line. */ + while (*ld1_argv) + ld1_argv++; + *ld1_argv++="-s"; + *ld1_argv=0; + *strip_flag=0; + } + } + else + { + FILE *x, *out; + struct liblist *lib; + static const char* argv[]={0, "-c", XLIBS_C_NAME, 0}; + const char **ld1_end, **ld1; + + /* Prepend suffixes to dynamic lib names. In addition, check if we are + linking a base relative executable. */ + for (ld1=ld1_argv; *ld1; ld1++) + { + int len=strlen(*ld1); + if (strncmp(*ld1, "-l", strlen("-l"))==0) + { + for (lib=head; lib; lib=lib->next) + if (strcmp(*ld1+strlen("-l"), lib->name)==0) + { + char *newname=(char*) + xmalloc(strlen(*ld1)+strlen(DYNAMIC_LIB_SUFFIX)+1); + strcpy(newname, *ld1); + strcat(newname, DYNAMIC_LIB_SUFFIX); + *ld1=newname; + break; + } + } + else if (len > 2 && !strcmp(*ld1 + len - 2, ".a")) + { + const char *libname; + int substituted=0; + + ((char *)(*ld1))[len - 2] = '\0'; + libname = strrchr(*ld1, '/'); + if (libname == NULL) + libname = strrchr(*ld1, ':'); + if (libname == NULL) + libname = *ld1 - 1; + if (!strncmp(libname + 1, "lib", 3)) + for (lib=head; lib; lib=lib->next) + if (strcmp(libname+4, lib->name)==0) + { + char *newname=(char*)xmalloc(strlen(*ld1)+ + strlen(DYNAMIC_LIB_SUFFIX)+3); + strcpy(newname, *ld1); + strcat(newname, DYNAMIC_LIB_SUFFIX); + strcat(newname, ".a"); + *ld1=newname; + substituted=1; + break; + } + if (!substituted) + ((char *)(*ld1))[len - 2] = '.'; + } + else if (strcmp(ld1[0], "-m")==0 && ld1[1] + && strcmp(ld1[1], "amiga_bss")==0) + { + flag_baserel=1; + break; + } + } + + out = fopen(XLIBS_C_NAME, "w"); + if (out == NULL) + fatal_error (input_location, "%s", XLIBS_C_NAME); + x = fopen(SHARED_X_NAME, "w"); + if (x == NULL) + fatal_error (input_location, "%s", SHARED_X_NAME); + + cat((flag_baserel ? A2IXDIR_PREFIX "/amiga_exe_baserel_script.x" + : A2IXDIR_PREFIX "/amiga_exe_script.x"), x); + for (lib = head; lib; lib = lib->next) + { + static char buf[256]; + sprintf(buf, A2IXDIR_PREFIX "/ldscripts/%s.x", lib->name); + fprintf(out, "extern long %sBase; long *__p%sBase = &%sBase;\n", + lib->cname, lib->cname, lib->cname); + cat(buf, x); + } /* {{ */ + fprintf(x, "}}\n"); + fclose(out); + fclose(x); + argv[0]=c_file_name; + fork_execute("gcc", (char **)argv, false); + + /* Unfortunately, unlike "-s", "-T" cannot be specified as the last + argument. We put it after "-L" args. */ + ld1_end=ld1_argv; + while (*ld1_end) + ld1_end++; + ld1_end++; + /* "ld1_end" now points after the terminating 0 of "ld1_argv". */ + + ld1=ld1_end-2; + while (ld1>ld1_argv && strncmp(*ld1, "-L", strlen("-L"))) + ld1--; + if (ld1==ld1_argv) + fatal_error (input_location, "no -L arguments"); + ld1++; + /* "ld1" now points after "-L". */ + + /* Shift all the arguments after "-L" one position right. */ + memmove(ld1+1, ld1, (ld1_end-ld1)*sizeof(*ld1)); + /* Put -Tshared.x in the now empty space. */ + *ld1="-T" SHARED_X_NAME; + } +} + +/* Be lazy and just call "postlink". */ + +void +amigaos_postlink_hook (const char *output_file) +{ + static const char *argv[]={"postlink", 0, 0, 0}; + if (flag_static) + return; + + if (flag_baserel) + { + argv[1]="-baserel"; + argv[2]=output_file; + } + else + argv[1]=output_file; + fork_execute("postlink", (char **)argv, false); +} diff --git a/gcc/bbb-opts.c b/gcc/bbb-opts.c new file mode 100755 index 000000000000..9e989e9b1ec0 --- /dev/null +++ gcc/bbb-opts.c @@ -0,0 +1,4705 @@ +/* Bebbo's Optimizations. + Copyright (C) 2010-2017 Free Software Foundation, Inc. + Copyright (C) 2017 Stefan "Bebbo" Franke. + + 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 + . */ + +/** + * SBF (Stefan "Bebbo" Franke): + * + * This pass performs multiple optimizations. + * + * #1 propagate_moves + * check if a->b->a can be moved out of a loop. + * + * #2 strcpy + * check if a temp reg can be eliminated. + * + * #3 const_comp_sub + * convert a compare with int constant into sub statement. + * + * #4 merge_add + * merge adds + * + * #5 elim_dead_assign + * eliminate some dead assignments. + * + * #6 shrink stack frame + * remove push/pop for unused variables + * + * #7 rename register + * rename registers without breaking register parameters, inline asm etc. + * + * Lessons learned: + * + * - do not trust existing code, better delete insns and inster a new one. + * - do not modify insns, create new insns from pattern + * - do not reuse registers, create new reg rtx instances + * + */ + +#include "config.h" +#define INCLUDE_VECTOR +#define INCLUDE_SET +#define INCLUDE_MAP +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tm_p.h" +#include "insn-config.h" +#include "recog.h" +#include "cfgrtl.h" +#include "emit-rtl.h" +#include "tree.h" +#include "tree-pass.h" +#include "conditions.h" +#include "langhooks.h" +#include +#include +#include + +int be_very_verbose; +bool be_verbose; + +extern struct lang_hooks lang_hooks; + +static void +update_insn_infos (void); +static unsigned +track_regs (); + +/* Lookup of the current function name. */ +extern tree current_function_decl; +static tree last_function_decl; +static char fxname[512]; +static char const * +get_current_function_name () +{ + if (current_function_decl == NULL) + strcpy (fxname, ""); + else + strcpy (fxname, lang_hooks.decl_printable_name (current_function_decl, 2)); + return fxname; +} + +/* a simple log to stdout. */ +static int +log (char const * fmt, ...) +{ + if (!be_verbose) + return 0; + + va_list args; + va_start(args, fmt); + if (last_function_decl != current_function_decl) + { + last_function_decl = current_function_decl; + printf (":bbb: in '%s'\n", get_current_function_name ()); + } + printf (":bbb: "); + int retval = vprintf (fmt, args); + va_end(args); + fflush (stdout); + return retval; +} + +enum proepis +{ + IN_CODE, IN_PROLOGUE, IN_EPILOGUE, IN_EPILOGUE_PARALLEL_POP +}; + +/** + * What's needed to track values? + */ +class track_var +{ + rtx value[FIRST_PSEUDO_REGISTER]; + unsigned mask[FIRST_PSEUDO_REGISTER]; + + bool + extend (rtx * z, unsigned * mask, machine_mode dstMode, rtx x) + { + switch (GET_CODE(x)) + { + case CONST_INT: + case CONST_FIXED: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + /* these can be used directly. */ + *z = x; + return true; + + case REG: + { + rtx v = value[REGNO(x)]; + unsigned mr = mask[REGNO(x)]; + /* try to expand the register. */ + if (v) + { + if (dstMode != GET_MODE(v) && (GET_CODE(v) != CONST_INT || mr == (1 << FIRST_PSEUDO_REGISTER))) + return false; + + *mask |= mr; + *z = v; + return true; + } + + /* store the reg otherwise. */ + *mask |= (1 << REGNO(x)); + if (GET_MODE(x) == dstMode) + *z = x; + else + *z = gen_rtx_REG (dstMode, REGNO(x)); + return true; + } + case PLUS: + case MINUS: + // handle only in combination with const + { + rtx y = XEXP(x, 0); + if (GET_CODE(y) != SYMBOL_REF && GET_CODE(y) == LABEL_REF && amiga_is_const_pic_ref (y)) + return false; + + if (GET_CODE(x) == PLUS) // create an own plus to be able to modify the constant offset (later). + *z = gen_rtx_PLUS(GET_MODE(x), y, XEXP(x, 1)); + else + *z = gen_rtx_MINUS(GET_MODE(x), y, XEXP(x, 1)); + return true; + } + + /* memory reads. */ + case MEM: + { + rtx m = XEXP(x, 0); + switch (GET_CODE(m)) + { + case SYMBOL_REF: + case LABEL_REF: + /* these can be used directly. */ + *z = x; + return true; + + case REG: + if (!extend (&m, mask, dstMode, m)) + return false; + + *z = gen_rtx_MEM (GET_MODE(x), m); + return true; + + case PLUS: + case MINUS: + // handle only in combination with const + { + rtx y = XEXP(m, 0); + if (!REG_P(y) && GET_CODE(y) != SYMBOL_REF && GET_CODE(y) == LABEL_REF && amiga_is_const_pic_ref (y)) + return false; + + if (REG_P(y)) + if (!extend (&y, mask, dstMode, y)) + return false; + + if (GET_CODE(x) == PLUS) // create an own plus to be able to modify the constant offset (later). + m = gen_rtx_PLUS(GET_MODE(m), y, XEXP(m, 1)); + else + m = gen_rtx_MINUS(GET_MODE(m), y, XEXP(m, 1)); + + *z = gen_rtx_MEM (GET_MODE(x), m); + return true; + } + default: + return false; + } + break; + } + default: + return false; + } + } + +public: + track_var (track_var const * o = 0) + { + if (o) + assign (o); + else + for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + value[i] = 0; + mask[i] = 0; + } + } + + int + find_alias (rtx src) + { + rtx z = 0; + unsigned m = 0; + if (extend (&z, &m, GET_MODE(src), src)) + { + for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + // do not alias small int value from -128 ... 127 + if (rtx_equal_p (z, value[i]) && (GET_CODE(z) != CONST_INT || INTVAL(z) > 127 || INTVAL(z) < -128)) + return i; + } + } + return -1; + } + void + invalidate_mem (rtx dst) + { + rtx z = 0; + unsigned m = 0; + if (extend (&z, &m, GET_MODE(dst), dst)) + { + for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + if (rtx_equal_p (z, value[i])) + { + value[i] = 0; + mask[i] = 0; + } + } + } + } + + rtx + get (unsigned regno) + { + if (regno >= FIRST_PSEUDO_REGISTER) + return 0; + + return value[regno]; + } + + void + set (machine_mode mode, unsigned regno, rtx x, unsigned index) + { + if (regno >= FIRST_PSEUDO_REGISTER) + return; + + if (mode == SFmode && regno < 16) + mode = SImode; + + if (!extend (&value[regno], &mask[regno], mode, x)) + { + clear (mode, regno, index); + } + } + + bool + equals (unsigned regno, rtx x) + { + if (regno >= FIRST_PSEUDO_REGISTER) + return false; + + if (x == 0 || value[regno] == 0) + return false; + + rtx z = 0; + unsigned m = 0; + if (!extend (&z, &m, GET_MODE(x), x)) + return false; + + return rtx_equal_p (z, value[regno]); + } + + void + clear (machine_mode mode, unsigned regno, unsigned index) + { + if (regno >= FIRST_PSEUDO_REGISTER) + return; + + if (mode == SFmode && regno < 16) + mode = SImode; + value[regno] = gen_rtx_raw_CONST_INT(mode, 0x100000000000000LL | ((long long int ) (regno) << 32) | index); + mask[regno] = 1 << FIRST_PSEUDO_REGISTER; + } + + void + clear_aftercall (unsigned index) + { + for (int i = 2; i < FIRST_PSEUDO_REGISTER; ++i) + { + if (mask[i] && mask[i] < 1 << FIRST_PSEUDO_REGISTER) + { + value[i] = 0; + mask[i] = 0; + } + } + clear (SImode, 0, index); + clear (SImode, 1, index); + clear (SImode, 8, index); + clear (SImode, 9, index); + } + + void + clear_for_mask (unsigned def, unsigned index) + { + if (!def) + return; + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + { + // register changed or used somehow + if ((1 << regno) & def) + clear (SImode, regno, index); + } + } + + void + assign (track_var const * o) + { + for (int i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + value[i] = o->value[i]; + mask[i] = o->mask[i]; + } + } + + /* only keep common values in both sides. */ + void + merge (track_var * o, unsigned) + { + for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + if (!rtx_equal_p (value[i], o->value[i])) + { + value[i] = o->value[i] = 0; + mask[i] = 0; + } + } + } + + /* true if a merge would not change anything. */ + bool + no_merge_needed (track_var const * o) const + { + for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + { + if (!rtx_equal_p (value[i], o->value[i])) + return false; + } + return true; + } +}; + +/* Information for each insn to detect alive registers. Enough for m68k. + * Why a class? Maybe extend it for general usage. + * + * Track use & def separate to determine starting points. + */ +class insn_info +{ + rtx_insn * insn; // the insn + +// usage flags + unsigned myuse; // bit set if registers are used in this statement + unsigned hard; // bit set if registers can't be renamed + unsigned use; // bit set if registers are used in program flow + unsigned def; // bit set if registers are defined here + + enum proepis proepi; + + bool stack; // part of stack frame insns + +// stuff to analyze insns + bool label; + bool jump; + bool call; + bool compare; + bool dst_mem; + bool src_mem; + bool dst_plus; + bool src_plus; + rtx_code src_op; + bool src_ee; + bool src_2nd; + bool src_const; + + machine_mode mode; + + rtx dst_reg; + rtx dst_mem_reg; + rtx dst_symbol; + rtx src_reg; + rtx src_mem_reg; + rtx src_symbol; + unsigned dst_mem_addr; + int src_intval; + unsigned src_mem_addr; + + bool visited; + + int sp_offset; + + int dst_autoinc; + int src_autoinc; + +// values for all variables - if used + track_var * track; + +public: + insn_info (rtx_insn * i = 0, enum proepis p = IN_CODE) : + insn (i), myuse (0), hard (0), use (0), def (0), proepi (p), stack (false), label (false), jump (false), call ( + false), compare (false), dst_mem (false), src_mem (false), dst_plus (false), src_plus (false), src_op ( + (rtx_code) 0), src_ee (false), src_2nd (false), src_const (false), mode (VOIDmode), dst_reg (0), dst_mem_reg ( + 0), dst_symbol (0), src_reg (0), src_mem_reg (0), src_symbol (0), dst_mem_addr (0), src_intval (0), src_mem_addr ( + 0), visited (false), sp_offset (0), dst_autoinc (0), src_autoinc (0), track (0) + { + } + + track_var * + get_track_var (); + + inline ptrdiff_t + operator < (insn_info const & o) const + { + return this - &o; + } + + int + get_index () const; + + void + plus_to_move (rtx_insn * newinsn); + + void + swap_adds (rtx_insn * newinsn, insn_info & ii); + + void + absolute2base (unsigned regno, unsigned base, rtx with_symbol); + + rtx + make_absolute2base (unsigned regno, unsigned base, rtx with_symbol, bool apply); + + inline bool + is_compare () const + { + return compare; + } + + inline machine_mode + get_mode () const + { + return mode; + } + + inline bool + is_dst_reg () const + { + return dst_reg; + } + + inline bool + is_dst_mem () const + { + return dst_mem; + } + + inline bool + is_src_mem () const + { + return src_mem; + } + + inline bool + is_src_mem_2nd () const + { + return src_2nd && src_mem; + } + + inline bool + has_dst_memreg () const + { + return dst_mem_reg; + } + + inline bool + has_src_memreg () const + { + return src_mem_reg; + } + + inline rtx + get_dst_symbol () const + { + return dst_symbol; + } + + inline rtx + get_src_symbol () const + { + return src_symbol; + } + inline bool + has_dst_addr () const + { + return dst_mem_addr; + } + + inline bool + has_src_addr () const + { + return src_mem_addr; + } + + inline bool + is_label () const + { + return label; + } + + inline bool + is_jump () const + { + return jump; + } + + inline bool + is_call () const + { + return call; + } + + inline unsigned + get_dst_mem_addr () const + { + return dst_mem_addr; + } + + inline unsigned + get_src_mem_addr () const + { + return src_mem_addr; + } + + inline bool + is_src_reg () const + { + return src_reg && !src_op; + } + + inline int + get_src_op () const + { + return src_op; + } + + inline bool + is_src_ee () const + { + return src_ee; + } + + inline bool + is_src_mem_plus () const + { + return src_mem && src_plus; + } + + inline bool + is_dst_mem_plus () const + { + return dst_mem && dst_plus; + } + + inline int + get_dst_regno () const + { + return dst_reg ? REGNO(dst_reg) : -1; + } + + inline int + get_src_regno () const + { + return src_reg ? REGNO(src_reg) : -1; + } + + inline rtx + get_src_reg () const + { + return src_reg; + } + + inline rtx + get_dst_reg () const + { + return dst_reg; + } + + inline int + get_src_mem_regno () const + { + return src_mem_reg ? REGNO(src_mem_reg) : -1; + } + + inline int + get_dst_mem_regno () const + { + return dst_mem_reg ? REGNO(dst_mem_reg) : -1; + } + + inline rtx + get_src_mem_reg () const + { + return src_mem_reg; + } + + inline rtx + get_dst_mem_reg () const + { + return dst_mem_reg; + } + + inline int + get_src_intval () const + { + return src_intval; + } + + inline int + get_dst_intval () const + { + return dst_mem_addr; + } + + inline bool + is_src_const () const + { + return src_const; + } + + inline void + mark_jump () + { + jump = true; + } + inline void + mark_call () + { + call = true; + } + inline void + mark_label () + { + label = true; + } + + void + fledder (rtx set); + + void + fledder_src_mem (rtx src); + + /* update usage. */ + void + update (insn_info & o) + { + myuse = o.myuse; + hard = o.hard; + use = o.use; + def = o.def; + } + + inline rtx_insn * + get_insn () const + { + return insn; + } + + void + mark_stack () + { + stack = true; + } + + bool + is_stack () const + { + return stack; + } + + inline enum proepis + in_proepi () const + { + return proepi; + } + + inline void + set_proepi (enum proepis p) + { + proepi = p; + } + + inline void + reset_flags () + { + label = false; + jump = false; + compare = false; + dst_mem = false; + src_mem = false; + dst_plus = false; + src_plus = false; + src_op = (rtx_code) 0; + src_ee = false; + src_const = false; + + mode = VOIDmode; + + dst_reg = 0; + dst_mem_reg = 0; + dst_symbol = 0; + src_reg = 0; + src_mem_reg = 0; + src_symbol = 0; + dst_mem_addr = 0; + + src_intval = 0; + src_mem_addr = 0; + + dst_autoinc = 0; + src_autoinc = 0; + } + + inline int + get_src_autoinc () const + { + return src_autoinc; + } + + inline int + get_dst_autoinc () const + { + return dst_autoinc; + } + + inline bool + is_empty () + { + return !def && !use && !hard; + } + + inline void + mark_myuse (int regno) + { + myuse |= 1 << regno; + use |= 1 << regno; + } + + inline void + mark_use (int regno) + { + use |= 1 << regno; + } + + inline void + mark_def (int regno) + { + def |= 1 << regno; + } + + inline void + mark_hard (int regno) + { + hard |= 1 << regno; + } + + inline void + unset (int regno) + { + use &= ~(1 << regno); + def &= ~(1 << regno); + hard &= ~(1 << regno); + } + + inline unsigned + get_use () const + { + return use; + } + + inline unsigned + get_myuse () const + { + return myuse; + } + + inline void + set_use (unsigned u) + { + use = u; + } + + inline unsigned + get_def () const + { + return def; + } + inline unsigned + get_hard () const + { + return hard; + } + + inline bool + is_use (int regno) + { + return (use & (1 << regno)) != 0; + } + + inline bool + is_myuse (int regno) + { + return (myuse & (1 << regno)) != 0; + } + + inline bool + is_def (int regno) + { + return (def & (1 << regno)) != 0; + } + + inline bool + is_hard (int regno) + { + return (hard & (1 << regno)) != 0; + } + + inline void + clear_hard_def () + { + hard = 0; + def = 0; + } + + /* + * update for previous insn. + * - remove regs which are defined here + * - add regs which are used here + * - reset _def + * - restrain _hard to used + */ + inline void + updateWith (insn_info const & o) + { + use &= ~o.def; + use |= o.use; + def = 0; + } + + inline insn_info & + merge (insn_info const & o) + { + myuse = o.myuse; + use = (use & ~o.def) | o.use; + def |= o.def; + hard |= o.hard; + return *this; + } + + inline insn_info & + or_use (insn_info const & o) + { + use |= o.myuse | o.def | o.hard; + return *this; + } + + inline insn_info & + drop_def () + { + use &= ~def; + return *this; + } + + inline insn_info & + make_hard () + { + hard = use | def; + return *this; + } + + inline insn_info & + make_clobber () + { + hard = use = def = use | def; + return *this; + } + + inline bool + contains (insn_info const & o) const + { + if (o.def & ~def) + return false; + if (o.use & ~use) + return false; + if (o.hard & ~hard) + return false; + return true; + } + + inline int + get_sp_offset () const + { + return sp_offset; + } + + inline void + set_sp_offset (int sp) + { + sp_offset = sp; + } + + inline bool + is_visited () const + { + return visited; + } + + inline void + mark_visited () + { + visited = true; + } + + inline void + clear_visited () + { + visited = false; + } + + void + scan (); + + void + scan_rtx (rtx); + + bool + make_post_inc (int regno); + + void + auto_inc_fixup (int regno, int size); + + /* return bits for alternate free registers. */ + unsigned + get_free_mask () const + { + if (def & hard) + return 0; + + if (!def) + return 0; + + unsigned def_no_cc = def & ~(1 << FIRST_PSEUDO_REGISTER); + if (def_no_cc > 0x4000) + return 0; + + unsigned mask = def_no_cc - 1; + /* more than one register -> don't touch. */ + if ((mask & ~def) != mask) + return 0; + + if (def_no_cc > 0xff) + mask &= 0xff00; + + return mask & ~use; + } + + unsigned + get_regbit () const + { + if (GET_MODE_SIZE(mode) > 4) + return 0; + return def & ~hard & ~use & 0x7fff; + } + + void + set_insn (rtx_insn * newinsn); + + void + a5_to_a7 (rtx a7); +}; + +bool +insn_info::make_post_inc (int regno) +{ + rtx pattern = PATTERN (insn); + rtx_insn * new_insn = make_insn_raw (pattern); + + // convert into POST_INC + rtx set0 = single_set (new_insn); + if (!set0) + return false; + + rtx set = set0; + + if (is_compare ()) + set = SET_SRC(set); + rtx mem = get_dst_mem_regno () == regno ? SET_DEST(set) : SET_SRC(set); + + if (src_op && get_src_mem_regno () == regno) + { + if (src_op == NEG || src_op == NOT || src_op == SIGN_EXTEND) + mem = XEXP(mem, 0); + else + mem = XEXP(mem, 1); + } + + rtx reg = XEXP(mem, 0); + + XEXP(mem, 0) = gen_rtx_POST_INC(SImode, reg); + + if (insn_invalid_p (new_insn, 0)) + { + XEXP(mem, 0) = reg; + insn_invalid_p (insn, 0); + return 0; + } + + SET_INSN_DELETED(insn); + (get_dst_mem_regno () == regno ? dst_autoinc : src_autoinc) = GET_MODE_SIZE(mode); + insn = emit_insn_after (PATTERN (new_insn), insn); + insn_invalid_p (insn, 0); + + return 1; +} + +static rtx +add_clobbers (rtx_insn * oldinsn) +{ + rtx pattern = PATTERN (oldinsn); + if (GET_CODE(pattern) != PARALLEL) + return pattern; + + int num_clobbers = 0; + for (int j = XVECLEN (pattern, 0) - 1; j >= 0; j--) + { + rtx x = XVECEXP(pattern, 0, j); + if (GET_CODE(x) == CLOBBER) + ++num_clobbers; + } + + if (!num_clobbers) + return pattern; + + rtx newpat = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (num_clobbers + 1)); + for (int j = XVECLEN (pattern, 0) - 1; j >= 0; j--) + { + rtx x = XVECEXP(pattern, 0, j); + if (GET_CODE(x) == CLOBBER) + XVECEXP(newpat, 0, num_clobbers--) = x; + } + + XVECEXP(newpat, 0, 0) = XVECEXP(pattern, 0, 0); + return newpat; +} + +void +insn_info::auto_inc_fixup (int regno, int size) +{ +// debug_rtx (insn); + rtx set0 = single_set (insn); + rtx set = set0; + if (is_compare ()) + set = SET_SRC(set); + + // add to register + if (get_src_regno () == regno) + { + rtx src = SET_SRC(set); + if (get_src_intval () == size) + { + src_intval = 0; + src_plus = false; + SET_SRC(set) = XEXP(src, 0); + } + else + XEXP(src, 1) = gen_rtx_CONST_INT (GET_MODE(XEXP(src, 1)), src_intval -= size); + } + else if (get_src_mem_regno () == regno) + { + // src mem used ? + rtx mem = SET_SRC(set); + if (src_op) + { + if (MEM_P(XEXP(mem, 0))) + mem = XEXP(mem, 0); + else + mem = XEXP(mem, 1); + } + rtx plus = XEXP(mem, 0); + + if (src_mem_addr == (unsigned) size) + { + XEXP(mem, 0) = XEXP(plus, 0); + src_mem_addr = 0; + src_plus = false; + } + else + XEXP(plus, 1) = gen_rtx_CONST_INT (GET_MODE(XEXP(plus, 1)), src_mem_addr -= size); + } + + if (get_dst_mem_regno () == regno) + { + rtx mem = SET_DEST(set); + rtx plus = XEXP(mem, 0); + if (dst_mem_addr == (unsigned) size) + { + XEXP(mem, 0) = XEXP(plus, 0); + dst_mem_addr = 0; + dst_plus = false; + } + else + XEXP(plus, 1) = gen_rtx_CONST_INT (GET_MODE(XEXP(plus, 1)), dst_mem_addr -= size); + } + + rtx pattern = add_clobbers (insn); + + SET_INSN_DELETED(insn); + insn = emit_insn_after (pattern, insn); +} + +track_var * +insn_info::get_track_var () +{ + if (!track) + track = new track_var (); + return track; +} + +void +insn_info::scan () +{ + rtx pattern = PATTERN (insn); + if (ANY_RETURN_P(pattern)) + { + tree type = TYPE_SIZE(TREE_TYPE (DECL_RESULT (current_function_decl))); + int sz = type ? TREE_INT_CST_LOW(type) : 0; + // log ("return size %d\n", sz); + if (sz <= 64) + { + mark_hard (0); + mark_myuse (0); + if (sz > 32) + { + mark_hard (1); + mark_myuse (1); + } + } + } + else if (CALL_P(insn)) + { + /* add mregparm registers. */ + for (rtx link = CALL_INSN_FUNCTION_USAGE(insn); link; link = XEXP(link, 1)) + { + rtx op, reg; + + if (GET_CODE (op = XEXP (link, 0)) == USE && REG_P(reg = XEXP (op, 0))) + for (unsigned r = REGNO(reg); r < END_REGNO (reg); ++r) + mark_myuse (r); + } + /* mark scratch registers. */ + mark_def (0); + mark_def (1); + mark_def (8); + mark_def (9); + /* also mark all registers as not renamable */ + hard = use; + } + scan_rtx (pattern); +} + +/* scan rtx for registers and set the corresponding flags. */ +void +insn_info::scan_rtx (rtx x) +{ + if (REG_P(x)) + { + for (int n = REG_NREGS(x), r = REGNO(x); n > 0; --n, ++r) + mark_myuse (r); + return; + } + + if (x == cc0_rtx) + { + mark_myuse (FIRST_PSEUDO_REGISTER); + return; + } + + RTX_CODE code = GET_CODE(x); + + /* handle SET and record use and def. */ + if (code == SET) + { + unsigned u = use; + unsigned mu = myuse; + use = myuse = 0; + rtx dst = SET_DEST(x); + scan_rtx (dst); + if (REG_P(dst) || ((GET_CODE(dst) == STRICT_LOW_PART || GET_CODE(dst) == SUBREG) && REG_P(XEXP(dst, 0)))) + { + def |= use; + if ((GET_CODE(dst) == STRICT_LOW_PART || GET_CODE(dst) == SUBREG)) + use |= u; + else + use = u; + myuse = mu; + } + + // avoid side effects from myuse -> def, e.g. adding the dst reg to def by src auto inc + mu = myuse; + myuse = 0; + scan_rtx (SET_SRC(x)); + myuse |= mu; + + int code = GET_CODE(SET_SRC(x)); + if (code == ASM_OPERANDS) + hard |= def | use; + return; + } + + if (code == TRAP_IF) + { + /* mark all registers used. */ + hard = use = myuse = (1 << FIRST_PSEUDO_REGISTER) - 1; + return; + } + + const char *fmt = GET_RTX_FORMAT(code); + for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + scan_rtx (XEXP(x, i)); + else if (fmt[i] == 'E') + for (int j = XVECLEN (x, i) - 1; j >= 0; j--) + { + unsigned u = use; + unsigned mu = myuse; + unsigned d = def; + scan_rtx (XVECEXP(x, i, j)); + use |= u; + myuse |= mu; + def |= d; + } + } + + if (code == POST_INC || code == PRE_DEC || code == CLOBBER) + def |= myuse; +} + +void +insn_info::fledder_src_mem (rtx src) +{ + src_mem = true; + rtx mem = XEXP(src, 0); + + if (GET_CODE(mem) == POST_INC) + src_autoinc = 1, mem = XEXP(mem, 0); + else if (GET_CODE(mem) == PRE_DEC) + src_autoinc = -1, mem = XEXP(mem, 0); + + if (REG_P(mem)) + src_mem_reg = mem; + else if (GET_CODE(mem) == CONST_INT) + src_mem_addr = INTVAL(mem); + else if (GET_CODE(mem) == SYMBOL_REF) + src_symbol = mem; + else if (GET_CODE(mem) == PLUS) + { + src_plus = true; + rtx reg = XEXP(mem, 0); + rtx konst = XEXP(mem, 1); + if (REG_P(reg) && GET_CODE(konst) == CONST_INT) + { + src_mem_reg = reg; + src_const = true; + src_mem_addr = INTVAL(konst); + } + } + else if (GET_CODE(mem) == CONST) + { + mem = XEXP(mem, 0); + if (GET_CODE(mem) == PLUS) + { + rtx sym = XEXP(mem, 0); + if (GET_CODE(sym) == SYMBOL_REF) + { + src_plus = true; + src_symbol = sym; + src_mem_addr = INTVAL(XEXP(mem, 1)); + } + } + } +} + +/* read the set and grab infos */ +void +insn_info::fledder (rtx set) +{ + if (!set || GET_CODE(set) == PARALLEL) + return; + + rtx dst = SET_DEST(set); + rtx src = SET_SRC(set); + + if (dst == cc0_rtx) + { + compare = true; + set = src; + dst = SET_DEST(set); + src = SET_SRC(set); + } + + if (GET_CODE(dst) == STRICT_LOW_PART || GET_CODE(dst) == SUBREG) + dst = XEXP(dst, 0); + + mode = GET_MODE(dst); + if (mode == VOIDmode) + mode = GET_MODE(src); + + if (REG_P(dst)) + { + dst_reg = dst; + } + else if (MEM_P(dst)) + { + dst_mem = true; + rtx mem = XEXP(dst, 0); + + if (GET_CODE(mem) == POST_INC) + dst_autoinc = 1, mem = XEXP(mem, 0); + else if (GET_CODE(mem) == PRE_DEC) + dst_autoinc = -1, mem = XEXP(mem, 0); + + if (REG_P(mem)) + dst_mem_reg = mem; + else if (GET_CODE(mem) == CONST_INT) + dst_mem_addr = INTVAL(mem); + else if (GET_CODE(mem) == SYMBOL_REF) + dst_symbol = mem; + else if (GET_CODE(mem) == PLUS) + { + dst_plus = true; + rtx reg = XEXP(mem, 0); + rtx konst = XEXP(mem, 1); + if (REG_P(reg) && GET_CODE(konst) == CONST_INT) + { + dst_mem_reg = reg; + dst_mem_addr = INTVAL(konst); + } + } + else if (GET_CODE(mem) == CONST) + { + mem = XEXP(mem, 0); + if (GET_CODE(mem) == PLUS) + { + rtx sym = XEXP(mem, 0); + if (GET_CODE(sym) == SYMBOL_REF) + { + dst_plus = true; + dst_symbol = sym; + dst_mem_addr = INTVAL(XEXP(mem, 1)); + } + } + } + } + + /* It' some kind of operation, e.g. PLUS, XOR, NEG, ... */ + rtx alt_src_reg = 0; + int code = GET_CODE(src); + if (!REG_P(src) && !MEM_P(src) && code != CONST_INT && code != CONST && code != CONST_WIDE_INT && code != CONST_DOUBLE + && code != CONST_FIXED && code != CONST_STRING) + { + src_op = GET_CODE(src); + const char *fmt = GET_RTX_FORMAT(code); + if (fmt[0] == 'e' && fmt[1] == 'e') + { + src_ee = true; + rtx operand = XEXP(src, 1); + if (GET_CODE(operand) == CONST_INT || GET_CODE(operand) == CONST_WIDE_INT) + src_const = true, src_intval = INTVAL(operand); + else if (REG_P(operand)) + { + alt_src_reg = operand; + } + else if (MEM_P(operand)) + { + // it' something like reg = op(reg, mem(...)) + src_2nd = true; + fledder_src_mem (operand); + } + } + src = XEXP(src, 0); + } + + if (REG_P(src)) + { + src_reg = src; + } + else if (MEM_P(src)) + { + fledder_src_mem (src); + } + else if (GET_CODE(src) == CONST_INT) + { + src_const = true; + src_intval = INTVAL(src); + } + if (alt_src_reg) + src_reg = alt_src_reg; +} + +/* create a copy for a reg. Optional specify a new register number. */ +static rtx +copy_reg (rtx reg, int newregno) +{ + if (newregno < 0) + newregno = REGNO(reg); + rtx x = gen_raw_REG (GET_MODE(reg), newregno); + x->jump = reg->jump; + x->call = reg->call; + x->unchanging = reg->unchanging; + x->volatil = reg->volatil; + x->in_struct = reg->in_struct; + x->used = reg->used; + x->frame_related = reg->frame_related; + x->return_val = reg->return_val; + + x->u.reg.attrs = reg->u.reg.attrs; + return x; +} + +/* Rename the register plus track all locs to undo these changes. */ +static rtx +find_reg_by_no (rtx x, unsigned oldregno) +{ + if (!x) + return 0; + + RTX_CODE code = GET_CODE(x); + + const char *fmt = GET_RTX_FORMAT(code); + for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + rtx y = XEXP(x, i); + if (REG_P(y)) + { + if (REGNO(y) == oldregno) + return y; + } + else + { + rtx r = find_reg_by_no (y, oldregno); + if (r) + return r; + } + } + else if (fmt[i] == 'E') + for (int j = XVECLEN (x, i) - 1; j >= 0; j--) + { + rtx z = XVECEXP(x, i, j); + rtx r = find_reg_by_no (z, oldregno); + if (r) + return r; + } + } + return 0; +} + +/* + * Collect some data. + */ +static std::vector infos; +typedef std::vector::iterator insn_info_iterator; + +// insn->u2.insn_uid -> rtx_insn * +static std::multimap label2jump; +typedef std::multimap::iterator l2j_iterator; + +// index -> index +static std::multimap jump2label; +typedef std::multimap::iterator j2l_iterator; + +static std::map insn2info; +typedef std::map::iterator i2i_iterator; + +static std::set scan_starts; +typedef std::set::iterator su_iterator; + +static insn_info * info0; +static unsigned usable_regs; + +static void +update_insn2index () +{ + infos.reserve (infos.size () * 8 / 7 + 2); + insn2info.clear (); + /* needs a separate pass since the insn_infos require fixed addresses for ->get_index() */ + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & ii = infos[i]; + insn2info.insert (std::make_pair (ii.get_insn (), &ii)); + } + info0 = &infos[0]; +} + +static void +update_label2jump () +{ + update_insn2index (); + + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + if (ii.is_label ()) + for (l2j_iterator i = label2jump.find (ii.get_insn ()->u2.insn_uid), k = i; + i != label2jump.end () && i->first == k->first; ++i) + jump2label.insert (std::make_pair (insn2info.find (i->second)->second->get_index (), index)); + } +} + +int +insn_info::get_index () const +{ + insn_info * ii = &infos[0]; + + if (ii == info0) + { + ptrdiff_t diff = ((char const *) this - (char const *) ii); + unsigned pos = diff / sizeof(insn_info); + if (pos < infos.size ()) + return pos; + } + +// realloc happened... + for (unsigned i = 0; i < infos.size (); ++i) + if (infos[i].get_insn () == this->insn) + return i; + +// whoops!? + return 0; +} + +void +insn_info::plus_to_move (rtx_insn * newinsn) +{ + insn = newinsn; + src_op = (rtx_code) 0; + src_reg = XEXP(PATTERN (newinsn), 1); + insn2info.insert (std::make_pair (insn, this)); +// usage flags did not change +} + +void +insn_info::swap_adds (rtx_insn * newinsn, insn_info & ii) +{ + insn = newinsn; + + std::swap (*this, ii); + + insn2info.insert (std::make_pair (insn, this)); + insn2info.insert (std::make_pair (ii.insn, &ii)); + +// usage flags did not change +} + +static +void +replace_reg (rtx x, unsigned regno, rtx newreg, int offset) +{ + RTX_CODE code = GET_CODE(x); + const char *fmt = GET_RTX_FORMAT(code); + for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + rtx y = XEXP(x, i); + if (REG_P(y) && REGNO(y) == regno) + { + XEXP(x, i) = newreg; + if (offset && i + 1 < GET_RTX_LENGTH(code)) + { + rtx c = XEXP(x, i + 1); + if (GET_CODE(c) == CONST_INT) + XEXP(x, i + 1) = gen_rtx_CONST_INT (GET_MODE(x), INTVAL(c) + offset); + } + } + else + replace_reg (y, regno, newreg, offset); + } + else if (fmt[i] == 'E') + for (int j = XVECLEN (x, i) - 1; j >= 0; j--) + replace_reg (XVECEXP(x, i, j), regno, newreg, offset); + } +} + +void +insn_info::a5_to_a7 (rtx a7) +{ + if (proepi == IN_EPILOGUE && src_mem_reg && get_src_mem_regno () == FRAME_POINTER_REGNUM) + { + rtx set = single_set (insn); + if (set) + { + SET_SRC(set) = gen_rtx_MEM (mode, gen_rtx_POST_INC(SImode, a7)); + return; + } + } + replace_reg (PATTERN (insn), FRAME_POINTER_REGNUM, a7, -4); +} + +void +insn_info::set_insn (rtx_insn * newinsn) +{ + insn = newinsn; + + reset_flags (); + + fledder (single_set (insn)); +} + +rtx +insn_info::make_absolute2base (unsigned regno, unsigned base, rtx with_symbol, bool apply) +{ + rtx set = single_set (get_insn ()); + rtx src = SET_SRC(set); + rtx dst = SET_DEST(set); + rtx reg = gen_raw_REG (SImode, regno); + bool vola = src->volatil; + + if (is_dst_mem () && (has_dst_addr () || get_dst_symbol ()) && !has_dst_memreg () && get_dst_symbol () == with_symbol) + { + unsigned addr = get_dst_mem_addr (); + unsigned offset = addr - base; + if (offset <= 0x7ffe) + { + if (base == addr) + dst = gen_rtx_MEM (mode, reg); + else + dst = gen_rtx_MEM (mode, gen_rtx_PLUS(SImode, reg, gen_rtx_CONST_INT (SImode, offset))); + + if (apply) + { + dst_mem_reg = reg; + dst_mem = true; + dst_mem_addr = offset; + dst_plus = offset != 0; + } + } + } + + if (is_src_mem () && (has_src_addr () || get_src_symbol ()) && !has_src_memreg () && get_src_symbol () == with_symbol) + { + unsigned addr = get_src_mem_addr (); + unsigned offset = addr - base; + if (offset <= 0x7ffe) + { + if (base == addr) + src = gen_rtx_MEM (mode, reg); + else + src = gen_rtx_MEM (mode, gen_rtx_PLUS(SImode, reg, gen_rtx_CONST_INT (SImode, offset))); + + /* some operation to the same value as dst. eg. eor #5,symbol+8 -> eor #5,8(ax) */ + if (src_op) + { + if (src_ee) + src = gen_rtx_fmt_ee(src_op, mode, src, gen_rtx_CONST_INT (mode, src_intval)); + else + { + if (src_op == SIGN_EXTEND) + { + PUT_MODE_RAW(src, mode == SImode ? HImode : mode == HImode ? QImode : SImode); + src->call = 1; + } + src = gen_rtx_fmt_e(src_op, mode, src); + } + } + + if (apply) + { + src_mem_reg = reg; + src_mem = true; + src_mem_addr = offset; + src_plus = offset != 0; + } + } + } + + rtx pattern = gen_rtx_SET(dst, src); + src->volatil = vola; + + return pattern; +} + +void +insn_info::absolute2base (unsigned regno, unsigned base, rtx with_symbol) +{ + rtx pattern = make_absolute2base (regno, base, with_symbol, true); + + SET_INSN_DELETED(insn); + insn = emit_insn_after (pattern, insn); + + mark_myuse (regno); + + insn2info.insert (std::make_pair (insn, this)); +} +/* + * Reset collected data. + */ +static void +clear (void) +{ + label2jump.clear (); + jump2label.clear (); + insn2info.clear (); + infos.clear (); + scan_starts.clear (); +} + +/* + * return true if the register is DEAD. + * Do not check at jumps. + */ +static bool +is_reg_dead (unsigned regno, unsigned _pos) +{ +// skip labels. + for (unsigned pos = _pos + 1; pos < infos.size (); ++pos) + { + insn_info & ii = infos[pos]; + // skip entries without info + if (ii.is_empty ()) + continue; + + // not dead if usage is reported in the next statement + return !ii.is_use (regno) && !ii.is_hard (regno); + } + return true; +} + +bool dump_reg_track; +void +append_reg_cache (FILE * f, rtx_insn * insn) +{ + i2i_iterator i = insn2info.find (insn); + if (i == insn2info.end ()) + return; + + insn_info & jj = *i->second; + unsigned index = jj.get_index (); + if (index + 1 < infos.size ()) + ++index; + insn_info & ii = infos[index]; + + track_var * track = ii.get_track_var (); + if (track == 0) + return; + + fprintf (f, "\n"); + + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + { + rtx v = track->get (regno); + if (v == 0) + continue; + +// if (GET_CODE(v) == CONST_INT && GET_MODE(v) == VOIDmode) +// continue; + + fprintf (f, "%s=", reg_names[regno]); + + print_inline_rtx (f, v, 12); + fprintf (f, "\n"); + } +} + +/* helper stuff to enhance the asm output. */ +int my_flag_regusage; +void +append_reg_usage (FILE * f, rtx_insn * insn) +{ + i2i_iterator i = insn2info.find (insn); + if (i == insn2info.end ()) + return; + + insn_info & ii = *i->second; + + if (f != stderr) + { + if (be_very_verbose > 1) + fprintf (f, "\n\t\t\t\t\t|%d\t", ii.get_index ()); + else + fprintf (f, "\n\t\t\t\t\t|\t"); + } + + fprintf (f, "%c ", ii.in_proepi () == IN_PROLOGUE ? 'p' : ii.in_proepi () >= IN_EPILOGUE ? 'e' : ' '); + + for (int j = 0; j < 8; ++j) + if (ii.is_use (j) || ii.is_def (j)) + { + fprintf (f, ii.is_hard (j) ? "!" : " "); + fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " "); + fprintf (f, "d%d ", j); + } + else + fprintf (f, " "); + + for (int j = 8; j < 16; ++j) + if (ii.is_use (j) || ii.is_def (j)) + { + fprintf (f, ii.is_hard (j) ? "!" : " "); + fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " "); + fprintf (f, "a%d ", j - 8); + } + else + fprintf (f, " "); + + if (ii.is_use (FIRST_PSEUDO_REGISTER)) + fprintf (f, ii.is_def (FIRST_PSEUDO_REGISTER) ? "+cc " : " cc "); + else + fprintf (f, " "); + + // append fp usage info if present + if ((ii.get_use () | ii.get_def ()) & ~0xffff) + { + for (int j = 16; j < 24; ++j) + if (ii.is_use (j) || ii.is_def (j)) + { + fprintf (f, ii.is_hard (j) ? "!" : " "); + fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " "); + fprintf (f, "f%d ", j - 16); + } + else + fprintf (f, " "); + } + + if (f == stderr) + fprintf (f, "\n"); + +} + +/* + * Helper function to dump the code. + * Sometimes used during debugging. + */ +static void +dump_insns (char const * name, bool all) +{ + fprintf (stderr, "====================================: %s\n", name); + if (all) + { + for (rtx_insn * insn = get_insns (); insn && insn != infos[0].get_insn (); insn = NEXT_INSN (insn)) + debug_rtx (insn); + } + for (unsigned i = 0; i < infos.size (); ++i) + { + fprintf (stderr, "%d: ", i); + + rtx_insn * insn = infos[i].get_insn (); + if (i < infos.size ()) + append_reg_usage (stderr, insn); + + fprintf (stderr, "\t"); + debug_rtx (insn); + + if (all) + { + rtx_insn * p = i + 1 < infos.size () ? infos[i + 1].get_insn () : 0; + for (rtx_insn * q = NEXT_INSN (insn); q && q != p; q = NEXT_INSN (q)) + debug_rtx (q); + } + } +} + +/* This is the important function to track register usage plus hard/live state. + * + * Start at bottom and work upwards. On all labels trigger all jumps referring to this label. + * A set destination into a register is a def. All other register references are an use. + * Hard registers cann't be renamed and are mandatory for regparms and asm_operands. + */ +static void +update_insn_infos (void) +{ + /* add all return (jump outs) and start analysis there. */ + std::set & todo = scan_starts; + + if (todo.begin () == todo.end ()) + todo.insert (infos.size () - 1); + + bool locka4 = flag_pic >= 3; + + while (!todo.empty ()) + { + int start = *todo.begin (); + todo.erase (todo.begin ()); + insn_info ii = infos[start]; + + enum proepis proepi = ii.in_proepi (); + + // mark sp reg as used. + if (proepi >= IN_EPILOGUE) + ii.mark_use (STACK_POINTER_REGNUM), infos[start].mark_use (STACK_POINTER_REGNUM); + + for (int pos = start; pos >= 0; --pos) + { + insn_info & pp = infos[pos]; + rtx_insn * insn = pp.get_insn (); + + // do not run into previous epilogue + if (pp.in_proepi () >= IN_EPILOGUE && !proepi) + break; + + proepi = pp.in_proepi (); + + /* no new information -> break. */ + if (pos != start && pp.is_visited () && !JUMP_P(insn) && pp.contains (ii)) + break; + + ii.clear_hard_def (); + ii.merge (pp); + + if (LABEL_P(insn)) + { + /* work on all jumps referring to that label. */ + l2j_iterator i = label2jump.find (insn->u2.insn_uid); + + /* no jump to here -> mark all registers as hard regs. + * This label is maybe used in an exception handler. + * Marking as hard also avoids stack frame removal. + */ + if (i == label2jump.end ()) + infos[pos + 1].make_hard (); + else + for (l2j_iterator k = i; i != label2jump.end () && i->first == k->first; ++i) + { + i2i_iterator j = insn2info.find (i->second); + if (j != insn2info.end ()) + { + unsigned index = j->second->get_index (); + insn_info & jj = infos[index]; + if (!jj.is_visited () || !jj.contains (ii)) + { + jj.updateWith (ii); + todo.insert (index); + } + } + } + + if (pos == start) + pp.mark_visited (); + + /* check previous insn for jump */ + if (pos > 0 && infos[pos - 1].is_jump ()) + { + rtx_insn * prev = infos[pos - 1].get_insn (); + rtx set = single_set (prev); + /* unconditional? -> break! */ + if (set && SET_DEST(set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE) + break; + } + + continue; + } + + pp.mark_visited (); + + rtx pattern = PATTERN (insn); + insn_info use (insn); + use.scan (); + if (locka4 && (use.get_myuse () & (1 << PIC_REG))) + use.mark_hard (PIC_REG); + + /* do not mark a node as visited, if it's in epilogue and not yet visited. */ + if (CALL_P(insn) || JUMP_P(insn)) + { + if (pos != start && ii.in_proepi ()) + { + su_iterator k = scan_starts.find (pos); + if (k != scan_starts.end ()) + { + pp.clear_visited (); + break; + } + } + } + else if (GET_CODE (pattern) == USE || GET_CODE (pattern) == CLOBBER) + { + use.make_clobber (); + } + else if (single_set (insn) == 0) + use.make_hard (); + else + /* if not cc0 defined check for mod. */ + if (!use.is_def (FIRST_PSEUDO_REGISTER)) + { + CC_STATUS_INIT; + NOTICE_UPDATE_CC(PATTERN (insn), insn); + if (cc_status.value1 || cc_status.value2) + use.mark_def (FIRST_PSEUDO_REGISTER); + } + + // TODO: use 2 bits for data regs, to indicate mode size +// // also check mode size if < 4, it's also a use for data registers. +// if (pp.get_dst_reg () && pp.get_dst_regno () < 8 && GET_MODE_SIZE(pp.get_mode()) < 4) +// use.mark_use (pp.get_dst_regno ()); + + /* mark not renameable in prologue/epilogue. */ + if (pp.in_proepi () != IN_CODE) + use.make_hard (); + + ii.merge (use); + pp.update (ii); + ii.updateWith (use); + } + } + + /* fill the mask of general used regs. */ + insn_info zz; + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & ii = infos[i]; + if (ii.in_proepi () != IN_PROLOGUE) + break; + + zz.or_use (ii); + } + + /* always allow a0/a1, d0/d1. */ + usable_regs = zz.get_use () | 0x303; + if (flag_pic) + usable_regs &= ~(1 << PIC_REG); + + if (infos.size () && infos[0].is_use (FRAME_POINTER_REGNUM)) + usable_regs &= ~(1 << FRAME_POINTER_REGNUM); + + usable_regs &= ~(1 << STACK_POINTER_REGNUM); +} + +enum AbortCodes +{ + E_OK, E_NO_JUMP_LABEL, E_JUMP_TABLE_MISMATCH, E_JUMP_GOTO_LABEL, E_SP_MISMATCH +}; + +/* + * Create a filtered view of insns - keep only those to work with. + */ +static unsigned +update_insns () +{ + rtx_insn *insn, *next; + unsigned result = 0; + rtx jump_table = 0; + + clear (); + + enum proepis inproepilogue = IN_PROLOGUE; + /* create a vector with relevant insn. */ + for (insn = get_insns (); insn; insn = next) + { + next = NEXT_INSN (insn); + + if (NONJUMP_INSN_P (insn) || LABEL_P(insn) || JUMP_P(insn) || CALL_P(insn)) + { + + infos.push_back (insn_info (insn, inproepilogue)); + insn_info & ii = infos[infos.size () - 1]; + + if (JUMP_P(insn)) + { + if (inproepilogue || ANY_RETURN_P(PATTERN (insn))) + { + if (ANY_RETURN_P(PATTERN (insn))) + ii.set_proepi (IN_EPILOGUE); + + scan_starts.insert (infos.size () - 1); + inproepilogue = IN_CODE; + rtx set = single_set (insn); + if (ANY_RETURN_P(PATTERN (insn)) + || (set && SET_DEST(set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE)) + continue; + } + + ii.mark_jump (); + if (jump_table) + { + if (XEXP(jump_table, 0) != insn) + { + if (be_very_verbose) + { + debug_rtx (insn); + debug_rtx (jump_table); + } + result = E_JUMP_TABLE_MISMATCH; + jump_table = 0; + continue; + } + + // -> jump_table_data + rtx table = PATTERN (XEXP(jump_table, 1)); + if (GET_CODE(table) == ADDR_DIFF_VEC || GET_CODE(table) == ADDR_VEC) + { + int k = GET_CODE(table) == ADDR_DIFF_VEC; + for (int j = 0; j < XVECLEN(table, k); ++j) + { + rtx ref = XVECEXP(table, k, j); + if (!LABEL_REF_NONLOCAL_P(ref)) + { + rtx label = XEXP(ref, 0); + label2jump.insert (std::make_pair (label->u2.insn_uid, insn)); + ii.set_proepi (IN_EPILOGUE); + } + } + } + else + { + if (be_very_verbose) + { + debug_rtx (insn); + debug_rtx (jump_table); + } + result = E_JUMP_GOTO_LABEL; + jump_table = 0; + continue; + } + jump_table = 0; + } + else + { + rtx_insn * label = (rtx_insn *) JUMP_LABEL(insn); + if (!label) + { + if (be_very_verbose) + debug_rtx (insn); + result = E_NO_JUMP_LABEL; + continue; + } + label2jump.insert (std::make_pair (label->u2.insn_uid, insn)); + } + } + else if (LABEL_P(insn)) + { + ii.mark_label (); + jump_table = 0; + ii.set_proepi (inproepilogue = IN_CODE); + if (infos.size () > 1) + scan_starts.insert (infos.size () - 1); + } + else if (CALL_P(insn)) + { + if (insn->jump) + { + ii.set_proepi (IN_EPILOGUE); + ii.mark_jump (); + scan_starts.insert (infos.size () - 1); + } + ii.mark_call (); + if (inproepilogue) + { + scan_starts.insert (infos.size () - 1); + inproepilogue = IN_CODE; + } + } + else + { + rtx set = single_set (insn); + if (set) + ii.fledder (set); + + for (rtx next, note = REG_NOTES(insn); note; note = next) + { + next = XEXP(note, 1); + if (REG_NOTE_KIND (note) == REG_LABEL_OPERAND) + { + jump_table = XEXP(note, 0); + } + } + + } + } + else if (NOTE_P(insn)) + { + if (NOTE_KIND(insn) == NOTE_INSN_PROLOGUE_END) + inproepilogue = IN_CODE; + else if (NOTE_KIND(insn) == NOTE_INSN_EPILOGUE_BEG) + inproepilogue = IN_EPILOGUE; + } + } + scan_starts.insert (infos.size () - 1); + update_insn2index (); + update_insn_infos (); + + return result; +} + +/* convert the lowest set bit into a register number. */ +static int +bit2regno (unsigned bit) +{ + if (!bit) + return -1; + + unsigned regno = 0; + while (!(bit & 1)) + { + ++regno; + bit >>= 1; + } + return regno; +} + +/* check if that register is touched between from and to, excluding from and to .*/ +static bool +is_reg_touched_between (unsigned regno, int from, int to) +{ + for (int index = from + 1; index < to; ++index) + { + insn_info & ii = infos[index]; + if (ii.is_myuse (regno) || ii.is_def (regno)) + return true; + } + return false; +} + +/* + * search backward and find the initial assignment for that regno. + */ +static unsigned +find_start (unsigned start, unsigned rename_regno) +{ + /* search the start. */ + while (start > 0) + { + unsigned startm1 = start - 1; + + /* do not run over RETURNS */ + insn_info & jj = infos[start]; + + /* stop at labels. If a label is a start pos, a search is maybe started again. */ + if (jj.is_label ()) + break; + + insn_info & bb = infos[startm1]; + if (jj.in_proepi () == IN_CODE && bb.in_proepi () >= IN_EPILOGUE) + break; + + /* found the definition without use. */ + if (jj.is_def (rename_regno) && !jj.is_use (rename_regno)) + break; + + start = startm1; + } + return start; +} + +/* + * Always prefer lower register numbers within the class. + */ +static unsigned +opt_reg_rename (void) +{ + update_label2jump (); +// dump_insns ("rename", 1); + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + + /* do not rename if register is hard or used in same statement. */ + const unsigned rename_regbit = ii.get_regbit (); + if (!rename_regbit) + continue; + + const unsigned rename_regno = bit2regno (rename_regbit); + + /* get the mask for free registers. */ + unsigned mask = ii.get_free_mask (); + + /* the mask contains the current src register. Add this register to the mask if it's dead here. */ + if (ii.get_src_reg () && is_reg_dead (ii.get_src_regno (), index)) + mask |= ii.get_use (); + + /* do not use a4 if compiling baserel */ + if (flag_pic >= 3) + mask &= ~(1 << PIC_REG); + + if (!mask) + continue; + + /* first = pos to start, second indicates to treat def as use. */ + std::set todo; + std::set found; + if (index + 1 < infos.size ()) + todo.insert (index + 1); + + found.insert (index); + /* a register was defined, follow all branches. */ + while (mask && todo.begin () != todo.end ()) + { + unsigned runpos = *todo.begin (); + todo.erase (todo.begin ()); + +// printf ("runpos %d \n", runpos); fflush (stdout); + for (unsigned pos = runpos; mask && pos < infos.size (); ++pos) + { + /* already searched. */ + if (found.find (pos) != found.end ()) + break; + + rtx_insn * insn = infos[pos].get_insn (); + if (LABEL_P(insn)) + { + found.insert (pos); + + /* for each jump to this label: + * check if the reg was used at that jump. + * if used, find def + */ + for (l2j_iterator i = label2jump.find (insn->u2.insn_uid), k = i; + i != label2jump.end () && i->first == k->first; ++i) + { + i2i_iterator j = insn2info.find (i->second); + if (j == insn2info.end ()) + { + mask = 0; + break; + } + + unsigned startat = j->second->get_index (); + if (found.find (startat) != found.end () || !infos[startat].is_use (rename_regno)) + continue; + + unsigned start = find_start (startat, rename_regno); +// printf ("label %d <- jump %d : start %d\n", pos, startat, start); fflush (stdout); + todo.insert (start); + } + + /* if this label is at a start, check if it is reachable from the previous insn, + * and if, check for use then search start. */ + if (pos > 0) + { + insn_info & bb = infos[pos - 1]; + rtx set = single_set (bb.get_insn ()); + if (ANY_RETURN_P(bb.get_insn ()) + || (set && SET_DEST(set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE)) + continue; + +// printf ("label start check %d use %d\n", pos, bb.is_use (rename_regno) || bb.is_def(rename_regno)); fflush (stdout); + + if (bb.is_use (rename_regno) || bb.is_def (rename_regno)) + { + unsigned start = find_start (pos - 1, rename_regno); + todo.insert (start); +// printf ("label %d : start %d \n", pos, start); fflush (stdout); + } + } + + continue; + } + + insn_info & jj = infos[pos]; + + /* marked as hard reg -> invalid rename */ + if (jj.get_use () & jj.get_hard () & rename_regbit) + { + mask = 0; + break; + } + + /* not used. and not a def */ + if (pos == runpos && (jj.get_def () & rename_regbit)) + { + /* continue since this pos was added by start search. */ + } + else if (!(jj.get_use () & rename_regbit)) + break; + + /* abort if some insn using this reg uses more than 1 reg. */ + if ((jj.get_myuse () & rename_regbit) && GET_MODE_SIZE(jj.get_mode()) > 4) + { + mask = 0; + break; + } + + /* update free regs. */ + mask &= ~jj.get_use (); + mask &= ~jj.get_def (); + if (!mask) + break; + + found.insert (pos); + + /* follow jump and/or next insn. */ + if (JUMP_P(insn)) + { + for (j2l_iterator i = jump2label.find (pos), k = i; i != jump2label.end () && i->first == k->first; + ++i) + { + unsigned label_index = i->second; + + /* add the label to the search list. */ + insn_info & bb = infos[label_index + 1]; + if (found.find (label_index) == found.end () && bb.is_use (rename_regno)) + { +// printf ("jump %d -> label %d \n", pos, label_index); fflush (stdout); + todo.insert (label_index); + } + } + rtx set = single_set (insn); + if (!set) + { + // it's a parallel pattern - search the set pc = ... + rtx pat = PATTERN (insn); + for (int j = XVECLEN (pat, 0) - 1; j >= 0; j--) + { + rtx x = XVECEXP(pat, 0, j); + if (XEXP(x, 0) == pc_rtx) + { + set = x; + break; + } + } + } + rtx jmpsrc = set ? SET_SRC(set) : 0; + if (!jmpsrc || GET_CODE(jmpsrc) != IF_THEN_ELSE) + break; + } + } + } + + if (mask) + { + int oldregno = bit2regno (rename_regbit); + int newregno = bit2regno (mask); + + /* check the renamed insns. */ + std::vector positions; + for (std::set::iterator i = found.begin (); i != found.end (); ++i) + { + insn_info & rr = infos[*i]; + rtx_insn * insn = rr.get_insn (); + + /* get rename locations. */ + rtx from = find_reg_by_no (PATTERN (insn), oldregno); + if (from) + { + rtx to = gen_raw_REG (GET_MODE(from), newregno); + validate_replace_rtx_group (from, to, insn); + + positions.push_back (*i); + } + } + + if (!apply_change_group ()) + continue; + + log ("(r) opt_reg_rename %s -> %s (%d locs, start at %d)\n", reg_names[oldregno], reg_names[newregno], + positions.size (), index); + + if (be_verbose) + { + for (std::vector::iterator i = positions.begin (); i != positions.end (); ++i) + printf ("%d ", *i); + printf ("\n"); + fflush (stdout); + } + + return 1; + } + } + return 0; +} + +/* + * #1 propagate a->b->a moves out of a loop. + * + * consider a loop: + * + * .L1 + * ... + * move d0, a0 ; (1) + * ... + * move xy, (a0)+ + * ... + * move a0, d0 ; (2) + * ... + * jxx .L1 + * + * Then the statements (1) and (2) can be moved out of the loop: + * + * move d0, a0 ; (3) + * .L1 + * ... + * move *, (a0)+ ; a0 is modified somehow + * ... + * jxx .L1 + * move a0, d0 ; (4) + * + * if all criteria are met: + * + * a) no other jump to .L1 -> (LABEL_NUSES(insn) == 1) + * b) no other use of d0 inside the loop + * c) no other use of a0 before (1) + * d) no other use of a1 after (2) + * + * Optional: + * - omit (4) if d0 is dead + * + * this will e.g. convert + .L6: + move.l d0,a1 + move.b (a1)+,d1 + move.l a1,d0 + move.b d1,(a0)+ + cmp.b #0, d1 + jne .L6 + * to + move.l d0,a1 + .L6: + move.b (a1)+,d1 + move.b d1,(a0)+ + cmp.b #0, d1 + jne .L6 + + * + * Also allow exit jumps, if the modification of the reg is const + * and insert a correction after the exit label. + * The label must only be reachable by the exit jump. + */ +static unsigned +opt_propagate_moves () +{ + unsigned change_count = 0; + rtx_insn * current_label = 0; + unsigned current_label_index; + std::vector reg_reg; + std::vector jump_out; + + /* start at 1 since there must be an insn before the label. */ + for (unsigned index = 1; index < infos.size (); ++index) + { + rtx_insn * insn = infos[index].get_insn (); + + if (LABEL_P(insn)) + { + if (LABEL_NUSES(insn) == 1) + { + current_label = insn; + current_label_index = index; + reg_reg.clear (); + jump_out.clear (); + } + else + current_label = 0; + } + + if (current_label == 0) + continue; + + if (NONJUMP_INSN_P(insn)) + { + // check for set reg, reg + rtx set = single_set (insn); + if (set) + { + rtx src = SET_SRC(set); + rtx dst = SET_DEST(set); + if (REG_P(src) && REG_P(dst)) + reg_reg.push_back (index); + } + else + current_label = 0; + + continue; + } + + if (JUMP_P(insn)) + { + rtx_insn * label = (rtx_insn *) JUMP_LABEL(insn); + if (label != current_label) + { + /* collect the labels for a later check if a fixup is possible. */ + if (LABEL_NUSES(label) == 1 && BARRIER_P(PREV_INSN (label))) + jump_out.push_back (label); + else + current_label = 0; + continue; + } + + if (reg_reg.size () > 1) + { + /* Search for reg/reg pairs. */ + for (std::vector::iterator i = reg_reg.begin (); i != reg_reg.end () && i + 1 != reg_reg.end (); + ) + { + bool inc = true; + for (std::vector::iterator j = i + 1; j != reg_reg.end ();) + { + rtx_insn * ii = infos[*i].get_insn (); + rtx seti = single_set (ii); + rtx srci = SET_SRC(seti); + rtx dsti = SET_DEST(seti); + rtx_insn * jj = infos[*j].get_insn (); + rtx setj = single_set (jj); + rtx srcj = SET_SRC(setj); + rtx dstj = SET_DEST(setj); + + if (rtx_equal_p (srci, dstj) && rtx_equal_p (srcj, dsti)) + { + /* Ensure correct usage. */ + if (is_reg_touched_between (REGNO(srci), current_label_index, *i) // label ... move src,x + || is_reg_touched_between (REGNO(srci), *i, *j) // move src,x ... move x,src + || is_reg_touched_between (REGNO(srci), *j, index) // move x,src ... jcc + || is_reg_touched_between (REGNO(dsti), *j, index) // label ... move src,x + || is_reg_touched_between (REGNO(dsti), *j, index) // move x,src ... jcc + ) + { + ++j; + continue; + } + + std::vector fixups; + + /* if there are jumps out of the loop, + * check if the modification occurs before the jump, + * and if, that it's a plus const. + */ + if (jump_out.size ()) + { + std::vector::iterator label_iter = jump_out.begin (); + int fixup = 0; + + for (unsigned k = *i + 1; k != *j; ++k) + { + rtx_insn * check = infos[k].get_insn (); + if (JUMP_P(check)) + { + fixups.push_back (fixup); + if (++label_iter == jump_out.end ()) + break; + continue; + } + + if (reg_overlap_mentioned_p (dsti, PATTERN (check))) + { + /* right now only support auto_incs. */ + rtx set = single_set (check); + rtx src = SET_SRC(set); + rtx dst = SET_DEST(set); + + if (reg_overlap_mentioned_p (dsti, dst)) + { + if (REG_P(dst)) + break; + if (!MEM_P(dst)) + break; + + rtx x = XEXP(dst, 0); + if (GET_CODE(x) == REG) + fixup += 0; // direct use + else if (GET_CODE(x) == PRE_INC || + GET_CODE(x) == POST_INC) + fixup -= GET_MODE_SIZE(GET_MODE(dst)); + else if (GET_CODE(dst) == PRE_DEC || + GET_CODE(dst) == POST_DEC) + fixup += GET_MODE_SIZE(GET_MODE(dst)); + else + break; + } + + if (reg_overlap_mentioned_p (dsti, src)) + { + if (REG_P(src)) + fixup += 0; + else + { + if (!MEM_P(src)) + break; + + rtx x = XEXP(src, 0); + if (GET_CODE(x) == REG) + fixup += 0; // direct use + else if (GET_CODE(x) == PRE_INC || + GET_CODE(x) == POST_INC) + fixup -= GET_MODE_SIZE(GET_MODE(dst)); + else if (GET_CODE(dst) == PRE_DEC || + GET_CODE(dst) == POST_DEC) + fixup += GET_MODE_SIZE(GET_MODE(dst)); + else + break; + } + } + } + } + } + + /* got a fixup for all jump_outs? */ + if (fixups.size () == jump_out.size ()) + { + rtx_insn * before = infos[current_label_index - 1].get_insn (); + rtx_insn * after = infos[index + 1].get_insn (); + rtx bset = single_set (before); + + log ("(p) propagate_moves condition met, moving regs %s, %s\n", + reg_names[REGNO(srci)], + reg_names[REGNO(dsti)]); + + /* Move in front of loop and mark as dead. */ + rtx_insn * newii = make_insn_raw (PATTERN (ii)); + SET_INSN_DELETED(ii); + + /* Plus check if the reg was just loaded. */ + if (bset) + { + rtx bdst = SET_DEST(bset); + if (REG_P(bdst) && REGNO(bdst) == REGNO(srci)) + { + SET_SRC(PATTERN(newii)) = SET_SRC(bset); +// SET_INSN_DELETED(ii); + } + } + else + add_reg_note (newii, REG_DEAD, srci); + + add_insn_after (newii, before, 0); + + /* Move behind loop - into next BB. */ + rtx_insn * newjj = make_insn_raw (PATTERN (jj)); + add_insn_before (newjj, after, 0); + SET_INSN_DELETED(jj); + + reg_reg.erase (j); + reg_reg.erase (i); + j = reg_reg.end (); + inc = false; + + /* add fixes if there were jumps out of the loop. */ + if (jump_out.size ()) + { + log ("(p) propagate_moves fixing %d jump outs\n", jump_out.size ()); + + for (unsigned k = 0; k < jump_out.size (); ++k) + { + rtx neu = gen_rtx_SET( + dstj, gen_rtx_PLUS (Pmode, dsti, gen_rtx_CONST_INT (Pmode, fixups[k]))); + emit_insn_after (neu, jump_out[k]); + } + } + ++change_count; + } + } + if (inc) + ++j; + } + if (inc) + ++i; + } + } + current_label = 0; + } + } + return change_count; +} + +/** + * Search for + * + * mov x,reg + * mov reg,x + * cmp #0, reg + * jxx + * + * patterns. + * + * Use a simple state machine to find the patterns. + */ +static unsigned +opt_strcpy () +{ + unsigned change_count = 0; +#if HAVE_cc0 + rtx_insn * x2reg = 0; + rtx_insn * reg2x; + unsigned int regno; + + for (unsigned index = 0; index < infos.size (); ++index) + { + rtx_insn * insn = infos[index].get_insn (); + + if (!NONJUMP_INSN_P(insn)) + { + x2reg = 0; + continue; + } + + rtx set = single_set (insn); + if (!set) + { + x2reg = 0; + continue; + } + + if (x2reg && reg2x) + { + rtx src = SET_SRC(set); + if (GET_CODE(src) == COMPARE) + { + rtx dst = XEXP(src, 0); + src = XEXP(src, 1); + +// if (CONST_INT_P(src) && INTVAL(src) == 0 && find_reg_note (insn, REG_DEAD, dst)) + if (REG_P(dst) && CONST_INT_P(src) && INTVAL(src) == 0 && is_reg_dead (REGNO(dst), index)) + { + /* now check via NOTICE_UPDATE_CC*/ + NOTICE_UPDATE_CC(PATTERN (reg2x), reg2x); + if (cc_status.flags == 0 && rtx_equal_p (dst, cc_status.value2)) + { + rtx pattern = gen_rtx_SET(SET_DEST(single_set (reg2x)), SET_SRC(single_set (x2reg))); + rtx_insn * newinsn = make_insn_raw (pattern); + + if (!insn_invalid_p (newinsn, 0)) + { + log ("(s) opt_strcpy condition met, removing compare and joining insns - omit reg %s\n", + reg_names[REGNO(dst)]); + + SET_INSN_DELETED(x2reg); + SET_INSN_DELETED(reg2x); + SET_INSN_DELETED(insn); + + insn = emit_insn_after (pattern, reg2x); + insn_invalid_p (insn, 0); + + ++change_count; + } + } + } + x2reg = 0; + continue; + } + reg2x = 0; + } + + /* check for reg2x first, maybe fallback to x2reg. */ + if (x2reg && reg2x == 0) + { + if (REG_P(SET_SRC(set)) && REGNO(SET_SRC(set)) == regno) + { + reg2x = insn; + continue; + } + x2reg = 0; + } + + /* check for a match for x2reg. */ + if (x2reg == 0) + { + if (REG_P(SET_DEST(set))) + { + x2reg = insn; + reg2x = 0; + regno = REGNO(SET_DEST(set)); + } + } + } +#endif + return change_count; +} + +/* + * convert + * + * set reg1, plus (reg2, const) + * set mem(reg2), y + * + * -> + * set reg1, reg2 + * set mem(reg1+), y + * + * if size of postinc == const + * + (insn 33 32 35 4 (set (reg/v/f:SI 8 a0 [orig:47 s ] [47]) + (plus:SI (reg/v/f:SI 9 a1 [orig:46 s ] [46]) + (const_int 1 [0x1]))) sn.c:5 141 {*addsi3_internal} + (nil)) + (insn 36 35 37 4 (set (mem:QI (reg/v/f:SI 9 a1 [orig:46 s ] [46]) [0 MEM[base: s_17, offset: 4294967295B]+0 S1 A8]) + (mem:QI (post_inc:SI (reg/v/f:SI 10 a2 [orig:53 s2 ] [53])) [0 MEM[base: s2_19, offset: 4294967295B]+0 S1 A8])) sn.c:5 46 {*m68k.md:1083} + (expr_list:REG_INC (reg/v/f:SI 10 a2 [orig:53 s2 ] [53]) + (nil))) + */ +static unsigned +opt_commute_add_move (void) +{ + unsigned change_count = 0; + + for (unsigned index = 0; index + 1 < infos.size (); ++index) + { + insn_info & ii = infos[index]; + if (ii.get_dst_regno () < 8 || ii.get_dst_regno () > 15 || ii.get_src_op () != PLUS + || ii.get_src_regno () == ii.get_dst_regno () || !ii.get_src_intval ()) + continue; + + insn_info & jj = infos[index + 1]; + + if (!jj.get_dst_mem_reg () || jj.get_dst_mem_regno () != ii.get_src_regno () + || jj.get_src_regno () == ii.get_dst_regno () || GET_MODE_SIZE(jj.get_mode()) != ii.get_src_intval ()) + continue; + + rtx_insn * insn = ii.get_insn (); + + rtx_insn * next = jj.get_insn (); + rtx set2 = single_set (next); + rtx dst = SET_DEST(set2); + if (!MEM_P(dst)) + continue; + + rtx pinc = gen_rtx_POST_INC(GET_MODE(dst), ii.get_dst_reg ()); + rtx newmem = replace_equiv_address_nv (dst, pinc); + + rtx_insn * newinsn = make_insn_raw (gen_rtx_SET(ii.get_dst_reg (), ii.get_src_reg ())); + + if (!insn_invalid_p (newinsn, 1) && validate_change (next, &SET_DEST(set2), newmem, 1) && apply_change_group ()) + { + log ("(a) commute_add_move found\n"); + + SET_INSN_DELETED(insn); + + insn = emit_insn_before (newinsn, next); + + add_reg_note (next, REG_INC, ii.get_dst_reg ()); + + ++change_count; + } + else + cancel_changes (0); + } + return change_count; +} + +/* + * Replace + * + * move x,dx + * cmp dx,dy + * + * if dx and dy are both dead after compare. + * + * with + * + * sub #n,dx + * + d0 d1 d2 a0 a1 a7 (insn 99 59 41 7 (set (reg:SI 2 d2) + (const_int 1 [0x1])) sn.c:8 38 {*movsi_m68k} + (nil)) + d0 d1 d2 a0 a1 a7 (insn 41 99 42 7 (set (cc0) + (compare (reg/v:SI 1 d1 [orig:54 n ] [54]) + (reg:SI 2 d2))) sn.c:8 16 {*m68k.md:499} + (expr_list:REG_DEAD (reg:SI 2 d2) + (expr_list:REG_DEAD (reg/v:SI 1 d1 [orig:54 n ] [54]) + (nil)))) + * + */ +static unsigned +opt_const_cmp_to_sub (void) +{ + unsigned change_count = 0; +#if HAVE_cc0 + if (infos.size () < 2) + return change_count; + + unsigned lastsub = 0; + for (unsigned index = infos.size () - 2; index > 0; --index) + { + insn_info & i1 = infos[index]; + + /* we wan't a compare or tst insn, */ + if (!i1.is_compare ()) + continue; + + if (GET_MODE_SIZE(i1.get_mode()) > 4 || !i1.is_dst_reg () || REGNO(i1.get_dst_reg()) > 7) + continue; + + /* src must be a reg dead register with a constant - or a #0 */ + if (!i1.get_src_reg () && (!i1.is_src_const () || i1.get_src_op () == PLUS)) + continue; + + /* allow an alive reg, if life ends at previous handled sub. */ + int lastsubval = 0; + if (lastsub == index + 3) + { + insn_info & pp = infos[lastsub]; + if (pp.get_dst_regno () != i1.get_dst_regno ()) + continue; + lastsubval = pp.get_src_intval (); + + // but still check for usage after this jump + j2l_iterator l = jump2label.find (index + 2); + if (l == jump2label.end ()) + continue; + + insn_info & label = infos[l->second + 1]; + if (label.is_use (i1.get_dst_regno ())) + continue; + } + else if (!is_reg_dead (i1.get_dst_regno (), index)) + continue; + + insn_info & i0 = infos[index - 1]; + int intval = 0; + /* compare with register - check previous insn for load with constant. */ + if (i1.is_src_reg ()) + { + if (!is_reg_dead (i1.get_src_regno (), index)) + continue; + + if (GET_MODE_SIZE(i0.get_mode()) > 4) + continue; + + if (!i0.is_dst_reg () || !i0.is_src_const () || i0.get_src_op ()) + continue; + + if (i0.get_dst_regno () != i1.get_src_regno ()) + continue; + + intval = -i0.get_src_intval (); + if (intval < -8 || intval > 7) + continue; + + /* is the next sub value in range? */ + if (lastsub == index + 3 && (lastsubval - intval < -8 || lastsubval - intval > 7)) + continue; + } + + /* next insn must be the jump. */ + insn_info & i2 = infos[index + 1]; + if (!i2.is_jump ()) + continue; + + rtx jmppattern = single_set (i2.get_insn ()); + if (!jmppattern) + continue; + + rtx jmpsrc = XEXP(jmppattern, 1); + if (GET_CODE(jmpsrc) != IF_THEN_ELSE) + continue; + + rtx condition = XEXP(jmpsrc, 0); + RTX_CODE code = GET_CODE(condition); + if (code != EQ && code != NE) + continue; + + if (intval) + { + rtx copyreg = copy_reg (i1.get_dst_reg (), -1); + /* create the sub statement. */ + rtx sub = gen_rtx_PLUS(i1.get_mode (), copyreg, gen_rtx_CONST_INT (i1.get_mode (), intval)); + + rtx_insn * subinsn = make_insn_raw (gen_rtx_SET(copyreg, sub)); + + if (insn_invalid_p (subinsn, 0)) + continue; + + /* delete move #x,dy. */ + SET_INSN_DELETED(i0.get_insn ()) + /* delete cmp dx,dy */ + SET_INSN_DELETED(i1.get_insn ()); + /* add a cmp #0 - to be removed in final() */ + + /* convert cmp/tst into sub */ + subinsn = emit_insn_before (PATTERN (subinsn), i1.get_insn ()); + i1.set_insn (subinsn); + + rtx neu = gen_rtx_SET(cc0_rtx, + gen_rtx_COMPARE (i1.get_mode (), copyreg, gen_rtx_CONST_INT (i1.get_mode (), 0))); + + emit_insn_before (neu, i2.get_insn ()); + + log ("(c) const_cmp_to_sub replaced %s == %s (%d) with sub %d,%s\n", reg_names[i1.get_dst_regno ()], + reg_names[i0.get_dst_regno ()], + -intval, -intval, reg_names[i1.get_dst_regno ()]); + + if (index + 3 == lastsub) + { + /* patch previous sub - or even a compare. */ + insn_info & pp = infos[lastsub]; + + int diff = lastsubval - intval; + rtx c = gen_rtx_CONST_INT (i1.get_mode (), diff); + + if (pp.is_compare ()) + { + /* still a compare with 0 -> insert the sub. */ + rtx copyreg = copy_reg (i1.get_dst_reg (), -1); + /* create the sub statement. */ + rtx sub = gen_rtx_PLUS(i1.get_mode (), copyreg, c); + rtx set = gen_rtx_SET(copyreg, sub); + emit_insn_before (set, pp.get_insn ()); + } + else + { + /* modify the sub. */ + XEXP(SET_SRC(PATTERN(pp.get_insn())), 1) = c; + } + } + + lastsub = index; + ++change_count; + } + } +#endif + return change_count; +} + +/* + * rare and only little gain - but :-) + lea (-1,a0),a1 + add.l d1,a1 + subq.l #1,d1 + -> + move.l a0,a1 + subq.l #1,d1 + add.l d1,a1 + */ +static unsigned +opt_merge_add (void) +{ + unsigned change_count = 0; + for (unsigned index = 0; index + 2 < infos.size (); ++index) + { + insn_info & ii0 = infos[index]; + insn_info & ii1 = infos[index + 1]; + insn_info & ii2 = infos[index + 2]; + + if (!ii2.is_dst_reg ()) + { + index += 2; + continue; + } + + if (!ii1.is_dst_reg ()) + { + ++index; + continue; + } + + if (!ii0.is_dst_reg () || ii0.get_src_op () != PLUS || ii1.get_src_op () != PLUS || ii2.get_src_op () != PLUS) + continue; + + if (!ii0.is_src_const () || !ii2.is_src_const () || ii0.get_src_intval () != ii2.get_src_intval ()) + continue; + + if (ii0.get_dst_regno () != ii1.get_dst_regno () || ii1.get_src_regno () != ii2.get_dst_regno ()) + continue; + + rtx_insn * insn1 = ii1.get_insn (); + + CC_STATUS_INIT; + NOTICE_UPDATE_CC(PATTERN (insn1), insn1); + if (cc_status.value1 || cc_status.value2) + continue; + + log ("(m) %d: merge_add applied\n", index); + + rtx_insn * insn0 = ii0.get_insn (); + rtx set = PATTERN (insn0); + + // convert lea (-1,a0),a1 into move.l a0,a1 + rtx_insn * newins0 = make_insn_raw (gen_rtx_SET(XEXP(set, 0), XEXP(XEXP(set, 1), 0))); + add_insn_after (newins0, insn0, 0); + SET_INSN_DELETED(insn0); + // update infos accordingly + ii0.plus_to_move (newins0); + + rtx_insn * insn2 = ii2.get_insn (); + rtx_insn * newins1 = make_insn_raw (PATTERN (insn1)); + add_insn_after (newins1, insn2, 0); + SET_INSN_DELETED(insn1); + ii1.swap_adds (newins1, ii2); + + ++change_count; + } + return change_count; +} + +/* Update the insn_infos to 'know' the sp offset. */ +static unsigned +track_sp () +{ +// reset visited flags - also check if sp is used as REG src. + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + ii.clear_visited (); + ii.set_sp_offset (0); + + // if sp is used as source, we cannot shrink the stack yet + // too complicated + if (ii.get_src_regno () == STACK_POINTER_REGNUM) + return -1; + } + +// add entry point + std::set todo; + todo.insert (0); + + while (todo.begin () != todo.end ()) + { + unsigned startpos = *todo.begin (); + todo.erase (todo.begin ()); + + int sp_offset = infos[startpos].get_sp_offset (); + + for (unsigned index = startpos; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + if (ii.in_proepi () != IN_CODE) + { + ii.set_sp_offset (sp_offset); + continue; + } + + // already visited? sp_offset must match + if (ii.is_visited ()) + { + if (ii.get_sp_offset () != sp_offset) + return E_SP_MISMATCH; + break; + } + + // mark current insn_info and set sp_offset + ii.mark_visited (); + ii.set_sp_offset (sp_offset); + + // add all referred labels + if (ii.is_jump ()) + { + for (j2l_iterator i = jump2label.find (index), k = i; i != jump2label.end () && i->first == k->first; ++i) + { + insn_info & ll = infos[i->second]; + if (ll.is_visited () && ll.get_sp_offset () != sp_offset) + return E_SP_MISMATCH; + + ll.set_sp_offset (sp_offset); + todo.insert (i->second); + } + continue; + } + + // is sp modified directly + if (ii.is_dst_reg () && ii.get_dst_regno () == STACK_POINTER_REGNUM) + { + // handle sp = sp + const_int + if (!ii.get_src_reg () || ii.get_src_regno () != STACK_POINTER_REGNUM || ii.get_src_op () != PLUS) + return E_SP_MISMATCH; + + sp_offset = sp_offset + ii.get_src_intval (); + continue; + } + + // handle dst mem autoinc + if (ii.is_dst_mem () && ii.get_dst_mem_regno () == STACK_POINTER_REGNUM && ii.get_dst_autoinc ()) + sp_offset += GET_MODE_SIZE(ii.get_mode()) * ii.get_dst_autoinc (); + + // handle src mem autoinc + if (ii.is_src_mem () && ii.get_src_mem_regno () == STACK_POINTER_REGNUM && ii.get_src_autoinc ()) + sp_offset += GET_MODE_SIZE(ii.get_mode()) * ii.get_src_autoinc (); + } + } + + return 0; +} + +/* recursive function to patch stack pointer offsets. */ +void +patch_sp (rtx x, int adjust, int spoffset) +{ + int code = GET_CODE(x); + if (code == PLUS) + { + rtx a = XEXP(x, 0); + rtx b = XEXP(x, 1); + if (REG_P(a) && REGNO(a) == STACK_POINTER_REGNUM && GET_CODE(b) == CONST_INT) + { + if (INTVAL(b) > -spoffset) + XEXP(x, 1) = gen_rtx_CONST_INT (GET_MODE(b), INTVAL(b) - adjust); + return; + } + } + const char *fmt = GET_RTX_FORMAT(code); + for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + patch_sp (XEXP(x, i), adjust, spoffset); + else if (fmt[i] == 'E') + for (int j = XVECLEN (x, i) - 1; j >= 0; j--) + patch_sp (XVECEXP(x, i, j), adjust, spoffset); + } +} + +/** + * 1. scan for all used registers. + * 2. scan the stack from for omittable push/pop + * 3. adjust stack frame + insns referring to stack pointer + * typical code: + subq.l #4,sp + movem.l #16190,-(sp) + move.l 52(sp),d2 + move.l 56(sp),d3 + + * or + link a5,#4 + movem.l #16190,-(sp) + move.l 8(a5),d2 + move.l 12(a5),d3 + * + * => with a5 check only prolog/epilog + * => without a5 adjust insns referring sp if offset > startoffset + current sp diff + * + * startvalue count(pushes)*4 + * newstartvalue = startvalue - omitted pushes + */ +static unsigned +opt_shrink_stack_frame (void) +{ + /* nothing to do. */ + if (!infos.size ()) + return 0; + + /* needed to track sp correctly. */ + update_label2jump (); + if (track_sp ()) + return 0; // do nothing on stack errors + + std::vector a5pos; + + unsigned pos = 0; + rtx_insn * insn = infos[pos].get_insn (); + if (JUMP_P(insn)) /* return -> empty function*/ + return 0; + + bool usea5 = false; + int paramstart = 4; + int a5offset = 0; + + /* + * Move prologue to temp. + * Only register push and parallel insn unless its a link a5 are moved. + */ + for (; pos < infos.size ();) + { + insn_info & ii = infos[pos]; + insn = ii.get_insn (); + + if (ii.in_proepi () != IN_PROLOGUE) + break; + + rtx pattern = PATTERN (insn); + if (GET_CODE(pattern) == PARALLEL) + { + rtx set = XVECEXP(pattern, 0, 0); + rtx dst = SET_DEST(set); + ii.mark_stack (); + /* ignore link a5 */ + if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM) + { + a5pos.push_back (pos); + usea5 = true; + set = XVECEXP(pattern, 0, 2); + a5offset = INTVAL(XEXP(SET_SRC(set), 1)); + } + ++pos; + continue; + } + if (GET_CODE(pattern) != SET) + { + /* (set (mem:BLK (scratch) [0 A8]) (unspec:BLK [ ...)) */ + if (MEM_P(SET_DEST(pattern)) && GET_CODE(SET_SRC(pattern)) == UNSPEC) + a5pos.push_back (pos); + ++pos; + continue; + } + + /* move only the push statements. */ + rtx src = SET_SRC(pattern); + rtx dest = SET_DEST(pattern); + if (REG_P(src)) + { + if (MEM_P(dest)) + { + rtx predec = XEXP(dest, 0); + if (GET_CODE(predec) == PRE_DEC) + { + rtx reg = XEXP(predec, 0); + if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM) + { + ii.mark_stack (); + } + } + } + } + else if (GET_CODE(src) == PLUS && REG_P(dest) && REGNO(dest) == STACK_POINTER_REGNUM) + { + /* check for stack variables. */ + rtx reg = XEXP(src, 0); + rtx cx = XEXP(src, 1); + if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM && CONST_INT_P(cx)) + paramstart -= INTVAL(cx); + } + + if (++pos >= infos.size ()) + { + return 0; + } + } + + if (pos == 0) + return 0; + + unsigned prologueend = pos; + + /* search epilogues - there can be multiple epilogues. */ + while (pos < infos.size ()) + { + while (pos < infos.size ()) + { + if (infos[pos].in_proepi () != IN_CODE) + break; + ++pos; + } + + /* move epilogues away. */ + for (; pos < infos.size (); ++pos) + { + insn_info & ii = infos[pos]; + insn = ii.get_insn (); + if (JUMP_P(insn) || LABEL_P(insn) || ii.in_proepi () == IN_CODE) + break; + + /* omit the frame pointer a5. */ + rtx pattern = PATTERN (insn); + if (GET_CODE(pattern) == PARALLEL) + { + rtx set = XVECEXP(pattern, 0, 0); + rtx dst = SET_DEST(set); + ii.mark_stack (); + /* unlink is last. */ + if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM) + { + a5pos.push_back (pos); + break; + } + + } + else if (GET_CODE(pattern) == SET) + { + /* check for move (a7+), x */ + rtx src = SET_SRC(pattern); + rtx dst = SET_DEST(pattern); + if (REG_P(dst)) + { + if (MEM_P(src)) + { + rtx postinc = XEXP(src, 0); + if (GET_CODE(postinc) == POST_INC) + { + rtx reg = XEXP(postinc, 0); + if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM) + ii.mark_stack (); + } + else if (GET_CODE(postinc) == PLUS) + { + rtx a5 = XEXP(postinc, 0); + if (REG_P(a5) && REGNO(a5) == FRAME_POINTER_REGNUM) + ii.mark_stack (); + } + } + } + } + } + ++pos; + } + /* gather usage stats without prologue/epilogue */ + insn_info ii; + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & jj = infos[i]; + if (jj.in_proepi () != IN_CODE) + continue; + + ii.or_use (jj); + } + unsigned freemask = ~ii.get_use () & 0x7fff; + + rtx a7 = gen_raw_REG (SImode, STACK_POINTER_REGNUM); + rtx a5 = gen_raw_REG (SImode, FRAME_POINTER_REGNUM); + + unsigned changed = 0; + unsigned adjust = 0; + unsigned regs_seen = 0; + unsigned regs_total_size = 0; + /* now all push/pop insns are in temp. */ + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & ii = infos[i]; + if (!ii.is_stack ()) + continue; + + insn = ii.get_insn (); + rtx pattern = PATTERN (insn); + /* check the pushed regs, either a vector or single statements */ + if (GET_CODE(pattern) == PARALLEL) + { + // do not touch the frame pointer parallel insn. + rtx set = XVECEXP(pattern, 0, 0); + rtx dst = SET_DEST(set); + if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM) + continue; + + if (ii.in_proepi () == IN_EPILOGUE) + ii.set_proepi (IN_EPILOGUE_PARALLEL_POP); + + regs_seen = 0; + regs_total_size = 0; + std::vector regs; + std::vector clobbers; + for (int j = 0; j < XVECLEN(pattern, 0); ++j) + { + rtx set = XVECEXP(pattern, 0, j); + if (GET_CODE(set) == CLOBBER) + { + clobbers.push_back (set); + continue; + } + rtx dst = SET_DEST(set); + rtx src = SET_SRC(set); + rtx reg; + if (MEM_P(src)) + reg = dst; + else if (MEM_P(dst)) + reg = src; + else + continue; + + if (i < prologueend) + paramstart += 4; + unsigned regbit = 1 << REGNO(reg); + + ++regs_seen; + if (freemask & regbit) + { + log (i < prologueend ? "(f) remove push for %s\n" : "(f) remove pop for %s\n", + reg_names[REGNO(reg)]); + if (i < prologueend) + adjust += GET_MODE_SIZE(GET_MODE(reg)); + } + else + { + regs_total_size += GET_MODE_SIZE(GET_MODE(reg)); + regs.push_back (copy_reg (reg, -1)); + } + } + + /* add room for add. + * push is always using -(a7) addressing. + * If a5 is used a movem offset(a5) is generated to pop saved registers.. + * Otherwise a7 is used and with (a7)+ addressing. + */ + int add1 = i < prologueend || !usea5 ? 1 : 0; + if (regs.size () < regs_seen) + { + log ("(f) shrinking stack frame from %d to %d\n", regs_seen, regs.size ()); + if (regs.size () <= 2) + { + changed = 1; + for (unsigned k = 0; k < regs.size (); ++k) + { + rtx reg = regs[k]; + if (i < prologueend) + { + /* push */ + rtx dec = gen_rtx_PRE_DEC(REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, a7); + rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, dec); + rtx set = gen_rtx_SET(mem, reg); + emit_insn_after (set, insn); + } + else + { + /* pop */ + rtx dec = gen_rtx_POST_INC(REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, a7); + rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, dec); + rtx set = gen_rtx_SET(reg, mem); + emit_insn_before (set, insn); + } + } + } + else + { + rtx parallel = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (regs.size () + add1 + clobbers.size ())); + rtx plus; + + int x = 0; + for (unsigned k = 0; k < regs.size (); ++k) + x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4; + + unsigned l = 0; + /* no add if a5 is used with pop */ + if (add1) + { + plus = gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, i < prologueend ? -x : x)); + XVECEXP(parallel, 0, l) = gen_rtx_SET(a7, plus); + ++l; + } + + if (i >= prologueend) + x = usea5 ? -x : 0; + + for (unsigned k = 0; k < regs.size (); ++k, ++l) + { + if (i < prologueend) + { + /* push */ + plus = gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, -x)); + x -= REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4; + rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus); + rtx set = gen_rtx_SET(mem, regs[k]); + XVECEXP(parallel, 0, l) = set; + } + else + { + /* pop */ + if (usea5) + { + x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4; + plus = gen_rtx_PLUS(SImode, a5, gen_rtx_CONST_INT (SImode, a5offset + x)); + rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus); + rtx set = gen_rtx_SET(regs[k], mem); + XVECEXP(parallel, 0, l) = set; + } + else + { + plus = x ? gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, x)) : a7; + x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4; + rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus); + rtx set = gen_rtx_SET(regs[k], mem); + XVECEXP(parallel, 0, l) = set; + } + } + } + + for (unsigned k = 0; k < clobbers.size (); ++k, ++l) + { + rtx clobber = clobbers[k]; + XVECEXP(parallel, 0, l) = clobber; + } + + rtx_insn * neu; + if (i < prologueend) + neu = emit_insn_after (parallel, insn); + else + neu = emit_insn_before (parallel, insn); + ii.set_insn (neu); + } + SET_INSN_DELETED(insn); + changed = 1; + } + } + else + { + rtx set = PATTERN (insn); + + if (i < prologueend) + { + /* move x,-(a7). */ + rtx src = SET_SRC(set); + paramstart += REGNO(src) > STACK_POINTER_REGNUM ? 12 : 4; + unsigned regbit = 1 << REGNO(src); + if (freemask & regbit) + { + adjust += REGNO(src) > STACK_POINTER_REGNUM ? 12 : 4; + log ("(f) remove push for %s\n", reg_names[REGNO(src)]); + SET_INSN_DELETED(insn); + ++changed; + } + } + else + { + /* move (a7)+,x */ + rtx dst = SET_DEST(set); + unsigned regbit = 1 << REGNO(dst); + if (freemask & regbit) + { + log ("(f) remove pop for %s\n", reg_names[REGNO(dst)]); + SET_INSN_DELETED(insn); + ++changed; + } + } + } + } + + /* fix sp offsets. */ + if (!usea5 && adjust) + { + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + if (ii.in_proepi () != IN_CODE) + continue; + + rtx pattern = single_set (ii.get_insn ()); + if (pattern) + patch_sp (pattern, adjust, ii.get_sp_offset ()); + } + } + + if (usea5 && a5offset == -4) + { + /* for now only drop the frame pointer if it's not used. + * Needs tracking of the sp to adjust the offsets. + */ + if (freemask & (1 << FRAME_POINTER_REGNUM)) + { + log ("(f) dropping unused frame pointer\n"); + for (std::vector::reverse_iterator i = a5pos.rbegin (); i != a5pos.rend (); ++i) + { + int index = *i; + SET_INSN_DELETED(infos[index].get_insn ()); + + // move to last insn in epilogue + while (index - 1 > 0 && infos[index - 1].in_proepi () >= IN_EPILOGUE) + --index; + + insn_info & ii = infos[index]; + if (ii.in_proepi () >= IN_EPILOGUE && ii.get_sp_offset () != 0) + { + log ("(f) adjusting exit sp\n"); + rtx pattern = gen_rtx_SET( + a7, gen_rtx_PLUS (SImode, a7, gen_rtx_CONST_INT (SImode, -ii.get_sp_offset ()))); + emit_insn_before (pattern, ii.get_insn ()); + } + } + + /* convert all parameter accesses via a5 into a7. */ + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & ii = infos[i]; + if (ii.get_myuse () & (1 << FRAME_POINTER_REGNUM)) + { + ii.a5_to_a7 (a7); + if (regs_seen && ii.in_proepi () == IN_EPILOGUE_PARALLEL_POP) + { + // exit sp insn needs an + + rtx pattern = PATTERN (ii.get_insn ()); + unsigned sz = XVECLEN(pattern, 0); + + rtx parallel = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (sz + 1)); + unsigned n = 0; + for (unsigned j = 0; j < sz; ++j) + { + rtx set = XVECEXP(pattern, 0, j); + rtx reg = SET_DEST(set); + rtx mem = SET_SRC(set); + rtx plus = XEXP(mem, 0); + if (n) + { + XEXP(plus, 1) = gen_rtx_CONST_INT (SImode, n); + } + else + { + XEXP(mem, 0) = XEXP(plus, 0); + } + n += GET_MODE_SIZE(GET_MODE(reg)); + XVECEXP(parallel, 0, j + 1) = set; + } + + rtx a = copy_reg (a7, -1); + a->frame_related = 1; + rtx plus = gen_rtx_PLUS(SImode, a, gen_rtx_CONST_INT (SImode, regs_total_size)); + rtx set = gen_rtx_SET(a, plus); + XVECEXP(parallel, 0, 0) = set; + SET_INSN_DELETED(ii.get_insn ()); + ii.set_insn (emit_insn_after (parallel, ii.get_insn ())); + } + } + + ii.unset (FRAME_POINTER_REGNUM); + } + + update_insn2index (); + ++changed; + } + } + + return changed; +} + +/* Update the insn_infos to 'know' the value for each register. + * + * assignments to registers are optimized by knowing the value. If the same value is assigned, omit that insn. + * + * I'm tracking + * + * rtx - the value + * + * mask - the referenced registers in the value, 0 means that rtx is const, with baserel a4 is not tracked + * + * if there is a value for the referenced register(s), the value is extended + * + * e.g. + * + * ; line 2 + * move.l 12(a7),a0 + * + * -> rtx = mem(plus(a7, 12)); 0x8000 + * + * ; line 10 + * move.l 4(a0),d0 + * + * -> rtx = mem(plus(mem(plus(a7, 12)), 4)); 0x8000; extended with value from a0, thus a7 is used only + * + * ;15 + * lea _label,a1 + * + * -> rtx = symbol_ref(_label) ; 0x0000 == const + * + * on jumps the current state is duplicated and merged at the given label + * + * on merge only identical info is kept, rest is discarded + * + * for each insn for all defined regs the value and mask is discarded before a new value is set. + * + * for each insn which is writing to memory, all non const values are discarded. + * + * + * after the track info is complete, each insn setting a register is evaluated against the track info. + * + * now redundant loads are found and eliminated + * + */ + +static unsigned +track_regs () +{ +// reset visited flags + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + ii.clear_visited (); + ii.set_sp_offset (0); + } + + update_label2jump (); + +// add entry point + std::multimap todo; + todo.insert (std::make_pair (0, new track_var ())); + + while (todo.begin () != todo.end ()) + { + unsigned startpos = todo.begin ()->first; + track_var * const track = todo.begin ()->second; + todo.erase (todo.begin ()); + + for (unsigned index = startpos; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + + // already visited? + if (index != startpos && ii.is_visited () && ii.get_track_var ()->no_merge_needed (track)) + break; + + // only keep common values at labels + if (ii.is_label ()) + { + if (ii.is_visited ()) + { + ii.get_track_var ()->merge (track, index); + } + else + { + ii.get_track_var ()->assign (track); + ii.mark_visited (); + } + continue; + } + + // mark current insn_info and set sp_offset + ii.mark_visited (); + ii.get_track_var ()->assign (track); + + if (ii.is_compare ()) + continue; + + unsigned def = ii.get_def () & 0xffffff; + if (def) + { + // more than one register set? or mask from clobber? + if (((def - 1) & def) || !ii.get_dst_reg ()) + track->clear_for_mask (def, index); + } + // do not clear if self assigned + int dregno = ii.get_dst_regno (); + if (dregno != ii.get_src_regno ()) + track->clear (ii.get_mode (), dregno, index); + + if (ii.is_call ()) + { + track->clear_aftercall (index); + continue; + } + + rtx set = single_set (ii.get_insn ()); + + // add all referred labels + if (ii.is_jump ()) + { + if (ANY_RETURN_P(ii.get_insn ())) + break; + + for (j2l_iterator i = jump2label.find (index), k = i; i != jump2label.end () && i->first == k->first; ++i) + todo.insert (std::make_pair (i->second, new track_var (track))); + + if (set && GET_CODE(SET_SRC(set)) == IF_THEN_ELSE) + continue; + + // unconditional jump + break; + } + + if (!set || !ii.get_def ()) + continue; + + if (dregno < 0) + { + track->invalidate_mem (SET_DEST(set)); + continue; + } + + // operation, autoinf or more than one register used: can't cache + if (ii.get_src_op () || ii.get_src_autoinc () || ((ii.get_myuse () - 1) & ii.get_myuse ())) + continue; + + rtx src = SET_SRC(set); + if (ii.is_src_mem () && src->volatil) + continue; + + track->set (ii.get_mode (), dregno, src, index); + } + delete track; + } + return 0; +} + +/* + * Some optimizations (e.g. propagate_moves) might result into an unused assignment behind the loop. + * delete those insns. + */ +static unsigned +opt_elim_dead_assign (int blocked_regno) +{ + track_regs (); + + unsigned change_count = 0; + for (int index = infos.size () - 1; index >= 0; --index) + { + insn_info & ii = infos[index]; + if (ii.in_proepi () || !ii.get_dst_reg () || ii.is_compare ()) + continue; + + rtx_insn * insn = ii.get_insn (); + rtx set = single_set (insn); + if (!set) + continue; + + if (ii.get_dst_reg () && REG_NREGS(ii.get_dst_reg ()) == 1 && ii.get_dst_regno () != blocked_regno + && is_reg_dead (ii.get_dst_regno (), index)) + { + log ("(e) %d: eliminate dead assign to %s\n", index, reg_names[ii.get_dst_regno ()]); + SET_INSN_DELETED(insn); + ++change_count; + continue; + } + + // check for redundant load + if (ii.get_src_op () == 0 && ii.get_dst_reg () && ii.get_dst_regno () != blocked_regno + && (!ii.is_myuse (ii.get_dst_regno ()) || ii.get_dst_regno () == ii.get_src_regno ())) + { + track_var * track = ii.get_track_var (); + + rtx src = SET_SRC(set); + if (track->equals (ii.get_dst_regno (), src)) + { + log ("(e) %d: eliminate redundant load to %s\n", index, reg_names[ii.get_dst_regno ()]); + SET_INSN_DELETED(insn); + ++change_count; + continue; + } + + if (ii.get_src_reg () && track->equals (ii.get_src_regno (), SET_DEST(set))) + { + log ("(e) %d: eliminate redundant reverse load to %s\n", index, reg_names[ii.get_dst_regno ()]); + SET_INSN_DELETED(insn); + ++change_count; + continue; + } + + // is there a register holding that value? + if (!ii.get_src_reg ()) + { + int aliasRegno = track->find_alias (src); + if (aliasRegno >= 0 && aliasRegno != ii.get_dst_regno ()) + { + log ("(e) %d: replace load with %s\n", index, reg_names[aliasRegno]); + validate_change (ii.get_insn (), &SET_SRC(set), gen_rtx_REG (ii.get_mode (), aliasRegno), 0); + ++change_count; + } + } + } + } + return change_count; +} + +/* + * Convert a series of move into absolute address into register based moves. + */ +static unsigned +opt_absolute (void) +{ + unsigned change_count = 0; + + for (unsigned i = 0; i < infos.size (); ++i) + { + insn_info & ii = infos[i]; + + if (ii.is_compare ()) + continue; + + if (ii.get_src_op () && ii.is_src_ee () && !ii.get_src_intval ()) + continue; + + bool is_dst = ii.is_dst_mem () && (ii.has_dst_addr () || ii.get_dst_symbol ()) && !ii.has_dst_memreg (); + bool is_src = ii.is_src_mem () && (ii.has_src_addr () || ii.get_src_symbol ()) && !ii.has_src_memreg (); + + if (!is_dst && !is_src) + continue; + + if (ii.get_mode () == VOIDmode) + continue; + + unsigned freemask = ~(ii.get_use () | ii.get_def ()) & 0x7f00 & usable_regs; + if (!freemask) + continue; + + rtx with_symbol = is_dst ? ii.get_dst_symbol () : ii.get_src_symbol (); + + std::vector found; + found.push_back (i); + int base = ii.get_dst_mem_addr (); + int max = base; + unsigned j = i + 1; + for (; j < infos.size (); ++j) + { + insn_info & jj = infos[j]; + /* TODO: continue also at jump target */ + if (jj.is_jump ()) + continue; + /* TODO: check if label is visited only from jump targets from herein. then the label is ok. */ + if (jj.is_label ()) + break; + + unsigned tempmask = freemask & ~(jj.get_use () | jj.get_def ()); + if (!tempmask) + break; + freemask = tempmask; + + if (jj.get_mode () == VOIDmode || jj.is_compare ()) + continue; + + if (jj.get_src_op () && jj.is_src_ee () && !jj.get_src_intval ()) + continue; + + bool j_dst = jj.is_dst_mem () && (jj.has_dst_addr () || jj.get_dst_symbol ()) && !jj.has_dst_memreg () + && jj.get_dst_symbol () == with_symbol; + bool j_src = jj.is_src_mem () && (jj.has_src_addr () || jj.get_src_symbol ()) && !jj.has_src_memreg () + && jj.get_src_symbol () == with_symbol; + + /* exclude operations on that symbol. */ + + if (j_dst) + { + int addr = jj.get_dst_mem_addr (); + if (addr < base) + { + if (max - addr <= 0x7ffe) + { + base = addr; + found.push_back (j); + continue; + } + } + else if (addr - base <= 0x7ffe) + { + if (addr > max) + max = addr; + found.push_back (j); + continue; + } + } + if (j_src) + { + int addr = jj.get_src_mem_addr (); + if (addr < base) + { + if (max - addr <= 0x7ffe) + { + base = addr; + found.push_back (j); + continue; + } + } + else if (addr - base <= 0x7ffe) + { + if (addr > max) + max = addr; + found.push_back (j); + continue; + } + } + } + + if (freemask && found.size () > 2) + { + unsigned regno = bit2regno (freemask); + /* check again. */ + for (std::vector::iterator k = found.begin (); k != found.end ();) + { + insn_info & kk = infos[*k]; + bool k_dst = kk.is_dst_mem () && (kk.has_dst_addr () || kk.get_dst_symbol ()) && !kk.has_dst_memreg () + && kk.get_dst_symbol () == with_symbol; + bool k_src = kk.is_src_mem () && (kk.has_src_addr () || kk.get_src_symbol ()) && !kk.has_src_memreg () + && kk.get_src_symbol () == with_symbol; + if (k_dst && kk.get_dst_mem_addr () - base > 0x7ffc) + k = found.erase (k); + else if (k_src && kk.get_src_mem_addr () - base > 0x7ffc) + k = found.erase (k); + else if (insn_invalid_p (make_insn_raw (kk.make_absolute2base (regno, base, with_symbol, false)), 0)) + k = found.erase (k); + else + ++k; + } + } + if (freemask && found.size () > 2) + { + unsigned regno = bit2regno (freemask); + if (with_symbol) + log ("(b) modifying %d symbol addresses for %s using %s\n", found.size (), + with_symbol->u.block_sym.fld[0].rt_str, reg_names[regno]); + else + log ("(b) modifying %d absolute addresses using %s\n", found.size (), reg_names[regno]); + + unsigned current_use = ii.get_use (); + + for (std::vector::iterator k = found.begin (); k != found.end (); ++k) + { + insn_info & kk = infos[*k]; + kk.absolute2base (regno, base, with_symbol); + insn_invalid_p (kk.get_insn (), 0); + } + + // load base into reg + rtx lea; + + if (with_symbol) + { + if (base) + lea = gen_rtx_SET( + gen_raw_REG (SImode, regno), + gen_rtx_CONST (SImode, gen_rtx_PLUS (SImode, with_symbol, gen_rtx_CONST_INT (SImode, base)))); + else + lea = gen_rtx_SET(gen_raw_REG (SImode, regno), with_symbol); + } + else + lea = gen_rtx_SET(gen_raw_REG (SImode, regno), gen_rtx_CONST_INT (SImode, base)); + rtx_insn * insn = emit_insn_before (lea, ii.get_insn ()); + insn_info nn (insn); + nn.set_use (current_use); + nn.scan (); + nn.fledder (lea); + nn.mark_def (regno); + infos.insert (infos.begin () + i, nn); + + /* mark until last hit is found. */ + for (unsigned k = i + 1; k < infos.size (); ++k) + { + infos[k].mark_use (regno); + if (k == *found.rbegin ()) + break; + } + ++change_count; + --i; + } + } + + if (change_count) + update_insn2index (); + + return change_count; +} + +static int +try_auto_inc (unsigned index, insn_info & ii, rtx reg) +{ + int const regno = REGNO(reg); + unsigned size = GET_MODE_SIZE(ii.get_mode ()); + if (size > 4) + return 0; + +// log ("starting auto_inc search for %s at %d\n", reg_names[regno], index); + + // track all fixups to modify + std::set fixups; + + // all paths to check + std::vector todo; + todo.push_back (index + 1); + + bool match_size = false; + std::set visited; + while (todo.size () > 0) + { + unsigned pos = todo[todo.size () - 1]; + todo.pop_back (); + + if (pos == index) + return 0; + + if (visited.find (pos) != visited.end ()) + continue; + visited.insert (pos); + + for (; pos < infos.size (); ++pos) + { + insn_info & jj = infos[pos]; + + // check all jumps labels for register usage + if (jj.is_label ()) + { + for (l2j_iterator j = label2jump.find (jj.get_insn ()->u2.insn_uid), k = j; + j != label2jump.end () && j->first == k->first; ++j) + { + insn_info * ll = insn2info.find (j->second)->second; + if (ll->is_use (regno)) + return 0; + } + break; + } + + // break if no longer used + if (!jj.is_use (regno)) + break; + + if (jj.in_proepi ()) + return 0; + + // add all labels + if (jj.is_jump ()) + { + for (j2l_iterator j = jump2label.find (pos), k = j; j != jump2label.end () && j->first == k->first; ++j) + todo.push_back (j->second); + continue; + } + + // not used directly + if (!jj.is_myuse (regno)) + continue; + + // can't fixup such kind of insn (yet) + if (single_set (jj.get_insn ()) == 0) + return 0; + + // if reg is src reg, op must be add and addend must be large enough + bool fix = false; + if (jj.get_src_mem_regno () == regno) + { + if (jj.get_dst_regno () == regno) + return 0; + + if (jj.get_src_mem_addr () < size) + return 0; + + if (jj.get_src_mem_addr () == size) + match_size = true; + + fix = true; + } + if (jj.get_dst_mem_regno () == regno) + { + if (jj.get_src_regno () == regno) + return 0; + + if (jj.get_dst_mem_addr () < size) + return 0; + + if (jj.get_dst_mem_addr () == size) + match_size = true; + + fix = true; + } + + if (!fix) + return 0; + + fixups.insert (pos); + + // done if this is an add + if (ii.is_def (regno)) + break; + } + } + + if (!match_size || !fixups.size ()) + return 0; + + if (!ii.make_post_inc (regno)) + return 0; + + log ("(i) auto_inc for %s at %d - %d fixups\n", reg_names[regno], index, fixups.size ()); + + // fix all offsets / adds + for (std::set::iterator k = fixups.begin (); k != fixups.end (); ++k) + { +// log ("(i) fixup at %d\n", *k); + insn_info & kk = infos[*k]; + kk.auto_inc_fixup (regno, size); + } + return 1; +} + +/* + * Convert a series of reg with offset ( (ax), 4(ax), 8(ax), ...) into autoincx ( (ax+), (ax+), (ax+), ...) + * + * 1. search a mem(reg) without offset and either src or dst is using that reg + * 2. follow paths until reg is dead + * 3. if there is another mem(reg) with offset check that + * a) offset fits last mode size + * b) all remaining insn using that reg can be updated by + * i) decrement the offset + * ii) decrement the add value + */ +static unsigned +opt_autoinc () +{ + unsigned change_count = 0; + + update_label2jump (); + + for (unsigned index = 0; index < infos.size (); ++index) + { + insn_info & ii = infos[index]; + + if (ii.in_proepi ()) + continue; + + if (!INSN_P(ii.get_insn ())) + continue; + +// // more than one reg used +// if (ii.get_myuse () & (ii.get_myuse () - 1)) +// continue; + +// // don't if fp regs are touched +// if ((ii.get_myuse () & 0xff0000)) +// continue; + + if (ii.is_src_mem () && ii.get_src_mem_regno () >= 8 && !ii.get_src_mem_addr () && !ii.get_src_autoinc () + && ii.get_src_mem_regno () != ii.get_dst_mem_regno () && ii.get_src_mem_regno () != ii.get_dst_regno ()) + change_count += try_auto_inc (index, ii, ii.get_src_mem_reg ()); + + if (ii.is_dst_mem () && ii.get_dst_mem_regno () >= 8 && !ii.get_dst_intval () && !ii.get_dst_autoinc () + && ii.get_src_mem_regno () != ii.get_dst_mem_regno () && ii.get_src_regno () != ii.get_dst_mem_regno ()) + change_count += try_auto_inc (index, ii, ii.get_dst_mem_reg ()); + + } + + return change_count; +} + +namespace +{ + + const pass_data pass_data_bbb_optimizations = + { RTL_PASS, /* type */ + "bebbo's-optimizers", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, //( TODO_df_finish | TODO_df_verify), /* todo_flags_finish */ + }; + + class pass_bbb_optimizations : public rtl_opt_pass + { + public: + pass_bbb_optimizations (gcc::context *ctxt) : + rtl_opt_pass (pass_data_bbb_optimizations, ctxt), pp (0) + { + } + + /* opt_pass methods: */ + virtual bool + gate (function *) + { + if (!string_bbb_opts) + string_bbb_opts = "+"; + + return TARGET_AMIGA && optimize > 0 && string_bbb_opts && !strchr (string_bbb_opts, '-'); + } + + virtual unsigned int + execute (function *) + { + return execute_bbb_optimizations (); + } + + opt_pass * + clone () + { + pass_bbb_optimizations * bbb = new pass_bbb_optimizations (m_ctxt); + bbb->pp = pp + 1; + return bbb; + } + + unsigned int pp; + + unsigned + execute_bbb_optimizations (void); + }; +// class pass_bbb_optimizations + + /* Main entry point to the pass. */ + unsigned + pass_bbb_optimizations::execute_bbb_optimizations (void) + { + dump_reg_track = strchr (string_bbb_opts, 'R') != 0; + be_very_verbose = strchr (string_bbb_opts, 'V') != 0; + be_verbose = strchr (string_bbb_opts, 'v') != 0; + if (be_verbose && be_very_verbose) + ++be_very_verbose; + if (be_very_verbose) + be_verbose = true; + + bool do_commute_add_move = strchr (string_bbb_opts, 'a') || strchr (string_bbb_opts, '+'); + bool do_absolute = strchr (string_bbb_opts, 'b') || strchr (string_bbb_opts, '+'); + bool do_const_cmp_to_sub = strchr (string_bbb_opts, 'c') || strchr (string_bbb_opts, '+'); + bool do_elim_dead_assign = strchr (string_bbb_opts, 'e') || strchr (string_bbb_opts, '+'); + bool do_shrink_stack_frame = strchr (string_bbb_opts, 'f') || strchr (string_bbb_opts, '+'); + bool do_autoinc = strchr (string_bbb_opts, 'i') || strchr (string_bbb_opts, '+'); + bool do_merge_add = strchr (string_bbb_opts, 'm') || strchr (string_bbb_opts, '+'); + bool do_propagate_moves = strchr (string_bbb_opts, 'p') || strchr (string_bbb_opts, '+'); + bool do_bb_reg_rename = strchr (string_bbb_opts, 'r') || strchr (string_bbb_opts, '+'); + bool do_opt_strcpy = strchr (string_bbb_opts, 's') || strchr (string_bbb_opts, '+'); + + if (be_very_verbose) + log ("ENTER\n"); + + unsigned r = update_insns (); + if (!r) + { + for (;;) + { + int done = 1; + if (do_opt_strcpy && opt_strcpy ()) + done = 0, update_insns (); + + if (do_commute_add_move && opt_commute_add_move ()) + done = 0, update_insns (); + + if (do_propagate_moves && opt_propagate_moves ()) + done = 0, update_insns (); + + if (do_const_cmp_to_sub && opt_const_cmp_to_sub ()) + done = 0, update_insns (); + + if (do_merge_add && opt_merge_add ()) + done = 0; + + if (do_elim_dead_assign && opt_elim_dead_assign (STACK_POINTER_REGNUM)) + done = 0, update_insns (); + + if (do_absolute && opt_absolute ()) + done = 0, update_insns (); + + if (do_autoinc && opt_autoinc ()) + done = 0, update_insns (); + + if (do_bb_reg_rename) + { + while (opt_reg_rename ()) + { + update_insns (); + done = 0; + } + } + + if (done) + break; + } + + if (do_shrink_stack_frame) + { + if (opt_shrink_stack_frame ()) + update_insns (); + } + + /* elim stack pointer stuff last. */ + if (do_elim_dead_assign) + opt_elim_dead_assign (FIRST_PSEUDO_REGISTER); + } + if (r && be_verbose) + log ("no bbb optimization code %d\n", r); + + if (strchr (string_bbb_opts, 'X') || strchr (string_bbb_opts, 'x')) + dump_insns ("bbb", strchr (string_bbb_opts, 'X')); + + if (dump_reg_track) + { + update_insns (); + track_regs (); + } + + return r; + } + +} // anon namespace + +rtl_opt_pass * +make_pass_bbb_optimizations (gcc::context * ctxt) +{ + return new pass_bbb_optimizations (ctxt); +} + +namespace +{ + + const pass_data pass_data_bbb_baserel = + { RTL_PASS, /* type */ + "bebbo's-baserel fixer", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, //( TODO_df_finish | TODO_df_verify), /* todo_flags_finish */ + }; + + class pass_bbb_baserel : public rtl_opt_pass + { + public: + pass_bbb_baserel (gcc::context *ctxt) : + rtl_opt_pass (pass_data_bbb_baserel, ctxt), pp (0) + { + } + + /* opt_pass methods: */ + virtual bool + gate (function *) + { + return TARGET_AMIGA && flag_pic >= 3; + } + + virtual unsigned int + execute (function *) + { + return execute_bbb_baserel (); + } + + opt_pass * + clone () + { + pass_bbb_baserel * bbb = new pass_bbb_baserel (m_ctxt); + return bbb; + } + + unsigned int pp; + + unsigned + execute_bbb_baserel (void); + }; +// class pass_bbb_optimizations + + /* Main entry point to the pass. */ + unsigned + pass_bbb_baserel::execute_bbb_baserel (void) + { + rtx_insn *insn, *next; + for (insn = get_insns (); insn; insn = next) + { + next = NEXT_INSN (insn); + + if (NONJUMP_INSN_P(insn)) + { + rtx set = single_set (insn); + if (!set) + continue; + + rtx * src = &SET_SRC(set); + if (MEM_P(*src)) + src = &XEXP(*src, 0); + + bool ispicref = false; + // fix add PLUS/MINUS into the unspec offset + if (GET_CODE(*src) == PLUS || GET_CODE(*src) == MINUS) + ispicref = amiga_is_const_pic_ref (XEXP(*src, 0)); + else + ispicref = amiga_is_const_pic_ref (*src); + + if (ispicref) + { + rtx dest = SET_DEST(set); + if (MEM_P(dest) && GET_CODE(XEXP(dest, 0)) != PRE_DEC) + { + // split the insn + rtx reg = gen_reg_rtx (Pmode); + + rtx pat0 = gen_rtx_SET(reg, *src); + //rtx_insn * n0 = + emit_insn_before (pat0, insn); + + rtx pat1 = gen_rtx_SET(dest, reg); + //rtx_insn * n1 = + emit_insn_before (pat1, insn); + + SET_INSN_DELETED(insn); + } + } + } + } + + return 0; + } + +} // anon namespace + +rtl_opt_pass * +make_pass_bbb_baserel (gcc::context * ctxt) +{ + return new pass_bbb_baserel (ctxt); +} diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index e8bafedd7357..642829b828c7 100644 --- gcc/c/c-decl.c +++ gcc/c/c-decl.c @@ -51,6 +51,8 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-ada-spec.h" #include "cilk.h" #include "builtins.h" +#include "output.h" +#include "tm_p.h" /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context @@ -4439,7 +4441,32 @@ c_decl_attributes (tree *node, tree attributes, int flags) attributes = tree_cons (get_identifier ("omp declare target"), NULL_TREE, attributes); } - return decl_attributes (node, attributes, flags); + + tree returned_attrs = decl_attributes (node, attributes, flags); + +#ifdef TARGET_AMIGA + /* add an attribute to the function decl's type if there are asm register parameters. */ + if (TREE_CODE (*node) == FUNCTION_DECL) + { + char const * synthetic = ""; + for (tree params = TYPE_ARG_TYPES(TREE_TYPE(*node)); params; params = TREE_CHAIN(params)) + { + tree asmattr = lookup_attribute("asm", TYPE_ATTRIBUTES(TREE_VALUE(params))); + if (asmattr) + synthetic = concat(synthetic, reg_names[TREE_FIXED_CST_PTR(TREE_VALUE(asmattr))->data.low], NULL); + } + if (strlen(synthetic) > 0) + { + tree asmid = get_identifier("asmregs"); + tree syntheticid = get_identifier(synthetic); + tree newattr = tree_cons(asmid, syntheticid, NULL_TREE); + + TYPE_ATTRIBUTES(TREE_TYPE(*node)) = chainon(newattr, TYPE_ATTRIBUTES(TREE_TYPE(*node))); + } + } +#endif + + return returned_attrs; } @@ -5024,6 +5051,29 @@ grokparm (const struct c_parm *parm, tree *expr) return decl; } +#ifdef TARGET_AMIGA + +/* Create a new variant of TYPE, equivalent but distinct. + This is so the caller can modify it. */ + +static tree +build_type_copy (tree type) + { + tree t, m = TYPE_MAIN_VARIANT (type); + + t = copy_node (type); + + TYPE_POINTER_TO (t) = 0; + TYPE_REFERENCE_TO (t) = 0; + + /* Add this type to the chain of variants of TYPE. */ + TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); + TYPE_NEXT_VARIANT (m) = t; + + return t; + } +#endif + /* Given a parsed parameter declaration, decode it into a PARM_DECL and push that on the current scope. EXPR is a pointer to an expression that needs to be evaluated for the side effects of array @@ -5041,6 +5091,59 @@ push_parm_decl (const struct c_parm *parm, tree *expr) decl = pushdecl (decl); +#ifdef TARGET_AMIGAOS + if (parm->asmspec) + { + tree atype = TREE_TYPE(decl); + const char *asmspec = TREE_STRING_POINTER(parm->asmspec); + if (*asmspec == '%') + ++asmspec; + int reg_number = decode_reg_name (asmspec); + + /* First detect errors in declaring global registers. */ + if (reg_number == -1) + error ("%Jregister name not specified for %qD", decl, decl); + else if (reg_number < 0) + error ("%Jinvalid register name for %qD", decl, decl); + else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode) + error ("%Jdata type of %qD isn%'t suitable for a register", decl, decl); + else if (!HARD_REGNO_MODE_OK(reg_number, TYPE_MODE (TREE_TYPE (decl)))) + error ("%Jregister specified for %qD isn%'t suitable for data type", + decl, decl); + /* Now handle properly declared static register variables. */ + else + { + /* Build tree for __attribute__ ((asm(regnum))). */ + FIXED_VALUE_TYPE fv = + { reg_number, 0, BImode }; + tree ttasm = get_identifier("asm"); + tree t, attrs = tree_cons(ttasm, build_fixed (ttasm, fv), NULL_TREE); + /* First check whether such a type already exists - if yes, use + that one. This is very important, since otherwise + common_type() would think that it sees two different + types and would try to merge them - this could result in + warning messages. */ + for (t = TYPE_MAIN_VARIANT(atype); t; t = TYPE_NEXT_VARIANT(t)) + if (comptypes (t, atype) == 1 + && attribute_list_equal (TYPE_ATTRIBUTES(t), attrs)) + break; + if (t) + atype = t; + else + { + /* Create a new variant, with differing attributes. + (Hack! Type with differing attributes should no longer be + a variant of its main type. See comment above for + explanation why this was necessary). */ + atype = build_type_copy (atype); + TYPE_ATTRIBUTES(atype) = chainon (attrs, TYPE_ATTRIBUTES(atype)); + } + TREE_TYPE(decl) = atype; +// printf("%s using %s, cdecl=%p, type=%p\n", IDENTIFIER_POINTER(DECL_NAME (decl)), asmspec, decl, atype); + } + } +#endif + finish_decl (decl, input_location, NULL_TREE, NULL_TREE, NULL_TREE); } diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index fc20bad8d992..91cfc2c5e193 100644 --- gcc/c/c-parser.c +++ gcc/c/c-parser.c @@ -3837,10 +3837,26 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs) c_parser_skip_until_found (parser, CPP_COMMA, NULL); return NULL; } + /** + * SBF: Add support for __asm("xy") register spec. + */ +#ifdef TARGET_AMIGAOS + tree asmspec = NULL_TREE; + if (c_parser_next_token_is_keyword (parser, RID_ASM)) + { + asmspec = c_parser_simple_asm_expr (parser); +// printf("asmspec: %s\n", TREE_STRING_POINTER(asmspec)); + } +#endif if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) postfix_attrs = c_parser_attributes (parser); - return build_c_parm (specs, chainon (postfix_attrs, prefix_attrs), + + struct c_parm * cparm = build_c_parm (specs, chainon (postfix_attrs, prefix_attrs), declarator); +#ifdef TARGET_AMIGAOS + cparm->asmspec = asmspec; +#endif + return cparm; } /* Parse a string literal in an asm expression. It should not be @@ -3892,6 +3908,7 @@ c_parser_asm_string_literal (c_parser *parser) static tree c_parser_simple_asm_expr (c_parser *parser) { + extern int in_assembler_directive; tree str; gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM)); /* ??? Follow the C++ parser rather than using the @@ -3903,7 +3920,13 @@ c_parser_simple_asm_expr (c_parser *parser) parser->lex_untranslated_string = false; return NULL_TREE; } + + // SBF: set in_assembler_directive to enable multi-line strings. And yes, it's a HACK. + in_assembler_directive = 1; str = c_parser_asm_string_literal (parser); + // SBF: in_assembler_directive disabled + in_assembler_directive = 0; + parser->lex_untranslated_string = false; if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) { diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index bb12a200f709..e3404fd8b0a6 100644 --- gcc/c/c-tree.h +++ gcc/c/c-tree.h @@ -453,6 +453,10 @@ struct c_parm { tree attrs; /* The declarator. */ struct c_declarator *declarator; +#ifdef TARGET_AMIGAOS + /* The optional asm spec to specify the register. */ + tree asmspec; +#endif }; /* Used when parsing an enum. Initialized by start_enum. */ diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index 6e92d4cdde22..378b1fc595bb 100644 --- gcc/cfgcleanup.c +++ gcc/cfgcleanup.c @@ -2001,6 +2001,15 @@ try_crossjump_to_edge (int mode, edge e1, edge e2, { rtx_insn *insn; +#ifdef TARGET_AMIGA + /* + * we need replicated labels, if the labels are too far away, + * since on 68000 there are only 8 bits for the offset. + */ + if (!TARGET_68020 && !TARGET_68040) + return false; +#endif + /* Replace references to LABEL1 with LABEL2. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { @@ -2016,8 +2025,9 @@ try_crossjump_to_edge (int mode, edge e1, edge e2, /* Avoid splitting if possible. We must always split when SRC2 has EH predecessor edges, or we may end up with basic blocks with both normal and EH predecessor edges. */ - if (newpos2 == BB_HEAD (src2) + if ((newpos2 == BB_HEAD (src2) && !(EDGE_PRED (src2, 0)->flags & EDGE_EH)) + ) redirect_to = src2; else { diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index b612293b1a7a..4215ae90c63d 100644 --- gcc/cfgexpand.c +++ gcc/cfgexpand.c @@ -2732,6 +2732,10 @@ tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs) { /* Conflicts between asm-declared register variables and the clobber list are not allowed. */ + /* + * SBF: Why? + */ +#ifndef TARGET_AMIGA tree overlap = tree_overlaps_hard_reg_set (t, clobbered_regs); if (overlap) @@ -2744,7 +2748,7 @@ tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs) DECL_REGISTER (overlap) = 0; return true; } - +#endif return false; } @@ -3255,11 +3259,15 @@ expand_asm_stmt (gasm *stmt) if (reg_overlap_mentioned_p (clobbered_reg, output_rvec[k])) internal_error ("asm clobber conflict with output operand"); +/** + * SBF: Why? + */ +#ifndef TARGET_AMIGA for (unsigned k = 0; k < ninputs - ninout; ++k) if (reg_overlap_mentioned_p (clobbered_reg, input_rvec[k])) internal_error ("asm clobber conflict with input operand"); +#endif } - XVECEXP (body, 0, i++) = gen_rtx_CLOBBER (VOIDmode, clobbered_reg); } diff --git a/gcc/collect2.c b/gcc/collect2.c index bffac802b8fe..f52a66ef1b58 100644 --- gcc/collect2.c +++ gcc/collect2.c @@ -1392,6 +1392,11 @@ main (int argc, char **argv) add_to_list (&libs, s); } #endif + /* begin-GG-local: dynamic libraries */ + #ifdef COLLECT2_LIBNAME_HOOK + COLLECT2_LIBNAME_HOOK(arg); + #endif + /* end-GG-local */ break; #ifdef COLLECT_EXPORT_LIST @@ -1492,6 +1497,11 @@ main (int argc, char **argv) add_to_list (&libs, arg); } #endif + /* begin-GG-local: dynamic libraries */ +#ifdef COLLECT2_LIBNAME_HOOK + COLLECT2_LIBNAME_HOOK(arg); +#endif + /* end-GG-local */ } } @@ -1608,6 +1618,11 @@ main (int argc, char **argv) fprintf (stderr, "\n"); } + /* begin-GG-local: dynamic libraries */ +#ifdef COLLECT2_PRELINK_HOOK + COLLECT2_PRELINK_HOOK(ld1_argv, &strip_flag); +#endif + /* end-GG-local */ /* Load the program, searching all libraries and attempting to provide undefined symbols from repository information. @@ -1648,6 +1663,8 @@ main (int argc, char **argv) } } + /* begin-GG-local: dynamic libraries */ +#ifndef COLLECT2_POSTLINK_HOOK /* Unless we have done it all already, examine the namelist and search for static constructors and destructors to call. Write the constructor and destructor tables to a .s file and reload. */ @@ -1674,6 +1691,10 @@ main (int argc, char **argv) frame_tables.number), frame_tables.number); } +#else /* COLLECT2_POSTLINK_HOOK */ + COLLECT2_POSTLINK_HOOK(output_file); +#endif +/* end-GG-local */ /* If the scan exposed nothing of special interest, there's no need to generate the glue code and relink so return now. */ @@ -1716,6 +1737,11 @@ main (int argc, char **argv) maybe_unlink (c_file); maybe_unlink (o_file); + /* begin-GG-local: dynamic libraries */ +#ifdef COLLECT2_EXTRA_CLEANUP + COLLECT2_EXTRA_CLEANUP(); +#endif + /* end-GG-local */ return 0; } @@ -1821,6 +1847,11 @@ main (int argc, char **argv) maybe_unlink (export_file); #endif + /* begin-GG-local: dynamic libraries */ +#ifdef COLLECT2_EXTRA_CLEANUP + COLLECT2_EXTRA_CLEANUP(); +#endif + /* end-GG-local */ return 0; } diff --git a/gcc/config.gcc b/gcc/config.gcc index bf3f32da08ac..7aa190620911 100644 --- gcc/config.gcc +++ gcc/config.gcc @@ -1940,6 +1940,27 @@ m68k-*-elf* | fido-*-elf*) ;; esac ;; +m68k*-*-amigaosvasm*) + default_m68k_cpu=68000 + tm_file="${tm_file} dbx.h newlib-stdint.h m68k/amigaos.h" + tm_defines="${tm_defines} MOTOROLA=1 TARGET_AMIGAOS TARGET_AMIGAOS_VASM TARGET_CPU_DEFAULT=0" + tmake_file="m68k/t-floatlib m68k/t-m68kbare m68k/t-amigaos" + tm_p_file="${tm_p_file} m68k/amigaos-protos.h" + extra_objs=amigaos.o + extra_options="${extra_options} m68k/amigaos.opt" + gnu_ld=yes + ;; +m68k*-*-amigaos*) + default_m68k_cpu=68000 + tm_file="${tm_file} dbx.h newlib-stdint.h m68k/amigaos.h" + tm_defines="${tm_defines} MOTOROLA=1 TARGET_AMIGAOS TARGET_CPU_DEFAULT=0" + tmake_file="m68k/t-floatlib m68k/t-m68kbare m68k/t-amigaos" + tm_p_file="${tm_p_file} m68k/amigaos-protos.h" + extra_objs=amigaos.o + extra_options="${extra_options} m68k/amigaos.opt" + gnu_ld=yes + CFLAGS="-Os" + ;; m68k*-*-netbsdelf*) default_m68k_cpu=68020 default_cf_cpu=5475 diff --git a/gcc/config/m68k/amigaos-protos.h b/gcc/config/m68k/amigaos-protos.h new file mode 100644 index 000000000000..e5cd6e950b52 --- /dev/null +++ gcc/config/m68k/amigaos-protos.h @@ -0,0 +1,55 @@ +/* Configuration for GNU C-compiler for m68k Amiga, running AmigaOS. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2003 + Free Software Foundation, Inc. + Contributed by Markus M. Wild (wild@amiga.physik.unizh.ch). + Heavily modified by Kamil Iskra (iskra@student.uci.agh.edu.pl). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#undef TARGET_AMIGAOS +#define TARGET_AMIGAOS 1 + +extern void amigaos_init_cumulative_args (CUMULATIVE_ARGS *, tree, tree); + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ + +#undef INIT_CUMULATIVE_ARGS +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ + (amigaos_init_cumulative_args(&(CUM), (FNTYPE), (INDIRECT))) + +#ifdef RTX_CODE +extern int read_only_operand (rtx); +extern void amigaos_select_section (tree, int, unsigned HOST_WIDE_INT); +extern void amigaos_encode_section_info (tree, rtx, int); +extern void amigaos_alternate_pic_setup (FILE *); +extern void amigaos_prologue_begin_hook (FILE *, int); +extern void amigaos_alternate_frame_setup_f (FILE *, int); +extern void amigaos_alternate_frame_setup (FILE *, int); +extern struct rtx_def* gen_stack_cleanup_call (rtx, rtx); +extern void amigaos_alternate_allocate_stack (rtx *); +#ifdef TREE_CODE +//extern void amigaos_function_arg_advance (CUMULATIVE_ARGS *); +extern struct rtx_def *amigaos_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree); +#endif +#endif +#ifdef TREE_CODE +extern tree amigaos_handle_decl_attribute (tree *, tree, tree, int, bool *); +extern tree amigaos_handle_type_attribute (tree *, tree, tree, int, bool *); +#endif diff --git a/gcc/config/m68k/amigaos.c b/gcc/config/m68k/amigaos.c new file mode 100644 index 000000000000..28d20a980978 --- /dev/null +++ gcc/config/m68k/amigaos.c @@ -0,0 +1,931 @@ +/* Configuration for GNU C-compiler for m68k Amiga, running AmigaOS. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2003 + Free Software Foundation, Inc. + Contributed by Markus M. Wild (wild@amiga.physik.unizh.ch). + Heavily modified by Kamil Iskra (iskra@student.uci.agh.edu.pl). + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +//work without flag_writable_strings which is not in GCC4 +#define REGPARMS_68K 1 + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "output.h" +#include "tree.h" +#include "attribs.h" +#include "flags.h" +#include "expr.h" +#include "toplev.h" +#include "tm_p.h" +#include "target.h" +#include "diagnostic-core.h" +#include "langhooks.h" +#include "function.h" +#include "config/m68k/amigaos.h" + +//#define MYDEBUG 1 +#ifdef MYDEBUG +#define DPRINTF(x) printf x; fflush(stdout); +#else +#define DPRINTF(x) +#endif + +//int amiga_declare_object; + +#if 0 + +//----- from 68k.c start + +/* Stack checking and automatic extension support. */ + +void +amigaos_prologue_begin_hook (FILE *stream, int fsize) + { + if (TARGET_STACKCHECK) + { + if (fsize < 256) + asm_fprintf (stream, "\tcmpl %s,%Rsp\n" + "\tjcc 0f\n" + "\tjra %U__stkovf\n" + "\t0:\n", + (flag_pic == 3 ? "a4@(___stk_limit:W)" : + (flag_pic == 4 ? "a4@(___stk_limit:L)" : + "___stk_limit"))); + else + asm_fprintf (stream, "\tmovel %I%d,%Rd0\n\tjbsr %U__stkchk_d0\n", + fsize); + } + } + + +//static rtx +//gen_stack_management_call (rtx stack_pointer, rtx arg, const char *func) +//{ +// rtx call_insn, call, seq, name; +// start_sequence (); +// +// /* Move arg to d0. */ +// emit_move_insn (gen_rtx_REG (SImode, 0), arg); +// +// /* Generate the function reference. */ +// name = gen_rtx_SYMBOL_REF (Pmode, func); +// SYMBOL_REF_FLAG (name) = 1; +// /* If optimizing, put it in a psedo so that several loads can be merged +// into one. */ +// if (optimize && ! flag_no_function_cse) +// name = copy_to_reg (name); +// +// /* Generate the function call. */ +// call = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (FUNCTION_MODE, name), +// const0_rtx); +// /* If we are doing stack extension, notify about the sp change. */ +// if (stack_pointer) +// call = gen_rtx_SET (VOIDmode, stack_pointer, call); +// +// /* Generate the call instruction. */ +// call_insn = emit_call_insn (call); +// /* Stack extension does not change memory in an unpredictable way. */ +// RTL_CONST_OR_PURE_CALL_P (call_insn) = 1; +// /* We pass an argument in d0. */ +// CALL_INSN_FUNCTION_USAGE (call_insn) = gen_rtx_EXPR_LIST (VOIDmode, +// gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 0)), 0); +// +// seq = get_insns (); +// end_sequence (); +// return seq; +//} +// +//rtx +//gen_stack_cleanup_call (rtx stack_pointer, rtx sa) +//{ +// return gen_stack_management_call (stack_pointer, sa, "__move_d0_sp"); +//} +// +//void +//amigaos_alternate_allocate_stack (rtx *operands) +//{ +// if (TARGET_STACKEXTEND) +// emit_insn (gen_stack_management_call (stack_pointer_rtx, operands[1], +// "__sub_d0_sp")); +// else +// { +// if (TARGET_STACKCHECK) +// emit_insn (gen_stack_management_call (0, operands[1], "__stkchk_d0")); +// anti_adjust_stack (operands[1]); +// } +// emit_move_insn (operands[0], virtual_stack_dynamic_rtx); +//} +#endif + +/* + * begin-GG-local: explicit register specification for parameters. + * + * Reworked and ported to gcc-6.2.0 by Stefan "Bebbo" Franke. + */ + +/** + * Define this here and add it to tm_p -> all know the custom type and allocate/use the correct size. + */ +struct amigaos_args +{ + int num_of_regs; + long regs_already_used; + int last_arg_reg; + int last_arg_len; + tree formal_type; /* New field: formal type of the current argument. */ +}; + +static struct amigaos_args mycum, othercum; + +/* Argument-passing support functions. */ + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ + +void +amigaos_init_cumulative_args (CUMULATIVE_ARGS *cump, tree fntype, tree decl) +{ + struct amigaos_args * cum = decl == current_function_decl ? &mycum : &othercum; + *cump = decl == current_function_decl; + cum->num_of_regs = amigaos_regparm > 0 ? amigaos_regparm : 0; + DPRINTF( + ("0amigaos_init_cumulative_args %s %p -> %d\r\n", decl ? lang_hooks.decl_printable_name (decl, 2) : "?", cum, cum->num_of_regs)); + + /* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ + + cum->last_arg_reg = -1; + cum->regs_already_used = 0; + + if (fntype) + { + tree attrs = TYPE_ATTRIBUTES(fntype); + if (attrs) + { + if (lookup_attribute ("stkparm", attrs)) + cum->num_of_regs = 0; + else + { + tree ratree = lookup_attribute ("regparm", attrs); + cum->num_of_regs = amigaos_regparm != 0 ? + amigaos_regparm : + AMIGAOS_DEFAULT_REGPARM; + if (ratree) + { + tree args = TREE_VALUE(ratree); + + if (args && TREE_CODE (args) == TREE_LIST) + { + tree val = TREE_VALUE(args); + if (TREE_CODE (val) == INTEGER_CST) + { + int no = TREE_INT_CST_LOW(val); + if (no > 0 && no < AMIGAOS_MAX_REGPARM) + cum->num_of_regs = no; + } + } + } + } + } + } + else + /* Libcall. */ + cum->num_of_regs = 0; + + if (cum->num_of_regs) + { + /* If this is a vararg call, put all arguments on stack. */ + tree param, next_param; + for (param = TYPE_ARG_TYPES(fntype); param; param = next_param) + { + next_param = TREE_CHAIN(param); + if (!next_param && TREE_VALUE (param) != void_type_node) + cum->num_of_regs = 0; + } + } + +#if ! defined (PCC_STATIC_STRUCT_RETURN) && defined (M68K_STRUCT_VALUE_REGNUM) + /* If return value is a structure, and we pass the buffer address in a + register, we can't use this register for our own purposes. + FIXME: Something similar would be useful for static chain. */ + if (fntype && aggregate_value_p (TREE_TYPE(fntype), fntype)) + cum->regs_already_used |= (1 << M68K_STRUCT_VALUE_REGNUM); +#endif + + if (fntype && DECL_STATIC_CHAIN(fntype)) + { + rtx reg = amigaos_static_chain_rtx (decl, 0); + if (reg) + cum->regs_already_used |= (1 << REGNO(reg)); + } + + if (fntype) + cum->formal_type = TYPE_ARG_TYPES(fntype); + else + /* Call to compiler-support function. */ + cum->formal_type = 0; + DPRINTF(("1amigaos_init_cumulative_args %p -> %d\r\n", cum, cum->num_of_regs)); +} + +int +amigaos_function_arg_reg (unsigned regno) +{ + return (mycum.regs_already_used & (1 << regno)) != 0; +} + +/* Update the data in CUM to advance over an argument. */ + +void +amigaos_function_arg_advance (cumulative_args_t cum_v, machine_mode, const_tree, bool) +{ + struct amigaos_args *cum = *get_cumulative_args (cum_v) ? &mycum : &othercum; + /* Update the data in CUM to advance over an argument. */ + + DPRINTF(("amigaos_function_arg_advance1 %p\r\n", cum)); + + if (cum->last_arg_reg != -1) + { + int count; + for (count = 0; count < cum->last_arg_len; count++) + cum->regs_already_used |= (1 << (cum->last_arg_reg + count)); + cum->last_arg_reg = -1; + } + + if (cum->formal_type) + cum->formal_type = TREE_CHAIN(cum->formal_type); +} + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. */ + +static struct rtx_def * +_m68k_function_arg (struct amigaos_args * cum, machine_mode mode, const_tree type) +{ + DPRINTF(("m68k_function_arg numOfRegs=%d\r\n", cum ? cum->num_of_regs : 0)); + + if (cum->num_of_regs) + { + int regbegin = -1, altregbegin = -1, len; + + /* FIXME: The last condition below is a workaround for a bug. */ + if (TARGET_68881 && FLOAT_MODE_P(mode) && + GET_MODE_UNIT_SIZE (mode) <= 12 && (GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT || mode == SCmode)) + { + regbegin = 16; /* FPx */ + len = GET_MODE_NUNITS(mode); + } + /* FIXME: Two last conditions below are workarounds for bugs. */ + else if (INTEGRAL_MODE_P (mode) && mode != CQImode && mode != CHImode) + { + if (!type || POINTER_TYPE_P(type)) + regbegin = 8; /* Ax */ + else + regbegin = 0; /* Dx */ + altregbegin = 8 - regbegin; + len = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; + } + + if (regbegin != -1) + { + int reg; + long mask; + + look_for_reg: mask = 1 << regbegin; + for (reg = 0; reg < cum->num_of_regs; reg++, mask <<= 1) + if (!(cum->regs_already_used & mask)) + { + int end; + for (end = reg; end < cum->num_of_regs && end < reg + len; end++, mask <<= 1) + if (cum->regs_already_used & mask) + break; + if (end == reg + len) + { + cum->last_arg_reg = reg + regbegin; + cum->last_arg_len = len; + break; + } + } + + if (reg == cum->num_of_regs && altregbegin != -1) + { + DPRINTF(("look for alt reg\n")); + regbegin = altregbegin; + altregbegin = -1; + goto look_for_reg; + } + } + + if (cum->last_arg_reg != -1) + { + DPRINTF(("-> gen_rtx_REG %d\r\n", cum->last_arg_reg)); + return gen_rtx_REG (mode, cum->last_arg_reg); + } + } + return 0; +} + +/* A C expression that controls whether a function argument is passed + in a register, and which register. */ + +struct rtx_def * +amigaos_function_arg (cumulative_args_t cum_v, machine_mode mode, const_tree type, bool) +{ + DPRINTF(("amigaos_function_arg %p\r\n", cum_v.p)); + + struct amigaos_args *cum = *get_cumulative_args (cum_v) ? &mycum : &othercum; + + tree asmtree = type ? TYPE_ATTRIBUTES(cum->formal_type ? TREE_VALUE(cum->formal_type) : type) : NULL_TREE; + //tree asmtree = type ? TYPE_ATTRIBUTES(type) : NULL_TREE; + + if (asmtree && 0 == strcmp ("asm", IDENTIFIER_POINTER(TREE_PURPOSE(asmtree)))) + { + int i; + cum->last_arg_reg = TREE_FIXED_CST_PTR(TREE_VALUE(asmtree))->data.low; + cum->last_arg_len = HARD_REGNO_NREGS(cum->last_arg_reg, mode); + + for (i = 0; i < cum->last_arg_len; i++) + { + if (cum->regs_already_used & (1 << (cum->last_arg_reg + i))) + { + error ("two parameters allocated for one register"); + break; + } + cum->regs_already_used |= (1 << (cum->last_arg_reg + i)); + } + return gen_rtx_REG (mode, cum->last_arg_reg); + } + return _m68k_function_arg (cum, mode, type); +} + +void +amiga_emit_regparm_clobbers (void) +{ + for (int i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + if (mycum.regs_already_used & (1 << i)) + { + rtx reg = gen_raw_REG (Pmode, i); + emit_insn (gen_rtx_CLOBBER(Pmode, gen_rtx_SET(reg, gen_rtx_MEM(Pmode, reg)))); + } +} + +/* Return zero if the attributes on TYPE1 and TYPE2 are incompatible, + one if they are compatible, and two if they are nearly compatible + (which causes a warning to be generated). */ + +int +amigaos_comp_type_attributes (const_tree type1, const_tree type2) +{ + DPRINTF(("amigaos_comp_type_attributes\n")); + /* Functions or methods are incompatible if they specify mutually exclusive + ways of passing arguments. */ + if (TREE_CODE(type1) == FUNCTION_TYPE || TREE_CODE(type1) == METHOD_TYPE) + { + tree attrs1 = TYPE_ATTRIBUTES(type1); + + tree asm1 = lookup_attribute("asmregs", attrs1); + tree stack1 = lookup_attribute("stkparm", attrs1); + tree reg1 = lookup_attribute("regparm", attrs1); + + tree attrs2 = TYPE_ATTRIBUTES(type2); + + tree asm2 = lookup_attribute("asmregs", attrs2); + tree stack2 = lookup_attribute("stkparm", attrs2); + tree reg2 = lookup_attribute("regparm", attrs2); + + if (reg1) + { + if (stack2 || asm2) + return 0; + + int no1 = TREE_INT_CST_LOW(TREE_VALUE(reg1)); + int no2 = reg2 ? TREE_INT_CST_LOW(TREE_VALUE(reg2)) : amigaos_regparm; + return no1 == no2; + } + + if (reg2) + { + if (stack1 || asm1) + return 0; + + int no2 = TREE_INT_CST_LOW(TREE_VALUE(reg2)); + return amigaos_regparm == no2; + } + + if (stack1) { + if (stack2) + return 1; + return amigaos_regparm == 0; + } + + if (stack2) + return amigaos_regparm == 0; + + if (asm1) + { + if (!asm2) + return 0; + + return 0 == strcmp(IDENTIFIER_POINTER(TREE_VALUE(asm1)), IDENTIFIER_POINTER(TREE_VALUE(asm2))); + } + + if (asm2) + return 0; + + } + else + { + tree attrs1 = TYPE_ATTRIBUTES(type1); + + tree chip1 = lookup_attribute("chip", attrs1); + tree fast1 = lookup_attribute("fast", attrs1); + tree far1 = lookup_attribute("far", attrs1); + + tree attrs2 = TYPE_ATTRIBUTES(type2); + + tree chip2 = lookup_attribute("chip", attrs2); + tree fast2 = lookup_attribute("fast", attrs2); + tree far2 = lookup_attribute("far", attrs2); + + if (chip1) + return chip2 && !fast2 && !far2; + + if (fast1) + return !chip2 && fast2 && !far2; + + if (far1) + return !chip2 && !fast2 && far2; + + return !chip2 && !fast2 && !far2; + } + return 1; +} +/* end-GG-local */ + +/* Handle a regparm, stkparm, saveds attribute; + arguments as in struct attribute_spec.handler. */ +tree +amigaos_handle_type_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) +{ + tree nnn = *node; + do + { // while (0); + DPRINTF(("%p with treecode %d\n", node, TREE_CODE(nnn))); + if (TREE_CODE (nnn) == FUNCTION_DECL || TREE_CODE (nnn) == FUNCTION_TYPE || TREE_CODE (nnn) == METHOD_TYPE) + { + /* 'regparm' accepts one optional argument - number of registers in + single class that should be used to pass arguments. */ + if (is_attribute_p ("regparm", name)) + { + DPRINTF(("regparm found\n")); + + if (lookup_attribute ("stkparm", TYPE_ATTRIBUTES(nnn))) + { + error ("`regparm' and `stkparm' are mutually exclusive"); + break; + } + if (args && TREE_CODE (args) == TREE_LIST) + { + tree val = TREE_VALUE(args); + DPRINTF(("regparm with val: %d\n", TREE_CODE(val))); + if (TREE_CODE (val) == INTEGER_CST) + { + int no = TREE_INT_CST_LOW(val); + if (no < 0 || no > AMIGAOS_MAX_REGPARM) + { + error ("`regparm' attribute: value %d not in [0 - %d]", no, + AMIGAOS_MAX_REGPARM); + break; + } + } + else + { + error ("invalid argument(s) to `regparm' attribute"); + break; + } + } + } + else if (is_attribute_p ("stkparm", name)) + { + if (lookup_attribute ("regparm", TYPE_ATTRIBUTES(nnn))) + { + error ("`regparm' and `stkparm' are mutually exclusive"); + break; + } + } + else if (is_attribute_p ("stackext", name)) + { + if (lookup_attribute ("interrupt", TYPE_ATTRIBUTES(nnn))) + { + error ("`stackext' and `interrupt' are mutually exclusive"); + break; + } + } + else if (is_attribute_p ("saveds", name)) + { + if (flag_pic < 3) + { + warning (OPT_Wattributes, "`%s' attribute is only usable with fbaserel", IDENTIFIER_POINTER(name)); + } + else + if (flag_resident) + { + error ("`saveds' can't be used with resident!\n"); + } + } + else + { + warning (OPT_Wattributes, "`%s' attribute only applies to data", IDENTIFIER_POINTER(name)); + } + } + else + { + if (is_attribute_p ("chip", name) || is_attribute_p ("fast", name) || is_attribute_p ("far", name)) + { + // OK + } + else + { + warning (OPT_Wattributes, "`%s' attribute only applies to functions", IDENTIFIER_POINTER(name)); + } + } + return NULL_TREE ; + } + while (0); + // error case + *no_add_attrs = true; + return NULL_TREE ; +} + +#define AMIGA_CHIP_SECTION_NAME ".datachip" +#define AMIGA_FAST_SECTION_NAME ".datafast" +#define AMIGA_FAR_SECTION_NAME ".datafar" + +void +amiga_insert_attribute (tree decl, tree * attr) +{ + if (!*attr) + return; + + tree name = TREE_PURPOSE(*attr); + + if (is_attribute_p("chip", name) || is_attribute_p("far", name) || is_attribute_p("fast", name)) + { + if (!TREE_TYPE(decl) == VAR_DECL) + { + error ("`%s' attribute can only be specified for variables", IDENTIFIER_POINTER(name)); + return; + } + + if (! TREE_STATIC (decl) && ! DECL_EXTERNAL (decl)) + { + error ("`%s' attribute cannot be specified for local variables", IDENTIFIER_POINTER(name)); + return; + } + + char const * section_name; + if (is_attribute_p("chip", name)) + section_name = AMIGA_CHIP_SECTION_NAME; + else if (is_attribute_p("fast", name)) + section_name = AMIGA_FAST_SECTION_NAME; + else if (is_attribute_p("far", name)) + section_name = AMIGA_FAR_SECTION_NAME; + + + /* The decl may have already been given a section attribute from + a previous declaration. Ensure they match. */ + if (DECL_SECTION_NAME (decl) == NULL) + set_decl_section_name(decl, section_name); + else if (strcmp (DECL_SECTION_NAME (decl), section_name) ) + { + error_at (DECL_SOURCE_LOCATION(decl), + "`%s' attribute conflicts with previous declaration", IDENTIFIER_POINTER(name)); + } + } + else + { +// warning (OPT_Wattributes, "`%s' attribute unknown", IDENTIFIER_POINTER(name)); + } +} + +extern bool +m68k_rtx_costs (rtx, machine_mode, int, int, int *, bool); + +bool +amigaos_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno, int *total, bool speed) +{ +// DPRINTF(("outer: %d, opno: %d", outer_code, opno)); + bool r = m68k_rtx_costs (x, mode, outer_code, opno, total, speed); +// *total *= 4; +// fprintf(stderr, "costs: %d, mode=%d, outer=%d, opno=%d, speed=%d, ok=%d\n", *total * 4, mode, outer_code, opno, speed, r); +// debug_rtx(x); + return r; +} + +/* Output assembly to switch to section NAME with attribute FLAGS. */ +#ifndef TARGET_AMIGAOS_VASM +extern void +amiga_named_section (const char *name, unsigned int flags, tree decl ) +{ + // only one code section - TODO: with amiga hunk this is no longer mandatory. + if (0 == strncmp (".text", name, 5)) + name = ".text"; + + if (0 == strncmp(".data", name, 5) && (!DECL_INITIAL (decl) || initializer_zerop (DECL_INITIAL (decl)))) + fprintf (asm_out_file, "\t.bss%s\n", name + 5); + else + fprintf (asm_out_file, "\t%s\n", name); +} +#else +extern void +amiga_named_section (const char *name, unsigned int flags, tree decl ATTRIBUTE_UNUSED) + { + if (0 == strncmp(".text", name, 5)) + name = ".text"; + + if (0 == strncmp("section ", name, 8)) + { +// fprintf (asm_out_file, "\t.section\t%s\n", name); + fprintf (asm_out_file, "\t%s\n", name); + } + else + { + fprintf (asm_out_file, "\tsection %s\n", name); + } + } +#endif + +/* Baserel support. */ + +/** + * Does x reference the pic_reg and is const or plus? + */ +static int +_amiga_is_const_pic_ref (const_rtx x) +{ + if (GET_CODE(x) == PLUS || GET_CODE(x) == MINUS) + { + if (GET_CODE(XEXP(x, 1)) == CONST_INT) + return _amiga_is_const_pic_ref(XEXP(x, 0)); + return false; + } + + if (GET_CODE(x) == CONST) + x = XEXP(x, 0); + if (GET_CODE(x) != PLUS) + return false; + + const_rtx reg = XEXP(x, 0); + if (!REG_P(reg) && REGNO(reg) != PIC_REG) + return false; + + const_rtx unspec = XEXP(x, 1); + while (GET_CODE(unspec) == PLUS || GET_CODE(unspec) == CONST) + unspec = XEXP(unspec, 0); + + if (GET_CODE(unspec) != UNSPEC) + return false; + + return true; +} + +int +amiga_is_const_pic_ref (const_rtx cnst) +{ + if (flag_pic < 3) + return false; + int r = _amiga_is_const_pic_ref (cnst); +// fprintf(stderr, r ? "valid pic: " : "invalid pic: "); +// debug_rtx(cnst); + return r; +} + + +/* Does operand (which is a symbolic_operand) live in text space? If + so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true. + + This function is used in base relative code generation. */ + +int +read_only_operand (rtx operand) +{ + if (GET_CODE (operand) == CONST) + operand = XEXP(XEXP (operand, 0), 0); + if (GET_CODE (operand) == SYMBOL_REF) + return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P(operand); + return 1; +} + +rtx +amigaos_struct_value_rtx (tree fntype, int incoming ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (Pmode, M68K_STRUCT_VALUE_REGNUM); +} + +rtx +amigaos_static_chain_rtx (const_tree decl, bool incoming ATTRIBUTE_UNUSED) +{ + if (!decl || !DECL_STATIC_CHAIN(decl)) + return 0; + + unsigned used = 0; + tree fntype = TREE_TYPE(decl); + if (fntype) + for (tree formal_type = TYPE_ARG_TYPES(fntype); formal_type; formal_type = TREE_CHAIN(formal_type)) + { + tree asmtree = TYPE_ATTRIBUTES(TREE_VALUE(formal_type)); + if (!asmtree || strcmp ("asm", IDENTIFIER_POINTER(TREE_PURPOSE(asmtree)))) + continue; + + unsigned regno = TREE_FIXED_CST_PTR(TREE_VALUE(asmtree))->data.low; + used |= 1 << regno; + } + + if (!(used & (1 << 9))) + return gen_rtx_REG (Pmode, 9); + if (!(used & (1 << 10))) + return gen_rtx_REG (Pmode, 10); + if (!(used & (1 << 11))) + return gen_rtx_REG (Pmode, 11); + if (!(used & (1 << 14))) + return gen_rtx_REG (Pmode, 14); + + return 0; +} + +/** + * Necessary to block some funny invalid combinations if baserel is used: + * +(const:SI (minus:SI (neg:SI (reg:SI 12 a4)) + (const:SI (plus:SI (unspec:SI [ + (symbol_ref:SI ("xyz") ) + (const_int 0 [0]) + ] 6) + +(plus:SI (reg:SI 10 a2) + (const:SI (minus:SI (neg:SI (reg:SI 12 a4)) + (const:SI (plus:SI (unspec:SI [ + (symbol_ref:SI ("xyz") ) + (const_int 0 [0]) + ] 6) + (const_int 1234 [0xe00]))))))) xyz.c:41 465 {*lea} + + */ +bool +amigaos_legitimate_src (rtx src) +{ + if (flag_pic < 3) + return true; + + if (MEM_P(src)) + { + rtx x = XEXP(src, 0); + if (GET_CODE(x) == PLUS || GET_CODE(x) == MINUS) { + if (amiga_is_const_pic_ref(XEXP(x, 0)) + || amiga_is_const_pic_ref(XEXP(x, 1))) + return false; + } + return true; + } + + if (GET_CODE(src) == PLUS || GET_CODE(src) == MINUS) + { + rtx x = XEXP(src, 0); + rtx y = XEXP(src, 1); + + /** handled in print_operand_address(...) */ + if (amiga_is_const_pic_ref(x)) + return GET_CODE(y) == CONST_INT; + + return amigaos_legitimate_src(x) && amigaos_legitimate_src(y) && !amiga_is_const_pic_ref(y); + } + + if (GET_CODE(src) == CONST) + { + rtx op = XEXP(src, 0); + if (GET_CODE(op) == MINUS || GET_CODE(op) == PLUS) + { + rtx x = XEXP(op, 0); + if (GET_CODE(x) == NOT || GET_CODE(x) == NEG || GET_CODE(x) == SIGN_EXTEND) + { + rtx reg = XEXP(x, 0); + if (!REG_P(reg)) + return true; + + return false; + } + } + + if (GET_CODE(op) == UNSPEC) + return false; + } + + return true; +} + +void +amigaos_restore_a4 (void) + { + if (flag_pic >= 3 && !flag_resident) + { + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); + tree attr = lookup_attribute ("saveds", attrs); + if (attr || TARGET_RESTORE_A4 || TARGET_ALWAYS_RESTORE_A4) + { + rtx a4 = gen_rtx_ASM_INPUT_loc(VOIDmode, "\tlea ___a4_init,a4", DECL_SOURCE_LOCATION (current_function_decl)); + a4->volatil = 1; + emit_insn(a4); + } + } + } + +void +amigaos_alternate_frame_setup_f (int fsize) + { +#if 0 + if (fsize < 128) + asm_fprintf (stream, "\tcmpl %s,%Rsp\n" + "\tjcc 0f\n" + "\tmoveq %I%d,%Rd0\n" + "\tmoveq %I0,%Rd1\n" + "\tjbsr %U__stkext_f\n" + "0:\tlink %Ra5,%I%d:W\n", + (flag_pic == 3 ? "a4@(___stk_limit:W)" : + (flag_pic == 4 ? "a4@(___stk_limit:L)" : + "___stk_limit")), + fsize, -fsize); + else + asm_fprintf (stream, "\tmovel %I%d,%Rd0\n\tjbsr %U__link_a5_d0_f\n", + fsize); +#endif + } + +void +amigaos_alternate_frame_setup (int fsize) + { +#if 0 + if (!fsize) + asm_fprintf (stream, "\tcmpl %s,%Rsp\n" + "\tjcc 0f\n" + "\tmoveq %I0,%Rd0\n" + "\tmoveq %I0,%Rd1\n" + "\tjbsr %U__stkext_f\n" + "0:\n", + (flag_pic == 3 ? "a4@(___stk_limit:W)" : + (flag_pic == 4 ? "a4@(___stk_limit:L)" : + "___stk_limit"))); + else if (fsize < 128) + asm_fprintf (stream, "\tcmpl %s,%Rsp\n" + "\tjcc 0f\n" + "\tmoveq %I%d,%Rd0\n" + "\tmoveq %I0,%Rd1\n" + "\tjbsr %U__stkext_f\n" + "0:\taddw %I%d,%Rsp\n", + (flag_pic == 3 ? "a4@(___stk_limit:W)" : + (flag_pic == 4 ? "a4@(___stk_limit:L)" : + "___stk_limit")), + fsize, -fsize); + else + asm_fprintf (stream, "\tmovel %I%d,%Rd0\n\tjbsr %U__sub_d0_sp_f\n", + fsize); +#endif + } + +#if 0 +extern bool debug_recog(char const * txt, int which_alternative, int n, rtx * operands) +{ + fprintf(stderr, "%s: %d ", txt, which_alternative); + for (int i = 0; i < n; ++i) + print_rtl(stderr, operands[i]); + fprintf(stderr, "\n--\n"); + return true; +} +#endif diff --git a/gcc/config/m68k/amigaos.h b/gcc/config/m68k/amigaos.h new file mode 100644 index 000000000000..1b60ed633a3a --- /dev/null +++ gcc/config/m68k/amigaos.h @@ -0,0 +1,504 @@ +/* Configuration for GNU C-compiler for m68k Amiga, running AmigaOS. + * + * This file is only included and used inside m68k.c to define the target. + * + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2003 + Free Software Foundation, Inc. + Contributed by Markus M. Wild (wild@amiga.physik.unizh.ch). + Heavily modified by Kamil Iskra (iskra@student.uci.agh.edu.pl). + + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef TARGET_AMIGAOS +#define TARGET_AMIGAOS 1 +#endif + +#if 0 +/* The function name __transfer_from_trampoline is not actually used. + The function definition just permits use of asm with operands" + (though the operand list is empty). */ + +#undef TRANSFER_FROM_TRAMPOLINE + +/* Call __flush_cache() after building the trampoline: it will call + an appropriate OS cache-clearing routine. */ + +#undef FINALIZE_TRAMPOLINE +#define FINALIZE_TRAMPOLINE(TRAMP) \ + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__flush_cache"), \ + 0, VOIDmode, 2, (TRAMP), Pmode, \ + GEN_INT (TRAMPOLINE_SIZE), SImode) + +#endif + +/* Compile using the first 'm68k_regparm' data, address and float + registers for arguments passing. */ +/*#define SUBTARGET_OPTIONS { "regparm=", &m68k_regparm_string, \ + N_("Use this register count to pass arguments"), 0},*/ + + +/* Nonzero if we need to generate special stack-allocating insns. + On most systems they are not needed. + When they are needed, also define ALTERNATE_ALLOCATE_STACK (see m68k.md) + to perform the necessary actions. */ +//#undef TARGET_ALTERNATE_ALLOCATE_STACK +//#define TARGET_ALTERNATE_ALLOCATE_STACK 0 + + +/* Compile with stack extension. */ + +#define MASK_STACKEXTEND 0x40000000 /* 1 << 30 */ +#define TARGET_STACKEXTEND (((target_flags & MASK_STACKEXTEND) \ + && !lookup_attribute ("interrupt", \ + TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) \ + || lookup_attribute ("stackext", \ + TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) + +///* Compile with stack checking. */ +// +#define MASK_STACKCHECK 0x20000000 /* 1 << 29 */ +#define TARGET_STACKCHECK ((target_flags & MASK_STACKCHECK) \ + && !(target_flags & MASK_STACKEXTEND) \ + && !lookup_attribute ("interrupt", \ + TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))) \ + && !lookup_attribute ("stackext", \ + TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) + +/* Compile with a4 restoring in public functions. */ + +#define MASK_RESTORE_A4 0x10000000 /* 1 << 28 */ +#define TARGET_RESTORE_A4 \ + ((target_flags & MASK_RESTORE_A4) && TREE_PUBLIC (current_function_decl)) + +/* Compile with a4 restoring in all functions. */ + +#define MASK_ALWAYS_RESTORE_A4 0x8000000 /* 1 << 27 */ +#define TARGET_ALWAYS_RESTORE_A4 (target_flags & MASK_ALWAYS_RESTORE_A4) + +/* Provide a dummy entry for the '-msmall-code' switch. This is used by + the assembler and '*_SPEC'. */ + +#undef SUBTARGET_SWITCHES +#define SUBTARGET_SWITCHES \ + { "small-code", 0, \ + "" /* Undocumented. */ }, \ + { "stackcheck", MASK_STACKCHECK, \ + N_("Generate stack-check code") }, \ + { "no-stackcheck", - MASK_STACKCHECK, \ + N_("Do not generate stack-check code") }, \ + { "stackextend", MASK_STACKEXTEND, \ + N_("Generate stack-extension code") }, \ + { "no-stackextend", - MASK_STACKEXTEND, \ + N_("Do not generate stack-extension code") }, \ + { "fixedstack", - (MASK_STACKCHECK|MASK_STACKEXTEND), \ + N_("Do not generate stack-check/stack-extension code") }, \ + { "restore-a4", MASK_RESTORE_A4, \ + N_("Restore a4 in public functions") }, \ + { "no-restore-a4", - MASK_RESTORE_A4, \ + N_("Do not restore a4 in public functions") }, \ + { "always-restore-a4", MASK_ALWAYS_RESTORE_A4, \ + N_("Restore a4 in all functions") }, \ + { "no-always-restore-a4", - MASK_ALWAYS_RESTORE_A4, \ + N_("Do not restore a4 in all functions") } + + +/* Support sections in chip, fast memory, currently '.datachip', '.datafast' + * and '.datafar' to abs addressing with baserel. */ +extern void +amiga_named_section (const char *name, unsigned int flags, tree decl); + +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION amiga_named_section + +/* Various ABI issues. */ + +/* This is (almost;-) BSD, so it wants DBX format. */ +#undef DBX_DEBUGGING_INFO +#define DBX_DEBUGGING_INFO + +/* GDB goes mad if it sees the function end marker. */ + +#define NO_DBX_FUNCTION_END 1 + +/* Allow folding division by zero. */ + +#define REAL_INFINITY + +/* Don't try using XFmode since we don't have appropriate runtime software + support. */ +#undef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE 64 + +/* We use A4 for the PIC pointer, not A5, which is the framepointer. */ + +#undef PIC_OFFSET_TABLE_REGNUM +#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 12 : INVALID_REGNUM) + +/* Use A5 as framepointer instead of A6, since the AmigaOS ABI requires A6 + to be used as a shared library base pointer in direct library calls. */ + +#undef FRAME_POINTER_REGNUM +#define FRAME_POINTER_REGNUM 13 + +#undef M68K_REGNAME +#define M68K_REGNAME(r) (reg_names[(r)]) + +/* The AmigaOS ABI does not define how structures should be returned, so, + contrary to 'm68k.h', we prefer a multithread-safe solution. */ + +#undef PCC_STATIC_STRUCT_RETURN + +/* Setup a default shell return value for those (gazillion..) programs that + (inspite of ANSI-C) declare main() to be void (or even VOID...) and thus + cause the shell to randomly caugh upon executing such programs (contrary + to Unix, AmigaOS scripts are terminated with an error if a program returns + with an error code above the `error' or even `failure' level + (which is configurable with the FAILAT command)). */ + +//+2004-06-24 Ulrich Weigand +//+ +//+ * c-decl.c (finish_function): Do not check for DEFAULT_MAIN_RETURN. +//+ * system.h (DEFAULT_MAIN_RETURN): Poison. +//+ * doc/tm.texi (DEFAULT_MAIN_RETURN): Remove documentation. +//+ + +//poison VAR +//#define DEFAULT_MAIN_RETURN c_expand_return (integer_zero_node) + +#undef WCHAR_TYPE +#define WCHAR_TYPE "unsigned short" + +/* XXX: section support */ +#if 0 +/* We define TARGET_ASM_NAMED_SECTION, but we don't support arbitrary sections, + including '.gcc_except_table', so we emulate the standard behaviour. */ +#undef TARGET_ASM_EXCEPTION_SECTION +#define TARGET_ASM_EXCEPTION_SECTION amiga_exception_section + +#undef TARGET_ASM_EH_FRAME_SECTION +#define TARGET_ASM_EH_FRAME_SECTION amiga_eh_frame_section +#endif + +/* Use sjlj exceptions because dwarf work only on elf targets */ +#undef DWARF2_UNWIND_INFO +#define DWARF2_UNWIND_INFO 0 + + +/* This is how to output an assembler line that says to advance the + location counter to a multiple of 2**LOG bytes. */ + +#ifndef ALIGN_ASM_OP +#define ALIGN_ASM_OP "\t.align\t" +#endif + +/* GAS supports alignment up to 32768 bytes. */ +#undef ASM_OUTPUT_ALIGN +#define ASM_OUTPUT_ALIGN(FILE, LOG) \ +do \ + { \ + if ((LOG) == 1) \ + fprintf ((FILE), "\t.even\n"); \ + else \ + fprintf ((FILE), "\t.align %d\n", (LOG)); \ + } \ +while (0) + +#if 0 + +/* Define this macro if references to a symbol must be treated + differently depending on something about the variable or + function named by the symbol (such as what section it is in). + + The macro definition, if any, is executed immediately after the + rtl for DECL or other node is created. + The value of the rtl will be a `mem' whose address is a + `symbol_ref'. + + The usual thing for this macro to do is to a flag in the + `symbol_ref' (such as `SYMBOL_REF_FLAG') or to store a modified + name string in the `symbol_ref' (if one bit is not enough + information). + + On the Amiga we use this to indicate if references to a symbol should be + absolute or base relative. */ + +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO amigaos_encode_section_info + +#define LIBCALL_ENCODE_SECTION_INFO(FUN) \ +do \ + { \ + if (flag_pic >= 3) \ + SYMBOL_REF_FLAG (FUN) = 1; \ + } \ +while (0) + +/* Select and switch to a section for EXP. */ + +//#undef TARGET_ASM_SELECT_SECTION +//#define TARGET_ASM_SELECT_SECTION amigaos_select_section + +/* Preserve A4 for baserel code if necessary. */ + +#define EXTRA_SAVE_REG(REGNO) \ +do { \ + if (flag_pic && flag_pic >= 3 && REGNO == PIC_OFFSET_TABLE_REGNUM \ + && amigaos_restore_a4()) \ + return true; \ +} while (0) + +/* Predicate for ALTERNATE_PIC_SETUP. */ + +#define HAVE_ALTERNATE_PIC_SETUP (flag_pic >= 3) + +/* Make a4 point at data hunk. */ + +#define ALTERNATE_PIC_SETUP(STREAM) \ + (amigaos_alternate_pic_setup (STREAM)) + +/* Attribute support. */ + +/* Generate the test of d0 before return to set cc register in 'interrupt' + function. */ + +#define EPILOGUE_END_HOOK(STREAM) \ +do \ + { \ + if (lookup_attribute ("interrupt", \ + TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) \ + asm_fprintf ((STREAM), "\ttstl %Rd0\n"); \ + } \ +while (0) + + +/* Stack checking and automatic extension support. */ + +#define PROLOGUE_BEGIN_HOOK(STREAM, FSIZE) \ + (amigaos_prologue_begin_hook ((STREAM), (FSIZE))) + +#define HAVE_ALTERNATE_FRAME_DESTR_F(FSIZE) \ + (TARGET_STACKEXTEND && current_function_calls_alloca) + +#define ALTERNATE_FRAME_DESTR_F(STREAM, FSIZE) \ + (asm_fprintf ((STREAM), "\tjra %U__unlk_a5_rts\n")) + +#define HAVE_ALTERNATE_RETURN \ + (TARGET_STACKEXTEND && frame_pointer_needed && \ + current_function_calls_alloca) + +#define ALTERNATE_RETURN(STREAM) + +#if 0 +#define HAVE_restore_stack_nonlocal TARGET_STACKEXTEND +#define gen_restore_stack_nonlocal gen_stack_cleanup_call + +#define HAVE_restore_stack_function TARGET_STACKEXTEND +#define gen_restore_stack_function gen_stack_cleanup_call + +#define HAVE_restore_stack_block TARGET_STACKEXTEND +#define gen_restore_stack_block gen_stack_cleanup_call + +#undef TARGET_ALTERNATE_ALLOCATE_STACK +#define TARGET_ALTERNATE_ALLOCATE_STACK 1 + +#define ALTERNATE_ALLOCATE_STACK(OPERANDS) \ +do \ + { \ + amigaos_alternate_allocate_stack (OPERANDS); \ + DONE; \ + } \ +while (0) +#endif + +/* begin-GG-local: dynamic libraries */ + +extern int amigaos_do_collecting (void); +extern void amigaos_gccopts_hook (const char *); +extern void amigaos_libname_hook (const char* arg); +extern void amigaos_collect2_cleanup (void); +extern void amigaos_prelink_hook (const char **, int *); +extern void amigaos_postlink_hook (const char *); + +/* This macro is used to check if all collect2 facilities should be used. + We need a few special ones, like stripping after linking. */ + +#define DO_COLLECTING (do_collecting || amigaos_do_collecting()) +#define COLLECT2_POSTLINK_HOOK(OUTPUT_FILE) amigaos_postlink_hook(OUTPUT_FILE) //new + +/* This macro is called in collect2 for every GCC argument name. + ARG is a part of commandline (without '\0' at the end). */ + +#define COLLECT2_GCC_OPTIONS_HOOK(ARG) amigaos_gccopts_hook(ARG) + +/* This macro is called in collect2 for every ld's "-l" or "*.o" or "*.a" + argument. ARG is a complete argument, with '\0' at the end. */ + +#define COLLECT2_LIBNAME_HOOK(ARG) amigaos_libname_hook(ARG) + +/* This macro is called at collect2 exit, to clean everything up. */ + +#define COLLECT2_EXTRA_CLEANUP amigaos_collect2_cleanup + +/* This macro is called just before the first linker invocation. + LD1_ARGV is "char** argv", which will be passed to "ld". STRIP is an + *address* of "strip_flag" variable. */ + +#define COLLECT2_PRELINK_HOOK(LD1_ARGV, STRIP) \ +amigaos_prelink_hook((const char **)(LD1_ARGV), (STRIP)) + +/* This macro is called just after the first linker invocation, in place of + "nm" and "ldd". OUTPUT_FILE is the executable's filename. */ + +#define COLLECT2_POSTLINK_HOOK(OUTPUT_FILE) amigaos_postlink_hook(OUTPUT_FILE) +/* end-GG-local */ + +#endif + +/* begin-GG-local: explicit register specification for parameters */ + +/* Note: this is an extension of m68k_args */ + + +#undef CLASS_MAX_NREGS +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((CLASS) == FP_REGS ? GET_MODE_NUNITS (MODE) \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + + +/* + On the m68k, this is a structure: + num_of_regs: number of data, address and float registers to use for + arguments passing (if it's 2, than pass arguments in d0, d1, a0, a1, + fp0 and fp1). 0 - pass everything on stack. vararg calls are + always passed entirely on stack. + regs_already_used: bitmask of the already used registers. + last_arg_reg - register number of the most recently passed argument. + -1 if passed on stack. + last_arg_len - number of registers used by the most recently passed + argument. +*/ + +extern void amigaos_init_cumulative_args (CUMULATIVE_ARGS *cum, tree); +extern void amigaos_function_arg_advance (cumulative_args_t, machine_mode, const_tree, bool); +extern rtx amigaos_function_arg (cumulative_args_t, machine_mode, const_tree, bool); +extern cumulative_args_t amigaos_pack_cumulative_args (CUMULATIVE_ARGS *); +extern int amigaos_comp_type_attributes (const_tree, const_tree); +extern tree amigaos_handle_type_attribute(tree *, tree, tree, int, bool*); + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ + +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE amigaos_function_arg_advance + +/* A C expression that controls whether a function argument is passed + in a register, and which register. */ + +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG amigaos_function_arg + +#undef TARGET_PACK_CUMULATIVE_ARGS +#define TARGET_PACK_CUMULATIVE_ARGS(CUM) \ + (amigaos_pack_cumulative_args(&(CUM))) + +#undef TARGET_COMP_TYPE_ATTRIBUTES +#define TARGET_COMP_TYPE_ATTRIBUTES amigaos_comp_type_attributes + + +/* end-GG-local */ + +#undef SUBTARGET_OVERRIDE_OPTIONS +#define SUBTARGET_OVERRIDE_OPTIONS \ +do \ + { \ + if (flag_resident) \ + { \ + if (flag_pic) \ + error ("-fbaserel and -resident are mutual exclusiv\n"); \ + flag_pic = flag_resident; \ + } \ + if (!TARGET_68020 && flag_pic==4) \ + error ("-fbaserel32 is not supported on the 68000 or 68010\n"); \ + if (amigaos_regparm > 0 && amigaos_regparm > AMIGAOS_MAX_REGPARM) \ + error ("-mregparm=x with 1 <= x <= %d\n", AMIGAOS_MAX_REGPARM); \ + } \ +while (0) + +/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, + affects_type_identity } */ +#define SUBTARGET_ATTRIBUTES \ + { "asmregs", 0, 0, false, false, false, 0, true }, \ + { "chip", 0, 0, false, true, false, amigaos_handle_type_attribute, false }, \ + { "fast", 0, 0, false, true, false, amigaos_handle_type_attribute, false }, \ + { "far", 0, 0, false, true, false, amigaos_handle_type_attribute, false }, \ + { "saveds", 0, 0, false, true, true, amigaos_handle_type_attribute, false }, \ + { "regparm", 1, 1, false, true, true, amigaos_handle_type_attribute,\ + true }, \ + { "stkparm", 0, 0, false, true, true, amigaos_handle_type_attribute,\ + true }, + +#define GOT_SYMBOL_NAME "" + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS amigaos_rtx_costs +bool +amigaos_rtx_costs (rtx, machine_mode, int, int, int *, bool); + +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX amigaos_struct_value_rtx +rtx +amigaos_struct_value_rtx(tree fntype, + int incoming ATTRIBUTE_UNUSED); + +#undef TARGET_STATIC_CHAIN +#define TARGET_STATIC_CHAIN amigaos_static_chain_rtx +rtx +amigaos_static_chain_rtx(const_tree fntype, + bool incoming ATTRIBUTE_UNUSED); + + +extern bool +amigaos_legitimate_src (rtx src); + +extern void +amigaos_restore_a4 (void); + +extern void +amigaos_alternate_frame_setup_f (int fsize); + +extern void +amigaos_alternate_frame_setup (int fsize); + + +#define HAVE_ALTERNATE_FRAME_SETUP_F(FSIZE) TARGET_STACKEXTEND + +#define ALTERNATE_FRAME_SETUP_F(FSIZE) \ + (amigaos_alternate_frame_setup_f ((FSIZE))) + +#define HAVE_ALTERNATE_FRAME_SETUP(FSIZE) TARGET_STACKEXTEND + +#define ALTERNATE_FRAME_SETUP(FSIZE) \ + (amigaos_alternate_frame_setup ((FSIZE))) + +#undef TARGET_INSERT_ATTRIBUTES +#define TARGET_INSERT_ATTRIBUTES amiga_insert_attribute + +void +amiga_insert_attribute (tree decl, tree * attr); diff --git a/gcc/config/m68k/amigaos.opt b/gcc/config/m68k/amigaos.opt new file mode 100644 index 000000000000..07406d27a777 --- /dev/null +++ gcc/config/m68k/amigaos.opt @@ -0,0 +1,63 @@ + +mregparm= +Target RejectNegative Var(amigaos_regparm) Joined UInteger Init(-1) +Pass arguments through registers. + +noixemul +Target RejectNegative +Do not use ixemul.library - use libnix instead to link + +ramiga-lib +Target RejectNegative +Use libinit.o as start file + +ramiga-libr +Target RejectNegative +Use libinitr.o as start file + +ramiga-dev +Target RejectNegative +Use devinit.o as start file + +msmall-code +Target RejectNegative Var(flag_smallcode,1) +small code model + +fbaserel +Target Report Var(flag_pic,3) +data is addressed relative to a4 + +fbaserel32 +Target Report Var(flag_pic,4) +data is addressed relative to a4 with 32 bit offsets + +resident +Target Common Report Var(flag_resident,3) +data is addressed relative to a4, linked as resident + +resident32 +Target Common Report Var(flag_resident,4) +data is addressed relative to a4 with 32 bit offsets, linked as resident + +mcrt= +Target RejectNegative Var(amigaos_crt) Joined +Specify startup binary + +fbbb= +Target RejectNegative Report Var(string_bbb_opts) Joined +-fbbb=Enable Bebbo's optimizations. ++ enable all optimizations +a commute add move instructions +b use register for base addresses +c convert load const and compare into a sub +e eliminate dead assignments + redundant loads +f shrink stack frame +i use post increment on addresses +m merge add and move statements +p propagate move assignment pairs out of loops +r register renaming to maybe save registers +s a strcpy optimization +v be verbose +V be very verbose +x dump insns +Default: -fbbb=+ which yields -fbbb=abcefimprs diff --git a/gcc/config/m68k/constraints.md b/gcc/config/m68k/constraints.md index b62120895304..1223852570c1 100644 --- gcc/config/m68k/constraints.md +++ gcc/config/m68k/constraints.md @@ -1,5 +1,5 @@ ;; Constraint definitions for m68k -;; Copyright (C) 2007-2016 Free Software Foundation, Inc. +;; Copyright (C) 2007-2015 Free Software Foundation, Inc. ;; This file is part of GCC. diff --git a/gcc/config/m68k/host-amigaos.c b/gcc/config/m68k/host-amigaos.c new file mode 100755 index 000000000000..8c72d516a378 --- /dev/null +++ gcc/config/m68k/host-amigaos.c @@ -0,0 +1,42 @@ +/* AmigaOS/m68k host-specific hook definitions. + Copyright (C) 2003 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 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "hosthooks.h" +#include "hosthooks-def.h" +#include "toplev.h" + +static void * amigaos_m68k_gt_pch_get_address (size_t); + +/* Return the address of the PCH address space, if the PCH will fit in it. */ + +static void * +amigaos_m68k_gt_pch_get_address (size_t sz ATTRIBUTE_UNUSED) +{ + fatal_error ("PCH not supported\n"); +} + +#undef HOST_HOOKS_GT_PCH_GET_ADDRESS +#define HOST_HOOKS_GT_PCH_GET_ADDRESS amigaos_m68k_gt_pch_get_address + +const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER; \ No newline at end of file diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index 03f474e1b63c..4533427db7a7 100644 --- gcc/config/m68k/m68k.c +++ gcc/config/m68k/m68k.c @@ -166,7 +166,10 @@ static bool m68k_save_reg (unsigned int regno, bool interrupt_handler); static bool m68k_ok_for_sibcall_p (tree, tree); static bool m68k_tls_symbol_p (rtx); static rtx m68k_legitimize_address (rtx, rtx, machine_mode); -static bool m68k_rtx_costs (rtx, machine_mode, int, int, int *, bool); +#ifndef TARGET_AMIGA +static +#endif +bool m68k_rtx_costs (rtx, machine_mode, int, int, int *, bool); #if M68K_HONOR_TARGET_STRICT_ALIGNMENT static bool m68k_return_in_memory (const_tree, const_tree); #endif @@ -174,10 +177,12 @@ static void m68k_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED; static void m68k_trampoline_init (rtx, tree, rtx); static int m68k_return_pops_args (tree, tree, int); static rtx m68k_delegitimize_address (rtx); +#ifndef TARGET_AMIGA static void m68k_function_arg_advance (cumulative_args_t, machine_mode, const_tree, bool); static rtx m68k_function_arg (cumulative_args_t, machine_mode, const_tree, bool); +#endif static bool m68k_cannot_force_const_mem (machine_mode mode, rtx x); static bool m68k_output_addr_const_extra (FILE *, rtx); static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; @@ -186,7 +191,11 @@ static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; #if INT_OP_GROUP == INT_OP_DOT_WORD #undef TARGET_ASM_ALIGNED_HI_OP +#ifndef TARGET_AMIGAOS_VASM #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" +#else +#define TARGET_ASM_ALIGNED_HI_OP "\tdc.w\t" +#endif #endif #if INT_OP_GROUP == INT_OP_NO_DOT @@ -322,6 +331,10 @@ static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; #undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL #define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 128 +#ifdef TARGET_AMIGA +#include "amigaos.h" +#endif + static const struct attribute_spec m68k_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, @@ -332,6 +345,9 @@ static const struct attribute_spec m68k_attribute_table[] = m68k_handle_fndecl_attribute, false }, { "interrupt_thread", 0, 0, true, false, false, m68k_handle_fndecl_attribute, false }, +#ifdef SUBTARGET_ATTRIBUTES + SUBTARGET_ATTRIBUTES +#endif { NULL, 0, 0, false, false, false, NULL, false } }; @@ -340,11 +356,21 @@ struct gcc_target targetm = TARGET_INITIALIZER; /* Base flags for 68k ISAs. */ #define FL_FOR_isa_00 FL_ISA_68000 #define FL_FOR_isa_10 (FL_FOR_isa_00 | FL_ISA_68010) -/* FL_68881 controls the default setting of -m68881. gcc has traditionally +/* "FL_68881 controls the default setting of -m68881. gcc has traditionally generated 68881 code for 68020 and 68030 targets unless explicitly told - not to. */ + not to." + + This is not true at least for the AMIGA. + gcc 2.93 does not set the 68881 flag. + + */ +#ifdef TARGET_AMIGA +#define FL_FOR_isa_20 (FL_FOR_isa_10 | FL_ISA_68020 \ + | FL_BITFIELD | FL_CAS) +#else #define FL_FOR_isa_20 (FL_FOR_isa_10 | FL_ISA_68020 \ | FL_BITFIELD | FL_68881 | FL_CAS) +#endif #define FL_FOR_isa_40 (FL_FOR_isa_20 | FL_ISA_68040) #define FL_FOR_isa_cpu32 (FL_FOR_isa_10 | FL_ISA_68020) @@ -545,7 +571,7 @@ m68k_option_override (void) : (m68k_cpu_flags & FL_COLDFIRE) != 0 ? FPUTYPE_COLDFIRE : FPUTYPE_68881); - /* Sanity check to ensure that msep-data and mid-sahred-library are not + /* Sanity check to ensure that msep-data and mid-shared-library are not * both specified together. Doing so simply doesn't make sense. */ if (TARGET_SEP_DATA && TARGET_ID_SHARED_LIBRARY) @@ -556,7 +582,7 @@ m68k_option_override (void) * -fpic but it hasn't been tested properly. */ if (TARGET_SEP_DATA || TARGET_ID_SHARED_LIBRARY) - flag_pic = 2; + flag_pic = TARGET_68020 ? 2 : 1; /* -mpcrel -fPIC uses 32-bit pc-relative displacements. Raise an error if the target does not support them. */ @@ -569,11 +595,15 @@ m68k_option_override (void) if (TARGET_PCREL && flag_pic == 0) flag_pic = 1; - if (!flag_pic) + /* SBF: use normal jumps/calls with baserel(32) modes. */ + if (!flag_pic || flag_pic > 2) { m68k_symbolic_call_var = M68K_SYMBOLIC_CALL_JSR; - +#ifndef TARGET_AMIGAOS_VASM m68k_symbolic_jump = "jra %a0"; +#else + m68k_symbolic_jump = "jmp %a0"; +#endif } else if (TARGET_ID_SHARED_LIBRARY) /* All addresses must be loaded from the GOT. */ @@ -866,8 +896,9 @@ m68k_save_reg (unsigned int regno, bool interrupt_handler) { if (crtl->saves_all_registers) return true; + /* SBF: do not save the PIC_REG with baserel(32) modes. */ if (crtl->uses_pic_offset_table) - return true; + return flag_pic < 3; /* Reload may introduce constant pool references into a function that thitherto didn't need a PIC register. Note that the test above will not catch that case because we will only set @@ -978,6 +1009,8 @@ m68k_set_frame_related (rtx_insn *insn) /* Emit RTL for the "prologue" define_expand. */ +extern void amiga_emit_regparm_clobbers(void); + void m68k_expand_prologue (void) { @@ -986,6 +1019,10 @@ m68k_expand_prologue (void) m68k_compute_frame_layout (); +#ifdef TARGET_AMIGA + amiga_emit_regparm_clobbers(); +#endif + if (flag_stack_usage_info) current_function_static_stack_size = current_frame.size + current_frame.offset; @@ -1021,6 +1058,11 @@ m68k_expand_prologue (void) if (frame_pointer_needed) { +#ifdef TARGET_AMIGA + if (HAVE_ALTERNATE_FRAME_SETUP_F (fsize_with_regs)) + ALTERNATE_FRAME_SETUP_F (fsize_with_regs); + else +#endif if (fsize_with_regs == 0 && TUNE_68040) { /* On the 68040, two separate moves are faster than link.w 0. */ @@ -1030,6 +1072,10 @@ m68k_expand_prologue (void) m68k_set_frame_related (emit_move_insn (frame_pointer_rtx, stack_pointer_rtx)); } +#ifdef TARGET_AMIGA + else if (HAVE_ALTERNATE_FRAME_SETUP (fsize_with_regs)) + ALTERNATE_FRAME_SETUP (fsize_with_regs); +#endif else if (fsize_with_regs < 0x8000 || TARGET_68020) m68k_set_frame_related (emit_insn (gen_link (frame_pointer_rtx, @@ -1127,9 +1173,14 @@ m68k_expand_prologue (void) current_frame.reg_mask, true, true)); } + /* SBF: do not load the PIC_REG with baserel(32) */ if (!TARGET_SEP_DATA - && crtl->uses_pic_offset_table) + && crtl->uses_pic_offset_table && flag_pic < 3) emit_insn (gen_load_got (pic_offset_table_rtx)); + +#ifdef TARGET_AMIGA + amigaos_restore_a4 (); +#endif } /* Return true if a simple (return) instruction is sufficient for this @@ -1419,6 +1470,7 @@ m68k_ok_for_sibcall_p (tree decl, tree exp) return false; } +#ifndef TARGET_AMIGA /* On the m68k all args are always pushed. */ static rtx @@ -1440,6 +1492,7 @@ m68k_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, ? (GET_MODE_SIZE (mode) + 3) & ~3 : (int_size_in_bytes (type) + 3) & ~3); } +#endif /* Convert X to a legitimate function call memory reference and return the result. */ @@ -1796,13 +1849,21 @@ output_btst (rtx *operands, rtx countop, rtx dataop, rtx_insn *insn, int signpos && next_insn_tests_no_inequality (insn)) { cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N | CC_NO_OVERFLOW; +#ifndef TARGET_AMIGAOS_VASM return "move%.w %1,%%ccr"; +#else + return "move%.w %1,ccr"; +#endif } if (count == 2 && DATA_REG_P (operands[1]) && next_insn_tests_no_inequality (insn)) { cc_status.flags = CC_NOT_NEGATIVE | CC_INVERTED | CC_NO_OVERFLOW; +#ifndef TARGET_AMIGAOS_VASM return "move%.w %1,%%ccr"; +#else + return "move%.w %1,ccr"; +#endif } /* count == 1 followed by bvc/bvs and count == 0 followed by bcc/bcs are also possible, but need @@ -1921,10 +1982,12 @@ m68k_legitimate_constant_address_p (rtx x, unsigned int reach, bool strict_p) if (!CONSTANT_ADDRESS_P (x)) return false; - if (flag_pic + if (flag_pic && flag_pic < 3 && !(strict_p && TARGET_PCREL) && symbolic_operand (x, VOIDmode)) - return false; + { + return false; + } if (M68K_OFFSETS_MUST_BE_WITHIN_SECTIONS_P && reach > 1) { @@ -2111,6 +2174,18 @@ m68k_legitimate_address_p (machine_mode mode, rtx x, bool strict_p) { struct m68k_address address; +#ifdef TARGET_AMIGA + if (MEM_P(x)) + return false; + /* SBF: the baserel(32) const plus pic_ref, symbol is an address. */ + if (amiga_is_const_pic_ref(x)) + return true; + + if (!amigaos_legitimate_src(x)) + return false; + +#endif + return m68k_decompose_address (mode, x, strict_p, &address); } @@ -2131,7 +2206,11 @@ m68k_legitimate_mem_p (rtx x, struct m68k_address *address) bool m68k_legitimate_constant_p (machine_mode mode, rtx x) { - return mode != XFmode && !m68k_illegitimate_symbolic_constant_p (x); + return mode != XFmode && !m68k_illegitimate_symbolic_constant_p (x) +#ifdef TARGET_AMIGA + && amigaos_legitimate_src (x) +#endif + ; } /* Return true if X matches the 'Q' constraint. It must be a memory @@ -2172,6 +2251,8 @@ m68k_get_gp (void) if (pic_offset_table_rtx == NULL_RTX) pic_offset_table_rtx = gen_rtx_REG (Pmode, PIC_REG); +// debug_rtx(pic_offset_table_rtx); + crtl->uses_pic_offset_table = 1; return pic_offset_table_rtx; @@ -2442,9 +2523,37 @@ legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED, if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) { gcc_assert (reg); + if (flag_pic < 3) + { + pic_ref = m68k_wrap_symbol_into_got_ref (orig, RELOC_GOT, reg); + pic_ref = m68k_move_to_reg (pic_ref, orig, reg); + } + #ifdef TARGET_AMIGA + else + { + + /* SBF: Does the symbol use common or bss and qualifies for pic_reg? + * Do not ref to .text via pic_reg! + */ + tree decl; + if (GET_CODE (orig) == SYMBOL_REF && !orig->frame_related && !SYMBOL_REF_FUNCTION_P(orig) + && (decl = SYMBOL_REF_DECL (orig)) && !(DECL_SECTION_NAME(decl)) + && !decl->common.typed.base.readonly_flag + && !decl->decl_with_vis.in_text_section) + { - pic_ref = m68k_wrap_symbol_into_got_ref (orig, RELOC_GOT, reg); - pic_ref = m68k_move_to_reg (pic_ref, orig, reg); + /* SBF: unfortunately using the wrapped symbol without MEM does not work. + * The pic_ref reference gets decomposed and leads to no working code. + */ + pic_ref = m68k_wrap_symbol (pic_ref, RELOC_GOT, m68k_get_gp (), reg); + + /* SBF: adding const avoids decomposing. */ + pic_ref = gen_rtx_CONST (Pmode, pic_ref); + } + else + pic_ref = gen_rtx_CONST (Pmode, pic_ref); + } +#endif } else if (GET_CODE (orig) == CONST) { @@ -2463,7 +2572,8 @@ legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED, orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, base == reg ? 0 : reg); - if (GET_CODE (orig) == CONST_INT) + /* SBF: use normal plus and rely on optimizer with baserel(32). */ + if (flag_pic < 3 && GET_CODE (orig) == CONST_INT) pic_ref = plus_constant (Pmode, base, INTVAL (orig)); else pic_ref = gen_rtx_PLUS (Pmode, base, orig); @@ -2787,7 +2897,10 @@ const_int_cost (HOST_WIDE_INT i) } } -static bool +#ifndef TARGET_AMIGA +static +#endif +bool m68k_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UNUSED, int *total, bool speed ATTRIBUTE_UNUSED) @@ -2863,6 +2976,7 @@ m68k_rtx_costs (rtx x, machine_mode mode, int outer_code, *total = COSTS_N_INSNS (TARGET_COLDFIRE ? 2 : 3); return true; } + return false; case ASHIFT: @@ -2931,6 +3045,25 @@ m68k_rtx_costs (rtx x, machine_mode mode, int outer_code, *total = 0; return false; + case MEM: + { + /* simple but not exact */ + rtx y = XEXP(x, 0); + int yc = GET_CODE(y); + if (yc == REG || yc == PRE_INC || yc == POST_INC || yc == POST_DEC) + *total += 4; + else + if (yc == PRE_DEC) + *total += 6; + else + *total += 8; + + if (mode != QImode && mode != QImode) + *total += 4; + + return true; + } + default: return false; } @@ -4456,7 +4589,9 @@ print_operand (FILE *file, rtx op, int letter) else if (letter == 'p') { output_addr_const (file, op); - if (!(GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op))) + /* SBF: do not add @PLTPC with baserel(32). */ + if (flag_pic < 3 + && !(GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op))) fprintf (file, "@PLTPC"); } else if (GET_CODE (op) == REG) @@ -4475,34 +4610,52 @@ print_operand (FILE *file, rtx op, int letter) && CONSTANT_ADDRESS_P (XEXP (op, 0)) && !(GET_CODE (XEXP (op, 0)) == CONST_INT && INTVAL (XEXP (op, 0)) < 0x8000 - && INTVAL (XEXP (op, 0)) >= -0x8000)) - fprintf (file, MOTOROLA ? ".l" : ":l"); + && INTVAL (XEXP (op, 0)) >= -0x8000) +#ifdef TARGET_AMIGA +/* SBF: Do not append some 'l' with baserel(32). */ + && !amiga_is_const_pic_ref(XEXP(op, 0)) +#endif + ) + fprintf (file, MOTOROLA ? ".l" : ":l"); } else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode) { long l; REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (op), l); +#ifndef TARGET_AMIGAOS_VASM asm_fprintf (file, "%I0x%lx", l & 0xFFFFFFFF); +#else + asm_fprintf (file, "%I$%lx", l & 0xFFFFFFFF); +#endif } else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == XFmode) { long l[3]; REAL_VALUE_TO_TARGET_LONG_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op), l); +#ifndef TARGET_AMIGAOS_VASM asm_fprintf (file, "%I0x%lx%08lx%08lx", l[0] & 0xFFFFFFFF, l[1] & 0xFFFFFFFF, l[2] & 0xFFFFFFFF); +#else + asm_fprintf (file, "%I$%lx%08lx%08lx", l[0] & 0xFFFFFFFF, + l[1] & 0xFFFFFFFF, l[2] & 0xFFFFFFFF); +#endif } else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode) { long l[2]; REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op), l); +#ifndef TARGET_AMIGAOS_VASM asm_fprintf (file, "%I0x%lx%08lx", l[0] & 0xFFFFFFFF, l[1] & 0xFFFFFFFF); +#else + asm_fprintf (file, "%I$%lx%08lx", l[0] & 0xFFFFFFFF, l[1] & 0xFFFFFFFF); +#endif } else { /* Use `print_operand_address' instead of `output_addr_const' to ensure that we print relevant PIC stuff. */ asm_fprintf (file, "%I"); - if (TARGET_PCREL + if ((TARGET_PCREL || flag_pic > 2) && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST)) print_operand_address (file, op); else @@ -4521,7 +4674,19 @@ m68k_get_reloc_decoration (enum m68k_reloc reloc) switch (reloc) { case RELOC_GOT: - if (MOTOROLA) + /* SBF: add the proper extension for baserel relocs with baserel(32). */ + if (TARGET_AMIGA) + { + if (flag_pic == 1) + return ".w"; + else if (flag_pic == 3) + return ":W"; + else if (flag_pic == 4) + return ":L"; + else + return ""; + } + if (MOTOROLA) { if (flag_pic == 1 && TARGET_68020) return "@GOT.w"; @@ -4671,8 +4836,58 @@ print_operand_address (FILE *file, rtx addr) { struct m68k_address address; +#ifdef TARGET_AMIGA + /* + * SBF: remove the const wrapper. + */ + if (amiga_is_const_pic_ref(addr)) + { + /* handle (plus (unspec ) (const_int) */ + rtx *x = &addr; + while (GET_CODE(*x) != PLUS) + x = &XEXP(*x, 0); + + x = &XEXP(*x, 1); // CONST + if (GET_CODE(*x) == CONST) + x = &XEXP(*x, 0); + + /* if there is a plus - swap it. + * we want n+symbol:W (not symbol:W+n) + */ + if (GET_CODE(*x) == PLUS) + { + rtx plus = *x; + fprintf (file, "%d+", (int) INTVAL (XEXP(plus, 1))); + + *x = XEXP(plus, 0); + print_operand_address(file, XEXP(addr, 0)); + *x = plus; + } + else + print_operand_address(file, XEXP(addr, 0)); + + return; + } + if (GET_CODE(addr) == PLUS && amiga_is_const_pic_ref(XEXP(addr, 0))) + { + fprintf (file, "%d+", (int) INTVAL (XEXP(addr, 1))); + print_operand_address(file, XEXP(XEXP(addr, 0),0)); + return; + } + + + if (symbolic_operand(addr, VOIDmode)) + { + memset (&address, 0, sizeof (address)); + address.offset = addr; + } + else +#endif if (!m68k_decompose_address (QImode, addr, true, &address)) - gcc_unreachable (); + { + debug_rtx(addr); + gcc_unreachable (); + } if (address.code == PRE_DEC) fprintf (file, MOTOROLA ? "-(%s)" : "%s@-", @@ -4714,6 +4929,14 @@ print_operand_address (FILE *file, rtx addr) } else output_addr_const (file, addr); + +#ifdef TARGET_AMIGA + if (SYMBOL_REF_FUNCTION_P(addr)) + { + if (flag_smallcode) + asm_fprintf(file, ":w(pc)"); + } +#endif } } else @@ -5155,7 +5378,9 @@ m68k_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, /* Value is true if hard register REGNO can hold a value of machine-mode MODE. On the 68000, we let the cpu registers can hold any mode, but - restrict the 68881 registers to floating-point modes. */ + restrict the 68881 registers to floating-point modes. + SBF: Disallow the frame pointer register, if the frame pointer is used. + */ bool m68k_regno_mode_ok (int regno, machine_mode mode) @@ -5169,7 +5394,7 @@ m68k_regno_mode_ok (int regno, machine_mode mode) else if (ADDRESS_REGNO_P (regno)) { if (regno + GET_MODE_SIZE (mode) / 4 <= 16) - return true; + return !frame_pointer_needed || regno != FRAME_POINTER_REGNUM; } else if (FP_REGNO_P (regno)) { @@ -5190,6 +5415,13 @@ m68k_secondary_reload_class (enum reg_class rclass, machine_mode mode, rtx x) { int regno; +#ifdef TARGET_AMIGA + /* SBF: check for baserel's const pic_ref + * and return ADDR_REGS or NO_REGS + */ + if (!MEM_P(x) && amiga_is_const_pic_ref(x)) + return rclass == ADDR_REGS ? NO_REGS : ADDR_REGS; +#endif regno = true_regnum (x); diff --git a/gcc/config/m68k/m68k.h b/gcc/config/m68k/m68k.h index 2aa858fa23b5..442a84e6a883 100644 --- gcc/config/m68k/m68k.h +++ gcc/config/m68k/m68k.h @@ -204,7 +204,11 @@ along with GCC; see the file COPYING3. If not see #define INT_OP_DC 3 /* dc.b, dc.w, dc.l */ /* Set the default. */ +#ifndef TARGET_AMIGAOS_VASM #define INT_OP_GROUP INT_OP_DOT_WORD +#else +#define INT_OP_GROUP INT_OP_DC +#endif /* Bit values used by m68k-devices.def to identify processor capabilities. */ #define FL_BITFIELD (1 << 0) /* Support bitfield instructions. */ @@ -378,14 +382,13 @@ along with GCC; see the file COPYING3. If not see { /* d0/d1/a0/a1 */ \ 0, 1, 8, 9, \ /* d2-d7 */ \ - 2, 3, 4, 5, 6, 7, \ + 2, 10, 3, 11, 4, 5, 6, 7, \ /* a2-a7/arg */ \ - 10, 11, 12, 13, 14, 15, 24, \ + 12, 13, 14, 15, 24, \ /* fp0-fp7 */ \ 16, 17, 18, 19, 20, 21, 22, 23\ } - /* On the m68k, ordinary registers hold 32 bits worth; for the 68881 registers, a single register is always enough for anything that can be stored in them at all. */ @@ -440,8 +443,8 @@ along with GCC; see the file COPYING3. If not see /* The m68k has three kinds of registers, so eight classes would be a complete set. One of them is not needed. */ enum reg_class { - NO_REGS, DATA_REGS, - ADDR_REGS, FP_REGS, + NO_REGS, DATA_REGS, D0_REGS, + ADDR_REGS, A0_REGS, FP_REGS, GENERAL_REGS, DATA_OR_FP_REGS, ADDR_OR_FP_REGS, ALL_REGS, LIM_REG_CLASSES }; @@ -449,8 +452,8 @@ enum reg_class { #define N_REG_CLASSES (int) LIM_REG_CLASSES #define REG_CLASS_NAMES \ - { "NO_REGS", "DATA_REGS", \ - "ADDR_REGS", "FP_REGS", \ + { "NO_REGS", "DATA_REGS", "D0_REGS" \ + "ADDR_REGS", "A0_REGS", "FP_REGS", \ "GENERAL_REGS", "DATA_OR_FP_REGS", \ "ADDR_OR_FP_REGS", "ALL_REGS" } @@ -458,7 +461,9 @@ enum reg_class { { \ {0x00000000}, /* NO_REGS */ \ {0x000000ff}, /* DATA_REGS */ \ + {0x00000001}, /* D0_REGS */ \ {0x0100ff00}, /* ADDR_REGS */ \ + {0x00000100}, /* A0_REGS */ \ {0x00ff0000}, /* FP_REGS */ \ {0x0100ffff}, /* GENERAL_REGS */ \ {0x00ff00ff}, /* DATA_OR_FP_REGS */ \ @@ -614,11 +619,11 @@ __transfer_from_trampoline () \ #define REGNO_OK_FOR_INDEX_P(REGNO) \ (INT_REGNO_P (REGNO) \ - || INT_REGNO_P (reg_renumber[REGNO])) + || (reg_renumber && INT_REGNO_P (reg_renumber[REGNO]))) #define REGNO_OK_FOR_BASE_P(REGNO) \ (ADDRESS_REGNO_P (REGNO) \ - || ADDRESS_REGNO_P (reg_renumber[REGNO])) + || (reg_renumber && ADDRESS_REGNO_P (reg_renumber[REGNO]))) #define REGNO_OK_FOR_INDEX_NONSTRICT_P(REGNO) \ (INT_REGNO_P (REGNO) \ @@ -727,9 +732,49 @@ do { if (cc_prev_status.flags & CC_IN_68881) \ if (cc_prev_status.flags & CC_NO_OVERFLOW) \ return NO_OV; \ return NORMAL; } while (0) + +#ifdef TARGET_AMIGAOS_VASM +#define ASM_OUTPUT_ASCII(MYFILE, MYSTRING, MYLENGTH) \ + do { \ + FILE *_hide_asm_out_file = (MYFILE); \ + const unsigned char *_hide_p = (const unsigned char *) (MYSTRING); \ + int _hide_thissize = (MYLENGTH); \ + { \ + FILE *asm_out_file = _hide_asm_out_file; \ + const unsigned char *p = _hide_p; \ + int thissize = _hide_thissize; \ + int i; \ + fprintf (asm_out_file, "\tdc.b \""); \ + \ + for (i = 0; i < thissize; i++) \ + { \ + int c = p[i]; \ + if (c == '\"' || c == '\\') \ + putc ('\\', asm_out_file); \ + if (ISPRINT (c)) \ + putc (c, asm_out_file); \ + else \ + { \ + fprintf (asm_out_file, "\\%o", c); \ + /* After an octal-escape, if a digit follows, \ + terminate one string constant and start another. \ + The VAX assembler fails to stop reading the escape \ + after three digits, so this is the only way we \ + can get it to parse the data properly. */ \ + if (i < thissize - 1 && ISDIGIT (p[i + 1])) \ + fprintf (asm_out_file, "\"\n\tdc.b \""); \ + } \ + } \ + fprintf (asm_out_file, "\"\n"); \ + } \ + } \ + while (0) +#endif + /* Control the assembler format that we output. */ +#ifndef TARGET_AMIGAOS_VASM #define ASM_APP_ON "#APP\n" #define ASM_APP_OFF "#NO_APP\n" #define TEXT_SECTION_ASM_OP "\t.text" @@ -739,6 +784,17 @@ do { if (cc_prev_status.flags & CC_IN_68881) \ #define LOCAL_LABEL_PREFIX "" #define USER_LABEL_PREFIX "_" #define IMMEDIATE_PREFIX "#" +#else +#define ASM_APP_ON "" +#define ASM_APP_OFF "" +#define TEXT_SECTION_ASM_OP "\tsection .text" +#define DATA_SECTION_ASM_OP "\tsection .data" +#define GLOBAL_ASM_OP "\txdef\t" +#define REGISTER_PREFIX "" +#define LOCAL_LABEL_PREFIX "_." +#define USER_LABEL_PREFIX "_" +#define IMMEDIATE_PREFIX "#" +#endif #define REGISTER_NAMES \ {REGISTER_PREFIX"d0", REGISTER_PREFIX"d1", REGISTER_PREFIX"d2", \ @@ -858,11 +914,17 @@ do { if (cc_prev_status.flags & CC_IN_68881) \ /* The m68k does not use absolute case-vectors, but we must define this macro anyway. */ -#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ +#ifndef TARGET_AMIGAOS_VASM +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ asm_fprintf (FILE, "\t.long %LL%d\n", VALUE) - -#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ asm_fprintf (FILE, "\t.word %LL%d-%LL%d\n", VALUE, REL) +#else +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + asm_fprintf (FILE, "\tdc.l %LL%d\n", VALUE) +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + asm_fprintf (FILE, "\tdc.w %LL%d-%LL%d\n", VALUE, REL) +#endif /* We don't have a way to align to more than a two-byte boundary, so do the best we can and don't complain. */ @@ -872,13 +934,24 @@ do { if (cc_prev_status.flags & CC_IN_68881) \ #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN /* Use "move.l %a4,%a4" to advance within code. */ +#ifndef TARGET_AMIGAOS_VASM #define ASM_OUTPUT_ALIGN_WITH_NOP(FILE,LOG) \ if ((LOG) > 0) \ fprintf ((FILE), "\t.balignw %u,0x284c\n", 1 << (LOG)); #endif +#else +#define ASM_OUTPUT_ALIGN_WITH_NOP(FILE,LOG) \ + if ((LOG) > 0) \ + fprintf ((FILE), "\tcnop 0,%u\n", 1 << (LOG)); +#endif +#ifndef TARGET_AMIGAOS_VASM #define ASM_OUTPUT_SKIP(FILE,SIZE) \ fprintf (FILE, "\t.skip %u\n", (int)(SIZE)) +#else +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\tds.b %u\n", (int)(SIZE)) +#endif #define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ ( fputs (".comm ", (FILE)), \ @@ -971,3 +1044,8 @@ extern int m68k_sched_address_bypass_p (rtx_insn *, rtx_insn *); extern int m68k_sched_indexed_address_bypass_p (rtx_insn *, rtx_insn *); #define CPU_UNITS_QUERY 1 + +#if 1 +extern void default_stabs_asm_out_constructor (rtx, int); +extern void default_stabs_asm_out_destructor (rtx, int); +#endif diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md index ec37bd76f55f..05ef02027f01 100644 --- gcc/config/m68k/m68k.md +++ gcc/config/m68k/m68k.md @@ -128,13 +128,11 @@ (UNSPECV_TAS_2 4) ]) -;; Registers by name. +;; Registers by name. SBF: Do not define PIC_REG / A6_REG here! (define_constants [(D0_REG 0) (A0_REG 8) (A1_REG 9) - (PIC_REG 13) - (A6_REG 14) (SP_REG 15) (FP0_REG 16) ]) @@ -1566,7 +1564,7 @@ ;; so we will prefer it to them. (define_insn "pushasi" - [(set (match_operand:SI 0 "push_operand" "=m") + [(set (match_operand:SI 0 "push_operand" "=<") (match_operand:SI 1 "address_operand" "p"))] "" "pea %a1" @@ -7204,6 +7202,7 @@ "operands[0] = replace_equiv_address (operands[0], stack_pointer_rtx);") ;; Changing pea X.w into a move.l is no real win here. +;; SBF: also disable converting pea for baserel insns! (define_peephole2 [(set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG) (match_operand:SI 0 "const_int_operand" ""))) @@ -7213,7 +7212,8 @@ && !reg_mentioned_p (stack_pointer_rtx, operands[2]) && !(CONST_INT_P (operands[2]) && INTVAL (operands[2]) != 0 && IN_RANGE (INTVAL (operands[2]), -0x8000, 0x7fff) - && !valid_mov3q_const (INTVAL (operands[2])))" + && !valid_mov3q_const (INTVAL (operands[2]))) + && !amiga_is_const_pic_ref(operands[2])" [(set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG) (match_dup 0))) (set (match_dup 1) (match_dup 2))] { diff --git a/gcc/config/m68k/m68kamigaos.h b/gcc/config/m68k/m68kamigaos.h new file mode 100644 index 000000000000..3f3aafc5f254 --- /dev/null +++ gcc/config/m68k/m68kamigaos.h @@ -0,0 +1,760 @@ +/* m68kelf support, derived from m68kv4.h */ + +/* Target definitions for GNU compiler for mc680x0 running AmigaOs + Copyright (C) 1991-2016 Free Software Foundation, Inc. + + Written by Ron Guilmette (rfg@netcom.com) and Fred Fish (fnf@cygnus.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 +. */ + +#ifndef TARGET_AMIGA +#define TARGET_AMIGA 1 +#endif + +#define HAS_INIT_SECTION + +#ifndef SWBEG_ASM_OP +#define SWBEG_ASM_OP "\t.swbeg\t" +#endif + +#ifdef TARGET_AMIGAOS_VASM +#undef ASM_STABS_OP +#define ASM_STABS_OP "|\t.stabs\t" + +#undef ASM_STABD_OP +#define ASM_STABD_OP "|\t.stabd\t" + +#undef ASM_STABN_OP +#define ASM_STABN_OP "|\t.stabn\t" +#endif + +#undef PIC_REG +#define PIC_REG 12 + +#undef FRAME_POINTER_REGNUM +#define FRAME_POINTER_REGNUM 13 + +#undef M68K_REGNAME +#define M68K_REGNAME(r) ( \ + ( ((r) == FRAME_POINTER_REGNUM) \ + && frame_pointer_needed) ? \ + M68K_FP_REG_NAME : reg_names[(r)]) + + + +/* Here are three prefixes that are used by asm_fprintf to + facilitate customization for alternate assembler syntaxes. + Machines with no likelihood of an alternate syntax need not + define these and need not use asm_fprintf. */ + +/* The prefix for register names. Note that REGISTER_NAMES + is supposed to include this prefix. Also note that this is NOT an + fprintf format string, it is a literal string */ + +#undef REGISTER_PREFIX +#define REGISTER_PREFIX "" + +/* The prefix for local (compiler generated) labels. + These labels will not appear in the symbol table. */ + +#undef LOCAL_LABEL_PREFIX +#ifndef TARGET_AMIGAOS_VASM +#define LOCAL_LABEL_PREFIX "." +#else +#define LOCAL_LABEL_PREFIX "_." +#endif + +/* The prefix to add to user-visible assembler symbols. */ + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + +/* config/m68k.md has an explicit reference to the program counter, + prefix this by the register prefix. */ + +#ifndef TARGET_AMIGAOS_VASM +#define ASM_RETURN_CASE_JUMP \ + do { \ + return "jmp %%pc@(2,%0:w)"; \ + } while (0) +#else +#define ASM_RETURN_CASE_JUMP \ + do { \ + return "jmp (2,pc,%0.w)"; \ + } while (0) +#endif + +/* This is how to output an assembler line that says to advance the + location counter to a multiple of 2**LOG bytes. */ + +#ifndef TARGET_AMIGAOS_VASM +#ifndef ALIGN_ASM_OP +#define ALIGN_ASM_OP "\t.align\t" +#endif +#else +#define ALIGN_ASM_OP "\talign\t" +#endif + +#undef ASM_OUTPUT_ALIGN +#ifndef TARGET_AMIGAOS_VASM +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ +do { \ + if ((LOG) > 0) \ + fprintf ((FILE), "%s%u\n", ALIGN_ASM_OP, 1 << (LOG)); \ +} while (0) +#else +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ +do { \ + if ((LOG) > 0) \ + fprintf ((FILE), "%s%u\n", ALIGN_ASM_OP, (LOG)); \ +} while (0) +#endif + +#if 0 +extern int amiga_declare_object; + +#define ASM_DECLARE_OBJECT_NAME(FILE,NAME,DECL) \ +if (!DECL_INITIAL (DECL) || \ + initializer_zerop (DECL_INITIAL (decl))) \ + { \ + amiga_declare_object = 1; \ + fprintf ((FILE), ".comm\t%s,", NAME); \ + } \ +else \ +ASM_OUTPUT_LABEL (FILE, NAME) + +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ +if (amiga_declare_object) \ + fprintf (FILE, "%u\n", (int)(SIZE)); \ +else \ + fprintf (FILE, "\t.skip %u\n", (int)(SIZE)); \ +amiga_declare_object = 0 +#endif + +/* Register in which address to store a structure value is passed to a + function. The default in m68k.h is a1. For m68k/SVR4 it is a0. */ + +#undef M68K_STRUCT_VALUE_REGNUM +#define M68K_STRUCT_VALUE_REGNUM A0_REG + +/* The static chain regnum defaults to a0, but we use that for + structure return, so have to use a1 for the static chain. */ + +#undef STATIC_CHAIN_REGNUM +#define STATIC_CHAIN_REGNUM A1_REG +#undef M68K_STATIC_CHAIN_REG_NAME +#define M68K_STATIC_CHAIN_REG_NAME REGISTER_PREFIX "a1" + +#ifndef TARGET_AMIGAOS_VASM +#define ASM_COMMENT_START "|" +#else +#define ASM_COMMENT_START "|" +#endif + +/* Define how the m68k registers should be numbered for Dwarf output. + The numbering provided here should be compatible with the native + SVR4 SDB debugger in the m68k/SVR4 reference port, where d0-d7 + are 0-7, a0-a8 are 8-15, and fp0-fp7 are 16-23. */ + +#undef DBX_REGISTER_NUMBER +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +#if 0 +/* SVR4 m68k assembler is bitching on the `comm i,1,1' which askes for + 1 byte alignment. Don't generate alignment for COMMON seems to be + safer until we the assembler is fixed. */ +#undef ASM_OUTPUT_ALIGNED_COMMON +/* Same problem with this one. */ +#undef ASM_OUTPUT_ALIGNED_LOCAL +#endif + +#undef ASM_OUTPUT_COMMON +#undef ASM_OUTPUT_LOCAL +#ifndef TARGET_AMIGAOS_VASM +#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".comm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u\n", (int)(SIZE))) +#else +#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ + ( switch_to_section (bss_section), \ + fputs ("|.comm\n\tcnop 0,4\n", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ":\n\tds.b %u\n", (int)(SIZE)), \ + fputs ("\txdef ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), "\n")) +#endif + +#ifndef TARGET_AMIGAOS_VASM +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".lcomm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u\n", (int)(SIZE))) +#else +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( switch_to_section (bss_section), \ + fputs ("|.lcomm\n\tcnop 0,4\n", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ":\n\tds.b %u\n", (int)(SIZE))) +#endif + +/* Currently, JUMP_TABLES_IN_TEXT_SECTION must be defined in order to + keep switch tables in the text section. */ + +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +/* In m68k svr4, using swbeg is the standard way to do switch + table. */ +#undef ASM_OUTPUT_BEFORE_CASE_LABEL +#define ASM_OUTPUT_BEFORE_CASE_LABEL(FILE,PREFIX,NUM,TABLE) \ + fprintf ((FILE), "%s&%d\n", SWBEG_ASM_OP, XVECLEN (PATTERN (TABLE), 1)); +/* end of stuff from m68kv4.h */ + +#ifndef TARGET_AMIGAOS_VASM +#undef BSS_SECTION_ASM_OP +#define BSS_SECTION_ASM_OP "\t.bss" +#else +#define BSS_SECTION_ASM_OP "\tsection\tbss" +#endif + +#ifndef TARGET_AMIGAOS_VASM +#undef DATA_SECTION_ASM_OP +#define DATA_SECTION_ASM_OP "\t.data" +#else +#define DATA_SECTION_ASM_OP "\tsection\tdata" +#endif + +#ifndef ASM_OUTPUT_ALIGNED_BSS +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) +#endif + + +/* Specs, switches. */ + +/* amiga/amigaos are the new "standard" defines for the Amiga. + MCH_AMIGA, AMIGA, __chip etc. are used in other compilers and are + provided for compatibility reasons. + When creating shared libraries, use different 'errno'. */ + +#undef TARGET_OS_CPP_BUILTINS +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__chip=__attribute__((__chip__))"); \ + builtin_define ("__fast=__attribute__((__fast__))"); \ + builtin_define ("__far=__attribute__((__far__))"); \ + builtin_define ("__saveds=__attribute__((__saveds__))"); \ + builtin_define ("__interrupt=__attribute__((__interrupt__))"); \ + builtin_define ("__stackext=__attribute__((__stackext__))"); \ + builtin_define ("__regargs=__attribute__((__regparm__(2)))"); \ + builtin_define ("__stdargs=__attribute__((__stkparm__))"); \ + builtin_define ("__aligned=__attribute__((__aligned__(4)))"); \ + builtin_define_std ("amiga"); \ + builtin_define_std ("amigaos"); \ + builtin_define_std ("AMIGA"); \ + builtin_define_std ("MCH_AMIGA"); \ + builtin_assert ("system=amigaos"); \ + } \ + while (0) + +#if 0 +if (target_flags & (MASK_RESTORE_A4|MASK_ALWAYS_RESTORE_A4)) \ + builtin_define ("errno=(*ixemul_errno)"); \ + +#endif + +/* put return values in FPU build in FP0 Reg */ +#undef FUNCTION_VALUE_REGNO_P +#define FUNCTION_VALUE_REGNO_P(N) \ + ((N) == D0_REG || (TARGET_68881 && (N) == FP0_REG)) + +// see 930623-1.c +// ((N) == D0_REG || (N) == A0_REG || (TARGET_68881 && (N) == FP0_REG)) + +/* Inform the program which CPU we compile for. */ + +//#undef TARGET_CPU_CPP_BUILTINS +/* + use --with-cpu=mc68040 etc.. instead on config. code was after #define TARGET_CPU_CPP_BUILTINS() + if (TARGET_68040_ONLY) \ + { \ + if (TARGET_68060) \ + builtin_define_std ("mc68060"); \ + else \ + builtin_define_std ("mc68040"); \ + } \ + else if (TARGET_68030 && !TARGET_68040) \ + builtin_define_std ("mc68030"); \ + else if (TARGET_68020) \ + builtin_define_std ("mc68020"); \ + builtin_define_std ("mc68000"); \ +*/ +/* +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("mc68040"); \ + if (flag_pic > 2) \ + { \ + builtin_define ("__pic__"); \ + if (flag_pic > 3) \ + builtin_define ("__PIC__"); \ + } \ + builtin_assert ("cpu=m68k"); \ + builtin_assert ("machine=m68k"); \ + } \ + while (0) +*/ + +/* When creating shared libraries, use different 'errno'. */ +#define CPP_IXEMUL_SPEC \ + "%{!ansi:-Dixemul} -D__ixemul__ -D__ixemul " \ + "%{malways-restore-a4:-Derrno=(*ixemul_errno)} " \ + "%{mrestore-a4:-Derrno=(*ixemul_errno)}" +#define CPP_LIBNIX_SPEC \ + "-isystem %:sdk_root(libnix/include) " \ + "%{!ansi:-Dlibnix} -D__libnix__ -D__libnix" +#define CPP_CLIB2_SPEC \ + "-isystem %:sdk_root(include) " \ + "%{!ansi:-DCLIB2} -D__CLIB2__ -D__CLIB2" + +/* Define __HAVE_68881__ in preprocessor according to the -m flags. + This will control the use of inline 68881 insns in certain macros. + Note: it should be set in TARGET_CPU_CPP_BUILTINS but TARGET_68881 + isn't the same -m68881 since its also true for -m680[46]0 ... + Differentiate between libnix and ixemul. */ + +#define CPP_SPEC \ + "%{m68881:-D__HAVE_68881__} " \ + "%{!ansi:" \ + "%{m68020:-Dmc68020} " \ + "%{mc68020:-Dmc68020} " \ + "%{m68020-40:-Dmc68020} " \ + "%{m68020-60:-Dmc68020} " \ + "%{m68030:-Dmc68030} " \ + "%{m68040:-Dmc68040} " \ + "%{m68060:-Dmc68060}} " \ + "%{m68020:-D__mc68020__ -D__mc68020} " \ + "%{mc68020:-D__mc68020__ -D__mc68020} " \ + "%{m68020-40:-D__mc68020__ -D__mc68020} " \ + "%{m68020-60:-D__mc68020__ -D__mc68020} " \ + "%{m68030:-D__mc68030__ -D__mc68030} " \ + "%{m68040:-D__mc68040__ -D__mc68040} " \ + "%{m68060:-D__mc68060__ -D__mc68060} " \ + "%{noixemul:%(cpp_libnix)} " \ + "%{mcrt=nix*:%(cpp_libnix)} " \ + "%{mcrt=ixemul:%(cpp_ixemul)} " \ + "%{mcrt=clib2:%(cpp_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(cpp_clib2)}}" + +/* Various -m flags require special flags to the assembler. */ + +#undef ASM_SPEC +#ifndef TARGET_AMIGAOS_VASM +#define ASM_SPEC \ + "%(asm_cpu) %(asm_cpu_default) %{msmall-code:-sc} %{!msmall-code:-S}" +#else +#define ASM_SPEC \ + "-gas -esc -ldots -Fhunk -quiet %(asm_cpu) %(asm_cpu_default) %{msmall-code:-sc}" +#endif + +#undef ASM_CPU_SPEC +#define ASM_CPU_SPEC \ + "%{mcpu=*:-m%*} " \ + "%{m68000|mc68000:-m68010} " \ + "%{m6802*|mc68020:-m68020} " \ + "%{m68030} " \ + "%{m68040} " \ + "%{m68060}" + +#ifndef TARGET_AMIGAOS_VASM +#define ASM_CPU_DEFAULT_SPEC \ + "%{!m680*:%{!mc680*:%{!mcpu=*:-m68000}}}" +#else +#define ASM_CPU_DEFAULT_SPEC \ + "%{!m680*:%{!mc680*:-m68000}}" +#endif + +/* Choose the right startup file, depending on whether we use base relative + code, base relative code with automatic relocation (-resident), their + 32-bit versions, libnix, profiling or plain crt0.o. */ + +#define STARTFILE_IXEMUL_SPEC \ + "%{fbaserel:%{!resident:bcrt0.o%s}}" \ + "%{resident:rcrt0.o%s}" \ + "%{fbaserel32:%{!resident32:lcrt0.o%s}}" \ + "%{resident32:scrt0.o%s}" \ + "%{!resident:%{!fbaserel:%{!resident32:%{!fbaserel32:" \ + "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}}}}}" +#define STARTFILE_LIBNIX_SPEC \ + "%:sdk_root(libnix/lib/libnix/ " \ + "%{ramiga-*:" \ + "%{ramiga-lib:libinit.o%s}" \ + "%{ramiga-libr:libinitr.o%s}" \ + "%{ramiga-dev:devinit.o%s}}" \ + "%{!ramiga-*:" \ + "%{resident:nrcrt0.o%s}" \ + "%{!resident:" \ + "%{fbaserel:nbcrt0.o%s}" \ + "%{!fbaserel:" \ + "%{fbaserel32:nlbcrt0.o%s}" \ + "%{!fbaserel32:ncrt0.o%s}}}}" \ + ")" + +#define STARTFILE_CLIB2_SPEC \ + "%:sdk_root(lib/ " \ + "%{resident32:nr32crt0.o%s}" \ + "%{!resident32:" \ + "%{fbaserel32:nb32crt0.o%s}" \ + "%{!fbaserel32:" \ + "%{resident:nrcrt0.o%s}" \ + "%{!resident:" \ + "%{fbaserel:nbcrt0.o%s}" \ + "%{!fbaserel:ncrt0.o%s}}}}" \ + ")" + +#undef STARTFILE_SPEC +#ifdef TARGET_AMIGAOS_VASM +#define STARTFILE_SPEC \ + "startup%O%s" +#else +#define STARTFILE_SPEC \ + "%{noixemul:%(startfile_libnix)} " \ + "%{mcrt=nix*:%(startfile_libnix)} " \ + "%{mcrt=ixemul:%(startfile_ixemul)} " \ + "%{mcrt=clib2:%(startfile_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(startfile_clib2)}}" +#endif + +#define ENDFILE_IXEMUL_SPEC "" +#define ENDFILE_LIBNIX_SPEC "-lstubs" +#define ENDFILE_CLIB2_SPEC "" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{noixemul:%(endfile_libnix)} " \ + "%{mcrt=nix*:%(endfile_libnix)} " \ + "%{mcrt=ixemul:%(endfile_ixemul)} " \ + "%{mcrt=clib2:%(endfile_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(endfile_clib2)}}" + + +/* Automatically search libamiga.a for AmigaOS specific functions. Note + that we first search the standard C library to resolve as much as + possible from there, since it has names that are duplicated in libamiga.a + which we *don't* want from there. Then search libamiga.a for any calls + that were not generated inline, and finally search the standard C library + again to resolve any references that libamiga.a might have generated. + This may only be a temporary solution since it might be better to simply + remove the things from libamiga.a that should be pulled in from libc.a + instead, which would eliminate the first reference to libc.a. Note that + if we don't search it automatically, it is very easy for the user to try + to put in a -lamiga himself and get it in the wrong place, so that (for + example) calls like sprintf come from -lamiga rather than -lc. */ + +#define LIB_IXEMUL_SPEC \ + "%{!p:%{!pg:-lc -lamiga -lc}} " \ + "%{p:-lc_p} %{pg:-lc_p}" +#define LIB_LIBNIX_SPEC \ + "-lnixmain -lnix " \ + "%{mcrt=*:-l%*} " \ + "%{!mcrt=*:-lnix20} " \ + "-lamiga " \ + "%{mstackcheck:-lstack} " \ + "%{mstackextend:-lstack}" +#define LIB_CLIB2_SPEC \ + "-lc -lamiga -ldebug " \ + "%{mstackcheck:-lstack} " \ + "%{mstackextend:-lstack}" + +#ifdef TARGET_AMIGAOS_VASM +#define LIB_SPEC \ + "-lvc -lamiga " +#else +#define LIB_SPEC \ + "%{noixemul:%(lib_libnix)} " \ + "%{mcrt=nix*:%(lib_libnix)} " \ + "%{mcrt=ixemul:%(lib_ixemul)} " \ + "%{mcrt=clib2:%(lib_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(lib_clib2)}}" +#endif + +#define LIBGCC_IXEMUL_SPEC "" +#define LIBGCC_LIBNIX_SPEC "-lnix -fl libnix " \ + "%{mcrt=*:-l%*} " \ + "%{!mcrt=*:-lnix20} -lstubs" +#define LIBGCC_CLIB2_SPEC "-lc" +#define LIBGCC_SPEC "-lgcc " \ + "%{noixemul:%(libgcc_libnix)} " \ + "%{mcrt=nix*:%(libgcc_libnix)} " \ + "%{mcrt=ixemul:%(libgcc_ixemul)} " \ + "%{mcrt=clib2:%(libgcc_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(libgcc_clib2)}}" + + +/* If debugging, tell the linker to output amiga-hunk symbols *and* a BSD + compatible debug hunk. + Also, pass appropriate linker flavours depending on user-supplied + commandline options. */ + +#define LINK_IXEMUL_SPEC "" +#define LINK_LIBNIX_SPEC "-L%:sdk_root(libnix/lib) -fl libnix" +#define LINK_CLIB2_SPEC "-L%:sdk_root(lib)" + +/* If debugging, tell the linker to output amiga-hunk symbols *and* a BSD + compatible debug hunk. + Also, pass appropriate linker flavours depending on user-supplied + commandline options. */ + +#ifdef TARGET_AMIGAOS_VASM +#define LINK_SPEC \ + "%{noixemul:%(link_libnix)} " \ + "%{mcrt=nix*:%(link_libnix)} " \ + "%{mcrt=ixemul:%(link_ixemul)} " \ + "%{mcrt=clib2:%(link_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(link_clib2)}} " \ + "%{fbaserel:%{!resident:-m amiga_bss -fl libb %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}}} " \ + "%{resident:-m amiga_bss -amiga-datadata-reloc -fl libb %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}} " \ + "%{fbaserel32:%{!resident32:-m amiga_bss -fl libb32 %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}}} " \ + "%{resident32:-m amiga_bss -amiga-datadata-reloc -fl libb32 %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}} " \ + "%{mcpu=68020:-fl libm020} " \ + "%{m68020:-fl libm020} " \ + "%{mc68020:-fl libm020} " \ + "%{m68030:-fl libm020} " \ + "%{m68040:-fl libm020} " \ + "%{m68060:-fl libm020} " \ + "%{m68020-40:-fl libm020} " \ + "%{m68020-60:-fl libm020} " \ + "%{m68881:-fl libm881}" +#else +#define LINK_SPEC \ + "%{noixemul:%(link_libnix)} " \ + "%{mcrt=nix*:%(link_libnix)} " \ + "%{mcrt=ixemul:%(link_ixemul)} " \ + "%{mcrt=clib2:%(link_clib2)} " \ + "%{!noixemul:%{!mcrt*:%(link_clib2)}} " \ + "%{fbaserel:%{!resident:-m amiga_bss -fl libb %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}}} " \ + "%{resident:-m amiga_bss -amiga-datadata-reloc -fl libb %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}} " \ + "%{fbaserel32:%{!resident32:-m amiga_bss -fl libb32 %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}}} " \ + "%{resident32:-m amiga_bss -amiga-datadata-reloc -fl libb32 %{noixemul:-fl libnix} %{mcrt=nix*:-fl libnix}} " \ + "%{g:-amiga-debug-hunk} " \ + "%{mcpu=68020:-fl libm020} " \ + "%{mcpu=68030:-fl libm020} " \ + "%{mcpu=68040:-fl libm020} " \ + "%{mcpu=68060:-fl libm020} " \ + "%{m68020:-fl libm020} " \ + "%{mc68020:-fl libm020} " \ + "%{m68030:-fl libm020} " \ + "%{m68040:-fl libm020} " \ + "%{m68060:-fl libm020} " \ + "%{m68020-40:-fl libm020} " \ + "%{m68020-60:-fl libm020} " \ + "%{m68881:-fl libm881}" +#endif + +/* Translate '-resident' to '-fbaserel' (they differ in linking stage only). + Don't put function addresses in registers for PC-relative code. */ + +#define CC1_SPEC \ + "%{resident:-fbaserel} " \ + "%{resident32:-fbaserel32} " \ + "%{msmall-code:-fno-function-cse}" + +#define LINK_CPU_SPEC \ + "%{m6802*|mc68020|m68030|m68040|m68060:-fl libm020} " \ + "%{m68881:-fl libm881}" + +/* [cahirwpz] A modified copy of LINK_COMMAND_SPEC from gcc/gcc.c file. + Don't prepend libgcc.a to link libraries and make sure the options is + at the end of command line. Otherwise linker chooses generic functions + from libgcc.a instead AmigaOS-specific counterparts from libnix.a. */ + +#ifdef TARGET_AMIGAOS_VASM +#define LINK_COMMAND_SPEC \ + "%{!fsyntax-only:" \ + "%{!c:" \ + "%{!M:" \ + "%{!MM:" \ + "%{!E:" \ + "%{!S:" \ + "%(linker) -Cvbcc %l %X %{o*} %{A} %{d} %{e*} %{m} " \ + "%{N} %{n} %{r} %{s} %{t} %{u*} %{x} %{z} %{Z} " \ + "%{!A:%{!nostdlib:%{!nostartfiles:%S}}} " \ + "%{static:} %{L*} %D %o " \ + "%{!nostdlib:%{!nodefaultlibs:%L}} " \ + "%{!A:%{!nostdlib:%{!nostartfiles:%E}}} " \ + "%{!nostdlib:%{!nodefaultlibs:%G}} " \ + "%{T*} }}}}}} " +#else +#define LINK_COMMAND_SPEC \ + "%{!fsyntax-only:" \ + "%{!c:" \ + "%{!M:" \ + "%{!MM:" \ + "%{!E:" \ + "%{!S:" \ + "%(linker) %l %X %{o*} %{A} %{d} %{e*} %{m} " \ + "%{N} %{n} %{r} %{s} %{t} %{u*} %{x} %{z} %{Z} " \ + "%{!A:%{!nostdlib:%{!nostartfiles:%S}}} " \ + "%{static:} %{L*} %D %o " \ + "%{!nostdlib:%{!nodefaultlibs:%L}} " \ + "%{!A:%{!nostdlib:%{!nostartfiles:%E}}} " \ + "%{!nostdlib:%{!nodefaultlibs:%G}} " \ + "%{T*} }}}}}} " +#endif + +extern const char * amiga_m68k_prefix_func(int, const char **); + +#define EXTRA_SPEC_FUNCTIONS \ + { "sdk_root", amiga_m68k_prefix_func }, + +/* This macro defines names of additional specifications to put in the specs + that can be used in various specifications like CC1_SPEC. Its definition + is an initializer with a subgrouping for each command option. + + Each subgrouping contains a string constant, that defines the + specification name, and a string constant that used by the GCC driver + program. + + Do not define this macro if it does not need to do anything. */ +#undef EXTRA_SPECS +#define EXTRA_SPECS \ + {"asm_cpu", ASM_CPU_SPEC }, \ + {"asm_cpu_default", ASM_CPU_DEFAULT_SPEC }, \ + {"link_cpu", LINK_CPU_SPEC }, \ + {"cpp_ixemul", CPP_IXEMUL_SPEC}, \ + {"cpp_libnix", CPP_LIBNIX_SPEC}, \ + {"cpp_clib2", CPP_CLIB2_SPEC}, \ + {"lib_ixemul", LIB_IXEMUL_SPEC}, \ + {"lib_libnix", LIB_LIBNIX_SPEC}, \ + {"lib_clib2", LIB_CLIB2_SPEC}, \ + {"link_ixemul", LINK_IXEMUL_SPEC}, \ + {"link_libnix", LINK_LIBNIX_SPEC}, \ + {"link_clib2", LINK_CLIB2_SPEC}, \ + {"startfile_ixemul", STARTFILE_IXEMUL_SPEC}, \ + {"startfile_libnix", STARTFILE_LIBNIX_SPEC}, \ + {"startfile_clib2", STARTFILE_CLIB2_SPEC}, \ + {"endfile_ixemul", ENDFILE_IXEMUL_SPEC}, \ + {"endfile_libnix", ENDFILE_LIBNIX_SPEC}, \ + {"endfile_clib2", ENDFILE_CLIB2_SPEC}, \ + {"libgcc_ixemul", LIBGCC_IXEMUL_SPEC}, \ + {"libgcc_libnix", LIBGCC_LIBNIX_SPEC}, \ + {"libgcc_clib2", LIBGCC_CLIB2_SPEC} + +/* begin-GG-local: dynamic libraries */ + +extern int amigaos_do_collecting (void); +extern void amigaos_gccopts_hook (const char *); +extern void amigaos_libname_hook (const char* arg); +extern void amigaos_collect2_cleanup (void); +extern void amigaos_prelink_hook (const char **, int *); +extern void amigaos_postlink_hook (const char *); + +/* This macro is used to check if all collect2 facilities should be used. + We need a few special ones, like stripping after linking. */ + +#define DO_COLLECTING (do_collecting || amigaos_do_collecting()) + +/* This macro is called in collect2 for every GCC argument name. + ARG is a part of commandline (without '\0' at the end). */ + +#define COLLECT2_GCC_OPTIONS_HOOK(ARG) amigaos_gccopts_hook(ARG) + +/* This macro is called in collect2 for every ld's "-l" or "*.o" or "*.a" + argument. ARG is a complete argument, with '\0' at the end. */ + +#define COLLECT2_LIBNAME_HOOK(ARG) amigaos_libname_hook(ARG) + +/* This macro is called at collect2 exit, to clean everything up. */ + +#define COLLECT2_EXTRA_CLEANUP amigaos_collect2_cleanup + +/* This macro is called just before the first linker invocation. + LD1_ARGV is "char** argv", which will be passed to "ld". STRIP is an + *address* of "strip_flag" variable. */ + +#define COLLECT2_PRELINK_HOOK(LD1_ARGV, STRIP) \ +amigaos_prelink_hook((const char **)(LD1_ARGV), (STRIP)) + +/* This macro is called just after the first linker invocation, in place of + "nm" and "ldd". OUTPUT_FILE is the executable's filename. */ + +#define COLLECT2_POSTLINK_HOOK(OUTPUT_FILE) amigaos_postlink_hook(OUTPUT_FILE) +/* end-GG-local */ + +#undef MAX_OFILE_ALIGNMENT +#define MAX_OFILE_ALIGNMENT ((1 << 15)*BITS_PER_UNIT) + +#undef FIXED_INCLUDE_DIR +#define FIXED_INCLUDE_DIR CROSS_INCLUDE_DIR "/../../include" + +// this disables tree_loop_distribute_patterns +#define C_COMMON_OVERRIDE_OPTIONS flag_no_builtin = 1 +/* Baserel support. */ + +extern int amiga_is_const_pic_ref(const_rtx x); + +#undef CONSTANT_ADDRESS_P +#define CONSTANT_ADDRESS_P(X) \ +((GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \ + || GET_CODE (X) == HIGH \ + )) + + + +/* Given that symbolic_operand(X), return TRUE if no special + base relative relocation is necessary */ + +#undef LEGITIMATE_PIC_OPERAND_P +#define LEGITIMATE_PIC_OPERAND_P(X) ( \ + ! symbolic_operand (X, VOIDmode) && \ + ! amiga_is_const_pic_ref(X)) + +// (GET_CODE(X) == CONST && (GET_CODE(XEXP(X, 0)) == SYMBOL_REF || GET_CODE(XEXP(X, 0)) == LABEL_REF) && !CONSTANT_POOL_ADDRESS_P (XEXP(X, 0))) || + +#undef TARGET_GCC_EXCEPT_TABLE +#define TARGET_GCC_EXCEPT_TABLE ".text" + +#undef TARGET_GCC_EXCEPT_TABLE_S +#define TARGET_GCC_EXCEPT_TABLE_S ".text" + +#define EH_TABLES_CAN_BE_READ_ONLY 1 + + +/* Max. number of data, address and float registers to be used for passing + integer, pointer and float arguments when TARGET_REGPARM. + It's 4, so d0-d3, a0-a3 and fp0-fp3 can be used. */ +#undef AMIGAOS_MAX_REGPARM +#define AMIGAOS_MAX_REGPARM 4 + +/* The default number of data, address and float registers to use when + user specified '-mregparm' switch, not '-mregparm=' option. */ +#undef AMIGAOS_DEFAULT_REGPARM +#define AMIGAOS_DEFAULT_REGPARM 2 + +/* 1 if N is a possible register number for function argument passing. */ +#undef FUNCTION_ARG_REGNO_P +#define FUNCTION_ARG_REGNO_P(N) amigaos_function_arg_reg(N) + +extern int +amigaos_function_arg_reg(unsigned regno); + +//extern bool debug_recog(char const * txt, int which_alternative, int n, rtx * operands); diff --git a/gcc/config/m68k/m68kemb.h b/gcc/config/m68k/m68kemb.h index 0d8d88c74ea9..29b3e194f12d 100644 --- gcc/config/m68k/m68kemb.h +++ gcc/config/m68k/m68kemb.h @@ -32,12 +32,14 @@ #define NEEDS_UNTYPED_CALL 1 /* Target OS builtins. */ +#ifndef TARGET_OS_CPP_BUILTINS #define TARGET_OS_CPP_BUILTINS() \ do \ { \ builtin_define ("__embedded__"); \ } \ while (0) +#endif /* Override the default LIB_SPEC from gcc.c. We don't currently support profiling, or libg.a. */ diff --git a/gcc/config/m68k/math-68881.h b/gcc/config/m68k/math-68881.h index 6d9f8b2d4a1f..20a5037cc525 100644 --- gcc/config/m68k/math-68881.h +++ gcc/config/m68k/math-68881.h @@ -37,7 +37,7 @@ September 1993, Use #undef before HUGE_VAL instead of #ifdef/#endif. */ /* Changed by Ian Lance Taylor: - September 1994, use extern inline instead of static inline. */ + September 1994, use inline instead of static inline. */ #ifndef __math_68881 #define __math_68881 @@ -64,7 +64,7 @@ }) #endif -__inline extern double +__inline double sin (double x) { double value; @@ -75,7 +75,7 @@ sin (double x) return value; } -__inline extern double +__inline double cos (double x) { double value; @@ -86,7 +86,7 @@ cos (double x) return value; } -__inline extern double +__inline double tan (double x) { double value; @@ -97,7 +97,7 @@ tan (double x) return value; } -__inline extern double +__inline double asin (double x) { double value; @@ -108,7 +108,7 @@ asin (double x) return value; } -__inline extern double +__inline double acos (double x) { double value; @@ -119,7 +119,7 @@ acos (double x) return value; } -__inline extern double +__inline double atan (double x) { double value; @@ -130,7 +130,7 @@ atan (double x) return value; } -__inline extern double +__inline double atan2 (double y, double x) { double pi, pi_over_2; @@ -187,7 +187,7 @@ atan2 (double y, double x) } } -__inline extern double +__inline double sinh (double x) { double value; @@ -198,7 +198,7 @@ sinh (double x) return value; } -__inline extern double +__inline double cosh (double x) { double value; @@ -209,7 +209,7 @@ cosh (double x) return value; } -__inline extern double +__inline double tanh (double x) { double value; @@ -220,7 +220,7 @@ tanh (double x) return value; } -__inline extern double +__inline double atanh (double x) { double value; @@ -231,7 +231,7 @@ atanh (double x) return value; } -__inline extern double +__inline double exp (double x) { double value; @@ -242,7 +242,7 @@ exp (double x) return value; } -__inline extern double +__inline double expm1 (double x) { double value; @@ -253,7 +253,7 @@ expm1 (double x) return value; } -__inline extern double +__inline double log (double x) { double value; @@ -264,7 +264,7 @@ log (double x) return value; } -__inline extern double +__inline double log1p (double x) { double value; @@ -275,7 +275,7 @@ log1p (double x) return value; } -__inline extern double +__inline double log10 (double x) { double value; @@ -286,7 +286,7 @@ log10 (double x) return value; } -__inline extern double +__inline double sqrt (double x) { double value; @@ -297,13 +297,13 @@ sqrt (double x) return value; } -__inline extern double +__inline double hypot (double x, double y) { return sqrt (x*x + y*y); } -__inline extern double +__inline double pow (double x, double y) { if (x > 0) @@ -352,7 +352,7 @@ pow (double x, double y) } } -__inline extern double +__inline double fabs (double x) { double value; @@ -363,7 +363,7 @@ fabs (double x) return value; } -__inline extern double +__inline double ceil (double x) { int rounding_mode, round_up; @@ -385,7 +385,7 @@ ceil (double x) return value; } -__inline extern double +__inline double floor (double x) { int rounding_mode, round_down; @@ -408,7 +408,7 @@ floor (double x) return value; } -__inline extern double +__inline double rint (double x) { int rounding_mode, round_nearest; @@ -430,7 +430,7 @@ rint (double x) return value; } -__inline extern double +__inline double fmod (double x, double y) { double value; @@ -442,7 +442,7 @@ fmod (double x, double y) return value; } -__inline extern double +__inline double drem (double x, double y) { double value; @@ -454,7 +454,7 @@ drem (double x, double y) return value; } -__inline extern double +__inline double scalb (double x, int n) { double value; @@ -466,7 +466,7 @@ scalb (double x, int n) return value; } -__inline extern double +__inline double logb (double x) { double exponent; @@ -477,7 +477,7 @@ logb (double x) return exponent; } -__inline extern double +__inline double ldexp (double x, int n) { double value; @@ -489,7 +489,7 @@ ldexp (double x, int n) return value; } -__inline extern double +__inline double frexp (double x, int *exp) { double float_exponent; @@ -514,7 +514,7 @@ frexp (double x, int *exp) return mantissa; } -__inline extern double +__inline double modf (double x, double *ip) { double temp; diff --git a/gcc/config/m68k/predicates.md b/gcc/config/m68k/predicates.md index 186436c42b77..9533e65ceaab 100644 --- gcc/config/m68k/predicates.md +++ gcc/config/m68k/predicates.md @@ -30,6 +30,10 @@ || GET_CODE (XEXP (op, 0)) == LABEL_REF || GET_CODE (XEXP (op, 0)) == CONST)) return 1; +#ifdef TARGET_AMIGA + if (flag_pic >= 3 && amiga_is_const_pic_ref(op)) + return 0; +#endif return general_operand (op, mode); }) diff --git a/gcc/config/m68k/t-amigaos b/gcc/config/m68k/t-amigaos new file mode 100755 index 000000000000..bf9c5279d04f --- /dev/null +++ gcc/config/m68k/t-amigaos @@ -0,0 +1,28 @@ +# Makefile fragment for AmigaOS target. + +# Extra object file linked to the cc1* executables. +amigaos.o: $(srcdir)/config/m68k/amigaos.c $(CONFIG_H) + $(CXX) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION) + +# Additional target dependent options for compiling libgcc.a. This just +# ensures that we don't compile libgcc* with anything other than a +# fixed stack. + +#TARGET_LIBGCC2_CFLAGS = -mfixedstack + +### begin-GG-local: dynamic libraries +# Extra objects that get compiled and linked to collect2 + +EXTRA_COLLECT2_OBJS = amigacollect2.o + +# Build supplimentary AmigaOS target support file for collect2 +amigacollect2.o: amigacollect2.c + $(CXX) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DA2IXDIR_PREFIX=\"$(prefix)/share/a2ixlibrary\" $< $(OUTPUT_OPTION) +### end-GG-local + +# Support for building multiple version of libgcc, libquadmath, libobjc and libstdc++-v3 +MULTILIB_OPTIONS = m68020 fbaserel/fbaserel32 +MULTILIB_DIRNAMES = libm020 libb libb32 +MULTILIB_EXTRA_OPTS = noixemul +MULTILIB_EXCEPTIONS = fbaserel32 \ No newline at end of file diff --git a/gcc/config/m68k/x-amigaos b/gcc/config/m68k/x-amigaos new file mode 100755 index 000000000000..a8f60b80f9f4 --- /dev/null +++ gcc/config/m68k/x-amigaos @@ -0,0 +1,104 @@ +# Makefile fragment for AmigaOS host + +# Each compilation environment (Manx, Dice, GCC, SAS/C, etc) provides its +# own equivalent of the UNIX /usr/include tree. For gcc, the standard headers +# are in /gg/include and system specific headers are in /gg/os-include. +# Use these paths for fixincludes. + +SYSTEM_HEADER_DIR = $(prefix)/include + +# Uncomment the following macro to get a resident GCC. We don't do it +# by default, since we want to support users with mc68000. +# WARNING! If you uncomment this, you MUST add the same flags to the +# libiberty's Makefile (libiberty is now linked into GCC executables). + +#RESIDENT = -m68020 -resident32 + +# Additional host flags that are not used when compiling with GCC_FOR_TARGET, +# such as when compiling the libgcc* runtime archives. GCC uses stack +# a lot, and since AmigaOS provides processes with a small, fixed size +# stack, we have to generate code that will extend it whenever necessary. + +XCFLAGS = -mstackextend $(RESIDENT) + +# AmigaOS supports "AmigaGuide(R)" hypertext files. For GCC, these are +# build with a custom "makeinfo". + +# Arrange for guides to be build with GCC, in the build directory. + +### begin-GG-local: gcc-amigaos +#EXTRA_DOC_TARGETS = guide gcc-amigaos-doc +### end-GG-local + +# Actually build guides + +guide:: doc/cpp.guide doc/gcc.guide doc/gccint.guide \ + doc/gccinstall.guide doc/cppinternals.guide + +doc/cpp.guide: $(TEXI_CPP_FILES) +doc/gcc.guide: $(TEXI_GCC_FILES) +doc/gccint.guide: $(TEXI_GCCINT_FILES) +doc/cppinternals.guide: $(TEXI_CPPINT_FILES) + +doc/%.guide: %.texi + if [ x$(BUILD_INFO) = xinfo ]; then \ + $(MAKEINFO) --amiga $(MAKEINFOFLAGS) -I $(docdir) \ + -I $(docdir)/include -o $@ $<; \ + fi + +# Duplicate entry to handle renaming of gccinstall.guide +doc/gccinstall.guide: $(TEXI_GCCINSTALL_FILES) + if [ x$(BUILD_INFO) = xinfo ]; then \ + $(MAKEINFO) --amiga $(MAKEINFOFLAGS) -I $(docdir) \ + -I $(docdir)/include -o $@ install.texi; \ + fi + +# Arrange for guides to be installed with GCC. + +### begin-GG-local: gcc-amigaos +#EXTRA_INSTALL_TARGETS = install-guide install-gcc-amigaos-doc +### end-GG-local + +# Where the guide files go + +guidedir = $(prefix)/guide + +# Actually install guides. + +installdirs-guide: + $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(guidedir) + +install-guide: doc installdirs-guide \ + $(DESTDIR)$(guidedir)/cpp.guide \ + $(DESTDIR)$(guidedir)/gcc.guide \ + $(DESTDIR)$(guidedir)/cppinternals.guide \ + $(DESTDIR)$(guidedir)/gccinstall.guide \ + $(DESTDIR)$(guidedir)/gccint.guide + +$(DESTDIR)$(guidedir)/%.guide: doc/%.guide installdirs-guide + rm -f $@ + if [ -f $< ]; then \ + for f in $(<)*; do \ + realfile=`echo $$f | sed -e 's|.*/\([^/]*\)$$|\1|'`; \ + $(INSTALL_DATA) $$f $(DESTDIR)$(guidedir)/$$realfile; \ + chmod a-x $(DESTDIR)$(guidedir)/$$realfile; \ + done; \ + else true; fi + +### begin-GG-local: gcc-amigaos +# Build and install gcc-amigaos.guide - documentation specific to the +# AmigaOS port of GCC. + +gcc-amigaos-doc:: doc/gcc-amigaos.info doc/gcc-amigaos.guide + +doc/gcc-amigaos.info doc/gcc-amigaos.guide: gcc-amigaos.texi + +install-gcc-amigaos-doc: doc installdirs installdirs-guide \ + $(DESTDIR)$(infodir)/gcc-amigaos.info \ + $(DESTDIR)$(guidedir)/gcc-amigaos.guide +### end-GG-local + +host-amigaos.o : $(srcdir)/config/m68k/host-amigaos.c $(CONFIG_H) $(SYSTEM_H) \ + coretypes.h hosthooks.h hosthooks-def.h toplev.h diagnostic.h + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + $(srcdir)/config/m68k/host-amigaos.c diff --git a/gcc/config/m68k/xm-amigaos.h b/gcc/config/m68k/xm-amigaos.h new file mode 100755 index 000000000000..bb571ba040b3 --- /dev/null +++ gcc/config/m68k/xm-amigaos.h @@ -0,0 +1,64 @@ +/* Configuration for GNU C-compiler for m68k Amiga, running AmigaOS. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2003 + Free Software Foundation, Inc. + Contributed by Markus M. Wild (wild@amiga.physik.unizh.ch). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef _FCNTL_H_ +#include +#endif + +/* AmigaOS specific headers, such as from the Native Developer Update kits, + go in SYSTEM_INCLUDE_DIR. STANDARD_INCLUDE_DIR is the equivalent of + Unix "/usr/include". All other include paths are set in Makefile. */ + +#define SYSTEM_INCLUDE_DIR "/gg/os-include" +#define STANDARD_INCLUDE_DIR "/gg/include" + +#define STANDARD_EXEC_PREFIX_1 "/gg/libexec/gcc/" +#define STANDARD_EXEC_PREFIX_2 "/gg/lib/gcc/" +#define STANDARD_STARTFILE_PREFIX_1 "/gg/lib/" +#define STANDARD_STARTFILE_PREFIX_2 "/gg/lib/" + +/* The AmigaOS stores file names with regard to upper/lower case, but actions + on existing files are case independent on the standard filesystems. + + A good example of where this causes problems is the conflict between the C + include file and the C++ include file , where the C++ + include file dir is searched first and thus causes includes of + to include instead. + + In order to solve this problem we define the macro OPEN_CASE_SENSITIVE as + the name of the function that takes the same args as open() and does case + dependent opens. */ + +#define OPEN_CASE_SENSITIVE(NAME, FLAGS, MODE) open ((NAME), (FLAGS) | O_CASE, (MODE)) + +/* On the AmigaOS, there are two pathname separators, '/' (DIR_SEPARATOR) + and ':' (VOL_SEPARATOR). DIR_SEPARATOR defaults to the correct + character, so we don't have to explicitly set it. */ + +#define DIR_SEPARATOR '/' +#define VOL_SEPARATOR ':' +#define DIR_SEPARATOR_2 VOL_SEPARATOR + +/* Zap PREFIX_INCLUDE_DIR, since with the AmigaOS port it is the same as + STANDARD_INCLUDE_DIR. */ + +#undef PREFIX_INCLUDE_DIR diff --git a/gcc/coretypes.h b/gcc/coretypes.h index 12067fdf5348..f0f069f6afa2 100644 --- gcc/coretypes.h +++ gcc/coretypes.h @@ -52,9 +52,9 @@ typedef const struct bitmap_head *const_bitmap; struct simple_bitmap_def; typedef struct simple_bitmap_def *sbitmap; typedef const struct simple_bitmap_def *const_sbitmap; -struct rtx_def; -typedef struct rtx_def *rtx; -typedef const struct rtx_def *const_rtx; +class rtx_def; +typedef class rtx_def *rtx; +typedef const class rtx_def *const_rtx; /* Subclasses of rtx_def, using indentation to show the class hierarchy, along with the relevant invariant. diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 98de84405428..1f23452afe19 100644 --- gcc/df-scan.c +++ gcc/df-scan.c @@ -1807,6 +1807,12 @@ df_ref_change_reg_with_loc_1 (struct df_reg_info *old_df, df_ref *ref_ptr; struct df_insn_info *insn_info = DF_REF_INSN_INFO (the_ref); + if (DF_REF_FLAGS_IS_SET(the_ref, DF_HARD_REG_LIVE)) + { + --df->hard_regs_live_count[DF_REF_REGNO(the_ref)]; + ++df->hard_regs_live_count[new_regno]; + } + DF_REF_REGNO (the_ref) = new_regno; DF_REF_REG (the_ref) = regno_reg_rtx[new_regno]; diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index f241073866b4..566aaaf4e370 100644 --- gcc/dwarf2out.c +++ gcc/dwarf2out.c @@ -451,7 +451,7 @@ switch_to_eh_frame_section (bool back ATTRIBUTE_UNUSED) /*global=*/1); lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); - flags = ((! flag_pic + flags = (( (!flag_pic || flag_pic > 2) || ((fde_encoding & 0x70) != DW_EH_PE_absptr && (fde_encoding & 0x70) != DW_EH_PE_aligned && (per_encoding & 0x70) != DW_EH_PE_absptr @@ -469,6 +469,16 @@ switch_to_eh_frame_section (bool back ATTRIBUTE_UNUSED) eh_frame_section = ((flags == SECTION_WRITE) ? data_section : readonly_data_section); #endif /* EH_FRAME_SECTION_NAME */ + +#ifdef TARGET_AMIGA + switch_to_section (data_section); + fputs("\t__EH_FRAME_OBJECT__:\n\t.long 0\n\t.long 0\n\t.long 0\n\t.long 0\n\t.long 0\n\t.long 0\n", asm_out_file); + fputs("\t.stabs \"__EH_FRAME_OBJECTS__\",24,0,0,__EH_FRAME_OBJECT__\n", asm_out_file); + + switch_to_section (eh_frame_section); + ASM_OUTPUT_LABEL (asm_out_file, "_EH_FRAME_BEGIN__"); + fputs("\t.stabs \"__EH_FRAME_BEGINS__\",22,0,0,__EH_FRAME_BEGIN__\n", asm_out_file); +#endif } switch_to_section (eh_frame_section); diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 0fcd9d95e5b4..b2a18e7e4188 100644 --- gcc/emit-rtl.c +++ gcc/emit-rtl.c @@ -2123,7 +2123,11 @@ change_address_1 (rtx memref, machine_mode mode, rtx addr, int validate, if (validate && !lra_in_progress) { if (reload_in_progress || reload_completed) - gcc_assert (memory_address_addr_space_p (mode, addr, as)); + { + bool r = memory_address_addr_space_p (mode, addr, as); + if (!r) debug_rtx(addr); + gcc_assert (r); + } else addr = memory_address_addr_space (mode, addr, as); } diff --git a/gcc/except.c b/gcc/except.c index 2a1073f80cc4..194478f8454c 100644 --- gcc/except.c +++ gcc/except.c @@ -142,6 +142,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "builtins.h" #include "tree-hash-traits.h" +#include "target-def.h" static GTY(()) int call_site_base; @@ -2850,14 +2851,14 @@ switch_to_exception_section (const char * ARG_UNUSED (fnname)) it linkonce if we have COMDAT groups to tie them together. */ if (DECL_COMDAT_GROUP (current_function_decl) && HAVE_COMDAT_GROUP) flags |= SECTION_LINKONCE; - sprintf (section_name, ".gcc_except_table.%s", fnname); + sprintf (section_name, TARGET_GCC_EXCEPT_TABLE_S, fnname); s = get_section (section_name, flags, current_function_decl); free (section_name); } else #endif exception_section - = s = get_section (".gcc_except_table", flags, NULL); + = s = get_section (TARGET_GCC_EXCEPT_TABLE, flags, NULL); } else exception_section diff --git a/gcc/final.c b/gcc/final.c index 55cf509611f7..fa8b2964a40d 100644 --- gcc/final.c +++ gcc/final.c @@ -2151,6 +2151,7 @@ call_from_call_insn (rtx_call_insn *insn) SEEN is used to track the end of the prologue, for emitting debug information. We force the emission of a line note after both NOTE_INSN_PROLOGUE_END and NOTE_INSN_FUNCTION_BEG. */ +rtx_insn * current_insn; rtx_insn * final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED, @@ -2160,6 +2161,7 @@ final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED, rtx set; #endif rtx_insn *next; + current_insn = insn; insn_counter++; @@ -3622,6 +3624,12 @@ do_assembler_dialects (const char *p, int *dialect) void output_asm_insn (const char *templ, rtx *operands) { + extern bool be_very_verbose; + extern void append_reg_usage(FILE *, rtx_insn *); + + extern bool dump_reg_track; + void append_reg_cache (FILE * f, rtx_insn * insn); + const char *p; int c; #ifdef ASSEMBLER_DIALECT @@ -3778,6 +3786,11 @@ output_asm_insn (const char *templ, rtx *operands) putc (c, asm_out_file); } + if (be_very_verbose) + append_reg_usage(asm_out_file, current_insn); + if (dump_reg_track) + append_reg_cache(asm_out_file, current_insn); + /* Write out the variable names for operands, if we know them. */ if (flag_verbose_asm) output_asm_operand_names (operands, oporder, ops); @@ -3995,6 +4008,7 @@ output_addr_const (FILE *file, rtx x) if (targetm.asm_out.output_addr_const_extra (file, x)) break; + debug_rtx(current_output_insn); output_operand_lossage ("invalid expression as operand"); } } diff --git a/gcc/function.c b/gcc/function.c index 6942a504127f..ebe3e1c37121 100644 --- gcc/function.c +++ gcc/function.c @@ -39,9 +39,9 @@ along with GCC; see the file COPYING3. If not see #include "rtl.h" #include "tree.h" #include "gimple-expr.h" +#include "tm_p.h" #include "cfghooks.h" #include "df.h" -#include "tm_p.h" #include "stringpool.h" #include "expmed.h" #include "optabs.h" diff --git a/gcc/gcc.c b/gcc/gcc.c index 85ea19bd3a09..a4da7d515ce5 100644 --- gcc/gcc.c +++ gcc/gcc.c @@ -10107,3 +10107,40 @@ driver_get_configure_time_options (void (*cb) (const char *option, obstack_free (&obstack, NULL); n_switches = 0; } + +#ifdef TARGET_AMIGA +const char * amiga_m68k_prefix_func(int argc, const char ** argv) { + char * p = 0; + /* if (standard_libexec_prefix) + { + char * glp = concat(standard_libexec_prefix, "", NULL); + p = strrchr(glp, '/'); + if (p) + { + *p = 0; + p = strrchr(glp, '/'); + if (p) + { + *p = 0; + p = strrchr(glp, '/'); + if (p) + { + p[1] = 0; + p = concat(glp, "m68k-unknown-amigaos/", NULL); + } + } + } + free(glp); + }*/ + if (!p) + p = concat("../../../../", "", NULL); + + for (int i = 0; i < argc; ++i) { + char * q = concat(p, argv[i], NULL); + free(p); + p = q; + } + //*p = 0; + return p; +} +#endif diff --git a/gcc/gcse.c b/gcc/gcse.c index 5b2c96ecb5a6..f74e733f9337 100644 --- gcc/gcse.c +++ gcc/gcse.c @@ -4075,7 +4075,9 @@ pass_rtl_pre::gate (function *fun) { return optimize > 0 && flag_gcse && !fun->calls_setjmp +#ifndef TARGET_AMIGA && optimize_function_for_speed_p (fun) +#endif && dbg_cnt (pre); } @@ -4118,6 +4120,9 @@ class pass_rtl_hoist : public rtl_opt_pass bool pass_rtl_hoist::gate (function *) { +#ifdef TARGET_AMIGA + return false; +#else return optimize > 0 && flag_gcse && !cfun->calls_setjmp /* It does not make sense to run code hoisting unless we are optimizing @@ -4125,6 +4130,7 @@ pass_rtl_hoist::gate (function *) bigger if we did PRE (when optimizing for space, we don't run PRE). */ && optimize_function_for_size_p (cfun) && dbg_cnt (hoist); +#endif } } // anon namespace diff --git a/gcc/ginclude/stddef.h b/gcc/ginclude/stddef.h index d711530d0535..4f08f81a3ae0 100644 --- gcc/ginclude/stddef.h +++ gcc/ginclude/stddef.h @@ -325,6 +325,7 @@ typedef __rune_t rune_t; #define __WCHAR_TYPE__ int #endif #ifndef __cplusplus +#define _WCHAR_T_ int typedef __WCHAR_TYPE__ wchar_t; #endif #endif diff --git a/gcc/hwint.h b/gcc/hwint.h index 4dd255d486c5..d5296a81d08e 100644 --- gcc/hwint.h +++ gcc/hwint.h @@ -295,7 +295,7 @@ abs_hwi (HOST_WIDE_INT x) inline unsigned HOST_WIDE_INT absu_hwi (HOST_WIDE_INT x) { - return x >= 0 ? (unsigned HOST_WIDE_INT)x : -(unsigned HOST_WIDE_INT)x; + return x >= 0 ? (unsigned HOST_WIDE_INT)x : -(signed HOST_WIDE_INT)x; } #endif /* ! GCC_HWINT_H */ diff --git a/gcc/ipa-chkp.c b/gcc/ipa-chkp.c index 86c48f14f64d..dc72a5f21021 100644 --- gcc/ipa-chkp.c +++ gcc/ipa-chkp.c @@ -23,6 +23,8 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "backend.h" +#include "tm_p.h" +#include "target.h" #include "tree.h" #include "gimple.h" #include "tree-pass.h" diff --git a/gcc/opts.c b/gcc/opts.c index 8f9862db57c2..6a87349e6a70 100644 --- gcc/opts.c +++ gcc/opts.c @@ -530,8 +530,10 @@ static const struct default_options default_options_table[] = { OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_finline_functions_called_once, NULL, 1 }, { OPT_LEVELS_3_PLUS, OPT_funswitch_loops, NULL, 1 }, { OPT_LEVELS_3_PLUS, OPT_fgcse_after_reload, NULL, 1 }, +#ifndef TARGET_AMIGA { OPT_LEVELS_3_PLUS, OPT_ftree_loop_vectorize, NULL, 1 }, { OPT_LEVELS_3_PLUS, OPT_ftree_slp_vectorize, NULL, 1 }, +#endif { OPT_LEVELS_3_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_DYNAMIC }, { OPT_LEVELS_3_PLUS, OPT_fipa_cp_clone, NULL, 1 }, { OPT_LEVELS_3_PLUS, OPT_ftree_partial_pre, NULL, 1 }, diff --git a/gcc/passes.c b/gcc/passes.c index e89618111245..1d53bb23b1b0 100644 --- gcc/passes.c +++ gcc/passes.c @@ -2269,6 +2269,29 @@ override_gate_status (opt_pass *pass, tree func, bool gate_status) } +void dump_insns(char const * name) +{ + rtx_insn *insn, *next; + fprintf(stderr, "====================================\npass: %s\n", name); + for (insn = get_insns(); insn; insn = next) + { + next = NEXT_INSN(insn); + debug_rtx(insn); +#if 0 + if (NONJUMP_INSN_P (insn)) + { + rtx set= single_set (insn); + if (!set) + continue; + + if (amiga_is_const_pic_ref(SET_SRC(set)) && MEM_P(SET_DEST(set))) + debug_rtx(insn); + } +#endif + } +} + + /* Execute PASS. */ bool @@ -2278,6 +2301,9 @@ execute_one_pass (opt_pass *pass) bool gate_status; + if (string_bbb_opts && strchr (string_bbb_opts, 'Y')) + dump_insns(pass->name); + /* IPA passes are executed on whole program, so cfun should be NULL. Other passes need function context set. */ if (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS) diff --git a/gcc/passes.def b/gcc/passes.def index 7db9c9577cf4..61d0e4d47377 100644 --- gcc/passes.def +++ gcc/passes.def @@ -386,6 +386,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_gen_hsail); NEXT_PASS (pass_expand); + NEXT_PASS (pass_bbb_baserel); NEXT_PASS (pass_rest_of_compilation); PUSH_INSERT_PASSES_WITHIN (pass_rest_of_compilation) @@ -458,6 +459,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_cprop_hardreg); NEXT_PASS (pass_fast_rtl_dce); NEXT_PASS (pass_reorder_blocks); + NEXT_PASS (pass_bbb_optimizations); NEXT_PASS (pass_branch_target_load_optimize2); NEXT_PASS (pass_leaf_regs); NEXT_PASS (pass_split_before_sched2); diff --git a/gcc/recog.c b/gcc/recog.c index 92b2aa31a777..73d1fd1b1336 100644 --- gcc/recog.c +++ gcc/recog.c @@ -3252,6 +3252,7 @@ peep2_attempt (basic_block bb, rtx_insn *insn, int match_len, rtx_insn *attempt) /* If we are splitting an RTX_FRAME_RELATED_P insn, do not allow it to match more than one insn, or to be split into more than one insn. */ old_insn = peep2_insn_data[peep2_current].insn; + if (RTX_FRAME_RELATED_P (old_insn)) { bool any_note = false; diff --git a/gcc/regrename.c b/gcc/regrename.c old mode 100644 new mode 100755 index 9643f328ea3e..1ed6557ee713 --- gcc/regrename.c +++ gcc/regrename.c @@ -374,44 +374,45 @@ find_rename_reg (du_head_p this_head, enum reg_class super_class, = (enum reg_class) targetm.preferred_rename_class (super_class); /* Pick and check the register from the tied chain iff the tied chain - is not renamed. */ + is not renamed. */ if (this_head->tied_chain && !this_head->tied_chain->renamed && check_new_reg_p (old_reg, this_head->tied_chain->regno, this_head, *unavailable)) return this_head->tied_chain->regno; /* If PREFERRED_CLASS is not NO_REGS, we iterate in the first pass - over registers that belong to PREFERRED_CLASS and try to find the - best register within the class. If that failed, we iterate in - the second pass over registers that don't belong to the class. - If PREFERRED_CLASS is NO_REGS, we iterate over all registers in - ascending order without any preference. */ + over registers that belong to PREFERRED_CLASS and try to find the + best register within the class. If that failed, we iterate in + the second pass over registers that don't belong to the class. + If PREFERRED_CLASS is NO_REGS, we iterate over all registers in + ascending order without any preference. */ has_preferred_class = (preferred_class != NO_REGS); for (pass = (has_preferred_class ? 0 : 1); pass < 2; pass++) { int new_reg; - for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++) - { - if (has_preferred_class + for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++) + { + if (has_preferred_class && (pass == 0) != TEST_HARD_REG_BIT (reg_class_contents[preferred_class], - new_reg)) - continue; + new_reg)) + continue; - if (!check_new_reg_p (old_reg, new_reg, this_head, *unavailable)) - continue; + if (!check_new_reg_p (old_reg, new_reg, this_head, *unavailable)) + continue; - if (!best_rename) - return new_reg; + if (!best_rename) + return new_reg; - /* In the first pass, we force the renaming of registers that - don't belong to PREFERRED_CLASS to registers that do, even - though the latters were used not very long ago. */ - if ((pass == 0 + /* In the first pass, we force the renaming of registers that + don't belong to PREFERRED_CLASS to registers that do, even + though the latters were used not very long ago. + Also use a register if no best_new_reg was found till now */ + if (((pass == 0 || !has_preferred_class) && !TEST_HARD_REG_BIT (reg_class_contents[preferred_class], best_new_reg)) || tick[best_new_reg] > tick[new_reg]) - best_new_reg = new_reg; + best_new_reg = new_reg; } if (pass == 0 && best_new_reg != old_reg) break; @@ -897,7 +898,7 @@ regrename_analyze (bitmap bb_mask) if (!range_overlaps_hard_reg_set_p (live, chain->regno, chain->nregs)) continue; - + n_succs_used++; dest_ri = (struct bb_rename_info *)e->dest->aux; @@ -921,7 +922,7 @@ regrename_analyze (bitmap bb_mask) printed = true; fprintf (dump_file, " merging chains %d (->%d) and %d (->%d) [%s]\n", - k, incoming_chain->id, j, chain->id, + k, incoming_chain->id, j, chain->id, reg_names[incoming_chain->regno]); } @@ -954,7 +955,7 @@ regrename_analyze (bitmap bb_mask) numbering in its subpatterns. */ bool -regrename_do_replace (struct du_head *head, int reg) +regrename_do_replace (struct du_head *head, int regno) { struct du_chain *chain; unsigned int base_regno = head->regno; @@ -962,19 +963,20 @@ regrename_do_replace (struct du_head *head, int reg) for (chain = head->first; chain; chain = chain->next_use) { - unsigned int regno = ORIGINAL_REGNO (*chain->loc); - struct reg_attrs *attr = REG_ATTRS (*chain->loc); - int reg_ptr = REG_POINTER (*chain->loc); + unsigned int orig_regno = ORIGINAL_REGNO(*chain->loc); + struct reg_attrs *attr = REG_ATTRS(*chain->loc); + int reg_ptr = REG_POINTER(*chain->loc); if (DEBUG_INSN_P (chain->insn) && REGNO (*chain->loc) != base_regno) - validate_change (chain->insn, &(INSN_VAR_LOCATION_LOC (chain->insn)), - gen_rtx_UNKNOWN_VAR_LOC (), true); + validate_change (chain->insn, &(INSN_VAR_LOCATION_LOC(chain->insn)), + gen_rtx_UNKNOWN_VAR_LOC (), + true); else { - validate_change (chain->insn, chain->loc, - gen_raw_REG (GET_MODE (*chain->loc), reg), true); - if (regno >= FIRST_PSEUDO_REGISTER) - ORIGINAL_REGNO (*chain->loc) = regno; + validate_change (chain->insn, chain->loc, + gen_raw_REG (GET_MODE(*chain->loc), regno), true); + if (orig_regno >= FIRST_PSEUDO_REGISTER) + ORIGINAL_REGNO (*chain->loc) = orig_regno; REG_ATTRS (*chain->loc) = attr; REG_POINTER (*chain->loc) = reg_ptr; } @@ -983,10 +985,29 @@ regrename_do_replace (struct du_head *head, int reg) if (!apply_change_group ()) return false; - mode = GET_MODE (*head->first->loc); + mode = GET_MODE(*head->first->loc); head->renamed = 1; - head->regno = reg; - head->nregs = hard_regno_nregs[reg][mode]; + head->regno = regno; + head->nregs = hard_regno_nregs[regno][mode]; + + /* SBF: also update the current df info, move from base_regno -> regno. */ + if (base_regno < FIRST_PSEUDO_REGISTER && regno < FIRST_PSEUDO_REGISTER) + for (chain = head->first; chain; chain = chain->next_use) + { + if (DEBUG_INSN_P (chain->insn) && VAR_LOC_UNKNOWN_P(INSN_VAR_LOCATION_LOC(chain->insn))) + continue; + /* undo regno patch - will be patched again */ + if (REGNO (*chain->loc) == regno) + SET_REGNO(*chain->loc, base_regno); + df_ref_change_reg_with_loc (*chain->loc, regno); + + SET_REGNO(*chain->loc, regno); + } + + /* Mark the old regno as no longer used. */ + if (!df->hard_regs_live_count[base_regno]) + df_set_regs_ever_live (base_regno, false); + return true; } @@ -1912,7 +1933,6 @@ const pass_data pass_data_regrename = 0, /* todo_flags_start */ TODO_df_finish, /* todo_flags_finish */ }; - class pass_regrename : public rtl_opt_pass { public: @@ -1923,7 +1943,7 @@ class pass_regrename : public rtl_opt_pass /* opt_pass methods: */ virtual bool gate (function *) { - return (optimize > 0 && (flag_rename_registers)); + return (optimize > 0 && (flag_rename_registers) && !TARGET_AMIGA); } virtual unsigned int execute (function *) { return regrename_optimize (); } diff --git a/gcc/target-def.h b/gcc/target-def.h index ec5e09e568e6..9fbfde121095 100644 --- gcc/target-def.h +++ gcc/target-def.h @@ -108,3 +108,11 @@ #include "hooks.h" #include "targhooks.h" #include "insn-target-def.h" + +#ifndef TARGET_GCC_EXCEPT_TABLE +#define TARGET_GCC_EXCEPT_TABLE ".gcc_except_table" +#endif + +#ifndef TARGET_GCC_EXCEPT_TABLE_S +#define TARGET_GCC_EXCEPT_TABLE_S ".gcc_except_table.%s" +#endif diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index 28dac22add66..16aa71ca4ee4 100644 --- gcc/tree-chkp.c +++ gcc/tree-chkp.c @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "backend.h" +#include "tm_p.h" #include "target.h" #include "rtl.h" #include "tree.h" diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 5f5055d3a6c1..76c0996850f4 100644 --- gcc/tree-pass.h +++ gcc/tree-pass.h @@ -590,6 +590,8 @@ extern rtl_opt_pass *make_pass_branch_target_load_optimize2 (gcc::context *ctxt); extern rtl_opt_pass *make_pass_leaf_regs (gcc::context *ctxt); extern rtl_opt_pass *make_pass_split_before_sched2 (gcc::context *ctxt); +extern rtl_opt_pass *make_pass_bbb_optimizations (gcc::context *ctxt); +extern rtl_opt_pass *make_pass_bbb_baserel (gcc::context *ctxt); extern rtl_opt_pass *make_pass_compare_elim_after_reload (gcc::context *ctxt); extern rtl_opt_pass *make_pass_sched2 (gcc::context *ctxt); extern rtl_opt_pass *make_pass_stack_regs (gcc::context *ctxt); diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index 9f09d30b1f91..ab9c0117b078 100644 --- gcc/var-tracking.c +++ gcc/var-tracking.c @@ -92,10 +92,10 @@ #include "target.h" #include "rtl.h" #include "tree.h" +#include "tm_p.h" #include "cfghooks.h" #include "alloc-pool.h" #include "tree-pass.h" -#include "tm_p.h" #include "insn-config.h" #include "regs.h" #include "emit-rtl.h" diff --git a/gcc/varasm.c b/gcc/varasm.c index b65f29c13a46..8ead5ec3fcbb 100644 --- gcc/varasm.c +++ gcc/varasm.c @@ -252,7 +252,6 @@ get_unnamed_section (unsigned int flags, void (*callback) (const void *), sect->unnamed.callback = callback; sect->unnamed.data = data; sect->unnamed.next = unnamed_sections; - unnamed_sections = sect; return sect; } @@ -2228,11 +2227,17 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, else { /* Special-case handling of vtv comdat sections. */ - if (sect->named.name + if ((sect->common.flags & SECTION_STYLE_MASK) == SECTION_NAMED && sect->named.name && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) handle_vtv_comdat_section (sect, decl); else - switch_to_section (sect); + { +#ifdef TARGET_AMIGA + if ((sect->common.flags & SECTION_STYLE_MASK) == SECTION_NAMED) + sect->named.decl = decl; +#endif + switch_to_section (sect); + } if (align > BITS_PER_UNIT) ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); assemble_variable_contents (decl, name, dont_output_data); @@ -4962,7 +4967,7 @@ output_constructor_regular_field (oc_local_state *local) if each element has the proper size. */ if (local->field != NULL_TREE || local->index != NULL_TREE) { - if (fieldpos > local->total_bytes) + if (fieldpos >= local->total_bytes) { assemble_zeros (fieldpos - local->total_bytes); local->total_bytes = fieldpos; diff --git a/libcpp/lex.c b/libcpp/lex.c index e5a0397f3099..c2131adeb38e 100644 --- libcpp/lex.c +++ libcpp/lex.c @@ -64,6 +64,10 @@ static tokenrun *next_tokenrun (tokenrun *); static _cpp_buff *new_buff (size_t); +/* + * SBF: This flag is set if an asm statement is parsed, to support multiline strings in __asm() + */ +int in_assembler_directive; /* Utility routine: @@ -1063,7 +1067,10 @@ _cpp_process_line_notes (cpp_reader *pfile, int in_comment) else if (note->type == 0) /* Already processed in lex_raw_string. */; else - abort (); + { +// abort (); + printf("ups: note type=%d\n", note->type); + } } } @@ -1875,6 +1882,20 @@ lex_string (cpp_reader *pfile, cpp_token *token, const uchar *base) break; else if (c == '\n') { + /* + * SBF: allow multi-line strings + * Ignore the line end and move to next line. + * Only fail, if there is no next line + */ + if (in_assembler_directive) + { + cpp_buffer *buffer = pfile->buffer; + if (buffer->cur < buffer->rlimit) + CPP_INCREMENT_LINE (pfile, 0); + buffer->need_line = true; + if (_cpp_get_fresh_line (pfile)) + continue; + } cur--; /* Unmatched quotes always yield undefined behavior, but greedy lexing means that what appears to be an unterminated diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index f09b39b0e85f..39f91d1567b6 100644 --- libgcc/Makefile.in +++ libgcc/Makefile.in @@ -230,7 +230,7 @@ endif # Options to use when compiling libgcc2.a. # LIBGCC2_DEBUG_CFLAGS = -g -LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(HOST_LIBGCC2_CFLAGS) \ +LIBGCC2_CFLAGS = $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(HOST_LIBGCC2_CFLAGS) \ $(LIBGCC2_DEBUG_CFLAGS) -DIN_LIBGCC2 \ -fbuilding-libgcc -fno-stack-protector \ $(INHIBIT_LIBC_CFLAGS) @@ -284,7 +284,7 @@ INTERNAL_CFLAGS = $(CFLAGS) $(LIBGCC2_CFLAGS) $(HOST_LIBGCC2_CFLAGS) \ $(INCLUDES) @set_have_cc_tls@ @set_use_emutls@ # Options to use when compiling crtbegin/end. -CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ +CRTSTUFF_CFLAGS = $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) \ $(NO_PIE_CFLAGS) -finhibit-size-directive -fno-inline -fno-exceptions \ -fno-zero-initialized-in-bss -fno-toplevel-reorder -fno-tree-vectorize \ -fbuilding-libgcc -fno-stack-protector $(FORCE_EXPLICIT_EH_REGISTRY) \ diff --git a/libgcc/config.host b/libgcc/config.host index 6f6810cf0baf..7117e7983b53 100644 --- libgcc/config.host +++ libgcc/config.host @@ -816,6 +816,11 @@ m32r-*-linux*) m32rle-*-linux*) tmake_file="$tmake_file m32r/t-linux t-fdpbit" ;; +m68k-*-amiga*) + tmake_file="$tmake_file m68k/t-glue m68k/t-floatlib soft-fp" + CFLAGS="-Os" +# tmake_file="$tmake_file m68k/t-glue soft-fp" + ;; m68k-*-elf* | fido-*-elf) tmake_file="$tmake_file m68k/t-floatlib" ;; diff --git a/libgcc/config/m68k/fpgnulib.c b/libgcc/config/m68k/fpgnulib.c index fe41edf26aa0..90926104d8fd 100644 --- libgcc/config/m68k/fpgnulib.c +++ libgcc/config/m68k/fpgnulib.c @@ -105,6 +105,7 @@ union long_double_long #ifndef EXTFLOAT +#ifdef __UNORDSF2 int __unordsf2(float a, float b) { @@ -118,7 +119,9 @@ __unordsf2(float a, float b) return 1; return 0; } +#endif +#ifdef __UNORDDF2 int __unorddf2(double a, double b) { @@ -134,7 +137,9 @@ __unorddf2(double a, double b) return 1; return 0; } +#endif +#ifdef __FLOATUNSIDF /* convert unsigned int to double */ double __floatunsidf (unsigned long a1) @@ -167,7 +172,9 @@ __floatunsidf (unsigned long a1) return dl.d; } +#endif +#ifdef __FLOATSIDF /* convert int to double */ double __floatsidf (long a1) @@ -213,7 +220,9 @@ __floatsidf (long a1) return dl.d; } +#endif +#ifdef __FLOATUNSISF /* convert unsigned int to float */ float __floatunsisf (unsigned long l) @@ -221,7 +230,10 @@ __floatunsisf (unsigned long l) double foo = __floatunsidf (l); return foo; } +#endif + +#ifdef __FLOATSISF /* convert int to float */ float __floatsisf (long l) @@ -229,7 +241,10 @@ __floatsisf (long l) double foo = __floatsidf (l); return foo; } +#endif + +#ifdef __EXTENDSFDF2 /* convert float to double */ double __extendsfdf2 (float a1) @@ -268,7 +283,9 @@ __extendsfdf2 (float a1) return dl.d; } +#endif +#ifdef __TRUNCDFSF2 /* convert double to float */ float __truncdfsf2 (double a1) @@ -336,7 +353,9 @@ __truncdfsf2 (double a1) fl.l = PACK (SIGND (dl1), exp, mant); return (fl.f); } +#endif +#ifdef __FIXDFSI /* convert double to int */ long __fixdfsi (double a1) @@ -368,7 +387,9 @@ __fixdfsi (double a1) return (SIGND (dl1) ? -l : l); } +#endif +#ifdef __FIXSFSI /* convert float to int */ long __fixsfsi (float a1) @@ -376,6 +397,7 @@ __fixsfsi (float a1) double foo = a1; return __fixdfsi (foo); } +#endif #else /* EXTFLOAT */ @@ -396,6 +418,8 @@ float __truncdfsf2 (double); long __fixdfsi (double); long __fixsfsi (float); +#if !defined(EXTFLOATCMP) + int __unordxf2(long double a, long double b) { @@ -445,38 +469,6 @@ __extenddfxf2 (double d) return ldl.ld; } -/* convert long double to double */ -double -__truncxfdf2 (long double ld) -{ - register long exp; - register union double_long dl; - register union long_double_long ldl; - - ldl.ld = ld; - /*printf ("xfdf in: %s\n", dumpxf (ld));*/ - - dl.l.upper = SIGNX (ldl); - if ((ldl.l.upper & ~SIGNBIT) == 0 && !ldl.l.middle && !ldl.l.lower) - { - dl.l.lower = 0; - return dl.d; - } - - exp = EXPX (ldl) - EXCESSX + EXCESSD; - /* ??? quick and dirty: keep `exp' sane */ - if (exp >= EXPDMASK) - exp = EXPDMASK - 1; - dl.l.upper |= exp << (32 - (EXPDBITS + 1)); - /* +1-1: add one for sign bit, but take one off for explicit-integer-bit */ - dl.l.upper |= (ldl.l.middle & MANTXMASK) >> (EXPDBITS + 1 - 1); - dl.l.lower = (ldl.l.middle & MANTXMASK) << (32 - (EXPDBITS + 1 - 1)); - dl.l.lower |= ldl.l.lower >> (EXPDBITS + 1 - 1); - - /*printf ("xfdf out: %g\n", dl.d);*/ - return dl.d; -} - /* convert a float to a long double */ long double __extendsfxf2 (float f) @@ -549,6 +541,8 @@ __negxf2 (long double x1) return - (double) x1; } +#else + long __cmpxf2 (long double x1, long double x2) { @@ -591,5 +585,38 @@ __gexf2 (long double x1, long double x2) return __cmpdf2 ((double) x1, (double) x2); } +/* convert long double to double */ +double +__truncxfdf2 (long double ld) +{ + register long exp; + register union double_long dl; + register union long_double_long ldl; + + ldl.ld = ld; + /*printf ("xfdf in: %s\n", dumpxf (ld));*/ + + dl.l.upper = SIGNX (ldl); + if ((ldl.l.upper & ~SIGNBIT) == 0 && !ldl.l.middle && !ldl.l.lower) + { + dl.l.lower = 0; + return dl.d; + } + + exp = EXPX (ldl) - EXCESSX + EXCESSD; + /* ??? quick and dirty: keep `exp' sane */ + if (exp >= EXPDMASK) + exp = EXPDMASK - 1; + dl.l.upper |= exp << (32 - (EXPDBITS + 1)); + /* +1-1: add one for sign bit, but take one off for explicit-integer-bit */ + dl.l.upper |= (ldl.l.middle & MANTXMASK) >> (EXPDBITS + 1 - 1); + dl.l.lower = (ldl.l.middle & MANTXMASK) << (32 - (EXPDBITS + 1 - 1)); + dl.l.lower |= ldl.l.lower >> (EXPDBITS + 1 - 1); + + /*printf ("xfdf out: %g\n", dl.d);*/ + return dl.d; +} +#endif /* EXTFLOATCMP */ + #endif /* !__mcoldfire__ */ #endif /* EXTFLOAT */ diff --git a/libgcc/config/m68k/t-floatlib b/libgcc/config/m68k/t-floatlib index 1ee8782d9fd2..2365433a59b3 100644 --- libgcc/config/m68k/t-floatlib +++ libgcc/config/m68k/t-floatlib @@ -1,11 +1,62 @@ -LIB1ASMSRC = m68k/lb1sf68.S -LIB1ASMFUNCS = _mulsi3 _udivsi3 _divsi3 _umodsi3 _modsi3 \ - _double _float _floatex \ - _eqdf2 _nedf2 _gtdf2 _gedf2 _ltdf2 _ledf2 \ - _eqsf2 _nesf2 _gtsf2 _gesf2 _ltsf2 _lesf2 +# +#LIB1ASMSRC = m68k/lb1sf68.S +#LIB1ASMFUNCS = _mulsi3 _udivsi3 _divsi3 _umodsi3 _modsi3 \ +# _double _float _floatex \ +# _eqdf2 _nedf2 _gtdf2 _gedf2 _ltdf2 _ledf2 \ +# _eqsf2 _nesf2 _gtsf2 _gesf2 _ltsf2 _lesf2 +# -LIB2ADD = $(srcdir)/config/m68k/fpgnulib.c xfgnulib.c +LIB2ADD = xfpgnulib.c xfpgnulib__unordsf2.c xfpgnulib__unorddf2.c \ + xfpgnulib__floatunsidf.c xfpgnulib__floatsidf.c xfpgnulib__floatunsisf.c \ + xfpgnulib__floatsisf.c xfpgnulib__extendsfdf2.c xfpgnulib__truncdfsf2.c \ + xfpgnulib__fixdfsi.c xfpgnulib__fixsfsi.c xfpgnulib__cmpxf2.c -xfgnulib.c: $(srcdir)/config/m68k/fpgnulib.c - echo '#define EXTFLOAT' > xfgnulib.c - cat $< >> xfgnulib.c +xfpgnulib__unordsf2.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __UNORDSF2' > xfpgnulib__unordsf2.c + cat $< >> xfpgnulib__unordsf2.c + +xfpgnulib__unorddf2.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __UNORDDF2' > xfpgnulib__unorddf2.c + cat $< >> xfpgnulib__unorddf2.c + +xfpgnulib__floatunsidf.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FLOATUNSIDF' > xfpgnulib__floatunsidf.c + cat $< >> xfpgnulib__floatunsidf.c + +xfpgnulib__floatsidf.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FLOATSIDF' > xfpgnulib__floatsidf.c + cat $< >> xfpgnulib__floatsidf.c + +xfpgnulib__floatunsisf.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FLOATUNSISF' > xfpgnulib__floatunsisf.c + cat $< >> xfpgnulib__floatunsisf.c + +xfpgnulib__floatsisf.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FLOATSISF' > xfpgnulib__floatsisf.c + cat $< >> xfpgnulib__floatsisf.c + +xfpgnulib__extendsfdf2.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __EXTENDSFDF2' > xfpgnulib__extendsfdf2.c + cat $< >> xfpgnulib__extendsfdf2.c + +xfpgnulib__truncdfsf2.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __TRUNCDFSF2' > xfpgnulib__truncdfsf2.c + cat $< >> xfpgnulib__truncdfsf2.c + +xfpgnulib__fixdfsi.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FIXDFSI' > xfpgnulib__fixdfsi.c + cat $< >> xfpgnulib__fixdfsi.c + +xfpgnulib__fixsfsi.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define __FIXSFSI' > xfpgnulib__fixsfsi.c + cat $< >> xfpgnulib__fixsfsi.c + +xfpgnulib.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define EXTFLOAT' > xfpgnulib.c + cat $< >> xfpgnulib.c + +xfpgnulib__cmpxf2.c: $(srcdir)/config/m68k/fpgnulib.c + echo '#define EXTFLOAT' > xfpgnulib__cmpxf2.c + echo '#define EXTFLOATCMP' >> xfpgnulib__cmpxf2.c + cat $< >> xfpgnulib__cmpxf2.c + \ No newline at end of file diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c index 1fb6026d123f..7bf0e4236f64 100644 --- libgcc/unwind-dw2.c +++ libgcc/unwind-dw2.c @@ -260,6 +260,9 @@ _Unwind_GetCFA (struct _Unwind_Context *context) } /* Overwrite the saved value for register INDEX in CONTEXT with VAL. */ +#ifdef TARGET_AMIGA +static int overregs[16]; +#endif inline void _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) @@ -271,6 +274,9 @@ _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) gcc_assert (index < (int) sizeof(dwarf_reg_size_table)); size = dwarf_reg_size_table[index]; +#ifdef TARGET_AMIGA + overregs[index] = val; +#endif if (_Unwind_IsExtendedContext (context) && context->by_value[index]) { context->reg[index] = _Unwind_Get_Unwind_Context_Reg_Val (val); @@ -279,6 +285,9 @@ _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) ptr = (void *) (_Unwind_Internal_Ptr) context->reg[index]; + if (!ptr) + return; + if (size == sizeof(_Unwind_Ptr)) * (_Unwind_Ptr *) ptr = val; else @@ -1612,10 +1621,10 @@ _Unwind_DebugHook (void *cfa __attribute__ ((__unused__)), macro because __builtin_eh_return must be invoked in the context of our caller. */ -#define uw_install_context(CURRENT, TARGET) \ +#define uw_install_context(CURRENT, TARGET, INDEX) \ do \ { \ - long offset = uw_install_context_1 ((CURRENT), (TARGET)); \ + long offset = uw_install_context_1 ((CURRENT), (TARGET), (INDEX)); \ void *handler = __builtin_frob_return_addr ((TARGET)->ra); \ _Unwind_DebugHook ((TARGET)->cfa, handler); \ __builtin_eh_return (offset, handler); \ @@ -1624,7 +1633,8 @@ _Unwind_DebugHook (void *cfa __attribute__ ((__unused__)), static long uw_install_context_1 (struct _Unwind_Context *current, - struct _Unwind_Context *target) + struct _Unwind_Context *target, + int index ATTRIBUTE_UNUSED) { long i; _Unwind_SpTmp sp_slot; @@ -1659,7 +1669,75 @@ uw_install_context_1 (struct _Unwind_Context *current, else if (t && c && t != c) memcpy (c, t, dwarf_reg_size_table[i]); } +#ifdef __AMIGA__ + /* SBF: evil hack to patch the values for d0/d1 into the stack location. + * search the movem insn and count the saved regs. + * Now patch the values into location. + * Always patch d0/d1 since override is always invoked for d0/d1. + * Then patch all other regs which the above code omitted. + */ + /* uw_install_context_1 is called from 4 different locations - each uses an unique index. + * So initialization is only done once. + */ + static unsigned short counts[4]; + static unsigned short masks[4]; + + unsigned short count = 0; + unsigned short reg_mask = masks[index]; + /* init each index once. */ + if (!reg_mask) + { + /* get the return address.*/ + unsigned short * sp = *(((unsigned short **)¤t) - 1); + /* search the movem -x(a5),regs insn.*/ + for (;;) + { + unsigned short s = *sp++; +// printf("%04x ", s); + gcc_assert(s != (unsigned short)0x4e75);// hit return? ouch! + if (s == (unsigned short)0x4ced) + break; + } + reg_mask = *sp; + /* count saved regs */ + for (unsigned short i = 0, m = reg_mask; i < 16; ++i) + { + if (m & 1) + ++count; + m >>= 1; + } + masks[index] = reg_mask; + counts[index] = count; + } + else + count = counts[index]; + /* regs are saved below local vars -> start at current */ + int * p = ((int *)current) - count; + + for (unsigned short i = 0, m = reg_mask; i < 16; ++i) + { + if (m & 1) + { + if (i <= 1 || (!current->reg[i] && (target->reg[i] || target->by_value[i]))) + { + int old = *p; + /* not set by the code above - set it here */ + if (i <= 1) // use the override values for d0/d1 + *p = overregs[i]; + else + if (target->by_value[i]) + *p = (int)target->reg[i]; + else + *p = *(int*)target->reg[i]; +// printf("patch reg %d from %08lx to %08lx\n", i, old, *p); + } + ++p; + } + m >>= 1; + } + +#endif /* If the current frame doesn't have a saved stack pointer, then we need to rely on EH_RETURN_STACKADJ_RTX to get our target stack pointer value reloaded. */ diff --git a/libgcc/unwind.inc b/libgcc/unwind.inc index 7413b55e3fab..bf07725ac840 100644 --- libgcc/unwind.inc +++ libgcc/unwind.inc @@ -132,7 +132,7 @@ _Unwind_RaiseException(struct _Unwind_Exception *exc) if (code != _URC_INSTALL_CONTEXT) return code; - uw_install_context (&this_context, &cur_context); + uw_install_context (&this_context, &cur_context, 0); } @@ -208,7 +208,7 @@ _Unwind_ForcedUnwind (struct _Unwind_Exception *exc, if (code != _URC_INSTALL_CONTEXT) return code; - uw_install_context (&this_context, &cur_context); + uw_install_context (&this_context, &cur_context, 1); } @@ -233,7 +233,7 @@ _Unwind_Resume (struct _Unwind_Exception *exc) gcc_assert (code == _URC_INSTALL_CONTEXT); - uw_install_context (&this_context, &cur_context); + uw_install_context (&this_context, &cur_context, 2); } @@ -258,7 +258,7 @@ _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc) gcc_assert (code == _URC_INSTALL_CONTEXT); - uw_install_context (&this_context, &cur_context); + uw_install_context (&this_context, &cur_context, 3); } diff --git a/libiberty/lrealpath.c b/libiberty/lrealpath.c index b27c8de990e9..78c06e46da58 100644 --- libiberty/lrealpath.c +++ libiberty/lrealpath.c @@ -73,7 +73,7 @@ extern char *canonicalize_file_name (const char *); #endif char * -lrealpath (const char *filename) +__xlrealpath (const char *filename) { /* Method 1: The system has a compile time upper bound on a filename path. Use that and realpath() to canonicalize the name. This is @@ -155,3 +155,19 @@ lrealpath (const char *filename) /* This system is a lost cause, just duplicate the filename. */ return strdup (filename); } + + +char * +lrealpath (const char *filename) +{ + char * r = __xlrealpath(filename); +#if defined (_WIN32) + if (strncmp(r, "/cygdrive/", 10) == 0) + { + r[9] = r[10]; + r[10] = ':'; + r = strdup(&r[9]); + } +#endif + return r; +} diff --git a/libobjc/configure b/libobjc/configure index 55fcc33dbe2d..a60258f422d8 100755 --- libobjc/configure +++ libobjc/configure @@ -7637,7 +7637,8 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. - lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + #lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + enable_shared=no ;; esac ;; diff --git a/libobjc/objc/objc.h b/libobjc/objc/objc.h index 37391a446bb0..6c73f53290e8 100644 --- libobjc/objc/objc.h +++ libobjc/objc/objc.h @@ -52,7 +52,11 @@ extern "C" { Important: this could change and we could switch to 'typedef bool BOOL' in the future. Do not depend on the type of BOOL. */ #undef BOOL +#ifdef AMIGA +typedef short BOOL; +#else typedef unsigned char BOOL; +#endif #define YES (BOOL)1 #define NO (BOOL)0 diff --git a/libstdc++-v3/config/os/newlib/ctype_configure_char.cc b/libstdc++-v3/config/os/newlib/ctype_configure_char.cc index 903de5625d77..ed0c757d42f8 100644 --- libstdc++-v3/config/os/newlib/ctype_configure_char.cc +++ libstdc++-v3/config/os/newlib/ctype_configure_char.cc @@ -65,6 +65,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_narrow_ok = 0; } +#ifdef __AMIGA__ + ctype::~ctype() + { + _S_destroy_c_locale(_M_c_locale_ctype); + if (_M_del) + delete[] this->table(); + } +#endif + char ctype::do_toupper(char __c) const { diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index 9bf152a01573..f162705f2625 100755 --- libstdc++-v3/configure +++ libstdc++-v3/configure @@ -8636,6 +8636,8 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; } # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + enable_shared=no + CXXFLAGS="$CXXFLAGS -noixemul" ;; esac ;; @@ -28883,6 +28885,10 @@ else # Base decisions on target environment. case "${host}" in + m68k-*-*) + # Nothing to do here. + ;; + arm*-*-symbianelf*) # This is a freestanding configuration; there is nothing to do here. ;; diff --git a/libstdc++-v3/configure.host b/libstdc++-v3/configure.host index 304a7f5aff61..fde4a72bd31b 100644 --- libstdc++-v3/configure.host +++ libstdc++-v3/configure.host @@ -226,6 +226,11 @@ case "${host_os}" in os_include_dir="os/generic" atomicity_dir="cpu/generic" ;; + amiga*) + os_include_dir="os/newlib" + CFLAGS="-Os -noixemul" + CPPFLAGS="-Os -noixemul" + ;; bsd*) # Plain BSD attempts to share FreeBSD files. os_include_dir="os/bsd/freebsd" diff --git a/libstdc++-v3/include/tr1/cstdint b/libstdc++-v3/include/tr1/cstdint index 7304d9008413..7d6ab77ce17a 100644 --- libstdc++-v3/include/tr1/cstdint +++ libstdc++-v3/include/tr1/cstdint @@ -30,7 +30,9 @@ #define _GLIBCXX_TR1_CSTDINT 1 #pragma GCC system_header - +#ifdef AMIGA +#include +#endif #include // For 8.22.1/1 (see C99, Notes 219, 220, 222) diff --git a/libstdc++-v3/src/c++11/ctype.cc b/libstdc++-v3/src/c++11/ctype.cc index fa370681dad5..f80e83034255 100644 --- libstdc++-v3/src/c++11/ctype.cc +++ libstdc++-v3/src/c++11/ctype.cc @@ -51,12 +51,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const size_t ctype::table_size; +#ifndef __AMIGA__ +/* moved to ctype_configure_char */ ctype::~ctype() { _S_destroy_c_locale(_M_c_locale_ctype); if (_M_del) delete[] this->table(); } +#endif // Fill in the narrowing cache and flag whether all values are // valid or not. _M_narrow_ok is set to 2 if memcpy can't