i386.opt: Add option -mcall-ms2sysv-xlogues.
gcc/ChangeLog: * config/i386/i386.opt: Add option -mcall-ms2sysv-xlogues. * config/i386/i386.h (x86_64_ms_sysv_extra_clobbered_registers): Change type to unsigned. (NUM_X86_64_MS_CLOBBERED_REGS): New macro. (struct machine_function): Add new members call_ms2sysv, call_ms2sysv_pad_in, call_ms2sysv_pad_out and call_ms2sysv_extra_regs. (struct machine_frame_state): New fields sp_realigned and sp_realigned_offset. * config/i386/i386.c (enum xlogue_stub): New enum. (enum xlogue_stub_sets): New enum. (class xlogue_layout): New class. (struct ix86_frame): New fields stack_realign_allocate_offset, stack_realign_offset and outlined_save_offset. Modify comments to detail stack layout when using out-of-line stubs. (ix86_target_string): Add -mcall-ms2sysv-xlogues option. (ix86_option_override_internal): Add sorry() for TARGET_SEH and -mcall-ms2sysv-xlogues. (stub_managed_regs): New static variable. (ix86_save_reg): Add new parameter ignore_outlined to optionally omit registers managed by out-of-line stub. (disable_call_ms2sysv_xlogues): New function. (ix86_compute_frame_layout): Modify re-alignment calculations, disable m->call_ms2sysv when appropriate and compute frame layout for out-of-line stubs. (sp_valid_at, fp_valid_at): New inline functions. (choose_basereg): New function. (choose_baseaddr): Add align parameter, use choose_basereg and modify all callers. (ix86_emit_save_reg_using_mov, ix86_emit_restore_sse_regs_using_mov): Use align parameter of choose_baseaddr to generated aligned SSE movs when possible. (pro_epilogue_adjust_stack): Modify to track machine_frame_state::sp_realigned. (ix86_nsaved_regs): Modify to accommodate changes to ix86_save_reg. (ix86_nsaved_sseregs): Likewise. (ix86_emit_save_regs): Likewise. (ix86_emit_save_regs_using_mov): Likewise. (ix86_emit_save_sse_regs_using_mov): Likewise. (get_scratch_register_on_entry): Likewise. (gen_frame_set): New function. (gen_frame_load): Likewise. (gen_frame_store): Likewise. (emit_outlined_ms2sysv_save): Likewise. (emit_outlined_ms2sysv_restore): Likewise. (ix86_expand_prologue): Modify stack re-alignment code and call emit_outlined_ms2sysv_save when appropriate. (ix86_emit_leave): Clear machine_frame_state::sp_realigned. Add parameter rtx_insn *insn, which allows the function to be used to only generate the notes. (ix86_expand_epilogue): Modify validity checks of frame and stack pointers, and call emit_outlined_ms2sysv_restore when appropriate. (ix86_expand_call): Modify to enable m->call_ms2sysv when appropriate. * config/i386/predicates.md (save_multiple): New predicate. (restore_multiple): Likewise. * config/i386/sse.md (save_multiple<mode>): New pattern. (save_multiple_realign<mode>): Likewise. (restore_multiple<mode>): Likewise. (restore_multiple_and_return<mode>): Likewise. (restore_multiple_leave_return<mode>): Likewise. * Makefile.in: Export HOSTCXX and HOSTCXXFLAGS to site.exp gcc/testsuite/ChangeLog: * gcc.target/x86_64/abi/ms-sysv/do-test.S: New file. * gcc.target/x86_64/abi/ms-sysv/gen.cc: Likewise. * gcc.target/x86_64/abi/ms-sysv/ms-sysv.c: Likewise. * gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp: Likewise. libgcc/ChangeLog: * config.host: Add i386/t-msabi to i386/t-linux file list. * config/i386/i386-asm.h: New file. * config/i386/resms64.S: New file. * config/i386/resms64f.S: New file. * config/i386/resms64fx.S: New file. * config/i386/resms64x.S: New file. * config/i386/savms64.S: New file. * config/i386/savms64f.S: New file. * config/i386/t-msabi: New file. From-SVN: r248029
This commit is contained in:
parent
b9bdd60b87
commit
d6d4d7701a
24 changed files with 3041 additions and 99 deletions
|
@ -1,3 +1,69 @@
|
|||
2017-05-14 Daniel Santos <daniel.santos@pobox.com>
|
||||
|
||||
* config/i386/i386.opt: Add option -mcall-ms2sysv-xlogues.
|
||||
* config/i386/i386.h
|
||||
(x86_64_ms_sysv_extra_clobbered_registers): Change type to unsigned.
|
||||
(NUM_X86_64_MS_CLOBBERED_REGS): New macro.
|
||||
(struct machine_function): Add new members call_ms2sysv,
|
||||
call_ms2sysv_pad_in, call_ms2sysv_pad_out and call_ms2sysv_extra_regs.
|
||||
(struct machine_frame_state): New fields sp_realigned and
|
||||
sp_realigned_offset.
|
||||
* config/i386/i386.c
|
||||
(enum xlogue_stub): New enum.
|
||||
(enum xlogue_stub_sets): New enum.
|
||||
(class xlogue_layout): New class.
|
||||
(struct ix86_frame): New fields stack_realign_allocate_offset,
|
||||
stack_realign_offset and outlined_save_offset. Modify comments to
|
||||
detail stack layout when using out-of-line stubs.
|
||||
(ix86_target_string): Add -mcall-ms2sysv-xlogues option.
|
||||
(ix86_option_override_internal): Add sorry() for TARGET_SEH and
|
||||
-mcall-ms2sysv-xlogues.
|
||||
(stub_managed_regs): New static variable.
|
||||
(ix86_save_reg): Add new parameter ignore_outlined to optionally omit
|
||||
registers managed by out-of-line stub.
|
||||
(disable_call_ms2sysv_xlogues): New function.
|
||||
(ix86_compute_frame_layout): Modify re-alignment calculations, disable
|
||||
m->call_ms2sysv when appropriate and compute frame layout for
|
||||
out-of-line stubs.
|
||||
(sp_valid_at, fp_valid_at): New inline functions.
|
||||
(choose_basereg): New function.
|
||||
(choose_baseaddr): Add align parameter, use choose_basereg and modify
|
||||
all callers.
|
||||
(ix86_emit_save_reg_using_mov, ix86_emit_restore_sse_regs_using_mov):
|
||||
Use align parameter of choose_baseaddr to generated aligned SSE movs
|
||||
when possible.
|
||||
(pro_epilogue_adjust_stack): Modify to track
|
||||
machine_frame_state::sp_realigned.
|
||||
(ix86_nsaved_regs): Modify to accommodate changes to ix86_save_reg.
|
||||
(ix86_nsaved_sseregs): Likewise.
|
||||
(ix86_emit_save_regs): Likewise.
|
||||
(ix86_emit_save_regs_using_mov): Likewise.
|
||||
(ix86_emit_save_sse_regs_using_mov): Likewise.
|
||||
(get_scratch_register_on_entry): Likewise.
|
||||
(gen_frame_set): New function.
|
||||
(gen_frame_load): Likewise.
|
||||
(gen_frame_store): Likewise.
|
||||
(emit_outlined_ms2sysv_save): Likewise.
|
||||
(emit_outlined_ms2sysv_restore): Likewise.
|
||||
(ix86_expand_prologue): Modify stack re-alignment code and call
|
||||
emit_outlined_ms2sysv_save when appropriate.
|
||||
(ix86_emit_leave): Clear machine_frame_state::sp_realigned. Add
|
||||
parameter rtx_insn *insn, which allows the function to be used to only
|
||||
generate the notes.
|
||||
(ix86_expand_epilogue): Modify validity checks of frame and stack
|
||||
pointers, and call emit_outlined_ms2sysv_restore when appropriate.
|
||||
(ix86_expand_call): Modify to enable m->call_ms2sysv when appropriate.
|
||||
* config/i386/predicates.md
|
||||
(save_multiple): New predicate.
|
||||
(restore_multiple): Likewise.
|
||||
* config/i386/sse.md
|
||||
(save_multiple<mode>): New pattern.
|
||||
(save_multiple_realign<mode>): Likewise.
|
||||
(restore_multiple<mode>): Likewise.
|
||||
(restore_multiple_and_return<mode>): Likewise.
|
||||
(restore_multiple_leave_return<mode>): Likewise.
|
||||
* Makefile.in: Export HOSTCXX and HOSTCXXFLAGS to site.exp
|
||||
|
||||
2017-05-14 Julia Koval <julia.koval@intel.com>
|
||||
|
||||
* config/i386/i386-builtin-types.def (VOID_FTYPE_INT_INT64): New type.
|
||||
|
|
|
@ -3810,7 +3810,9 @@ site.exp: ./config.status Makefile
|
|||
@echo "set CFLAGS \"\"" >> ./site.tmp
|
||||
@echo "set CXXFLAGS \"\"" >> ./site.tmp
|
||||
@echo "set HOSTCC \"$(CC)\"" >> ./site.tmp
|
||||
@echo "set HOSTCXX \"$(CXX)\"" >> ./site.tmp
|
||||
@echo "set HOSTCFLAGS \"$(CFLAGS)\"" >> ./site.tmp
|
||||
@echo "set HOSTCXXFLAGS \"$(CXXFLAGS)\"" >> ./site.tmp
|
||||
# TEST_ALWAYS_FLAGS are flags that should be passed to every compilation.
|
||||
# They are passed first to allow individual tests to override them.
|
||||
@echo "set TEST_ALWAYS_FLAGS \"$(SYSROOT_CFLAGS_FOR_TARGET)\"" >> ./site.tmp
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2163,7 +2163,9 @@ extern int const dbx_register_map[FIRST_PSEUDO_REGISTER];
|
|||
extern int const dbx64_register_map[FIRST_PSEUDO_REGISTER];
|
||||
extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
extern int const x86_64_ms_sysv_extra_clobbered_registers[12];
|
||||
extern unsigned const x86_64_ms_sysv_extra_clobbered_registers[12];
|
||||
#define NUM_X86_64_MS_CLOBBERED_REGS \
|
||||
(ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers))
|
||||
|
||||
/* Before the prologue, RA is at 0(%esp). */
|
||||
#define INCOMING_RETURN_ADDR_RTX \
|
||||
|
@ -2482,6 +2484,17 @@ struct GTY(()) machine_frame_state
|
|||
set, the SP/FP offsets above are relative to the aligned frame
|
||||
and not the CFA. */
|
||||
BOOL_BITFIELD realigned : 1;
|
||||
|
||||
/* Indicates whether the stack pointer has been re-aligned. When set,
|
||||
SP/FP continue to be relative to the CFA, but the stack pointer
|
||||
should only be used for offsets >= sp_realigned_offset, while
|
||||
the frame pointer should be used for offsets < sp_realigned_offset.
|
||||
The flags realigned and sp_realigned are mutually exclusive. */
|
||||
BOOL_BITFIELD sp_realigned : 1;
|
||||
|
||||
/* If sp_realigned is set, this is the offset from the CFA that the
|
||||
stack pointer was realigned to. */
|
||||
HOST_WIDE_INT sp_realigned_offset;
|
||||
};
|
||||
|
||||
/* Private to winnt.c. */
|
||||
|
@ -2565,6 +2578,24 @@ struct GTY(()) machine_function {
|
|||
pass arguments and can be used for indirect sibcall. */
|
||||
BOOL_BITFIELD arg_reg_available : 1;
|
||||
|
||||
/* If true, we're out-of-lining reg save/restore for regs clobbered
|
||||
by ms_abi functions calling a sysv function. */
|
||||
BOOL_BITFIELD call_ms2sysv : 1;
|
||||
|
||||
/* If true, the incoming 16-byte aligned stack has an offset (of 8) and
|
||||
needs padding. */
|
||||
BOOL_BITFIELD call_ms2sysv_pad_in : 1;
|
||||
|
||||
/* If true, the size of the stub save area plus inline int reg saves will
|
||||
result in an 8 byte offset, so needs padding. */
|
||||
BOOL_BITFIELD call_ms2sysv_pad_out : 1;
|
||||
|
||||
/* This is the number of extra registers saved by stub (valid range is
|
||||
0-6). Each additional register is only saved/restored by the stubs
|
||||
if all successive ones are. (Will always be zero when using a hard
|
||||
frame pointer.) */
|
||||
unsigned int call_ms2sysv_extra_regs:3;
|
||||
|
||||
/* During prologue/epilogue generation, the current frame state.
|
||||
Otherwise, the frame state at the end of the prologue. */
|
||||
struct machine_frame_state fs;
|
||||
|
|
|
@ -538,6 +538,10 @@ Enum(calling_abi) String(sysv) Value(SYSV_ABI)
|
|||
EnumValue
|
||||
Enum(calling_abi) String(ms) Value(MS_ABI)
|
||||
|
||||
mcall-ms2sysv-xlogues
|
||||
Target Report Mask(CALL_MS2SYSV_XLOGUES) Save
|
||||
Use libgcc stubs to save and restore registers clobbered by 64-bit Microsoft to System V ABI calls.
|
||||
|
||||
mveclibabi=
|
||||
Target RejectNegative Joined Var(ix86_veclibabi_type) Enum(ix86_veclibabi) Init(ix86_veclibabi_type_none)
|
||||
Vector library ABI to use.
|
||||
|
|
|
@ -1657,3 +1657,84 @@
|
|||
(ior (match_operand 0 "register_operand")
|
||||
(and (match_code "const_int")
|
||||
(match_test "op == constm1_rtx"))))
|
||||
|
||||
;; Return true if the vector ends with between 12 and 18 register saves using
|
||||
;; RAX as the base address.
|
||||
(define_predicate "save_multiple"
|
||||
(match_code "parallel")
|
||||
{
|
||||
const unsigned len = XVECLEN (op, 0);
|
||||
unsigned i;
|
||||
|
||||
/* Starting from end of vector, count register saves. */
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
rtx src, dest, addr;
|
||||
rtx e = XVECEXP (op, 0, len - 1 - i);
|
||||
|
||||
if (GET_CODE (e) != SET)
|
||||
break;
|
||||
|
||||
src = SET_SRC (e);
|
||||
dest = SET_DEST (e);
|
||||
|
||||
if (!REG_P (src) || !MEM_P (dest))
|
||||
break;
|
||||
|
||||
addr = XEXP (dest, 0);
|
||||
|
||||
/* Good if dest address is in RAX. */
|
||||
if (REG_P (addr) && REGNO (addr) == AX_REG)
|
||||
continue;
|
||||
|
||||
/* Good if dest address is offset of RAX. */
|
||||
if (GET_CODE (addr) == PLUS
|
||||
&& REG_P (XEXP (addr, 0))
|
||||
&& REGNO (XEXP (addr, 0)) == AX_REG)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
return (i >= 12 && i <= 18);
|
||||
})
|
||||
|
||||
|
||||
;; Return true if the vector ends with between 12 and 18 register loads using
|
||||
;; RSI as the base address.
|
||||
(define_predicate "restore_multiple"
|
||||
(match_code "parallel")
|
||||
{
|
||||
const unsigned len = XVECLEN (op, 0);
|
||||
unsigned i;
|
||||
|
||||
/* Starting from end of vector, count register restores. */
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
rtx src, dest, addr;
|
||||
rtx e = XVECEXP (op, 0, len - 1 - i);
|
||||
|
||||
if (GET_CODE (e) != SET)
|
||||
break;
|
||||
|
||||
src = SET_SRC (e);
|
||||
dest = SET_DEST (e);
|
||||
|
||||
if (!MEM_P (src) || !REG_P (dest))
|
||||
break;
|
||||
|
||||
addr = XEXP (src, 0);
|
||||
|
||||
/* Good if src address is in RSI. */
|
||||
if (REG_P (addr) && REGNO (addr) == SI_REG)
|
||||
continue;
|
||||
|
||||
/* Good if src address is offset of RSI. */
|
||||
if (GET_CODE (addr) == PLUS
|
||||
&& REG_P (XEXP (addr, 0))
|
||||
&& REGNO (XEXP (addr, 0)) == SI_REG)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
return (i >= 12 && i <= 18);
|
||||
})
|
||||
|
|
|
@ -20031,3 +20031,40 @@
|
|||
(match_operand:VI48_512 1 "nonimmediate_operand" "vm")))]
|
||||
"TARGET_AVX512VPOPCNTDQ"
|
||||
"vpopcnt<ssemodesuffix>\t{%1, %0<mask_operand2>|%0<mask_operand2>, %1}")
|
||||
|
||||
;; Save multiple registers out-of-line.
|
||||
(define_insn "save_multiple<mode>"
|
||||
[(match_parallel 0 "save_multiple"
|
||||
[(use (match_operand:P 1 "symbol_operand"))])]
|
||||
"TARGET_SSE && TARGET_64BIT"
|
||||
"call\t%P1")
|
||||
|
||||
;; Restore multiple registers out-of-line.
|
||||
(define_insn "restore_multiple<mode>"
|
||||
[(match_parallel 0 "restore_multiple"
|
||||
[(use (match_operand:P 1 "symbol_operand"))])]
|
||||
"TARGET_SSE && TARGET_64BIT"
|
||||
"call\t%P1")
|
||||
|
||||
;; Restore multiple registers out-of-line and return.
|
||||
(define_insn "restore_multiple_and_return<mode>"
|
||||
[(match_parallel 0 "restore_multiple"
|
||||
[(return)
|
||||
(use (match_operand:P 1 "symbol_operand"))
|
||||
(set (reg:DI SP_REG) (reg:DI R10_REG))
|
||||
])]
|
||||
"TARGET_SSE && TARGET_64BIT"
|
||||
"jmp\t%P1")
|
||||
|
||||
;; Restore multiple registers out-of-line when hard frame pointer is used,
|
||||
;; perform the leave operation prior to returning (from the function).
|
||||
(define_insn "restore_multiple_leave_return<mode>"
|
||||
[(match_parallel 0 "restore_multiple"
|
||||
[(return)
|
||||
(use (match_operand:P 1 "symbol_operand"))
|
||||
(set (reg:DI SP_REG) (plus:DI (reg:DI BP_REG) (const_int 8)))
|
||||
(set (reg:DI BP_REG) (mem:DI (reg:DI BP_REG)))
|
||||
(clobber (mem:BLK (scratch)))
|
||||
])]
|
||||
"TARGET_SSE && TARGET_64BIT"
|
||||
"jmp\t%P1")
|
||||
|
|
|
@ -1128,7 +1128,8 @@ i386_pe_seh_unwind_emit (FILE *asm_out_file, rtx_insn *insn)
|
|||
|
||||
case REG_CFA_DEF_CFA:
|
||||
case REG_CFA_EXPRESSION:
|
||||
/* Only emitted with DRAP, which we disable. */
|
||||
/* Only emitted with DRAP and aligned memory access using a
|
||||
realigned SP, both of which we disable. */
|
||||
gcc_unreachable ();
|
||||
break;
|
||||
|
||||
|
|
|
@ -1212,7 +1212,7 @@ See RS/6000 and PowerPC Options.
|
|||
-msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
|
||||
-mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
|
||||
-malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol
|
||||
-mmitigate-rop -mgeneral-regs-only}
|
||||
-mmitigate-rop -mgeneral-regs-only -mcall-ms2sysv-xlogues}
|
||||
|
||||
@emph{x86 Windows Options}
|
||||
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
|
||||
|
@ -25307,6 +25307,17 @@ You can control this behavior for specific functions by
|
|||
using the function attributes @code{ms_abi} and @code{sysv_abi}.
|
||||
@xref{Function Attributes}.
|
||||
|
||||
@item -mcall-ms2sysv-xlogues
|
||||
@opindex mcall-ms2sysv-xlogues
|
||||
@opindex mno-call-ms2sysv-xlogues
|
||||
Due to differences in 64-bit ABIs, any Microsoft ABI function that calls a
|
||||
System V ABI function must consider RSI, RDI and XMM6-15 as clobbered. By
|
||||
default, the code for saving and restoring these registers is emitted inline,
|
||||
resulting in fairly lengthy prologues and epilogues. Using
|
||||
@option{-mcall-ms2sysv-xlogues} emits prologues and epilogues that
|
||||
use stubs in the static portion of libgcc to perform these saves and restores,
|
||||
thus reducing function size at the cost of a few extra instructions.
|
||||
|
||||
@item -mtls-dialect=@var{type}
|
||||
@opindex mtls-dialect
|
||||
Generate code to access thread-local storage using the @samp{gnu} or
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2017-05-14 Daniel Santos <daniel.santos@pobox.com>
|
||||
|
||||
* gcc.target/x86_64/abi/ms-sysv/do-test.S: New file.
|
||||
* gcc.target/x86_64/abi/ms-sysv/gen.cc: Likewise.
|
||||
* gcc.target/x86_64/abi/ms-sysv/ms-sysv.c: Likewise.
|
||||
* gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp: Likewise.
|
||||
|
||||
2017-05-14 Julia Koval <julia.koval@intel.com>
|
||||
|
||||
* gcc.target/i386/xgetsetbv.c: New test.
|
||||
|
|
163
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
Normal file
163
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* Assembly proxy functions for ms_abi tests.
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
# ifdef __ELF__
|
||||
# define ELFFN_BEGIN(fn) .type fn,@function
|
||||
# define ELFFN_END(fn) .size fn,.-fn
|
||||
# else
|
||||
# define ELFFN_BEGIN(fn)
|
||||
# define ELFFN_END(fn)
|
||||
# endif
|
||||
|
||||
# define FUNC(fn) \
|
||||
.global fn; \
|
||||
ELFFN_BEGIN(fn); \
|
||||
fn:
|
||||
|
||||
#define FUNC_END(fn) ELFFN_END(fn)
|
||||
|
||||
# ifdef __AVX__
|
||||
# define MOVAPS vmovaps
|
||||
# else
|
||||
# define MOVAPS movaps
|
||||
# endif
|
||||
|
||||
/* TODO: Is there a cleaner way to provide these offsets? */
|
||||
.struct 0
|
||||
test_data_save:
|
||||
|
||||
.struct test_data_save + 224
|
||||
test_data_input:
|
||||
|
||||
.struct test_data_save + 448
|
||||
test_data_output:
|
||||
|
||||
.struct test_data_save + 672
|
||||
test_data_fn:
|
||||
|
||||
.struct test_data_save + 680
|
||||
test_data_retaddr:
|
||||
|
||||
.text
|
||||
|
||||
regs_to_mem:
|
||||
MOVAPS %xmm6, (%rax)
|
||||
MOVAPS %xmm7, 0x10(%rax)
|
||||
MOVAPS %xmm8, 0x20(%rax)
|
||||
MOVAPS %xmm9, 0x30(%rax)
|
||||
MOVAPS %xmm10, 0x40(%rax)
|
||||
MOVAPS %xmm11, 0x50(%rax)
|
||||
MOVAPS %xmm12, 0x60(%rax)
|
||||
MOVAPS %xmm13, 0x70(%rax)
|
||||
MOVAPS %xmm14, 0x80(%rax)
|
||||
MOVAPS %xmm15, 0x90(%rax)
|
||||
mov %rsi, 0xa0(%rax)
|
||||
mov %rdi, 0xa8(%rax)
|
||||
mov %rbx, 0xb0(%rax)
|
||||
mov %rbp, 0xb8(%rax)
|
||||
mov %r12, 0xc0(%rax)
|
||||
mov %r13, 0xc8(%rax)
|
||||
mov %r14, 0xd0(%rax)
|
||||
mov %r15, 0xd8(%rax)
|
||||
retq
|
||||
|
||||
mem_to_regs:
|
||||
MOVAPS (%rax), %xmm6
|
||||
MOVAPS 0x10(%rax),%xmm7
|
||||
MOVAPS 0x20(%rax),%xmm8
|
||||
MOVAPS 0x30(%rax),%xmm9
|
||||
MOVAPS 0x40(%rax),%xmm10
|
||||
MOVAPS 0x50(%rax),%xmm11
|
||||
MOVAPS 0x60(%rax),%xmm12
|
||||
MOVAPS 0x70(%rax),%xmm13
|
||||
MOVAPS 0x80(%rax),%xmm14
|
||||
MOVAPS 0x90(%rax),%xmm15
|
||||
mov 0xa0(%rax),%rsi
|
||||
mov 0xa8(%rax),%rdi
|
||||
mov 0xb0(%rax),%rbx
|
||||
mov 0xb8(%rax),%rbp
|
||||
mov 0xc0(%rax),%r12
|
||||
mov 0xc8(%rax),%r13
|
||||
mov 0xd0(%rax),%r14
|
||||
mov 0xd8(%rax),%r15
|
||||
retq
|
||||
|
||||
# NOTE: Not MT safe
|
||||
FUNC(do_test_unaligned)
|
||||
.cfi_startproc
|
||||
# The below alignment checks are to verify correctness of the test
|
||||
# its self.
|
||||
|
||||
# Verify that incoming stack is aligned + 8
|
||||
pushf
|
||||
test $0x8, %rsp
|
||||
jne L0
|
||||
int $3 # Stack not unaligned
|
||||
|
||||
FUNC(do_test_aligned)
|
||||
# Verify that incoming stack is aligned
|
||||
pushf
|
||||
test $0xf, %rsp
|
||||
je L0
|
||||
int $3 # Stack not aligned
|
||||
L0:
|
||||
popf
|
||||
|
||||
# Save registers
|
||||
lea test_data(%rip), %rax
|
||||
call regs_to_mem
|
||||
|
||||
# Load register with random data
|
||||
lea test_data + test_data_input(%rip), %rax
|
||||
call mem_to_regs
|
||||
|
||||
# Save original return address
|
||||
pop %rax
|
||||
movq %rax, test_data + test_data_retaddr(%rip)
|
||||
|
||||
# Call the test function
|
||||
call *test_data + test_data_fn(%rip)
|
||||
|
||||
# Restore the original return address
|
||||
movq test_data + test_data_retaddr(%rip), %rcx
|
||||
push %rcx
|
||||
|
||||
# Save test function return value and store resulting register values
|
||||
push %rax
|
||||
lea test_data + test_data_output(%rip), %rax
|
||||
call regs_to_mem
|
||||
|
||||
# Restore registers
|
||||
lea test_data(%rip), %rax
|
||||
call mem_to_regs
|
||||
pop %rax
|
||||
retq
|
||||
.cfi_endproc
|
||||
FUNC_END(do_test_aligned)
|
||||
FUNC_END(do_test_unaligned)
|
||||
|
||||
#endif /* __x86_64__ */
|
807
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
Normal file
807
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
Normal file
|
@ -0,0 +1,807 @@
|
|||
/* Test program generator for 64-bit Microsoft ABI.
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <ios>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* A basic Effective C++ Item 6. */
|
||||
class uncopyable
|
||||
{
|
||||
private:
|
||||
uncopyable (const uncopyable &) = delete;
|
||||
const uncopyable& operator= (const uncopyable &) = delete;
|
||||
|
||||
protected:
|
||||
uncopyable() {}
|
||||
~uncopyable() {}
|
||||
};
|
||||
|
||||
/* A simple class for adding text delimiters. */
|
||||
class list_delimiter : protected uncopyable
|
||||
{
|
||||
int m_pos;
|
||||
string m_delim;
|
||||
static string s_empty;
|
||||
|
||||
list_delimiter ();
|
||||
|
||||
public:
|
||||
list_delimiter (const char *delim, int init_pos = 0)
|
||||
: m_pos (init_pos), m_delim(delim) {}
|
||||
const string &get () {return m_pos++ ? m_delim : s_empty;}
|
||||
void reset () {m_pos = 0;}
|
||||
int get_pos () {return m_pos;}
|
||||
};
|
||||
|
||||
string list_delimiter::s_empty = "";
|
||||
|
||||
/* Bitmasks for representing non-volatile retisters of an ms_abi call that
|
||||
are not already clobbered by a sysv_abi call. */
|
||||
enum optional_regs
|
||||
{
|
||||
OPTIONAL_REG_RBX = 0x01,
|
||||
OPTIONAL_REG_RBP = 0x02,
|
||||
OPTIONAL_REG_R12 = 0x04,
|
||||
OPTIONAL_REG_R13 = 0x08,
|
||||
OPTIONAL_REG_R14 = 0x10,
|
||||
OPTIONAL_REG_R15 = 0x20,
|
||||
|
||||
OPTIONAL_REG_ALL = 0x3f,
|
||||
OPTIONAL_REG_HFP_ALL = OPTIONAL_REG_ALL & (~OPTIONAL_REG_RBP)
|
||||
};
|
||||
|
||||
static const char * const optional_regs_str[] = {
|
||||
"rbx",
|
||||
"rbp",
|
||||
"r12",
|
||||
"r13",
|
||||
"r14",
|
||||
"r15",
|
||||
};
|
||||
|
||||
/* A simple type & name representation of a function parameter. */
|
||||
class arg
|
||||
{
|
||||
string name;
|
||||
string type;
|
||||
bool type_is_integral:1;
|
||||
|
||||
public:
|
||||
arg(const char *name, const char *type, bool type_is_integral);
|
||||
|
||||
bool is_type_integral () const {return type_is_integral;}
|
||||
const string &get_name () const {return name;}
|
||||
const string &get_type () const {return type;}
|
||||
};
|
||||
|
||||
arg::arg(const char *name, const char *type, bool type_is_integral)
|
||||
: name (name), type (type), type_is_integral (type_is_integral)
|
||||
{
|
||||
}
|
||||
|
||||
/* A stupid operator<< implementation for arg objects. */
|
||||
template<class T> T &operator<< (T &out, const arg &a)
|
||||
{
|
||||
return out << a.get_type () << " " << a.get_name ();
|
||||
}
|
||||
|
||||
/* Bitmask representation of all possible varients of a test function. The
|
||||
value FN_VAR_MSABI is only used internally to distinguish between an
|
||||
ms_abi and sysv_abi function. */
|
||||
enum fn_variants {
|
||||
FN_VAR_MSABI = 0x01,
|
||||
FN_VAR_HFP = 0x02,
|
||||
FN_VAR_REALIGN = 0x04,
|
||||
FN_VAR_ALLOCA = 0x08,
|
||||
FN_VAR_VARARGS = 0x10,
|
||||
FN_VAR_SIBCALL = 0x20,
|
||||
FN_VAR_SHRINK_WRAP = 0x40,
|
||||
|
||||
FN_VAR_HFP_OR_REALIGN = FN_VAR_HFP | FN_VAR_REALIGN,
|
||||
FN_VAR_MASK = 0x7f,
|
||||
FN_VAR_COUNT = 7
|
||||
};
|
||||
|
||||
/* Representation of a Microsoft or System V ABI function with varying
|
||||
parameters, quirks and optimization goals.
|
||||
|
||||
Function name nomenclature:
|
||||
(msabi|sysv)_[xx_][r|f][a][v][s][w]<n>
|
||||
| | | | | | | |
|
||||
| | | | | | | Number of extra (long) parameters
|
||||
| | | | | | shrink wrap
|
||||
| | | | | sibling call
|
||||
| | | | varargs
|
||||
| | | alloca
|
||||
| | Forced realignment or hard frame pointer
|
||||
| Explicit clobbers (hexidecimal mask, ms_abi only)
|
||||
Calling Convention */
|
||||
class fn : protected uncopyable
|
||||
{
|
||||
private:
|
||||
const vector<arg> &m_args;
|
||||
string m_name;
|
||||
string m_attr_decl_str;
|
||||
string m_attr_def_str;
|
||||
int m_clobbers:FN_VAR_COUNT;
|
||||
int m_var;
|
||||
|
||||
public:
|
||||
fn (const vector<arg> &args, int clobbers, int var);
|
||||
|
||||
void print_params (ostream &out) const;
|
||||
void print_decl (ostream &out, bool for_def = false) const;
|
||||
void print_noinfo_def (ostream &out) const;
|
||||
void print_def (ostream &out) const;
|
||||
const string &get_name () const {return m_name;}
|
||||
const vector<arg> &get_args () const {return m_args;}
|
||||
|
||||
bool get_hfp_or_realign () const {return m_var & FN_VAR_HFP_OR_REALIGN;}
|
||||
bool get_msabi () const {return m_var & FN_VAR_MSABI;}
|
||||
bool get_hfp () const {return m_var & FN_VAR_HFP;}
|
||||
bool get_realign () const {return m_var & FN_VAR_REALIGN;}
|
||||
bool get_alloca () const {return m_var & FN_VAR_ALLOCA;}
|
||||
bool get_varargs () const {return m_var & FN_VAR_VARARGS;}
|
||||
bool get_sibcall () const {return m_var & FN_VAR_SIBCALL;}
|
||||
bool get_shrink_wrap () const {return m_var & FN_VAR_SHRINK_WRAP;}
|
||||
};
|
||||
|
||||
fn::fn (const vector<arg> &args, int clobbers, int var)
|
||||
: m_args (args)
|
||||
, m_name ()
|
||||
, m_attr_decl_str ()
|
||||
, m_attr_def_str ("noinline")
|
||||
, m_clobbers (clobbers)
|
||||
, m_var (var)
|
||||
{
|
||||
assert (!(var & ~FN_VAR_MASK));
|
||||
|
||||
if (get_hfp () && get_realign ())
|
||||
throw invalid_argument ("`hfp' with `realign' does nothing.");
|
||||
|
||||
if (get_varargs () && args.empty ())
|
||||
throw invalid_argument ("Need at least one normal argument to use varargs");
|
||||
|
||||
assert (!(get_hfp () || get_realign ()) || !(clobbers & OPTIONAL_REG_RBP));
|
||||
|
||||
stringstream name;
|
||||
name << (get_msabi () ? "msabi_" : "sysv_");
|
||||
if (get_msabi ())
|
||||
name << setfill('0') << setw(2) << hex << m_clobbers << "_";
|
||||
name << (get_realign () ? "r" : (get_hfp () ? "f" : ""))
|
||||
<< (get_alloca () ? "a" : "")
|
||||
<< (get_varargs () ? "v" : "")
|
||||
<< (get_sibcall () ? "s" : "")
|
||||
<< (get_shrink_wrap () ? "w" : "")
|
||||
<< setw(0) << dec << (unsigned)args.size();
|
||||
m_name = name.str();
|
||||
|
||||
list_delimiter decl_comma (", ", !m_attr_decl_str.empty ());
|
||||
list_delimiter def_comma (", ", !m_attr_def_str.empty ());
|
||||
if (get_msabi ())
|
||||
{
|
||||
m_attr_decl_str += decl_comma.get ();
|
||||
m_attr_decl_str += "ms_abi";
|
||||
m_attr_def_str += def_comma.get ();
|
||||
m_attr_def_str += "ms_abi";
|
||||
}
|
||||
|
||||
if (get_realign ())
|
||||
{
|
||||
m_attr_def_str += def_comma.get();
|
||||
m_attr_def_str += "__force_align_arg_pointer__";
|
||||
}
|
||||
else if (get_hfp ())
|
||||
{
|
||||
m_attr_def_str += def_comma.get();
|
||||
m_attr_def_str += "optimize (\"no-omit-frame-pointer\")";
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the parameters for a function declaration. */
|
||||
void fn::print_params (ostream &out) const
|
||||
{
|
||||
list_delimiter comma (", ");
|
||||
|
||||
vector<arg>::const_iterator i;
|
||||
if (get_alloca () && !get_msabi ())
|
||||
out << comma.get () << "void *alloca_mem";
|
||||
for (i = m_args.begin(); i != m_args.end(); ++i)
|
||||
out << comma.get () << *i;
|
||||
|
||||
if (get_varargs ())
|
||||
out << comma.get () << (get_msabi () ? "..." : "va_list argptr");
|
||||
}
|
||||
|
||||
/* Print the declaration for a function. */
|
||||
void fn::print_decl (ostream &out, bool for_def) const
|
||||
{
|
||||
const string &attr_str = (for_def ? m_attr_def_str : m_attr_decl_str);
|
||||
if (!for_def)
|
||||
out << "extern ";
|
||||
|
||||
if (!attr_str.empty ())
|
||||
out << "__attribute__ ((" << attr_str << ")) ";
|
||||
|
||||
out << "long " << m_name << " (";
|
||||
print_params (out);
|
||||
out << ")";
|
||||
if (!for_def)
|
||||
out << ";" << endl;
|
||||
}
|
||||
|
||||
/* Output a volatile "_noinfo" function pointer definition. */
|
||||
void fn::print_noinfo_def (ostream &out) const
|
||||
{
|
||||
out << "static ";
|
||||
if (!m_attr_decl_str.empty ())
|
||||
out << "__attribute__ ((" << m_attr_decl_str << ")) ";
|
||||
out << "long (*const volatile " << m_name << "_noinfo) (";
|
||||
print_params (out);
|
||||
out << ") = " << m_name << ";" << endl;
|
||||
}
|
||||
|
||||
/* Print the definition of a function. */
|
||||
void fn::print_def (ostream &out) const
|
||||
{
|
||||
vector<arg>::const_iterator i;
|
||||
|
||||
print_decl (out, true);
|
||||
out << endl << "{" << endl;
|
||||
|
||||
if (get_msabi () && get_alloca ())
|
||||
{
|
||||
const char *size_str = m_args.empty () ? "42" : "a";
|
||||
out << " void *alloca_mem = alloca (8 + " << size_str << ");" << endl
|
||||
<< " *(long*)alloca_mem = FLAG_ALLOCA;" << endl;
|
||||
}
|
||||
if (get_msabi () && get_varargs ())
|
||||
out << " va_list argptr;" << endl;
|
||||
if (get_shrink_wrap ())
|
||||
out << " if (shrink_wrap_global == FLAG_SHRINK_WRAP_FAST_PATH)" << endl
|
||||
<< " return FLAG_SHRINK_WRAP_FAST_PATH;" << endl;
|
||||
|
||||
list_delimiter comma (", ");
|
||||
if (m_clobbers)
|
||||
{
|
||||
out << " __asm__ __volatile__ (\"\" :::";
|
||||
unsigned c;
|
||||
unsigned mask = m_clobbers;
|
||||
comma.reset ();
|
||||
for (c = 0, mask = m_clobbers; mask; ++c, mask >>= 1)
|
||||
if (mask & 1)
|
||||
out << comma.get () << "\"" << optional_regs_str[c] << "\"";
|
||||
out << ");" << endl;
|
||||
}
|
||||
|
||||
if (get_msabi () && get_varargs ())
|
||||
{
|
||||
assert (!m_args.empty ());
|
||||
out << " va_start(argptr, " << m_args.back ().get_name () << ");" << endl;
|
||||
}
|
||||
|
||||
out << " return ";
|
||||
if (get_msabi ())
|
||||
{
|
||||
if (get_sibcall ())
|
||||
out << "do_sibcall_noinfo (";
|
||||
|
||||
comma.reset ();
|
||||
out << "sysv_"
|
||||
<< (get_alloca () ? "a" : "")
|
||||
<< (get_varargs () ? "v" : "")
|
||||
<< m_args.size ()
|
||||
<< "_noinfo (";
|
||||
|
||||
if (get_alloca ())
|
||||
out << comma.get () << "alloca_mem";
|
||||
for (i = m_args.begin(); i != m_args.end(); ++i)
|
||||
out << comma.get () << i->get_name ();
|
||||
if (get_varargs ())
|
||||
out << comma.get () << "argptr";
|
||||
out << ")";
|
||||
if (get_shrink_wrap ())
|
||||
out << " + FLAG_SHRINK_WRAP_SLOW_PATH";
|
||||
if (get_sibcall ())
|
||||
out << ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
list_delimiter plus (" + ");
|
||||
for (i = m_args.begin(); i != m_args.end(); ++i)
|
||||
if (i->is_type_integral ())
|
||||
out << plus.get () << i->get_name ();
|
||||
if (get_alloca ())
|
||||
out << plus.get () << "*(long*)alloca_mem";
|
||||
if (!plus.get_pos ())
|
||||
out << "0";
|
||||
}
|
||||
out << ";" << endl;
|
||||
if (get_msabi () && get_varargs ())
|
||||
out << " va_end(argptr);" << endl;
|
||||
out << "}" << endl << endl;
|
||||
}
|
||||
|
||||
/* Global variables. */
|
||||
string argv0;
|
||||
string out_file_name;
|
||||
unsigned int extra_params_min = 0;
|
||||
unsigned int extra_params_max = 5;
|
||||
unsigned fn_variant_mask = FN_VAR_MASK;
|
||||
bool omit_rbp_clobbers = false;
|
||||
vector<class fn*> sysv_funcs;
|
||||
vector<class fn*> msabi_funcs;
|
||||
|
||||
|
||||
/* Emit extern for do_test_aligned and do_test_unaligned (defined in do_test.S)
|
||||
followed by all of the various do_test* function function pointers that
|
||||
are just aliases of them. */
|
||||
static void make_do_tests_decl (const vector<class arg> &args, ostream &out)
|
||||
{
|
||||
vector<class arg>::const_iterator ai;
|
||||
unsigned i, varargs, unaligned;
|
||||
|
||||
out << "extern __attribute__ ((ms_abi)) long do_test_aligned ();" << endl
|
||||
<< "extern __attribute__ ((ms_abi)) long do_test_unaligned ();" << endl;
|
||||
|
||||
list_delimiter comma (", ");
|
||||
for (i = extra_params_min; i <= args.size (); ++i)
|
||||
for (unaligned = 0; unaligned <= 1; ++unaligned)
|
||||
for (varargs = 0; varargs <= 1; ++varargs)
|
||||
{
|
||||
if (!i && varargs) /* skip varargs version when no other args */
|
||||
continue;
|
||||
|
||||
comma.reset ();
|
||||
out << "static __attribute__ ((ms_abi)) long (*const do_test_"
|
||||
<< (unaligned ? "u" : "")
|
||||
<< (varargs ? "v" : "") << i << ") (";
|
||||
|
||||
unsigned j;
|
||||
for (j = 0, ai = args.begin (); j < i; ++j, ++ai)
|
||||
out << comma.get () << ai->get_type () << " "
|
||||
<< ai->get_name ();
|
||||
if (varargs)
|
||||
out << comma.get () << "...";
|
||||
out << ") = (void*)do_test_" << (unaligned ? "un" : "")
|
||||
<< "aligned;" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate do_tests function. We actually break it up into multiple
|
||||
do_test_xxxx functions to keep compile times down (with just one large
|
||||
function, it is a very slow build). */
|
||||
void make_do_test (const vector<class arg> &args,
|
||||
const vector<class fn*> &msabi_funcs,
|
||||
ostream &out)
|
||||
{
|
||||
const unsigned TESTS_PER_FN_MAX = 64;
|
||||
unsigned i;
|
||||
vector<string> do_tests_fn_names;
|
||||
unsigned fn_count = 0;
|
||||
unsigned test_count = TESTS_PER_FN_MAX;
|
||||
string params_str;
|
||||
string param_names_str;
|
||||
string param_types_str;
|
||||
|
||||
/* Init some commonly used strings. */
|
||||
{
|
||||
stringstream s1, s2, s3;
|
||||
list_delimiter comma(", ");
|
||||
for (auto arg : args)
|
||||
{
|
||||
const string &c = comma.get ();
|
||||
s1 << c << arg;
|
||||
s2 << c << arg.get_name ();
|
||||
s3 << c << arg.get_type ();
|
||||
}
|
||||
params_str = s1.str ();
|
||||
param_names_str = s2.str ();
|
||||
param_types_str = s3.str ();
|
||||
}
|
||||
|
||||
vector<class fn*>::const_iterator fi;
|
||||
for (fi = msabi_funcs.begin(); fi != msabi_funcs.end(); ++fi)
|
||||
{
|
||||
const fn &f = **fi;
|
||||
unsigned unaligned, shrink_wrap;
|
||||
|
||||
for (unaligned = 0; unaligned <= !!f.get_realign (); ++unaligned)
|
||||
for (shrink_wrap = 0; shrink_wrap <= !!f.get_shrink_wrap ();
|
||||
++shrink_wrap)
|
||||
{
|
||||
const vector<class arg> &fargs = f.get_args ();
|
||||
|
||||
/* To prevent unwieldy build times, we split up tests to 64-ish per
|
||||
function. */
|
||||
if (++test_count > TESTS_PER_FN_MAX)
|
||||
{
|
||||
test_count = 1;
|
||||
if (fn_count > 0) {
|
||||
out << "}" << endl << endl;
|
||||
}
|
||||
|
||||
stringstream fn_name;
|
||||
fn_name << "do_tests_" << setfill('0') << setw(4) << hex
|
||||
<< fn_count++;
|
||||
do_tests_fn_names.push_back (fn_name.str ());
|
||||
|
||||
out << "static __attribute__((noinline)) void "
|
||||
<< fn_name.str () << " (" << params_str << ")" << endl
|
||||
<< "{" << endl
|
||||
<< " long ret;" << endl;
|
||||
}
|
||||
|
||||
/* Call init_test. */
|
||||
out << endl
|
||||
<< " init_test (" << f.get_name () << ", \""
|
||||
<< f.get_name () << "\", ";
|
||||
|
||||
if (f.get_realign ())
|
||||
out << (unaligned ? "ALIGNMENT_MISALIGNED"
|
||||
: "ALIGNMENT_ALIGNED");
|
||||
else
|
||||
out << "ALIGNMENT_NOT_TESTED";
|
||||
|
||||
out << ", ";
|
||||
if (f.get_shrink_wrap ())
|
||||
out << (shrink_wrap ? "SHRINK_WRAP_SLOW_PATH"
|
||||
: "SHRINK_WRAP_FAST_PATH");
|
||||
else
|
||||
out << "SHRINK_WRAP_NONE";
|
||||
out << ", ";
|
||||
|
||||
/* Calculated the expected return value. */
|
||||
if (f.get_shrink_wrap () && shrink_wrap == 0)
|
||||
out << "FLAG_SHRINK_WRAP_FAST_PATH";
|
||||
else
|
||||
{
|
||||
list_delimiter plus (" + ");
|
||||
for (auto const &arg : fargs)
|
||||
out << plus.get () << arg.get_name ();
|
||||
if (f.get_sibcall ())
|
||||
out << plus.get () << "FLAG_SIBCALL";
|
||||
if (f.get_alloca ())
|
||||
out << plus.get () << "FLAG_ALLOCA";
|
||||
if (f.get_shrink_wrap () && shrink_wrap == 1)
|
||||
out << plus.get () << "FLAG_SHRINK_WRAP_SLOW_PATH";
|
||||
if (!plus.get_pos ())
|
||||
out << "0";
|
||||
}
|
||||
out << ");" << endl;
|
||||
/* End if init_test call. */
|
||||
|
||||
if (f.get_realign () && unaligned == 1)
|
||||
out << " __asm__ __volatile__ (\"subq $8,%%rsp\":::\"cc\");"
|
||||
<< endl;
|
||||
|
||||
out << " ret = do_test_"
|
||||
<< (f.get_realign () && unaligned == 1 ? "u" : "")
|
||||
<< (f.get_varargs () ? "v" : "")
|
||||
<< fargs.size () << " (";
|
||||
|
||||
list_delimiter comma (", ");
|
||||
for (auto const &arg : fargs)
|
||||
out << comma.get () << arg.get_name ();
|
||||
out << ");" << endl;
|
||||
|
||||
if (f.get_realign () && unaligned == 1)
|
||||
out << " __asm__ __volatile__ (\"addq $8,%%rsp\":::\"cc\");"
|
||||
<< endl;
|
||||
|
||||
out << " check_results (ret);" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close the last function and define the main do_tests function. */
|
||||
out << "}" << endl << endl;
|
||||
|
||||
/* Define _noinfo pointers to each do_tests_* function. */
|
||||
for (auto const &fn_name : do_tests_fn_names)
|
||||
out << " static void (*volatile " << fn_name << "_noinfo) ("
|
||||
<< param_types_str << ") = " << fn_name << ";" << endl;
|
||||
|
||||
/* Define main do_tests () function. */
|
||||
out << endl
|
||||
<< "void do_tests ()" << endl
|
||||
<< "{" << endl;
|
||||
i = 1;
|
||||
for (auto const &arg : args)
|
||||
{
|
||||
out << " " << arg.get_type () << " " << arg.get_name () << " = " << i
|
||||
<< ";" << endl;
|
||||
i <<= 1;
|
||||
}
|
||||
out << endl;
|
||||
|
||||
/* Call do_tests_*_noinfo functions. */
|
||||
for (auto const &fn_name : do_tests_fn_names)
|
||||
out << " " << fn_name << "_noinfo (" << param_names_str << ");" << endl;
|
||||
out << "}" << endl << endl;
|
||||
}
|
||||
|
||||
/* Generate output file. */
|
||||
void generate_header (const string &args)
|
||||
{
|
||||
vector<class arg> all_args;
|
||||
vector<vector<class arg> > arg_sets;
|
||||
|
||||
ofstream out;
|
||||
out.exceptions (ios::failbit | ios::badbit);
|
||||
out.open (out_file_name);
|
||||
out << "/* Generated with " << args << " */" << endl << endl;
|
||||
|
||||
assert (extra_params_max < 26);
|
||||
|
||||
/* Build the extra argument array. */
|
||||
for (unsigned int i = 0; i < extra_params_max; ++i)
|
||||
{
|
||||
char name[2] = "a";
|
||||
name[0] += i;
|
||||
class arg myarg (name, "long", true);
|
||||
|
||||
all_args.push_back (myarg);
|
||||
}
|
||||
|
||||
arg_sets.resize (extra_params_max - extra_params_min + 1);
|
||||
for (unsigned int i = 0; i < arg_sets.size (); ++i)
|
||||
arg_sets[i].insert (arg_sets[i].end(), all_args.begin(),
|
||||
all_args.begin () + i + extra_params_min);
|
||||
|
||||
/* Print sysv functions */
|
||||
for (const vector<class arg> &as : arg_sets)
|
||||
{
|
||||
const int alloca_max = !!(fn_variant_mask & FN_VAR_MSABI);
|
||||
const int varargs_max = !!(fn_variant_mask & FN_VAR_VARARGS);
|
||||
fn *fn;
|
||||
for (int _alloca = 0; _alloca <= alloca_max; ++_alloca)
|
||||
for (int varargs = 0; varargs <= varargs_max; ++varargs)
|
||||
{
|
||||
try {
|
||||
int var = (_alloca ? FN_VAR_ALLOCA : 0)
|
||||
| (varargs ? FN_VAR_VARARGS : 0);
|
||||
fn = new ::fn (as, 0, var);
|
||||
} catch (invalid_argument) {
|
||||
continue;
|
||||
}
|
||||
sysv_funcs.push_back (fn);
|
||||
fn->print_def (out);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print _noinfo function pointers for sysv functions. */
|
||||
for (const fn *f : sysv_funcs)
|
||||
f->print_noinfo_def (out);
|
||||
|
||||
/* Print ms_abi functions. */
|
||||
unsigned int var;
|
||||
for (var = 0; var <= FN_VAR_MASK; ++var)
|
||||
{
|
||||
/* We only want ms_abi fns for this. */
|
||||
if (! (var & FN_VAR_MSABI))
|
||||
continue;
|
||||
|
||||
/* */
|
||||
if ((var & fn_variant_mask) != var)
|
||||
continue;
|
||||
|
||||
unsigned clobbers;
|
||||
for (clobbers = 0; clobbers <= OPTIONAL_REG_ALL; ++clobbers)
|
||||
{
|
||||
/* Skip clobbers that would be invalid. */
|
||||
if (clobbers & OPTIONAL_REG_RBP)
|
||||
{
|
||||
/* Whole program built with hard frame pointer. */
|
||||
if (omit_rbp_clobbers)
|
||||
continue;
|
||||
|
||||
/* Uses BP explicitly. */
|
||||
if (var & FN_VAR_HFP_OR_REALIGN)
|
||||
continue;
|
||||
|
||||
/* Alloca seems to require DRAP, which uses BP. */
|
||||
if (var & FN_VAR_ALLOCA)
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto const &as : arg_sets)
|
||||
{
|
||||
fn *fn;
|
||||
try {
|
||||
fn = new ::fn (as, clobbers, var);
|
||||
} catch (invalid_argument) {
|
||||
continue;
|
||||
}
|
||||
|
||||
msabi_funcs.push_back (fn);
|
||||
fn->print_def (out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << endl;
|
||||
make_do_tests_decl (all_args, out);
|
||||
out << endl;
|
||||
|
||||
make_do_test (all_args, msabi_funcs, out);
|
||||
out.close ();
|
||||
}
|
||||
|
||||
/* Parse a string into a long and return true upon success. */
|
||||
static bool long_optarg (const char *optarg, long &dest)
|
||||
{
|
||||
char *end;
|
||||
|
||||
errno = 0;
|
||||
dest = strtol(optarg, &end, 0);
|
||||
if (errno)
|
||||
cerr << strerror(errno) << endl;
|
||||
|
||||
while (isspace(*end))
|
||||
++end;
|
||||
|
||||
/* Error if errno non-zero or junk at end of string. */
|
||||
return errno || *end;
|
||||
}
|
||||
|
||||
void usage ()
|
||||
{
|
||||
cerr
|
||||
<< "Usage: " << argv0 << " [options] <output_file>" << endl
|
||||
<< endl
|
||||
<< " -p <n|n-n>, --max-extra-params <expr>" << endl
|
||||
<< " A single or range of extra parameters" << endl
|
||||
<< " Examples:" << endl
|
||||
<< " -p0-5" << endl
|
||||
<< " -p12" << endl
|
||||
<< endl
|
||||
<< " -v <n>, --variant-mask <n>" << endl
|
||||
<< " Set mask of test variants (see enum fn_variants for values," << endl
|
||||
<< " defaults to 0x" << hex << FN_VAR_MASK << " [FN_VAR_MASK])" << endl
|
||||
<< endl
|
||||
<< " -0, --omit-rbp-clobbers" << endl
|
||||
<< " Omit tests that clobber RBP." << endl;
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
/* Parse string representing a number range or a list of numbers. */
|
||||
void set_extra_param_counts (const char *str)
|
||||
{
|
||||
char copy[0x40];
|
||||
char *max_str;
|
||||
bool bad = false;
|
||||
long int min, max;
|
||||
|
||||
strncpy (copy, str, sizeof (copy) - 1);
|
||||
max_str = strchr(copy, '-');
|
||||
if (max_str)
|
||||
*max_str++ = 0;
|
||||
|
||||
bad = long_optarg (copy, min);
|
||||
if (max_str)
|
||||
bad = bad || long_optarg (max_str, max);
|
||||
else
|
||||
max = min;
|
||||
|
||||
if (min > max)
|
||||
usage ();
|
||||
|
||||
extra_params_min = min;
|
||||
extra_params_max = max;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
argv0 = argv[0];
|
||||
const char *short_options = "p:v:0";
|
||||
const struct option long_options[] = {
|
||||
{"extra-params", required_argument, 0, 'p'},
|
||||
{"variant-mask", required_argument, 0, 'v'},
|
||||
{"omit-rbp-clobbers", no_argument, 0, '0'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
int c;
|
||||
while ((c = getopt_long (argc, argv, short_options, long_options,
|
||||
&option_index)) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
long l;
|
||||
|
||||
case 'p':
|
||||
set_extra_param_counts (optarg);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
if (long_optarg (optarg, l) || (l & ~FN_VAR_MASK))
|
||||
{
|
||||
cerr << "ERROR: Bad value for -v: `" << optarg << "`" << endl;
|
||||
usage ();
|
||||
}
|
||||
fn_variant_mask = (unsigned)l;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
omit_rbp_clobbers = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
usage ();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 1)
|
||||
usage ();
|
||||
out_file_name = argv[optind];
|
||||
|
||||
/* Can't skip msabi funcions. */
|
||||
fn_variant_mask |= FN_VAR_MSABI;
|
||||
|
||||
/* If whole program has HFP, explicit tests that enable it are redundant. */
|
||||
if (omit_rbp_clobbers)
|
||||
fn_variant_mask &= ~FN_VAR_HFP;
|
||||
|
||||
stringstream argv_str;
|
||||
|
||||
for (int i = 0; i < argc; ++i)
|
||||
argv_str << (i ? " " : "") << argv[i];
|
||||
|
||||
int ret = 0;
|
||||
try
|
||||
{
|
||||
generate_header (argv_str.str());
|
||||
}
|
||||
catch (exception &e)
|
||||
{
|
||||
cerr << "ERROR: While writing `" << out_file_name << "': "
|
||||
<< strerror(errno) << endl;
|
||||
ret = 1;
|
||||
}
|
||||
for_each (sysv_funcs.begin (), sysv_funcs.end (), default_delete<fn> ());
|
||||
for_each (msabi_funcs.begin (), msabi_funcs.end (), default_delete<fn> ());
|
||||
|
||||
return ret;
|
||||
}
|
373
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
Normal file
373
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/* Test program for 64-Bit Microsoft to System V function calls.
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
/* This is a single-threaded test program for Microsoft 64-bit ABI functions.
|
||||
It is aimed at verifying correctness of pro/epilogues of ms_abi functions
|
||||
that call sysv_abi functions to assure clobbered registers are properly
|
||||
saved and restored and attempt to detect any flaws in the behavior of these
|
||||
functions. The following variants are tested:
|
||||
|
||||
* Either uses hard frame pointer, re-aligns the stack or neither,
|
||||
* Uses alloca (and thus DRAP) or not,
|
||||
* Uses sibling call optimization or not,
|
||||
* Uses variable argument list or not, and
|
||||
* Has shrink-wrapped code or not.
|
||||
|
||||
In addition, an ms_abi function is generated for each of these combinations
|
||||
clobbering each unique combination additional registers (excluding BP when
|
||||
a frame pointer is used). Shrink-wrap variants are called in a way that
|
||||
both the fast and slow path are used. Re-aligned variants are called with
|
||||
an aligned and mis-aligned stack.
|
||||
|
||||
Each ms_abi function is called via an assembly stub that first saves all
|
||||
volatile registers and fills them with random values. The ms_abi function
|
||||
is then called. After the function returns, the value of all volatile
|
||||
registers is verified against the random data and then restored. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <alloca.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef __x86_64__
|
||||
# error Test only valid on x86_64
|
||||
#endif
|
||||
|
||||
enum reg_data_sets
|
||||
{
|
||||
REG_SET_SAVE,
|
||||
REG_SET_INPUT,
|
||||
REG_SET_OUTPUT,
|
||||
|
||||
REG_SET_COUNT
|
||||
};
|
||||
|
||||
enum flags
|
||||
{
|
||||
FLAG_ALLOCA = 0x01000000,
|
||||
FLAG_SIBCALL = 0x02000000,
|
||||
FLAG_SHRINK_WRAP_FAST_PATH = 0x08000000,
|
||||
FLAG_SHRINK_WRAP_SLOW_PATH = 0x0c000000,
|
||||
};
|
||||
|
||||
enum alignment_option
|
||||
{
|
||||
ALIGNMENT_NOT_TESTED,
|
||||
ALIGNMENT_ALIGNED,
|
||||
ALIGNMENT_MISALIGNED,
|
||||
|
||||
ALIGNMENT_COUNT,
|
||||
};
|
||||
|
||||
enum shrink_wrap_option
|
||||
{
|
||||
SHRINK_WRAP_NONE,
|
||||
SHRINK_WRAP_FAST_PATH,
|
||||
SHRINK_WRAP_SLOW_PATH,
|
||||
|
||||
SHRINK_WRAP_COUNT
|
||||
};
|
||||
|
||||
union regdata {
|
||||
struct {
|
||||
__uint128_t sseregs[10];
|
||||
union {
|
||||
uint64_t intregs[8];
|
||||
struct {
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
uint64_t rbx;
|
||||
uint64_t rbp;
|
||||
uint64_t r12;
|
||||
uint64_t r13;
|
||||
uint64_t r14;
|
||||
uint64_t r15;
|
||||
};
|
||||
};
|
||||
};
|
||||
uint32_t u32_arr[56];
|
||||
} __attribute__((aligned (16)));
|
||||
|
||||
struct test_data
|
||||
{
|
||||
union regdata regdata[REG_SET_COUNT];
|
||||
void *fn;
|
||||
void *retaddr;
|
||||
const char *name;
|
||||
enum alignment_option alignment;
|
||||
enum shrink_wrap_option shrink_wrap;
|
||||
long ret_expected;
|
||||
} test_data;
|
||||
|
||||
static int shrink_wrap_global;
|
||||
static void __attribute((sysv_abi)) do_tests ();
|
||||
static void init_test (void *fn, const char *name,
|
||||
enum alignment_option alignment,
|
||||
enum shrink_wrap_option shrink_wrap, long ret_expected);
|
||||
static void check_results (long ret);
|
||||
static __attribute__((ms_abi)) long do_sibcall (long arg);
|
||||
static __attribute__((ms_abi)) long
|
||||
(*const volatile do_sibcall_noinfo) (long) = do_sibcall;
|
||||
|
||||
/* Defines do_tests (). */
|
||||
#include "ms-sysv-generated.h"
|
||||
|
||||
static int arbitrarily_fail;
|
||||
static const char *argv0;
|
||||
|
||||
static void __attribute__((noinline))
|
||||
init_test (void *fn, const char *name, enum alignment_option alignment,
|
||||
enum shrink_wrap_option shrink_wrap, long ret_expected)
|
||||
{
|
||||
int i;
|
||||
union regdata *data = &test_data.regdata[REG_SET_INPUT];
|
||||
|
||||
assert (alignment < ALIGNMENT_COUNT);
|
||||
assert (shrink_wrap < SHRINK_WRAP_COUNT);
|
||||
|
||||
memset (&test_data, 0, sizeof (test_data));
|
||||
for (i = 55; i >= 0; --i)
|
||||
data->u32_arr[i] = (uint32_t)lrand48 ();
|
||||
test_data.fn = fn;
|
||||
test_data.name = name;
|
||||
test_data.alignment = alignment;
|
||||
test_data.shrink_wrap = shrink_wrap;
|
||||
test_data.ret_expected = ret_expected;
|
||||
|
||||
switch (shrink_wrap)
|
||||
{
|
||||
case SHRINK_WRAP_NONE:
|
||||
case SHRINK_WRAP_COUNT:
|
||||
break;
|
||||
case SHRINK_WRAP_FAST_PATH:
|
||||
shrink_wrap_global = FLAG_SHRINK_WRAP_FAST_PATH;
|
||||
break;
|
||||
case SHRINK_WRAP_SLOW_PATH:
|
||||
shrink_wrap_global = FLAG_SHRINK_WRAP_SLOW_PATH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *alignment_str[ALIGNMENT_COUNT] =
|
||||
{
|
||||
"", "aligned", "misaligned"
|
||||
};
|
||||
|
||||
static const char *shrink_wrap_str[SHRINK_WRAP_COUNT] =
|
||||
{
|
||||
"", "shrink-wrap fast path", "shrink-wrap slow path"
|
||||
};
|
||||
|
||||
static const char *test_descr ()
|
||||
{
|
||||
static char buffer[0x400];
|
||||
|
||||
if (test_data.alignment || test_data.shrink_wrap)
|
||||
snprintf (buffer, sizeof (buffer) - 1, "`%s' (%s%s%s)",
|
||||
test_data.name,
|
||||
alignment_str[test_data.alignment],
|
||||
(test_data.alignment && test_data.shrink_wrap ? ", " : ""),
|
||||
shrink_wrap_str[test_data.shrink_wrap]);
|
||||
else
|
||||
snprintf (buffer, sizeof (buffer) - 1, "`%s'", test_data.name);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static const char *regnames[] = {
|
||||
"XMM6",
|
||||
"XMM7",
|
||||
"XMM8",
|
||||
"XMM9",
|
||||
"XMM10",
|
||||
"XMM11",
|
||||
"XMM12",
|
||||
"XMM13",
|
||||
"XMM14",
|
||||
"XMM15",
|
||||
"RSI",
|
||||
"RDI",
|
||||
"RBX",
|
||||
"RBP",
|
||||
"R12",
|
||||
"R13",
|
||||
"R14",
|
||||
"R15",
|
||||
};
|
||||
|
||||
static void print_header (int *header_printed)
|
||||
{
|
||||
if (!*header_printed)
|
||||
fprintf (stderr, " %-35s %-35s\n", "Expected", "Got");
|
||||
*header_printed = 1;
|
||||
}
|
||||
|
||||
static int compare_reg128 (const __uint128_t *a, const __uint128_t *b,
|
||||
const char *name, int *header_printed)
|
||||
{
|
||||
if (!memcmp (a, b, sizeof (*a)))
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
long ha = *((long*)a);
|
||||
long la = *((long*)a + 16);
|
||||
long hb = *((long*)b);
|
||||
long lb = *((long*)a + 16);
|
||||
print_header (header_printed);
|
||||
fprintf (stderr, "%-5s: 0x%016lx %016lx != 0x%016lx %016lx\n",
|
||||
name, ha, la, hb, lb);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_reg64 (long a, long b, const char *name,
|
||||
int *header_printed)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
print_header (header_printed);
|
||||
fprintf (stderr, "%s: 0x%016lx != 0x%016lx\n", name, a, b);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void __attribute__((noinline)) check_results (long ret)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned bad = 0;
|
||||
int header_printed = 0;
|
||||
|
||||
union regdata *a = &test_data.regdata[REG_SET_INPUT];
|
||||
union regdata *b = &test_data.regdata[REG_SET_OUTPUT];
|
||||
|
||||
a = __builtin_assume_aligned(a, 16);
|
||||
b = __builtin_assume_aligned(b, 16);
|
||||
|
||||
if (arbitrarily_fail) {
|
||||
uint64_t u64 = lrand48 ();
|
||||
if (u64 % 100 == 0)
|
||||
b->u32_arr[u64 % 56] = 0xfdfdfdfd;
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; ++i)
|
||||
bad |= compare_reg128 (&a->sseregs[i], &b->sseregs[i], regnames[i],
|
||||
&header_printed);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
bad |= compare_reg64 (a->intregs[i], b->intregs[i], regnames[i + 10],
|
||||
&header_printed);
|
||||
|
||||
if (ret != test_data.ret_expected)
|
||||
{
|
||||
fprintf (stderr, "Wrong return value: got 0x%016lx, expected 0x%016lx\n",
|
||||
ret, test_data.ret_expected);
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
if (bad)
|
||||
{
|
||||
fprintf (stderr, "Failed on test function %s\n", test_descr ());
|
||||
raise (SIGTRAP);
|
||||
exit (-1);
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((ms_abi, noinline)) long do_sibcall (long arg) {
|
||||
return arg + FLAG_SIBCALL;
|
||||
}
|
||||
|
||||
void usage ()
|
||||
{
|
||||
fprintf (stderr, "Usage: %s [-s <seed>] [-f]\n", argv0);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
static long long_optarg (const char *optarg, const char *optstr)
|
||||
{
|
||||
char *end;
|
||||
long ret;
|
||||
|
||||
errno = 0;
|
||||
ret = strtol(optarg, &end, 0);
|
||||
|
||||
while (isspace (*end))
|
||||
++end;
|
||||
|
||||
if (errno || *end)
|
||||
{
|
||||
fprintf (stderr, "ERROR: Bad value for %s: `%s`\n", optstr, optarg);
|
||||
if (errno)
|
||||
fprintf (stderr, "%s\n", strerror (errno));
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
long seed = 0;
|
||||
int c;
|
||||
argv0 = argv[0];
|
||||
|
||||
assert (!((long)&test_data.regdata[REG_SET_SAVE] & 15));
|
||||
assert (!((long)&test_data.regdata[REG_SET_INPUT] & 15));
|
||||
assert (!((long)&test_data.regdata[REG_SET_OUTPUT] & 15));
|
||||
|
||||
while ((c = getopt (argc, argv, "s:f")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 's':
|
||||
seed = long_optarg (optarg, "-s");
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
arbitrarily_fail = 1;
|
||||
fprintf (stderr, "NOTE: Aribrary failure enabled (-f).\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
srand48 (seed);
|
||||
do_tests ();
|
||||
|
||||
/* Just in case we don't have enough tests to randomly trigger the
|
||||
failure. */
|
||||
if (arbitrarily_fail)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
178
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
Normal file
178
gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
Normal file
|
@ -0,0 +1,178 @@
|
|||
# Tests for ms_abi to sysv_abi calls.
|
||||
# Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
# Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Exit immediately if this isn't a native x86_64 target.
|
||||
if { (![istarget x86_64-*-*] && ![istarget i?86-*-*])
|
||||
|| ![is-effective-target lp64] || ![isnative] } then {
|
||||
unsupported "$subdir"
|
||||
return
|
||||
}
|
||||
|
||||
global GCC_RUNTEST_PARALLELIZE_DIR
|
||||
|
||||
load_lib gcc-dg.exp
|
||||
|
||||
proc runtest_ms_sysv { cflags generator_args } {
|
||||
global GCC_UNDER_TEST HOSTCXX HOSTCXXFLAGS tmpdir srcdir subdir \
|
||||
parallel_dir next_test
|
||||
|
||||
set objdir "$tmpdir/ms-sysv"
|
||||
set generator "$tmpdir/ms-sysv-generate.exe"
|
||||
set generated_header "$objdir/ms-sysv-generated.h"
|
||||
set do_test_o "$objdir/do-test.o"
|
||||
set ms_sysv_o "$objdir/ms-sysv.o"
|
||||
set ms_sysv_exe "$objdir/ms-sysv.exe"
|
||||
set status 0
|
||||
set warn_flags "-Wall"
|
||||
set this_test $next_test
|
||||
incr next_test
|
||||
|
||||
# Do parallelization here
|
||||
if [catch {set fd [open "$parallel_dir/$this_test" \
|
||||
[list RDWR CREAT EXCL]]} ] {
|
||||
if { [lindex $::errorCode 1] eq "EEXIST" } then {
|
||||
# Another job is running this test
|
||||
return
|
||||
} else {
|
||||
error "Failed to open $parallel_dir/$this_test: $::errorCode"
|
||||
set status 1
|
||||
}
|
||||
} else {
|
||||
close $fd
|
||||
}
|
||||
|
||||
# Detect when hard frame pointers are enabled (or required) so we know not
|
||||
# to generate bp clobbers.
|
||||
if [regexp "^(.+ +| *)-(O0|fno-omit-frame-pointer|p|pg)( +.*)?$" \
|
||||
$cflags match] then {
|
||||
set generator_args "$generator_args --omit-rbp-clobbers"
|
||||
}
|
||||
|
||||
set descr "$subdir CFLAGS=\"$cflags\" generator_args=\"$generator_args\""
|
||||
verbose "$tmpdir: Running test $descr" 1
|
||||
|
||||
# Cleanup any previous test in objdir
|
||||
file delete -force $objdir
|
||||
file mkdir $objdir
|
||||
|
||||
# Build the generator (only needs to be done once).
|
||||
set src "$srcdir/$subdir/gen.cc"
|
||||
if { $status == 0 } then {
|
||||
if { (![file exists "$generator"]) || ([file mtime "$generator"]
|
||||
< [file mtime "$src"]) } {
|
||||
# Temporarily switch to the environment for the host compiler.
|
||||
restore_ld_library_path_env_vars
|
||||
set cxx "$HOSTCXX $HOSTCXXFLAGS $warn_flags -std=c++11"
|
||||
set status [remote_exec host "$cxx -o $generator $src"]
|
||||
set status [lindex $status 0]
|
||||
set_ld_library_path_env_vars
|
||||
if { $status != 0 } then {
|
||||
warning "Could not build $subdir generator"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate header
|
||||
if { $status == 0 } then {
|
||||
set status [remote_exec host "$generator $generator_args $generated_header"]
|
||||
set status [lindex $status 0]
|
||||
if { $status != 0 } then {
|
||||
warning "Could not generate $generated_header"
|
||||
}
|
||||
}
|
||||
|
||||
set cc "$GCC_UNDER_TEST -I$objdir -I$srcdir/$subdir $cflags $warn_flags"
|
||||
|
||||
# Assemble do-test.S
|
||||
set src "$srcdir/$subdir/do-test.S"
|
||||
if { $status == 0 } then {
|
||||
set status [remote_exec build "$cc -c -o $do_test_o $src"]
|
||||
set status [lindex $status 0]
|
||||
if { $status != 0 } then {
|
||||
warning "Could not assemble $src"
|
||||
}
|
||||
}
|
||||
|
||||
# Build ms-sysv.c
|
||||
set src "$srcdir/$subdir/ms-sysv.c"
|
||||
if { $status == 0 } then {
|
||||
set status [remote_exec build "$cc -c -o $ms_sysv_o $src" "" "" "" 1200]
|
||||
set status [lindex $status 0]
|
||||
if { $status != 0 } then {
|
||||
warning "Could not build $src."
|
||||
}
|
||||
}
|
||||
|
||||
# Link
|
||||
if { $status == 0 } then {
|
||||
set status [remote_exec build "$cc -o $ms_sysv_exe $ms_sysv_o $do_test_o"]
|
||||
set status [lindex $status 0]
|
||||
if { $status != 0 } then {
|
||||
warning "Link failed."
|
||||
}
|
||||
}
|
||||
|
||||
# Execute
|
||||
if { $status == 0 } then {
|
||||
set status [remote_exec build "$ms_sysv_exe"]
|
||||
set status [lindex $status 0]
|
||||
}
|
||||
|
||||
if { $status != 0 } then {
|
||||
fail $descr
|
||||
} else {
|
||||
pass $descr
|
||||
}
|
||||
}
|
||||
|
||||
dg-init
|
||||
|
||||
# Setup parallelization
|
||||
set next_test 0
|
||||
set parallel_dir "$env(GCC_RUNTEST_PARALLELIZE_DIR)/abi-ms-sysv"
|
||||
file mkdir "$env(GCC_RUNTEST_PARALLELIZE_DIR)"
|
||||
file mkdir "$parallel_dir"
|
||||
|
||||
if { ![file isdirectory "$parallel_dir"] } then {
|
||||
error "Failed to create directory $parallel_dir: $::errorCode"
|
||||
return
|
||||
}
|
||||
|
||||
set gen_opts "-p0-5"
|
||||
set all_options [list "-O2" "-O0 -g3"]
|
||||
|
||||
# Run without -mcall-ms2sysv-xlogues always
|
||||
foreach opt $all_options {
|
||||
runtest_ms_sysv "$opt" "$gen_opts"
|
||||
}
|
||||
|
||||
# Skip -mcall-ms2sysv-xlogues on Windows (not supported)
|
||||
if { ![istarget *-*-cygwin*] && ![istarget *-*-mingw*] } {
|
||||
foreach opt $all_options {
|
||||
runtest_ms_sysv "-mcall-ms2sysv-xlogues $opt" "$gen_opts"
|
||||
}
|
||||
}
|
||||
|
||||
dg-finish
|
|
@ -1,3 +1,15 @@
|
|||
2017-05-14 Daniel Santos <daniel.santos@pobox.com>
|
||||
|
||||
* config.host: Add i386/t-msabi to i386/t-linux file list.
|
||||
* config/i386/i386-asm.h: New file.
|
||||
* config/i386/resms64.S: New file.
|
||||
* config/i386/resms64f.S: New file.
|
||||
* config/i386/resms64fx.S: New file.
|
||||
* config/i386/resms64x.S: New file.
|
||||
* config/i386/savms64.S: New file.
|
||||
* config/i386/savms64f.S: New file.
|
||||
* config/i386/t-msabi: New file.
|
||||
|
||||
2017-05-09 Andreas Tobler <andreast@gcc.gnu.org>
|
||||
|
||||
* config.host: Use the generic FreeBSD t-slibgcc-elf-ver for
|
||||
|
@ -9,7 +21,7 @@
|
|||
pc-relative indirect handling for fuchsia.
|
||||
* config/t-slibgcc-fuchsia: New file.
|
||||
* config.host (*-*-fuchsia*, aarch64*-*-fuchsia*, arm*-*-fuchsia*,
|
||||
x86_64-*-fuchsia*): Add definitions.
|
||||
x86_64-*-fuchsia*): Add definitions.
|
||||
|
||||
2017-04-19 Martin Liska <mliska@suse.cz>
|
||||
|
||||
|
|
|
@ -1368,7 +1368,7 @@ case ${host} in
|
|||
i[34567]86-*-linux* | x86_64-*-linux* | \
|
||||
i[34567]86-*-kfreebsd*-gnu | x86_64-*-kfreebsd*-gnu | \
|
||||
i[34567]86-*-gnu*)
|
||||
tmake_file="${tmake_file} t-tls i386/t-linux t-slibgcc-libgcc"
|
||||
tmake_file="${tmake_file} t-tls i386/t-linux i386/t-msabi t-slibgcc-libgcc"
|
||||
if test "$libgcc_cv_cfi" = "yes"; then
|
||||
tmake_file="${tmake_file} t-stack i386/t-stack-i386"
|
||||
fi
|
||||
|
|
82
libgcc/config/i386/i386-asm.h
Normal file
82
libgcc/config/i386/i386-asm.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* Defines common perprocessor and assembly macros for use by various stubs.
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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 I386_ASM_H
|
||||
#define I386_ASM_H
|
||||
|
||||
#ifdef __ELF__
|
||||
# define ELFFN(fn) .type fn,@function
|
||||
#else
|
||||
# define ELFFN(fn)
|
||||
#endif
|
||||
|
||||
#define FUNC_START(fn) \
|
||||
.global fn; \
|
||||
ELFFN (fn); \
|
||||
fn:
|
||||
|
||||
#define HIDDEN_FUNC(fn)\
|
||||
FUNC_START (fn) \
|
||||
.hidden fn; \
|
||||
|
||||
#define FUNC_END(fn) .size fn,.-fn
|
||||
|
||||
#ifdef __SSE2__
|
||||
# ifdef __AVX__
|
||||
# define MOVAPS vmovaps
|
||||
# else
|
||||
# define MOVAPS movaps
|
||||
# endif
|
||||
|
||||
/* Save SSE registers 6-15. off is the offset of rax to get to xmm6. */
|
||||
.macro SSE_SAVE off=0
|
||||
MOVAPS %xmm15,(\off - 0x90)(%rax)
|
||||
MOVAPS %xmm14,(\off - 0x80)(%rax)
|
||||
MOVAPS %xmm13,(\off - 0x70)(%rax)
|
||||
MOVAPS %xmm12,(\off - 0x60)(%rax)
|
||||
MOVAPS %xmm11,(\off - 0x50)(%rax)
|
||||
MOVAPS %xmm10,(\off - 0x40)(%rax)
|
||||
MOVAPS %xmm9, (\off - 0x30)(%rax)
|
||||
MOVAPS %xmm8, (\off - 0x20)(%rax)
|
||||
MOVAPS %xmm7, (\off - 0x10)(%rax)
|
||||
MOVAPS %xmm6, \off(%rax)
|
||||
.endm
|
||||
|
||||
/* Restore SSE registers 6-15. off is the offset of rsi to get to xmm6. */
|
||||
.macro SSE_RESTORE off=0
|
||||
MOVAPS (\off - 0x90)(%rsi), %xmm15
|
||||
MOVAPS (\off - 0x80)(%rsi), %xmm14
|
||||
MOVAPS (\off - 0x70)(%rsi), %xmm13
|
||||
MOVAPS (\off - 0x60)(%rsi), %xmm12
|
||||
MOVAPS (\off - 0x50)(%rsi), %xmm11
|
||||
MOVAPS (\off - 0x40)(%rsi), %xmm10
|
||||
MOVAPS (\off - 0x30)(%rsi), %xmm9
|
||||
MOVAPS (\off - 0x20)(%rsi), %xmm8
|
||||
MOVAPS (\off - 0x10)(%rsi), %xmm7
|
||||
MOVAPS \off(%rsi), %xmm6
|
||||
.endm
|
||||
|
||||
#endif /* __SSE2__ */
|
||||
#endif /* I386_ASM_H */
|
57
libgcc/config/i386/resms64.S
Normal file
57
libgcc/config/i386/resms64.S
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* Epilogue stub for 64-bit ms/sysv clobbers: restore
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Epilogue routine for restoring 64-bit ms/sysv registers. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__resms64_18)
|
||||
mov -0x70(%rsi),%r15
|
||||
HIDDEN_FUNC(__resms64_17)
|
||||
mov -0x68(%rsi),%r14
|
||||
HIDDEN_FUNC(__resms64_16)
|
||||
mov -0x60(%rsi),%r13
|
||||
HIDDEN_FUNC(__resms64_15)
|
||||
mov -0x58(%rsi),%r12
|
||||
HIDDEN_FUNC(__resms64_14)
|
||||
mov -0x50(%rsi),%rbp
|
||||
HIDDEN_FUNC(__resms64_13)
|
||||
mov -0x48(%rsi),%rbx
|
||||
HIDDEN_FUNC(__resms64_12)
|
||||
mov -0x40(%rsi),%rdi
|
||||
SSE_RESTORE off=0x60
|
||||
mov -0x38(%rsi),%rsi
|
||||
ret
|
||||
FUNC_END(__resms64_12)
|
||||
FUNC_END(__resms64_13)
|
||||
FUNC_END(__resms64_14)
|
||||
FUNC_END(__resms64_15)
|
||||
FUNC_END(__resms64_16)
|
||||
FUNC_END(__resms64_17)
|
||||
FUNC_END(__resms64_18)
|
||||
|
||||
#endif /* __x86_64__ */
|
55
libgcc/config/i386/resms64f.S
Normal file
55
libgcc/config/i386/resms64f.S
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* Epilogue stub for 64-bit ms/sysv clobbers: restore (with hard frame pointer)
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Epilogue routine for restoring 64-bit ms/sysv registers when hard frame
|
||||
pointer is used. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__resms64f_17)
|
||||
mov -0x68(%rsi),%r15
|
||||
HIDDEN_FUNC(__resms64f_16)
|
||||
mov -0x60(%rsi),%r14
|
||||
HIDDEN_FUNC(__resms64f_15)
|
||||
mov -0x58(%rsi),%r13
|
||||
HIDDEN_FUNC(__resms64f_14)
|
||||
mov -0x50(%rsi),%r12
|
||||
HIDDEN_FUNC(__resms64f_13)
|
||||
mov -0x48(%rsi),%rbx
|
||||
HIDDEN_FUNC(__resms64f_12)
|
||||
mov -0x40(%rsi),%rdi
|
||||
SSE_RESTORE off=0x60
|
||||
mov -0x38(%rsi),%rsi
|
||||
ret
|
||||
FUNC_END(__resms64f_12)
|
||||
FUNC_END(__resms64f_13)
|
||||
FUNC_END(__resms64f_14)
|
||||
FUNC_END(__resms64f_15)
|
||||
FUNC_END(__resms64f_16)
|
||||
FUNC_END(__resms64f_17)
|
||||
|
||||
#endif /* __x86_64__ */
|
57
libgcc/config/i386/resms64fx.S
Normal file
57
libgcc/config/i386/resms64fx.S
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* Epilogue stub for 64-bit ms/sysv clobbers: restore, leave and return
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Epilogue routine for 64-bit ms/sysv registers when hard frame pointer
|
||||
* used -- restores registers, restores frame pointer and then returns
|
||||
* from the function. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__resms64fx_17)
|
||||
mov -0x68(%rsi),%r15
|
||||
HIDDEN_FUNC(__resms64fx_16)
|
||||
mov -0x60(%rsi),%r14
|
||||
HIDDEN_FUNC(__resms64fx_15)
|
||||
mov -0x58(%rsi),%r13
|
||||
HIDDEN_FUNC(__resms64fx_14)
|
||||
mov -0x50(%rsi),%r12
|
||||
HIDDEN_FUNC(__resms64fx_13)
|
||||
mov -0x48(%rsi),%rbx
|
||||
HIDDEN_FUNC(__resms64fx_12)
|
||||
mov -0x40(%rsi),%rdi
|
||||
SSE_RESTORE off=0x60
|
||||
mov -0x38(%rsi),%rsi
|
||||
leaveq
|
||||
ret
|
||||
FUNC_END(__resms64fx_12)
|
||||
FUNC_END(__resms64fx_13)
|
||||
FUNC_END(__resms64fx_14)
|
||||
FUNC_END(__resms64fx_15)
|
||||
FUNC_END(__resms64fx_16)
|
||||
FUNC_END(__resms64fx_17)
|
||||
|
||||
#endif /* __x86_64__ */
|
59
libgcc/config/i386/resms64x.S
Normal file
59
libgcc/config/i386/resms64x.S
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* Epilogue stub for 64-bit ms/sysv clobbers: restore and return
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Epilogue routine for restoring 64-bit ms/sysv registers and returning from
|
||||
* function. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__resms64x_18)
|
||||
mov -0x70(%rsi),%r15
|
||||
HIDDEN_FUNC(__resms64x_17)
|
||||
mov -0x68(%rsi),%r14
|
||||
HIDDEN_FUNC(__resms64x_16)
|
||||
mov -0x60(%rsi),%r13
|
||||
HIDDEN_FUNC(__resms64x_15)
|
||||
mov -0x58(%rsi),%r12
|
||||
HIDDEN_FUNC(__resms64x_14)
|
||||
mov -0x50(%rsi),%rbp
|
||||
HIDDEN_FUNC(__resms64x_13)
|
||||
mov -0x48(%rsi),%rbx
|
||||
HIDDEN_FUNC(__resms64x_12)
|
||||
mov -0x40(%rsi),%rdi
|
||||
SSE_RESTORE off=0x60
|
||||
mov -0x38(%rsi),%rsi
|
||||
mov %r10,%rsp
|
||||
ret
|
||||
FUNC_END(__resms64x_12)
|
||||
FUNC_END(__resms64x_13)
|
||||
FUNC_END(__resms64x_14)
|
||||
FUNC_END(__resms64x_15)
|
||||
FUNC_END(__resms64x_16)
|
||||
FUNC_END(__resms64x_17)
|
||||
FUNC_END(__resms64x_18)
|
||||
|
||||
#endif /* __x86_64__ */
|
57
libgcc/config/i386/savms64.S
Normal file
57
libgcc/config/i386/savms64.S
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* Prologue stub for 64-bit ms/sysv clobbers: save
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Prologue routine for saving 64-bit ms/sysv registers. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__savms64_18)
|
||||
mov %r15,-0x70(%rax)
|
||||
HIDDEN_FUNC(__savms64_17)
|
||||
mov %r14,-0x68(%rax)
|
||||
HIDDEN_FUNC(__savms64_16)
|
||||
mov %r13,-0x60(%rax)
|
||||
HIDDEN_FUNC(__savms64_15)
|
||||
mov %r12,-0x58(%rax)
|
||||
HIDDEN_FUNC(__savms64_14)
|
||||
mov %rbp,-0x50(%rax)
|
||||
HIDDEN_FUNC(__savms64_13)
|
||||
mov %rbx,-0x48(%rax)
|
||||
HIDDEN_FUNC(__savms64_12)
|
||||
mov %rdi,-0x40(%rax)
|
||||
mov %rsi,-0x38(%rax)
|
||||
SSE_SAVE off=0x60
|
||||
ret
|
||||
FUNC_END(__savms64_12)
|
||||
FUNC_END(__savms64_13)
|
||||
FUNC_END(__savms64_14)
|
||||
FUNC_END(__savms64_15)
|
||||
FUNC_END(__savms64_16)
|
||||
FUNC_END(__savms64_17)
|
||||
FUNC_END(__savms64_18)
|
||||
|
||||
#endif /* __x86_64__ */
|
55
libgcc/config/i386/savms64f.S
Normal file
55
libgcc/config/i386/savms64f.S
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* Prologue stub for 64-bit ms/sysv clobbers: save (with hard frame pointer)
|
||||
Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||
Contributed by Daniel Santos <daniel.santos@pobox.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.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "i386-asm.h"
|
||||
|
||||
/* Prologue routine for saving 64-bit ms/sysv registers when realignment is
|
||||
* needed or hard frame pointer used. */
|
||||
|
||||
.text
|
||||
HIDDEN_FUNC(__savms64f_17)
|
||||
mov %r15,-0x68(%rax)
|
||||
HIDDEN_FUNC(__savms64f_16)
|
||||
mov %r14,-0x60(%rax)
|
||||
HIDDEN_FUNC(__savms64f_15)
|
||||
mov %r13,-0x58(%rax)
|
||||
HIDDEN_FUNC(__savms64f_14)
|
||||
mov %r12,-0x50(%rax)
|
||||
HIDDEN_FUNC(__savms64f_13)
|
||||
mov %rbx,-0x48(%rax)
|
||||
HIDDEN_FUNC(__savms64f_12)
|
||||
mov %rdi,-0x40(%rax)
|
||||
mov %rsi,-0x38(%rax)
|
||||
SSE_SAVE off=0x60
|
||||
ret
|
||||
FUNC_END(__savms64f_12)
|
||||
FUNC_END(__savms64f_13)
|
||||
FUNC_END(__savms64f_14)
|
||||
FUNC_END(__savms64f_15)
|
||||
FUNC_END(__savms64f_16)
|
||||
FUNC_END(__savms64f_17)
|
||||
|
||||
#endif /* __x86_64__ */
|
7
libgcc/config/i386/t-msabi
Normal file
7
libgcc/config/i386/t-msabi
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Makefile fragment to support -mcall-ms2sysv-xlogues
|
||||
LIB2ADD_ST += $(srcdir)/config/i386/savms64.S \
|
||||
$(srcdir)/config/i386/resms64.S \
|
||||
$(srcdir)/config/i386/resms64x.S \
|
||||
$(srcdir)/config/i386/savms64f.S \
|
||||
$(srcdir)/config/i386/resms64f.S \
|
||||
$(srcdir)/config/i386/resms64fx.S
|
Loading…
Add table
Reference in a new issue