diff options
Diffstat (limited to 'gcc/ada/s-mastop-x86.adb')
-rw-r--r-- | gcc/ada/s-mastop-x86.adb | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/gcc/ada/s-mastop-x86.adb b/gcc/ada/s-mastop-x86.adb new file mode 100644 index 00000000000..96ac1138d7e --- /dev/null +++ b/gcc/ada/s-mastop-x86.adb @@ -0,0 +1,594 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- SYSTEM.MACHINE_STATE_OPERATIONS -- +-- -- +-- B o d y -- +-- (Version for x86) -- +-- -- +-- Copyright (C) 1999-2004 Ada Core Technologies, Inc. -- +-- -- +-- GNAT is free software; you can redistribute it and/or modify it under -- +-- terms of the GNU General Public License as published by the Free Soft- -- +-- ware Foundation; either version 2, or (at your option) any later ver- -- +-- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- +-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- +-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- +-- for more details. You should have received a copy of the GNU General -- +-- Public License distributed with GNAT; see file COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- Extensive contributions were provided by Ada Core Technologies Inc. -- +-- -- +------------------------------------------------------------------------------ + +-- Note: it is very important that this unit not generate any exception +-- tables of any kind. Otherwise we get a nasty rtsfind recursion problem. +-- This means no subprograms, including implicitly generated ones. + +with Unchecked_Conversion; +with System.Storage_Elements; +with System.Machine_Code; use System.Machine_Code; +with System.Memory; + +package body System.Machine_State_Operations is + + function "+" (Left, Right : Address) return Address; + pragma Import (Intrinsic, "+"); + -- Provide addition operation on type Address (this may not be directly + -- available if type System.Address is non-private and the operations on + -- the type are made abstract to hide them from public users of System). + + use System.Exceptions; + + type Uns8 is mod 2 ** 8; + type Uns32 is mod 2 ** 32; + + type Bits5 is mod 2 ** 5; + type Bits6 is mod 2 ** 6; + + function To_Address is new Unchecked_Conversion (Uns32, Address); + + type Uns32_Ptr is access all Uns32; + function To_Uns32_Ptr is new Unchecked_Conversion (Uns32, Uns32_Ptr); + + -- Note: the type Uns32 has an alignment of 4. However, in some cases + -- values of type Uns32_Ptr will not be aligned (notably in the case + -- where we get the immediate field from an instruction). However this + -- does not matter in practice, since the x86 does not require that + -- operands be aligned. + + ---------------------- + -- General Approach -- + ---------------------- + + -- For the x86 version of this unit, the Subprogram_Info_Type values + -- are simply the starting code address for the subprogram. Popping + -- of stack frames works by analyzing the code in the prolog, and + -- deriving from this analysis the necessary information for restoring + -- the registers, including the return point. + + --------------------------- + -- Description of Prolog -- + --------------------------- + + -- If a frame pointer is present, the prolog looks like + + -- pushl %ebp + -- movl %esp,%ebp + -- subl $nnn,%esp omitted if nnn = 0 + -- pushl %edi omitted if edi not used + -- pushl %esi omitted if esi not used + -- pushl %ebx omitted if ebx not used + + -- If a frame pointer is not present, the prolog looks like + + -- subl $nnn,%esp omitted if nnn = 0 + -- pushl %ebp omitted if ebp not used + -- pushl %edi omitted if edi not used + -- pushl %esi omitted if esi not used + -- pushl %ebx omitted if ebx not used + + -- Note: any or all of the save over call registers may be used and + -- if so, will be saved using pushl as shown above. The order of the + -- pushl instructions will be as shown above for gcc generated code, + -- but the code in this unit does not assume this. + + ------------------------- + -- Description of Call -- + ------------------------- + + -- A call looks like: + + -- pushl ... push parameters + -- pushl ... + -- call ... perform the call + -- addl $nnn,%esp omitted if no parameters + + -- Note that we are not absolutely guaranteed that the call is always + -- followed by an addl operation that readjusts %esp for this particular + -- call. There are two reasons for this: + + -- 1) The addl can be delayed and combined in the case where more than + -- one call appears in sequence. This can be suppressed by using the + -- switch -fno-defer-pop and for Ada code, we automatically use + -- this switch, but we could still be dealing with C code that was + -- compiled without using this switch. + + -- 2) Scheduling may result in moving the addl instruction away from + -- the call. It is not clear if this actually can happen at the + -- current time, but it is certainly conceptually possible. + + -- The addl after the call is important, since we need to be able to + -- restore the proper %esp value when we pop the stack. However, we do + -- not try to compensate for either of the above effects. As noted above, + -- case 1 does not occur for Ada code, and it does not appear in practice + -- that case 2 occurs with any significant frequency (we have never seen + -- an example so far for gcc generated code). + + -- Furthermore, it is only in the case of -fomit-frame-pointer that we + -- really get into trouble from not properly restoring %esp. If we have + -- a frame pointer, then the worst that happens is that %esp is slightly + -- more depressed than it should be. This could waste a bit of space on + -- the stack, and even in some cases cause a storage leak on the stack, + -- but it will not affect the functional correctness of the processing. + + ---------------------------------------- + -- Definitions of Instruction Formats -- + ---------------------------------------- + + type Rcode is (eax, ecx, edx, ebx, esp, ebp, esi, edi); + pragma Warnings (Off, Rcode); + -- Code indicating which register is referenced in an instruction + + -- The following define the format of a pushl instruction + + Op_pushl : constant Bits5 := 2#01010#; + + type Ins_pushl is record + Op : Bits5 := Op_pushl; + Reg : Rcode; + end record; + + for Ins_pushl use record + Op at 0 range 3 .. 7; + Reg at 0 range 0 .. 2; + end record; + + Ins_pushl_ebp : constant Ins_pushl := (Op_pushl, Reg => ebp); + + type Ins_pushl_Ptr is access all Ins_pushl; + + -- For the movl %esp,%ebp instruction, we only need to know the length + -- because we simply skip past it when we analyze the prolog. + + Ins_movl_length : constant := 2; + + -- The following define the format of addl/subl esp instructions + + Op_Immed : constant Bits6 := 2#100000#; + + Op2_addl_Immed : constant Bits5 := 2#11100#; + pragma Unreferenced (Op2_addl_Immed); + + Op2_subl_Immed : constant Bits5 := 2#11101#; + + type Word_Byte is (Word, Byte); + pragma Unreferenced (Byte); + + type Ins_addl_subl_byte is record + Op : Bits6; -- Set to Op_Immed + w : Word_Byte; -- Word/Byte flag (set to 1 = byte) + s : Boolean; -- Sign extension bit (1 = extend) + Op2 : Bits5; -- Secondary opcode + Reg : Rcode; -- Register + Imm8 : Uns8; -- Immediate operand + end record; + + for Ins_addl_subl_byte use record + Op at 0 range 2 .. 7; + w at 0 range 1 .. 1; + s at 0 range 0 .. 0; + Op2 at 1 range 3 .. 7; + Reg at 1 range 0 .. 2; + Imm8 at 2 range 0 .. 7; + end record; + + type Ins_addl_subl_word is record + Op : Bits6; -- Set to Op_Immed + w : Word_Byte; -- Word/Byte flag (set to 0 = word) + s : Boolean; -- Sign extension bit (1 = extend) + Op2 : Bits5; -- Secondary opcode + Reg : Rcode; -- Register + Imm32 : Uns32; -- Immediate operand + end record; + + for Ins_addl_subl_word use record + Op at 0 range 2 .. 7; + w at 0 range 1 .. 1; + s at 0 range 0 .. 0; + Op2 at 1 range 3 .. 7; + Reg at 1 range 0 .. 2; + Imm32 at 2 range 0 .. 31; + end record; + + type Ins_addl_subl_byte_Ptr is access all Ins_addl_subl_byte; + type Ins_addl_subl_word_Ptr is access all Ins_addl_subl_word; + + --------------------- + -- Prolog Analysis -- + --------------------- + + -- The analysis of the prolog answers the following questions: + + -- 1. Is %ebp used as a frame pointer? + -- 2. How far is SP depressed (i.e. what is the stack frame size) + -- 3. Which registers are saved in the prolog, and in what order + + -- The following data structure stores the answers to these questions + + subtype SOC is Rcode range ebx .. edi; + -- Possible save over call registers + + SOC_Max : constant := 4; + -- Max number of SOC registers that can be pushed + + type SOC_Push_Regs_Type is array (1 .. 4) of Rcode; + -- Used to hold the register codes of pushed SOC registers + + type Prolog_Type is record + + Frame_Reg : Boolean; + -- This is set to True if %ebp is used as a frame register, and + -- False otherwise (in the False case, %ebp may be saved in the + -- usual manner along with the other SOC registers). + + Frame_Length : Uns32; + -- Amount by which ESP is decremented on entry, includes the effects + -- of push's of save over call registers as indicated above, e.g. if + -- the prolog of a routine is: + -- + -- pushl %ebp + -- movl %esp,%ebp + -- subl $424,%esp + -- pushl %edi + -- pushl %esi + -- pushl %ebx + -- + -- Then the value of Frame_Length would be 436 (424 + 3 * 4). A + -- precise definition is that it is: + -- + -- %esp on entry minus %esp after last SOC push + -- + -- That definition applies both in the frame pointer present and + -- the frame pointer absent cases. + + Num_SOC_Push : Integer range 0 .. SOC_Max; + -- Number of save over call registers actually saved by pushl + -- instructions (other than the initial pushl to save the frame + -- pointer if a frame pointer is in use). + + SOC_Push_Regs : SOC_Push_Regs_Type; + -- The First Num_SOC_Push entries of this array are used to contain + -- the codes for the SOC registers, in the order in which they were + -- pushed. Note that this array excludes %ebp if it is used as a frame + -- register, since although %ebp is still considered an SOC register + -- in this case, it is saved and restored by a separate mechanism. + -- Also we will never see %esp represented in this list. Again, it is + -- true that %esp is saved over call, but it is restored by a separate + -- mechanism. + + end record; + + procedure Analyze_Prolog (A : Address; Prolog : out Prolog_Type); + -- Given the address of the start of the prolog for a procedure, + -- analyze the instructions of the prolog, and set Prolog to contain + -- the information obtained from this analysis. + + ---------------------------------- + -- Machine_State_Representation -- + ---------------------------------- + + -- The type Machine_State is defined in the body of Ada.Exceptions as + -- a Storage_Array of length 1 .. Machine_State_Length. But really it + -- has structure as defined here. We use the structureless declaration + -- in Ada.Exceptions to avoid this unit from being implementation + -- dependent. The actual definition of Machine_State is as follows: + + type SOC_Regs_Type is array (SOC) of Uns32; + + type MState is record + eip : Uns32; + -- The instruction pointer location (which is the return point + -- value from the next level down in all cases). + + Regs : SOC_Regs_Type; + -- Values of the save over call registers + end record; + + for MState use record + eip at 0 range 0 .. 31; + Regs at 4 range 0 .. 5 * 32 - 1; + end record; + -- Note: the routines Enter_Handler, and Set_Machine_State reference + -- the fields in this structure non-symbolically. + + type MState_Ptr is access all MState; + + function To_MState_Ptr is + new Unchecked_Conversion (Machine_State, MState_Ptr); + + ---------------------------- + -- Allocate_Machine_State -- + ---------------------------- + + function Allocate_Machine_State return Machine_State is + use System.Storage_Elements; + + begin + return Machine_State + (Memory.Alloc (MState'Max_Size_In_Storage_Elements)); + end Allocate_Machine_State; + + -------------------- + -- Analyze_Prolog -- + -------------------- + + procedure Analyze_Prolog (A : Address; Prolog : out Prolog_Type) is + Ptr : Address; + Ppl : Ins_pushl_Ptr; + Pas : Ins_addl_subl_byte_Ptr; + + function To_Ins_pushl_Ptr is + new Unchecked_Conversion (Address, Ins_pushl_Ptr); + + function To_Ins_addl_subl_byte_Ptr is + new Unchecked_Conversion (Address, Ins_addl_subl_byte_Ptr); + + function To_Ins_addl_subl_word_Ptr is + new Unchecked_Conversion (Address, Ins_addl_subl_word_Ptr); + + begin + Ptr := A; + Prolog.Frame_Length := 0; + + if Ptr = Null_Address then + Prolog.Num_SOC_Push := 0; + Prolog.Frame_Reg := True; + return; + end if; + + if To_Ins_pushl_Ptr (Ptr).all = Ins_pushl_ebp then + Ptr := Ptr + 1 + Ins_movl_length; + Prolog.Frame_Reg := True; + else + Prolog.Frame_Reg := False; + end if; + + Pas := To_Ins_addl_subl_byte_Ptr (Ptr); + + if Pas.Op = Op_Immed + and then Pas.Op2 = Op2_subl_Immed + and then Pas.Reg = esp + then + if Pas.w = Word then + Prolog.Frame_Length := Prolog.Frame_Length + + To_Ins_addl_subl_word_Ptr (Ptr).Imm32; + Ptr := Ptr + 6; + + else + Prolog.Frame_Length := Prolog.Frame_Length + Uns32 (Pas.Imm8); + Ptr := Ptr + 3; + + -- Note: we ignore sign extension, since a sign extended + -- value that was negative would imply a ludicrous frame size. + end if; + end if; + + -- Now scan push instructions for SOC registers + + Prolog.Num_SOC_Push := 0; + + loop + Ppl := To_Ins_pushl_Ptr (Ptr); + + if Ppl.Op = Op_pushl and then Ppl.Reg in SOC then + Prolog.Num_SOC_Push := Prolog.Num_SOC_Push + 1; + Prolog.SOC_Push_Regs (Prolog.Num_SOC_Push) := Ppl.Reg; + Prolog.Frame_Length := Prolog.Frame_Length + 4; + Ptr := Ptr + 1; + + else + exit; + end if; + end loop; + + end Analyze_Prolog; + + ------------------- + -- Enter_Handler -- + ------------------- + + procedure Enter_Handler (M : Machine_State; Handler : Handler_Loc) is + begin + Asm ("mov %0,%%edx", Inputs => Machine_State'Asm_Input ("r", M)); + Asm ("mov %0,%%eax", Inputs => Handler_Loc'Asm_Input ("r", Handler)); + + Asm ("mov 4(%%edx),%%ebx"); -- M.Regs (ebx) + Asm ("mov 12(%%edx),%%ebp"); -- M.Regs (ebp) + Asm ("mov 16(%%edx),%%esi"); -- M.Regs (esi) + Asm ("mov 20(%%edx),%%edi"); -- M.Regs (edi) + Asm ("mov 8(%%edx),%%esp"); -- M.Regs (esp) + Asm ("jmp %*%%eax"); + end Enter_Handler; + + ---------------- + -- Fetch_Code -- + ---------------- + + function Fetch_Code (Loc : Code_Loc) return Code_Loc is + begin + return Loc; + end Fetch_Code; + + ------------------------ + -- Free_Machine_State -- + ------------------------ + + procedure Free_Machine_State (M : in out Machine_State) is + begin + Memory.Free (Address (M)); + M := Machine_State (Null_Address); + end Free_Machine_State; + + ------------------ + -- Get_Code_Loc -- + ------------------ + + function Get_Code_Loc (M : Machine_State) return Code_Loc is + + Asm_Call_Size : constant := 2; + -- Minimum size for a call instruction under ix86. Using the minimum + -- size is safe here as the call point computed from the return point + -- will always be inside the call instruction. + + MS : constant MState_Ptr := To_MState_Ptr (M); + + begin + if MS.eip = 0 then + return To_Address (MS.eip); + else + -- When doing a call the return address is pushed to the stack. + -- We want to return the call point address, so we substract + -- Asm_Call_Size from the return address. This value is set + -- to 5 as an asm call takes 5 bytes on x86 architectures. + + return To_Address (MS.eip - Asm_Call_Size); + end if; + end Get_Code_Loc; + + -------------------------- + -- Machine_State_Length -- + -------------------------- + + function Machine_State_Length + return System.Storage_Elements.Storage_Offset + is + begin + return MState'Max_Size_In_Storage_Elements; + end Machine_State_Length; + + --------------- + -- Pop_Frame -- + --------------- + + procedure Pop_Frame + (M : Machine_State; + Info : Subprogram_Info_Type) + is + MS : constant MState_Ptr := To_MState_Ptr (M); + PL : Prolog_Type; + + SOC_Ptr : Uns32; + -- Pointer to stack location after last SOC push + + Rtn_Ptr : Uns32; + -- Pointer to stack location containing return address + + begin + Analyze_Prolog (Info, PL); + + -- Case of frame register, use EBP, safer than ESP + + if PL.Frame_Reg then + SOC_Ptr := MS.Regs (ebp) - PL.Frame_Length; + Rtn_Ptr := MS.Regs (ebp) + 4; + MS.Regs (ebp) := To_Uns32_Ptr (MS.Regs (ebp)).all; + + -- No frame pointer, use ESP, and hope we have it exactly right! + + else + SOC_Ptr := MS.Regs (esp); + Rtn_Ptr := SOC_Ptr + PL.Frame_Length; + end if; + + -- Get saved values of SOC registers + + for J in reverse 1 .. PL.Num_SOC_Push loop + MS.Regs (PL.SOC_Push_Regs (J)) := To_Uns32_Ptr (SOC_Ptr).all; + SOC_Ptr := SOC_Ptr + 4; + end loop; + + MS.eip := To_Uns32_Ptr (Rtn_Ptr).all; + MS.Regs (esp) := Rtn_Ptr + 4; + end Pop_Frame; + + ----------------------- + -- Set_Machine_State -- + ----------------------- + + procedure Set_Machine_State (M : Machine_State) is + N : constant Asm_Output_Operand := No_Output_Operands; + + begin + Asm ("mov %0,%%edx", N, Machine_State'Asm_Input ("r", M)); + + -- At this stage, we have the following situation (note that we + -- are assuming that the -fomit-frame-pointer switch has not been + -- used in compiling this procedure. + + -- (value of M) + -- return point + -- old ebp <------ current ebp/esp value + + -- The values of registers ebx/esi/edi are unchanged from entry + -- so they have the values we want, and %edx points to the parameter + -- value M, so we can store these values directly. + + Asm ("mov %%ebx,4(%%edx)"); -- M.Regs (ebx) + Asm ("mov %%esi,16(%%edx)"); -- M.Regs (esi) + Asm ("mov %%edi,20(%%edx)"); -- M.Regs (edi) + + -- The desired value of ebp is the old value + + Asm ("mov 0(%%ebp),%%eax"); + Asm ("mov %%eax,12(%%edx)"); -- M.Regs (ebp) + + -- The return point is the desired eip value + + Asm ("mov 4(%%ebp),%%eax"); + Asm ("mov %%eax,(%%edx)"); -- M.eip + + -- Finally, the desired %esp value is the value at the point of + -- call to this routine *before* pushing the parameter value. + + Asm ("lea 12(%%ebp),%%eax"); + Asm ("mov %%eax,8(%%edx)"); -- M.Regs (esp) + end Set_Machine_State; + + ------------------------------ + -- Set_Signal_Machine_State -- + ------------------------------ + + procedure Set_Signal_Machine_State + (M : Machine_State; + Context : System.Address) + is + pragma Warnings (Off, M); + pragma Warnings (Off, Context); + + begin + null; + end Set_Signal_Machine_State; + +end System.Machine_State_Operations; |