aboutsummaryrefslogtreecommitdiff
path: root/libgcc
diff options
context:
space:
mode:
authorChung-Ju Wu <jasonwucj@gmail.com>2018-06-02 14:22:12 +0000
committerChung-Ju Wu <jasonwucj@gcc.gnu.org>2018-06-02 14:22:12 +0000
commitcf3cd43d5ac56ae73679f0bb8b76807aaaf868dd (patch)
tree90aef7941c0537da42c7844369f7e4133fc7bb9c /libgcc
parent2140297cb3091c19ccf8fd3d741dcd840aa0ff8b (diff)
downloadgcc-cf3cd43d5ac56ae73679f0bb8b76807aaaf868dd.zip
gcc-cf3cd43d5ac56ae73679f0bb8b76807aaaf868dd.tar.gz
gcc-cf3cd43d5ac56ae73679f0bb8b76807aaaf868dd.tar.bz2
[NDS32] Support Linux target for nds32.
gcc/ * config.gcc (nds32*): Use nds32-linux.opt and nds32-elf.opt. (nds32le-*-*, nds32be-*-*): Integrate checking process. (nds32*-*-*): Add glibc and uclibc conditions. * common/config/nds32/nds32-common.c (nds32_except_unwind_info): New. (TARGET_EXCEPT_UNWIND_INFO): Define. * config/nds32/elf.h: New file. * config/nds32/linux.h: New file. * config/nds32/nds32-elf.opt: New file. * config/nds32/nds32-linux.opt: New file. * config/nds32/nds32-fp-as-gp.c (pass_nds32_fp_as_gp::gate): Consider TARGET_LINUX_ABI. * config/nds32/nds32.c (nds32_conditional_register_usage): Consider TARGET_LINUX_ABI. (nds32_asm_file_end): Ditto. (nds32_print_operand): Ditto. (nds32_insert_attributes): Ditto. (nds32_init_libfuncs): New function. (TARGET_HAVE_TLS): Define. (TARGET_INIT_LIBFUNCS): Define. * config/nds32/nds32.h (TARGET_DEFAULT_RELAX): Apply different relax spec content. (TARGET_ELF): Apply different mcmodel setting. (LINK_SPEC, LIB_SPEC, STARTFILE_SPEC, ENDFILE_SPEC): The content has been migrated into elf.h and linux.h files. * config/nds32/nds32.md (add_pc): Consider TARGET_LINUX_ABI. * config/nds32/nds32.opt (mvh): Consider TARGET_LINUX_ABI. (mcmodel): The content has been migrated into nds32-elf.opt and nds32-linux.opt files. * config/nds32/t-elf: New file. * config/nds32/t-linux: New file. libgcc/ * config.host (nds32*-linux*): New. * config/nds32/linux-atomic.c: New file. * config/nds32/linux-unwind.h: New file. Co-Authored-By: Kito Cheng <kito.cheng@gmail.com> Co-Authored-By: Monk Chiang <sh.chiang04@gmail.com> From-SVN: r261116
Diffstat (limited to 'libgcc')
-rw-r--r--libgcc/ChangeLog7
-rw-r--r--libgcc/config.host17
-rw-r--r--libgcc/config/nds32/linux-atomic.c282
-rw-r--r--libgcc/config/nds32/linux-unwind.h156
4 files changed, 462 insertions, 0 deletions
diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog
index 9a94e83..d75d934 100644
--- a/libgcc/ChangeLog
+++ b/libgcc/ChangeLog
@@ -1,3 +1,10 @@
+2018-06-02 Chung-Ju Wu <jasonwucj@gmail.com>
+ Monk Chiang <sh.chiang04@gmail.com>
+
+ * config.host (nds32*-linux*): New.
+ * config/nds32/linux-atomic.c: New file.
+ * config/nds32/linux-unwind.h: New file.
+
2018-05-31 Uros Bizjak <ubizjak@gmail.com>
PR target/85591
diff --git a/libgcc/config.host b/libgcc/config.host
index f8fd782..18cabaf 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -979,6 +979,23 @@ msp430*-*-elf)
tmake_file="$tm_file t-crtstuff t-fdpbit msp430/t-msp430"
extra_parts="$extra_parts libmul_none.a libmul_16.a libmul_32.a libmul_f5.a"
;;
+nds32*-linux*)
+ # Basic makefile fragment and extra_parts for crt stuff.
+ # We also append c-isr library implementation.
+ tmake_file="${tmake_file} t-slibgcc-libgcc"
+ tmake_file="${tmake_file} nds32/t-nds32-glibc nds32/t-crtstuff t-softfp-sfdf t-softfp"
+ # The header file of defining MD_FALLBACK_FRAME_STATE_FOR.
+ md_unwind_header=nds32/linux-unwind.h
+ # Append library definition makefile fragment according to --with-nds32-lib=X setting.
+ case "${with_nds32_lib}" in
+ "" | glibc | uclibc )
+ ;;
+ *)
+ echo "Cannot accept --with-nds32-lib=$with_nds32_lib, available values are: glibc uclibc" 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
nds32*-elf*)
# Basic makefile fragment and extra_parts for crt stuff.
# We also append c-isr library implementation.
diff --git a/libgcc/config/nds32/linux-atomic.c b/libgcc/config/nds32/linux-atomic.c
new file mode 100644
index 0000000..6da7be9
--- /dev/null
+++ b/libgcc/config/nds32/linux-atomic.c
@@ -0,0 +1,282 @@
+/* Linux-specific atomic operations for NDS32 Linux.
+ Copyright (C) 2012-2018 Free Software Foundation, Inc.
+
+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 the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* We implement byte, short and int versions of each atomic operation
+ using the kernel helper defined below. There is no support for
+ 64-bit operations yet. */
+
+/* This function copy form NDS32 Linux-kernal. */
+static inline int
+__kernel_cmpxchg (int oldval, int newval, int *mem)
+{
+ int temp1, temp2, temp3, offset;
+
+ asm volatile ("msync\tall\n"
+ "movi\t%0, #0\n"
+ "1:\n"
+ "\tllw\t%1, [%4+%0]\n"
+ "\tsub\t%3, %1, %6\n"
+ "\tcmovz\t%2, %5, %3\n"
+ "\tcmovn\t%2, %1, %3\n"
+ "\tscw\t%2, [%4+%0]\n"
+ "\tbeqz\t%2, 1b\n"
+ : "=&r" (offset), "=&r" (temp3), "=&r" (temp2), "=&r" (temp1)
+ : "r" (mem), "r" (newval), "r" (oldval) : "memory");
+
+ return temp1;
+}
+
+#define HIDDEN __attribute__ ((visibility ("hidden")))
+
+#ifdef __NDS32_EL__
+#define INVERT_MASK_1 0
+#define INVERT_MASK_2 0
+#else
+#define INVERT_MASK_1 24
+#define INVERT_MASK_2 16
+#endif
+
+#define MASK_1 0xffu
+#define MASK_2 0xffffu
+
+#define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \
+ int HIDDEN \
+ __sync_fetch_and_##OP##_4 (int *ptr, int val) \
+ { \
+ int failure, tmp; \
+ \
+ do { \
+ tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
+ failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \
+ } while (failure != 0); \
+ \
+ return tmp; \
+ }
+
+FETCH_AND_OP_WORD (add, , +)
+FETCH_AND_OP_WORD (sub, , -)
+FETCH_AND_OP_WORD (or, , |)
+FETCH_AND_OP_WORD (and, , &)
+FETCH_AND_OP_WORD (xor, , ^)
+FETCH_AND_OP_WORD (nand, ~, &)
+
+#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
+#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
+
+/* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
+ subword-sized quantities. */
+
+#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \
+ TYPE HIDDEN \
+ NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val) \
+ { \
+ int *wordptr = (int *) ((unsigned long) ptr & ~3); \
+ unsigned int mask, shift, oldval, newval; \
+ int failure; \
+ \
+ shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
+ mask = MASK_##WIDTH << shift; \
+ \
+ do { \
+ oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST); \
+ newval = ((PFX_OP (((oldval & mask) >> shift) \
+ INF_OP (unsigned int) val)) << shift) & mask; \
+ newval |= oldval & ~mask; \
+ failure = __kernel_cmpxchg (oldval, newval, wordptr); \
+ } while (failure != 0); \
+ \
+ return (RETURN & mask) >> shift; \
+ }
+
+
+SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
+
+SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
+
+#define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \
+ int HIDDEN \
+ __sync_##OP##_and_fetch_4 (int *ptr, int val) \
+ { \
+ int tmp, failure; \
+ \
+ do { \
+ tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
+ failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \
+ } while (failure != 0); \
+ \
+ return PFX_OP (tmp INF_OP val); \
+ }
+
+OP_AND_FETCH_WORD (add, , +)
+OP_AND_FETCH_WORD (sub, , -)
+OP_AND_FETCH_WORD (or, , |)
+OP_AND_FETCH_WORD (and, , &)
+OP_AND_FETCH_WORD (xor, , ^)
+OP_AND_FETCH_WORD (nand, ~, &)
+
+SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
+
+SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
+
+int HIDDEN
+__sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
+{
+ int actual_oldval, fail;
+
+ while (1)
+ {
+ actual_oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
+
+ if (oldval != actual_oldval)
+ return actual_oldval;
+
+ fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
+
+ if (!fail)
+ return oldval;
+ }
+}
+
+#define SUBWORD_VAL_CAS(TYPE, WIDTH) \
+ TYPE HIDDEN \
+ __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
+ TYPE newval) \
+ { \
+ int *wordptr = (int *)((unsigned long) ptr & ~3), fail; \
+ unsigned int mask, shift, actual_oldval, actual_newval; \
+ \
+ shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
+ mask = MASK_##WIDTH << shift; \
+ \
+ while (1) \
+ { \
+ actual_oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST); \
+ \
+ if (((actual_oldval & mask) >> shift) != (unsigned int) oldval) \
+ return (actual_oldval & mask) >> shift; \
+ \
+ actual_newval = (actual_oldval & ~mask) \
+ | (((unsigned int) newval << shift) & mask); \
+ \
+ fail = __kernel_cmpxchg (actual_oldval, actual_newval, \
+ wordptr); \
+ \
+ if (!fail) \
+ return oldval; \
+ } \
+ }
+
+SUBWORD_VAL_CAS (unsigned short, 2)
+SUBWORD_VAL_CAS (unsigned char, 1)
+
+typedef unsigned char bool;
+
+bool HIDDEN
+__sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
+{
+ int failure = __kernel_cmpxchg (oldval, newval, ptr);
+ return (failure == 0);
+}
+
+#define SUBWORD_BOOL_CAS(TYPE, WIDTH) \
+ bool HIDDEN \
+ __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
+ TYPE newval) \
+ { \
+ TYPE actual_oldval \
+ = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval); \
+ return (oldval == actual_oldval); \
+ }
+
+SUBWORD_BOOL_CAS (unsigned short, 2)
+SUBWORD_BOOL_CAS (unsigned char, 1)
+
+int HIDDEN
+__sync_lock_test_and_set_4 (int *ptr, int val)
+{
+ int failure, oldval;
+
+ do {
+ oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
+ failure = __kernel_cmpxchg (oldval, val, ptr);
+ } while (failure != 0);
+
+ return oldval;
+}
+
+#define SUBWORD_TEST_AND_SET(TYPE, WIDTH) \
+ TYPE HIDDEN \
+ __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \
+ { \
+ int failure; \
+ unsigned int oldval, newval, shift, mask; \
+ int *wordptr = (int *) ((unsigned long) ptr & ~3); \
+ \
+ shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
+ mask = MASK_##WIDTH << shift; \
+ \
+ do { \
+ oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST); \
+ newval = (oldval & ~mask) \
+ | (((unsigned int) val << shift) & mask); \
+ failure = __kernel_cmpxchg (oldval, newval, wordptr); \
+ } while (failure != 0); \
+ \
+ return (oldval & mask) >> shift; \
+ }
+
+SUBWORD_TEST_AND_SET (unsigned short, 2)
+SUBWORD_TEST_AND_SET (unsigned char, 1)
+
+#define SYNC_LOCK_RELEASE(TYPE, WIDTH) \
+ void HIDDEN \
+ __sync_lock_release_##WIDTH (TYPE *ptr) \
+ { \
+ /* All writes before this point must be seen before we release \
+ the lock itself. */ \
+ __builtin_nds32_msync_all (); \
+ *ptr = 0; \
+ }
+
+SYNC_LOCK_RELEASE (int, 4)
+SYNC_LOCK_RELEASE (short, 2)
+SYNC_LOCK_RELEASE (char, 1)
diff --git a/libgcc/config/nds32/linux-unwind.h b/libgcc/config/nds32/linux-unwind.h
new file mode 100644
index 0000000..921edf9
--- /dev/null
+++ b/libgcc/config/nds32/linux-unwind.h
@@ -0,0 +1,156 @@
+/* DWARF2 EH unwinding support for NDS32 Linux signal frame.
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+ Contributed by Andes Technology Corporation.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef inhibit_libc
+
+/* Do code reading to identify a signal frame, and set the frame
+ state data appropriately. See unwind-dw2.c for the structs.
+ The corresponding bits in the Linux kernel are in
+ arch/nds32/kernel/signal.c. */
+
+#include <signal.h>
+#include <asm/unistd.h>
+
+/* Exactly the same layout as the kernel structures, unique names. */
+
+/* arch/nds32/kernel/signal.c */
+struct _sigframe {
+ struct ucontext uc;
+ unsigned long retcode;
+};
+
+struct _rt_sigframe {
+ siginfo_t info;
+ struct _sigframe sig;
+};
+#define SIGRETURN 0xeb0e0a64
+#define RT_SIGRETURN 0xab150a64
+
+#define MD_FALLBACK_FRAME_STATE_FOR nds32_fallback_frame_state
+
+/* This function is supposed to be invoked by uw_frame_state_for()
+ when there is no unwind data available.
+
+ Generally, given the _Unwind_Context CONTEXT for a stack frame,
+ we need to look up its caller and decode information into FS.
+ However, if the exception handling happens within a signal handler,
+ the return address of signal handler is a special module, which
+ contains signal return syscall and has no FDE in the .eh_frame section.
+ We need to implement MD_FALLBACK_FRAME_STATE_FOR so that we can
+ unwind through signal frames. */
+static _Unwind_Reason_Code
+nds32_fallback_frame_state (struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+ u_int32_t *pc = (u_int32_t *) context->ra;
+ struct sigcontext *sc_;
+ _Unwind_Ptr new_cfa;
+
+#ifdef __NDS32_EB__
+#error "Signal handler is not supported for force unwind."
+#endif
+
+ if ((_Unwind_Ptr) pc & 3)
+ return _URC_END_OF_STACK;
+
+ /* Check if we are going through a signal handler.
+ See arch/nds32/kernel/signal.c implementation.
+ SWI_SYS_SIGRETURN -> (0xeb0e0a64)
+ SWI_SYS_RT_SIGRETURN -> (0xab150a64)
+ FIXME: Currently we only handle little endian (EL) case. */
+ if (pc[0] == SIGRETURN)
+ {
+ /* Using '_sigfame' memory address to locate kernal's sigcontext.
+ The sigcontext structures in arch/nds32/include/asm/sigcontext.h. */
+ struct _sigframe *rt_;
+ rt_ = context->cfa;
+ sc_ = &rt_->uc.uc_mcontext;
+ }
+ else if (pc[0] == RT_SIGRETURN)
+ {
+ /* Using '_sigfame' memory address to locate kernal's sigcontext. */
+ struct _rt_sigframe *rt_;
+ rt_ = context->cfa;
+ sc_ = &rt_->sig.uc.uc_mcontext;
+ }
+ else
+ return _URC_END_OF_STACK;
+
+ /* Update cfa from sigcontext. */
+ new_cfa = (_Unwind_Ptr) sc_;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+ fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;
+
+#define NDS32_PUT_FS_REG(NUM, NAME) \
+ (fs->regs.reg[NUM].how = REG_SAVED_OFFSET, \
+ fs->regs.reg[NUM].loc.offset = (_Unwind_Ptr) &(sc_->NAME) - new_cfa)
+
+ /* Restore all registers value. */
+ NDS32_PUT_FS_REG (0, nds32_r0);
+ NDS32_PUT_FS_REG (1, nds32_r1);
+ NDS32_PUT_FS_REG (2, nds32_r2);
+ NDS32_PUT_FS_REG (3, nds32_r3);
+ NDS32_PUT_FS_REG (4, nds32_r4);
+ NDS32_PUT_FS_REG (5, nds32_r5);
+ NDS32_PUT_FS_REG (6, nds32_r6);
+ NDS32_PUT_FS_REG (7, nds32_r7);
+ NDS32_PUT_FS_REG (8, nds32_r8);
+ NDS32_PUT_FS_REG (9, nds32_r9);
+ NDS32_PUT_FS_REG (10, nds32_r10);
+ NDS32_PUT_FS_REG (11, nds32_r11);
+ NDS32_PUT_FS_REG (12, nds32_r12);
+ NDS32_PUT_FS_REG (13, nds32_r13);
+ NDS32_PUT_FS_REG (14, nds32_r14);
+ NDS32_PUT_FS_REG (15, nds32_r15);
+ NDS32_PUT_FS_REG (16, nds32_r16);
+ NDS32_PUT_FS_REG (17, nds32_r17);
+ NDS32_PUT_FS_REG (18, nds32_r18);
+ NDS32_PUT_FS_REG (19, nds32_r19);
+ NDS32_PUT_FS_REG (20, nds32_r20);
+ NDS32_PUT_FS_REG (21, nds32_r21);
+ NDS32_PUT_FS_REG (22, nds32_r22);
+ NDS32_PUT_FS_REG (23, nds32_r23);
+ NDS32_PUT_FS_REG (24, nds32_r24);
+ NDS32_PUT_FS_REG (25, nds32_r25);
+
+ NDS32_PUT_FS_REG (28, nds32_fp);
+ NDS32_PUT_FS_REG (29, nds32_gp);
+ NDS32_PUT_FS_REG (30, nds32_lp);
+ NDS32_PUT_FS_REG (31, nds32_sp);
+
+ /* Restore PC, point to trigger signal instruction. */
+ NDS32_PUT_FS_REG (32, nds32_ipc);
+
+#undef NDS32_PUT_FS_REG
+
+ /* The retaddr is PC, use PC to find FDE. */
+ fs->retaddr_column = 32;
+ fs->signal_frame = 1;
+
+ return _URC_NO_REASON;
+}
+
+#endif