@@ -2263,4 +2263,8 @@ extern const char *host_detect_local_cpu (int argc, const char **argv);
/* For switching between functions with different target attributes. */
#define SWITCHABLE_TARGET 1
+/* Define SECTION_ARM_PURECODE as the ARM specific section attribute
+ representation for SHF_ARM_PURECODE in GCC. */
+#define SECTION_ARM_PURECODE SECTION_MACH_DEP
+
#endif /* ! GCC_ARM_H */
From 6f3e37973e0c4dbd393325addf42265c42726240 Mon Sep 17 00:00:00 2001
From: Andre Simoes Dias Vieira <andsim01@arm.com>
Date: Tue, 14 Jun 2016 11:17:12 +0100
Subject: [PATCH] -mpure-code for ARM
---
gcc/config/arm/arm.c | 150 ++++++++++++++++++++-
gcc/config/arm/arm.h | 4 +
gcc/config/arm/arm.opt | 6 +
gcc/config/arm/elf.h | 3 +-
gcc/doc/invoke.texi | 11 +-
gcc/doc/tm.texi | 6 +
gcc/doc/tm.texi.in | 2 +
gcc/target.def | 10 ++
gcc/targhooks.h | 2 +
.../gcc.target/arm/pure-code/ffunction-sections.c | 17 +++
.../gcc.target/arm/pure-code/no-literal-pool.c | 73 ++++++++++
.../gcc.target/arm/pure-code/pure-code.exp | 54 ++++++++
gcc/varasm.c | 57 +++++---
13 files changed, 366 insertions(+), 29 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/arm/pure-code/ffunction-sections.c
create mode 100644 gcc/testsuite/gcc.target/arm/pure-code/no-literal-pool.c
create mode 100644 gcc/testsuite/gcc.target/arm/pure-code/pure-code.exp
@@ -214,8 +214,8 @@ static bool arm_return_in_memory (const_tree, const_tree);
static void arm_unwind_emit (FILE *, rtx_insn *);
static bool arm_output_ttype (rtx);
static void arm_asm_emit_except_personality (rtx);
-static void arm_asm_init_sections (void);
#endif
+static void arm_asm_init_sections (void);
static rtx arm_dwarf_register_span (rtx);
static tree arm_cxx_guard_type (void);
@@ -299,7 +299,10 @@ static unsigned HOST_WIDE_INT arm_asan_shadow_offset (void);
static void arm_sched_fusion_priority (rtx_insn *, int, int *, int*);
static bool arm_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT,
const_tree);
-
+static section *arm_function_section (tree, enum node_frequency, bool, bool);
+static bool arm_asm_elf_flags_numeric (unsigned int flags, unsigned int *num);
+static unsigned int arm_elf_section_type_flags (tree decl, const char *name,
+ int reloc);
/* Table of machine attributes. */
static const struct attribute_spec arm_attribute_table[] =
@@ -587,8 +590,8 @@ static const struct attribute_spec arm_attribute_table[] =
#define TARGET_ASM_EMIT_EXCEPT_PERSONALITY arm_asm_emit_except_personality
#undef TARGET_ASM_INIT_SECTIONS
-#define TARGET_ASM_INIT_SECTIONS arm_asm_init_sections
#endif /* ARM_UNWIND_INFO */
+#define TARGET_ASM_INIT_SECTIONS arm_asm_init_sections
#undef TARGET_DWARF_REGISTER_SPAN
#define TARGET_DWARF_REGISTER_SPAN arm_dwarf_register_span
@@ -729,6 +732,15 @@ static const struct attribute_spec arm_attribute_table[] =
#undef TARGET_SCHED_FUSION_PRIORITY
#define TARGET_SCHED_FUSION_PRIORITY arm_sched_fusion_priority
+#undef TARGET_ASM_FUNCTION_SECTION
+#define TARGET_ASM_FUNCTION_SECTION arm_function_section
+
+#undef TARGET_ASM_ELF_FLAGS_NUMERIC
+#define TARGET_ASM_ELF_FLAGS_NUMERIC arm_asm_elf_flags_numeric
+
+#undef TARGET_SECTION_TYPE_FLAGS
+#define TARGET_SECTION_TYPE_FLAGS arm_elf_section_type_flags
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Obstack for minipool constant handling. */
@@ -2815,6 +2827,17 @@ arm_option_check_internal (struct gcc_options *opts)
&& ((!(arm_arch7 && !arm_arch_notm) && !arm_arch7em)
|| (TARGET_THUMB1_P (flags) || flag_pic || TARGET_NEON)))
error ("-mslow-flash-data only supports non-pic code on armv7-m targets");
+
+#ifdef OBJECT_FORMAT_ELF
+ /* We only support -mpure-code on Thumb-2 M-profile targets. */
+ if (target_pure_code
+ && (!arm_arch_thumb2 || arm_arch_notm || flag_pic || TARGET_NEON))
+ error ("-mpure-code only supports non-pic code on armv7-m targets");
+#else
+ if (target_pure_code)
+ error ("-mpure-code is only available for the ELF object format.");
+#endif
+
}
/* Recompute the global settings depending on target attribute options. */
@@ -3453,8 +3476,9 @@ arm_option_override (void)
global_options.x_param_values,
global_options_set.x_param_values);
- /* Currently, for slow flash data, we just disable literal pools. */
- if (target_slow_flash_data)
+ /* Currently, for slow flash data, we just disable literal pools. We also
+ disable it for -mpure-code. */
+ if (target_slow_flash_data || target_pure_code)
arm_disable_literal_pool = true;
/* Disable scheduling fusion by default if it's not armv7 processor
@@ -27208,17 +27232,22 @@ arm_asm_emit_except_personality (rtx personality)
output_addr_const (asm_out_file, personality);
fputc ('\n', asm_out_file);
}
+#endif /* ARM_UNWIND_INFO */
/* Implement TARGET_ASM_INITIALIZE_SECTIONS. */
static void
arm_asm_init_sections (void)
{
+#if ARM_UNWIND_INFO
exception_section = get_unnamed_section (0, output_section_asm_op,
"\t.handlerdata");
-}
#endif /* ARM_UNWIND_INFO */
+ if (target_pure_code)
+ text_section->unnamed.data = "\t.section .text,\"0x20000006\",%%progbits";
+}
+
/* Output unwind directives for the start/end of a function. */
void
@@ -30529,4 +30558,113 @@ arm_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT vcall_offset,
return true;
}
+/* Implement the TARGET_ASM_ELF_FLAGS_NUMERIC hook.
+
+ For sections with SECTION_ARM_PURECODE flag set, make sure you pass the
+ machine specific flag attribute when printing the numeric value. */
+
+static bool
+arm_asm_elf_flags_numeric (unsigned int flags, unsigned int *num)
+{
+
+ if (flags & SECTION_ARM_PURECODE)
+ {
+ *num = 0x20000000;
+
+ if (!(flags & SECTION_DEBUG))
+ *num |= 0x2;
+#if defined (HAVE_GAS_SECTION_EXCLUDE) && HAVE_GAS_SECTION_EXCLUDE == 1
+ if (flags & SECTION_EXCLUDE)
+ *num |= 0x80000000;
+#endif
+ if (flags & SECTION_WRITE)
+ *num |= 0x1;
+ if (flags & SECTION_CODE)
+ *num |= 0x4;
+ if (flags & SECTION_MERGE)
+ *num |= 0x10;
+ if (flags & SECTION_STRINGS)
+ *num |= 0x20;
+ if (flags & SECTION_TLS)
+ *num |= 0x400;
+ if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ *num |= 0x200;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Implement the TARGET_ASM_FUNCTION_SECTION hook.
+
+ If -mpure_code is passed as an option, make sure all functions are in
+ sections that have the SHF_ARM_PURECODE attribute. */
+
+static section *
+arm_function_section (tree decl, enum node_frequency freq,
+ bool startup, bool exit)
+{
+ const char * section_name;
+ section * sec;
+
+ if (!decl || TREE_CODE (decl) != FUNCTION_DECL)
+ return default_function_section (decl, freq, startup, exit);
+
+ if (!target_pure_code)
+ return default_function_section (decl, freq, startup, exit);
+
+
+ section_name = DECL_SECTION_NAME (decl);
+
+ /* If a function is not in a named section then it falls under the 'default'
+ text section, also known as '.text'. We can preserve previous behavior as
+ the default text section already has the SHF_ARM_PURECODE section
+ attribute. */
+ if (!section_name)
+ {
+ section *default_sec = default_function_section (decl, freq, startup,
+ exit);
+
+ /* If default_sec is not null, then it must be a special section like for
+ example .text.startup. We set the pure-code attribute and return the
+ same section to preserve behavior. */
+ if (default_sec)
+ default_sec->common.flags |= SECTION_ARM_PURECODE;
+ return default_sec;
+ }
+
+ /* Otherwise look whether a section has already been created with
+ 'section_name'. */
+ sec = get_named_section (decl, section_name, 0);
+ if (!sec)
+ /* If that is not the case passing NULL as the name section to
+ 'get_named_section' will create a section with the declarations
+ section name. */
+ sec = get_named_section (decl, NULL, 0);
+
+ /* Set the SHF_ARM_PURECODE attribute. */
+ sec->common.flags |= SECTION_ARM_PURECODE;
+
+ return sec;
+}
+
+/* Implements the TARGET_SECTION_FLAGS hook.
+
+ If DECL is a function declaration and -mpure-code is passed as an option
+ then add the SFH_ARM_PURECODE attribute to the section flags. NAME is the
+ sections name and RELOC indicates whether the declarations initializer may
+ contain runtime relocations. */
+
+static unsigned int
+arm_elf_section_type_flags (tree decl, const char *name, int reloc)
+{
+ unsigned int flags = default_section_type_flags (decl, name, reloc);
+
+ if (decl && TREE_CODE (decl) == FUNCTION_DECL && target_pure_code)
+ flags |= SECTION_ARM_PURECODE;
+
+ return flags;
+}
+
#include "gt-arm.h"
@@ -281,3 +281,9 @@ Assume loading data from flash is slower than fetching instructions.
masm-syntax-unified
Target Report Var(inline_asm_unified) Init(0) Save
Assume unified syntax for inline assembly code.
+
+mpure-code
+Target Report Var(target_pure_code) Init(0)
+Assembly generated for functions are put into sections that only contain code.
+This requires that no literal pools or jump tables are generated for such
+functions.
@@ -104,7 +104,8 @@
the code more efficient, but for Thumb-1 it's better to put them out of
band unless we are generating compressed tables. */
#define JUMP_TABLES_IN_TEXT_SECTION \
- (TARGET_32BIT || (TARGET_THUMB && (optimize_size || flag_pic)))
+ ((TARGET_32BIT || (TARGET_THUMB && (optimize_size || flag_pic))) \
+ && !target_pure_code)
#ifndef LINK_SPEC
#define LINK_SPEC "%{mbig-endian:-EB} %{mlittle-endian:-EL} -X"
@@ -639,7 +639,8 @@ Objective-C and Objective-C++ Dialects}.
-mneon-for-64bits @gol
-mslow-flash-data @gol
-masm-syntax-unified @gol
--mrestrict-it}
+-mrestrict-it @gol
+-mpure-code}
@emph{AVR Options}
@gccoptlist{-mmcu=@var{mcu} -maccumulate-args -mbranch-cost=@var{cost} @gol
@@ -14498,6 +14499,14 @@ Print CPU tuning information as comment in assembler file. This is
an option used only for regression testing of the compiler and not
intended for ordinary use in compiling code. This option is disabled
by default.
+
+@item -mpure-code
+@opindex mpure-code
+GCC will generate assembly where instructions and data are in separate sections.
+It also gives all text sections the ELF processor-specific section attribute
+SHF_ARM_PURECODE. This option is only available for ELF targets in thumb mode
+that support Thumb2.
+
@end table
@node AVR Options
@@ -7543,6 +7543,12 @@ is non-NULL, it is the @code{VAR_DECL} or @code{FUNCTION_DECL} with which
this section is associated.
@end deftypefn
+@deftypefn {Target Hook} bool TARGET_ASM_ELF_FLAGS_NUMERIC (unsigned int @var{flags}, unsigned int *@var{num})
+If this function returns true, it will pass the section attributes using
+their numeric value in @var{num} based on the bits set in @var{flags}. This
+function is called by @code{default_elf_asm_named_section}.
+@end deftypefn
+
@deftypefn {Target Hook} {section *} TARGET_ASM_FUNCTION_SECTION (tree @var{decl}, enum node_frequency @var{freq}, bool @var{startup}, bool @var{exit})
Return preferred text (sub)section for function @var{decl}.
Main purpose of this function is to separate cold, normal and hot
@@ -5229,6 +5229,8 @@ of the filename using this macro.
@hook TARGET_ASM_NAMED_SECTION
+@hook TARGET_ASM_ELF_FLAGS_NUMERIC
+
@hook TARGET_ASM_FUNCTION_SECTION
@hook TARGET_ASM_FUNCTION_SWITCHED_TEXT_SECTIONS
@@ -432,6 +432,16 @@ this section is associated.",
void, (const char *name, unsigned int flags, tree decl),
default_no_named_section)
+/* Tell assembler what section attributes to assign this elf section
+ declaration, using their numerical value. */
+DEFHOOK
+(elf_flags_numeric,
+ "If this function returns true, it will pass the section attributes using\n\
+their numeric value in @var{num} based on the bits set in @var{flags}. This\n\
+function is called by @code{default_elf_asm_named_section}.",
+ bool, (unsigned int flags, unsigned int *num),
+ default_asm_elf_flags_numeric)
+
/* Return preferred text (sub)section for function DECL.
Main purpose of this function is to separate cold, normal and hot
functions. STARTUP is true when function is known to be used only
@@ -254,5 +254,7 @@ extern void default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE
int second_time ATTRIBUTE_UNUSED);
extern bool default_optab_supported_p (int, machine_mode, machine_mode,
optimization_type);
+extern bool default_asm_elf_flags_numeric (unsigned int flags,
+ unsigned int *num);
#endif /* GCC_TARGHOOKS_H */
new file mode 100644
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-fpic" "-fPIC" } { "" } } */
+/* { dg-options "-ffunction-sections -mpure-code" } */
+#include <limits.h>
+
+char * foo (void)
+{
+ return "foo";
+}
+
+unsigned int bar (unsigned int b)
+{
+ return UINT_MAX - b;
+}
+
+/* { dg-final { scan-assembler {\.section\t\.text\.foo[^\n]*\"0x20000006\"} } } */
+/* { dg-final { scan-assembler {\.section\t\.text\.bar[^\n]*\"0x20000006\"} } } */
new file mode 100644
@@ -0,0 +1,73 @@
+/* The option -mslow-flash-data is just for performance tuning, it
+ doesn't totally disable the use of literal pools. But for below
+ simple cases, the use of literal pool should be replaced by
+ movw/movt or read-only constant pool. */
+
+/* { dg-do compile } */
+/* { dg-options "-mpure-code" } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-fpic" "-fPIC" } { "" } } */
+
+float sf;
+double df;
+long long l;
+static char *p = "Hello World";
+
+float
+testsf (float *p)
+{
+ if (*p > 1.1234f)
+ return 2.1234f;
+ else
+ return 3.1234f;
+}
+
+double
+testdf (double *p)
+{
+ if (*p > 4.1234)
+ return 2.1234;
+ else
+ return 3.1234;
+}
+
+long long
+testll (long long *p)
+{
+ if (*p > 0x123456789ABCDEFll)
+ return 0x111111111ll;
+ else
+ return 0x222222222ll;
+}
+
+char *
+testchar ()
+{
+ return p + 4;
+}
+
+int
+foo (int a, int b)
+{
+ int i;
+ volatile int *labelref = &&label1;
+
+ if (a > b)
+ {
+ while (i < b)
+ {
+ a += *labelref;
+ i += 1;
+ }
+ goto *labelref;
+ }
+ else
+ b = b + 3;
+
+ a = a * b;
+
+label1:
+ return a + b;
+}
+
+/* { dg-final { scan-assembler-not "\\.(float|l\\?double|\d?byte|short|int|long|quad|word)\\s+\[^.\]" } } */
+/* { dg-final { scan-assembler "text,\"0x20000006\"" } } */
new file mode 100644
@@ -0,0 +1,54 @@
+# Copyright (C) 1997-2016 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite for ARM's -mpure-code option, using the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+ set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# The -mpure-code option is only available for M-profile targets that support
+# thumb2.
+if {[check_effective_target_arm_thumb2_ok]
+ && ![check_effective_target_arm_arm_ok]} then {
+# Initialize `dg'.
+dg-init
+
+set saved-dg-do-what-default ${dg-do-what-default}
+set dg-do-what-default "assemble"
+
+set saved-lto_torture_options ${LTO_TORTURE_OPTIONS}
+
+# Add -ffat-lto-objects option to all LTO options such that we can do assembly
+# scans.
+proc add_fat_objects { list } {
+ set res {}
+ foreach el $list {set res [lappend res [concat $el " -ffat-lto-objects"]]}
+ return $res
+};
+set LTO_TORTURE_OPTIONS [add_fat_objects ${LTO_TORTURE_OPTIONS}]
+
+gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] \
+ "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
+}
@@ -6239,11 +6239,19 @@ default_no_named_section (const char *name ATTRIBUTE_UNUSED,
#define TLS_SECTION_ASM_FLAG 'T'
#endif
+bool
+default_asm_elf_flags_numeric (unsigned int /* flags */,
+ unsigned int * /* num */)
+{
+ return false;
+}
+
void
default_elf_asm_named_section (const char *name, unsigned int flags,
tree decl)
{
char flagchars[11], *f = flagchars;
+ unsigned int numeric_value;
/* If we have already declared this section, we can use an
abbreviated form to switch back to it -- unless this section is
@@ -6256,31 +6264,38 @@ default_elf_asm_named_section (const char *name, unsigned int flags,
return;
}
- if (!(flags & SECTION_DEBUG))
- *f++ = 'a';
+ /* If we have a machine specific flag, then use the numeric value to pass
+ this on to GAS. */
+ if (targetm.asm_out.elf_flags_numeric (flags, &numeric_value))
+ snprintf (f, sizeof (flagchars), "0x%08x", numeric_value);
+ else
+ {
+ if (!(flags & SECTION_DEBUG))
+ *f++ = 'a';
#if defined (HAVE_GAS_SECTION_EXCLUDE) && HAVE_GAS_SECTION_EXCLUDE == 1
- if (flags & SECTION_EXCLUDE)
- *f++ = 'e';
+ if (flags & SECTION_EXCLUDE)
+ *f++ = 'e';
#endif
- if (flags & SECTION_WRITE)
- *f++ = 'w';
- if (flags & SECTION_CODE)
- *f++ = 'x';
- if (flags & SECTION_SMALL)
- *f++ = 's';
- if (flags & SECTION_MERGE)
- *f++ = 'M';
- if (flags & SECTION_STRINGS)
- *f++ = 'S';
- if (flags & SECTION_TLS)
- *f++ = TLS_SECTION_ASM_FLAG;
- if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
- *f++ = 'G';
+ if (flags & SECTION_WRITE)
+ *f++ = 'w';
+ if (flags & SECTION_CODE)
+ *f++ = 'x';
+ if (flags & SECTION_SMALL)
+ *f++ = 's';
+ if (flags & SECTION_MERGE)
+ *f++ = 'M';
+ if (flags & SECTION_STRINGS)
+ *f++ = 'S';
+ if (flags & SECTION_TLS)
+ *f++ = TLS_SECTION_ASM_FLAG;
+ if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ *f++ = 'G';
#ifdef MACH_DEP_SECTION_ASM_FLAG
- if (flags & SECTION_MACH_DEP)
- *f++ = MACH_DEP_SECTION_ASM_FLAG;
+ if (flags & SECTION_MACH_DEP)
+ *f++ = MACH_DEP_SECTION_ASM_FLAG;
#endif
- *f = '\0';
+ *f = '\0';
+ }
fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars);
--
1.9.1