diff options
author | Chung-Ju Wu <jasonwucj@gmail.com> | 2018-06-02 14:22:12 +0000 |
---|---|---|
committer | Chung-Ju Wu <jasonwucj@gcc.gnu.org> | 2018-06-02 14:22:12 +0000 |
commit | cf3cd43d5ac56ae73679f0bb8b76807aaaf868dd (patch) | |
tree | 90aef7941c0537da42c7844369f7e4133fc7bb9c /libgcc | |
parent | 2140297cb3091c19ccf8fd3d741dcd840aa0ff8b (diff) | |
download | gcc-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/ChangeLog | 7 | ||||
-rw-r--r-- | libgcc/config.host | 17 | ||||
-rw-r--r-- | libgcc/config/nds32/linux-atomic.c | 282 | ||||
-rw-r--r-- | libgcc/config/nds32/linux-unwind.h | 156 |
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 |