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:
Daniel Santos 2017-05-14 12:22:08 +02:00 committed by Uros Bizjak
parent b9bdd60b87
commit d6d4d7701a
24 changed files with 3041 additions and 99 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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);
})

View File

@ -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")

View File

@ -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;

View File

@ -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

View File

@ -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.

View 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__ */

View 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;
}

View 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;
}

View 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

View File

@ -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>

View File

@ -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

View 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 */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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