aboutsummaryrefslogtreecommitdiff
blob: e795c42da9d44d307a922cb0b9050497d10699a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* Copyright (C) 1991, 1992, 1994, 1997 Free Software Foundation, Inc.
   Derived from @(#)_setjmp.s	5.7 (Berkeley) 6/27/88,
   Copyright (c) 1980 Regents of the University of California.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <setjmp.h>

#ifndef	__GNUC__
  #error This file uses GNU C extensions; you must compile with GCC.
#endif


#define	REI	02	/* Vax `rei' opcode.  */

/* Jump to the position specified by ENV, causing the
   setjmp call there to return VAL, or 1 if VAL is 0.  */
__NORETURN
void
__longjmp (env, val)
     const __jmp_buf env;
     int val;
{
  register long int *fp asm("fp");
  long int *regsave;
  unsigned long int flags;

  if (env.__fp == NULL)
    __libc_fatal("longjmp: Invalid ENV argument.\n");

  if (val == 0)
    val = 1;

  asm volatile("loop:");

  flags = *(long int *) (6 + (char *) fp);
  regsave = (long int *) (20 + (char *) fp);
  if (flags & 1)
    /* R0 was saved by the caller.
       Store VAL where it will be restored from.  */
    *regsave++ = val;
  if (flags & 2)
    /* R1 was saved by the caller.
       Store ENV where it will be restored from.  */
    *regsave = env;

  /* Was the FP saved in the last call the same one in ENV?  */
  asm volatile("cmpl %0, 12(fp);"
	       /* Yes, return to it.  */
	       "beql done;"
	       /* The FP in ENV is less than the one saved in the last call.
		  This means we have already returned from the function that
		  called `setjmp' with ENV!  */
	       "blssu latejump;" : /* No outputs.  */ : "g" (env.__fp));

  /* We are more than one level below the state in ENV.
     Return to where we will pop another stack frame.  */
  asm volatile("movl $loop, 16(fp);"
	       "ret");

  asm volatile("done:");
  {
    char return_insn asm("*16(fp)");
    if (return_insn == REI)
      /* We're returning with an `rei' instruction.
	 Do a return with PSL-PC pop.  */
      asm volatile("movab 0f, 16(fp)");
    else
      /* Do a standard return.  */
      asm volatile("movab 1f, 16(fp)");

    /* Return.  */
    asm volatile("ret");
  }

  asm volatile("0:"	/* `rei' return.  */
	       /* Compensate for PSL-PC push.  */
	       "addl2 %0, sp;"
	       "1:"	/* Standard return.  */
	       /* Return to saved PC.  */
	       "jmp %1" : /* No outputs.  */ :
	       "g" (8), "g" (env.__pc));

  /* Jump here when the FP saved in ENV points
     to a function that has already returned.  */
  asm volatile("latejump:");
  __libc_fatal("longjmp: Attempt to jump to a function that has returned.\n");
}