summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulia Koval <julia.koval@intel.com>2015-09-11 08:31:51 +0300
committerJulia Koval <julia.koval@intel.com>2015-09-11 17:43:24 +0300
commitaaca35d5af5f5bab7b3d892739ab6d342e41389c (patch)
tree254af3ce8b56a07d06edf9047c17c87dc9d5ab8b
parent2100aaaa8173889930363cdc32275cb8a895d2a7 (diff)
downloadgcc-aaca35d5af5f5bab7b3d892739ab6d342e41389c.tar.gz
Interrupt
-rw-r--r--gcc/config/i386/i386-protos.h2
-rw-r--r--gcc/config/i386/i386.c167
-rw-r--r--gcc/config/i386/i386.md9
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-args-err.c9
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-iamcu.c33
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-redzone.c10
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-retval-err.c11
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-sibcall.c12
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt.c32
10 files changed, 283 insertions, 15 deletions
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef40a2e..cac267d9a9b 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -26,6 +26,8 @@ extern bool ix86_handle_option (struct gcc_options *opts,
/* Functions in i386.c */
extern bool ix86_target_stack_probe (void);
extern bool ix86_can_use_return_insn_p (void);
+extern bool ix86_is_interrupt_p (void);
+extern bool ix86_is_exception_p (void);
extern void ix86_setup_frame_addresses (void);
extern HOST_WIDE_INT ix86_initial_elimination_offset (int, int);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 7a97f3725d2..5c486f8b403 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -4503,16 +4503,6 @@ ix86_conditional_register_usage (void)
{
int i, c_mask;
- /* For 32-bit targets, squash the REX registers. */
- if (! TARGET_64BIT)
- {
- for (i = FIRST_REX_INT_REG; i <= LAST_REX_INT_REG; i++)
- fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
- for (i = FIRST_REX_SSE_REG; i <= LAST_REX_SSE_REG; i++)
- fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
- for (i = FIRST_EXT_REX_SSE_REG; i <= LAST_EXT_REX_SSE_REG; i++)
- fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
- }
/* See the definition of CALL_USED_REGISTERS in i386.h. */
c_mask = (TARGET_64BIT_MS_ABI ? (1 << 3)
@@ -4535,6 +4525,39 @@ ix86_conditional_register_usage (void)
SET_HARD_REG_BIT (reg_class_contents[(int)CLOBBERED_REGS], i);
}
+ if (current_function_decl && ix86_is_interrupt_p ())
+ {
+ for (i = AX_REG; i <= DI_REG; i++)
+ {
+ call_used_regs[i] = 0;
+ fixed_regs[i] = 0;
+ }
+ for (i = XMM0_REG; i <= XMM7_REG; i++)
+ {
+ call_used_regs[i] = 0;
+ fixed_regs[i] = 0;
+ }
+ for (i = R8_REG; i <FIRST_PSEUDO_REGISTER; i++)
+ {
+ call_used_regs[i] = 0;
+ fixed_regs[i] = 0;
+ }
+ }
+
+
+ /* For 32-bit targets, squash the REX registers. */
+ if (! TARGET_64BIT)
+ {
+ for (i = FIRST_REX_INT_REG; i <= LAST_REX_INT_REG; i++)
+ fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
+ for (i = FIRST_REX_SSE_REG; i <= LAST_REX_SSE_REG; i++)
+ fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
+ for (i = FIRST_EXT_REX_SSE_REG; i <= LAST_EXT_REX_SSE_REG; i++)
+ fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
+ }
+
+
+
/* If MMX is disabled, squash the registers. */
if (! TARGET_MMX)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
@@ -5562,6 +5585,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
tree type, decl_or_type;
rtx a, b;
+ if (ix86_is_interrupt_p ())
+ return false;
+
+
/* If we are generating position-independent code, we cannot sibcall
optimize direct calls to global functions, as the PLT requires
%ebx be live. (Darwin does not have a PLT.) */
@@ -6440,8 +6467,10 @@ ix86_call_abi_override (const_tree fndecl)
static void
ix86_maybe_switch_abi (void)
{
- if (TARGET_64BIT &&
- call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI))
+ if (call_used_regs[AX_REG] == ix86_is_interrupt_p ())
+ reinit_regs ();
+ else if ((TARGET_64BIT &&
+ call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI)))
reinit_regs ();
}
@@ -10135,7 +10164,7 @@ ix86_nsaved_sseregs (void)
int nregs = 0;
int regno;
- if (!TARGET_64BIT_MS_ABI)
+ if (!TARGET_64BIT_MS_ABI && !ix86_is_interrupt_p ())
return 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11466,6 +11495,14 @@ ix86_expand_prologue (void)
/* DRAP should not coexist with stack_realign_fp */
gcc_assert (!(crtl->drap_reg && stack_realign_fp));
+ if (ix86_is_interrupt_p () && ix86_using_red_zone ())
+ {
+ emit_insn (gen_adddi3 (
+ gen_rtx_REG (DImode, SP_REG),
+ gen_rtx_REG (DImode, SP_REG),
+ GEN_INT (-128)));
+ }
+
memset (&m->fs, 0, sizeof (m->fs));
/* Initialize CFA state for before the prologue. */
@@ -12437,7 +12474,29 @@ ix86_expand_epilogue (int style)
return;
}
- if (crtl->args.pops_args && crtl->args.size)
+ if (ix86_is_interrupt_p ())
+ {
+ if (ix86_using_red_zone ())
+ emit_insn (gen_adddi3 (
+ gen_rtx_REG (DImode, SP_REG),
+ gen_rtx_REG (DImode, SP_REG),
+ GEN_INT (128)));
+ if (ix86_is_exception_p ())
+ {
+ if (!TARGET_64BIT)
+ emit_insn (gen_addsi3 (
+ gen_rtx_REG (SImode, SP_REG),
+ gen_rtx_REG (SImode, SP_REG),
+ GEN_INT (POINTER_SIZE_UNITS)));
+ else
+ emit_insn (gen_adddi3 (
+ gen_rtx_REG (DImode, SP_REG),
+ gen_rtx_REG (DImode, SP_REG),
+ GEN_INT (POINTER_SIZE_UNITS)));
+ }
+ emit_jump_insn (gen_simple_return_interrupt ());
+ }
+ else if (crtl->args.pops_args && crtl->args.size)
{
rtx popc = GEN_INT (crtl->args.pops_args);
@@ -25752,12 +25811,23 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
}
}
+ if (ix86_is_interrupt_p ())
+ {
+ unsigned regno;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (!call_used_regs[regno] && !fixed_regs[regno] && !STACK_REGNO_P (regno) && !MMX_REGNO_P (regno))
+ {
+ clobber_reg(&use, gen_rtx_REG (GET_MODE(regno_reg_rtx[regno]), regno));
+ }
+ }
+ }
+
if (vec_len > 1)
call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (vec_len, vec));
call = emit_call_insn (call);
if (use)
CALL_INSN_FUNCTION_USAGE (call) = use;
-
return call;
}
@@ -42204,6 +42274,23 @@ ix86_hard_regno_mode_ok (int regno, machine_mode mode)
return false;
}
+bool
+ix86_is_interrupt_p ()
+{
+ return lookup_attribute ("interrupt",
+ DECL_ATTRIBUTES (current_function_decl)) ||
+ lookup_attribute ("exception",
+ DECL_ATTRIBUTES (current_function_decl));
+}
+
+bool
+ix86_is_exception_p ()
+{
+ return lookup_attribute ("exception",
+ DECL_ATTRIBUTES (current_function_decl));
+}
+
+
/* A subroutine of ix86_modes_tieable_p. Return true if MODE is a
tieable integer mode. */
@@ -43126,6 +43213,51 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
return NULL_TREE;
}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+
+ /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+ but the function type contains args and return type data. */
+ tree func_type = TREE_TYPE (*node);
+ tree first_arg_type = TYPE_ARG_TYPES (func_type);
+ tree return_type = TREE_TYPE (func_type);
+ if (first_arg_type && ! VOID_TYPE_P ( TREE_VALUE (first_arg_type)))
+ error ("Interrupt service routine can't have arguments");
+ if (! VOID_TYPE_P (return_type))
+ error ("Interrupt service routine can't have non-void return value");
+
+ return NULL_TREE;
+}
+
+static tree
+ix86_handle_exception_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+
+ tree func_type = TREE_TYPE (*node);
+ tree return_type = TREE_TYPE (func_type);
+ if (! VOID_TYPE_P (return_type))
+ error ("Interrupt service routine can't have non-void return value");
+
+ return NULL_TREE;
+}
+
+
static bool
ix86_ms_bitfield_layout_p (const_tree record_type)
{
@@ -47127,6 +47259,11 @@ static const struct attribute_spec ix86_attribute_table[] =
false },
{ "callee_pop_aggregate_return", 1, 1, false, true, true,
ix86_handle_callee_pop_aggregate_return, true },
+{ "interrupt", 0, 0, true, false, false, ix86_handle_interrupt_attribute,
+ false },
+ { "exception", 0, 0, true, false, false, ix86_handle_exception_attribute,
+ false },
+
/* End element. */
{ NULL, 0, 0, false, false, false, NULL, false }
};
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 7017913afe2..b7568ba0c22 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
UNSPEC_BNDCU
UNSPEC_BNDCN
UNSPEC_MPX_FENCE
+
+ ;; IRET support
+ UNSPEC_INTERRUPT_RETURN
])
(define_c_enum "unspecv" [
@@ -12094,6 +12097,12 @@
(set_attr "modrm" "0")
(set_attr "maybe_prefix_bnd" "1")])
+(define_insn "simple_return_interrupt"
+ [(simple_return)
+ (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+ ""
+ "iret")
+
;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
;; instruction Athlon and K8 have.
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-args-err.c b/gcc/testsuite/gcc.target/i386/interrupt-args-err.c
new file mode 100644
index 00000000000..1ea093e25a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-args-err.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+extern void foo (int,int) __attribute__ ((interrupt)); /* { dg-error "Interrupt service routine can't have arguments" } */
+extern void bar (int, int);
+
+void foo (int x, int y)
+{
+ bar (x, y);
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 00000000000..9c9d266cec2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=iamcu" } */
+
+extern void foo (void) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo ()
+{
+ int a,b,c,d,e,f,i;
+ a = bar(5);
+ b = bar(a);
+ c = bar(b);
+ d = bar(c);
+ e = bar(d);
+ f = bar(e);
+ for (i = 1; i < 10; i++)
+ {
+ a += bar (a + i) + bar(b + i) + bar(c + i) + bar(d + i) + bar(e + i) + bar(f+i);
+ }
+}
+/* { dg-final { scan-assembler "push.\t%.ax" } }*/
+/* { dg-final { scan-assembler "pop.\t%.ax" } }*/
+/* { dg-final { scan-assembler "push.\t%.dx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.dx" } }*/
+/* { dg-final { scan-assembler "push.\t%.cx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.cx" } }*/
+/* { dg-final { scan-assembler "push.\t%.bx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.bx" } }*/
+/* { dg-final { scan-assembler "push.\t%.si" } }*/
+/* { dg-final { scan-assembler "pop.\t%.si" } }*/
+/* { dg-final { scan-assembler "push.\t%.di" } }*/
+/* { dg-final { scan-assembler "pop.\t%.di" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone.c
new file mode 100644
index 00000000000..d59eb663966
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone.c
@@ -0,0 +1,10 @@
+/* { dg-do compile { target { ! { ia32 } } } } */
+/* { dg-options "-mabi=sysv" } */
+extern int a;
+void __attribute__((interrupt)) bar ()
+ {
+ a = 4;
+ }
+
+/* { dg-final { scan-assembler "addq\t\\\$-?128, %rsp" } } */
+/* { dg-final { scan-assembler "subq\t\\\$-?128, %rsp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-retval-err.c b/gcc/testsuite/gcc.target/i386/interrupt-retval-err.c
new file mode 100644
index 00000000000..7e8990c8c4d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-retval-err.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+
+extern int foo (void) __attribute__ ((interrupt)); /* { dg-error "Interrupt service routine can't have non-void return value" } */
+extern int bar (int, int);
+
+int foo (void)
+{
+ int x, y;
+ x = y = 1;
+ return bar (x, y);
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 00000000000..6d418bef59a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo ()
+{
+ bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 00000000000..33de0bc4186
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+extern void bar(int);
+
+void f1(){ bar(1); }
+__attribute__((interrupt))
+void f2(){ bar(2); }
+void f3(){ bar(3); }
+__attribute__((exception))
+void f4(){ bar(4); }
+void f5(){ bar(5); }
+
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt.c b/gcc/testsuite/gcc.target/i386/interrupt.c
new file mode 100644
index 00000000000..9153a521640
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+
+extern void foo (void) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo ()
+{
+ int a,b,c,d,e,f,i;
+ a = bar(5);
+ b = bar(a);
+ c = bar(b);
+ d = bar(c);
+ e = bar(d);
+ f = bar(e);
+ for (i = 1; i < 10; i++)
+ {
+ a += bar (a + i) + bar(b + i) + bar(c + i) + bar(d + i) + bar(e + i) + bar(f+i);
+ }
+}
+/* { dg-final { scan-assembler "push.\t%.ax" } }*/
+/* { dg-final { scan-assembler "pop.\t%.ax" } }*/
+/* { dg-final { scan-assembler "push.\t%.dx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.dx" } }*/
+/* { dg-final { scan-assembler "push.\t%.cx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.cx" } }*/
+/* { dg-final { scan-assembler "push.\t%.bx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.bx" } }*/
+/* { dg-final { scan-assembler "push.\t%.si" } }*/
+/* { dg-final { scan-assembler "pop.\t%.si" } }*/
+/* { dg-final { scan-assembler "push.\t%.di" } }*/
+/* { dg-final { scan-assembler "pop.\t%.di" } }*/
+/* { dg-final { scan-assembler "iret" } }*/