--  Mcode back-end for ortho - Dwarf generator.
--  Copyright (C) 2006 Tristan Gingold
--
--  This program 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 2 of the License, or
--  (at your option) any later version.
--
--  This program 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.
--
--  You should have received a copy of the GNU General Public License
--  along with this program.  If not, see <gnu.org/licenses>.

with Ada.Text_IO;
with GNAT.Directory_Operations;

with Tables;

with Dwarf; use Dwarf;
with Ortho_Code.Flags; use Ortho_Code.Flags;
with Ortho_Code.Decls;
with Ortho_Code.Types;
with Ortho_Code.Consts;
with Ortho_Ident;
with Ortho_Code.Binary;

package body Ortho_Code.Dwarf is
   --  Dwarf debugging format.
   --  Debugging.
   Line1_Sect : Section_Acc := null;
   Line_Last : Int32 := 0;
   Line_Pc : Pc_Type := 0;

   --  Constant.
   Min_Insn_Len : constant := 1;
   Line_Base : constant := 1;
   Line_Range : constant := 4;
   Line_Opcode_Base : constant := 13;
   Line_Max_Addr : constant := (255 - Line_Opcode_Base) / Line_Range;
   -- + Line_Base;

   Cur_File : Natural := 0;
   Last_File : Natural := 0;

   Orig_Sym : Symbol;
   End_Sym : Symbol;
   Abbrev_Sym : Symbol;
   Info_Sym : Symbol;
   Line_Sym : Symbol;

   Abbrev_Last : Unsigned_32;

   procedure Gen_String_Nul (Str : String)
   is
   begin
      Prealloc (Str'Length + 1);
      for I in Str'Range loop
         Gen_8 (Character'Pos (Str (I)));
      end loop;
      Gen_8 (0);
   end Gen_String_Nul;

   procedure Gen_Sleb128 (V : Int32)
   is
      V1 : Uns32 := To_Uns32 (V);
      V2 : Uns32;
      B : Byte;
      function Shift_Right_Arithmetic (Value : Uns32; Amount : Natural)
                                      return Uns32;
      pragma Import (Intrinsic, Shift_Right_Arithmetic);
   begin
      loop
         B := Byte (V1 and 16#7F#);
         V2 := Shift_Right_Arithmetic (V1, 7);
         if (V2 = 0 and (B and 16#40#) = 0)
           or (V2 = -1 and (B and 16#40#) /= 0)
         then
            Gen_8 (B);
            exit;
         else
            Gen_8 (B or 16#80#);
            V1 := V2;
         end if;
      end loop;
   end Gen_Sleb128;

   procedure Gen_Uleb128 (V : Unsigned_32)
   is
      V1 : Unsigned_32 := V;
      B : Byte;
   begin
      loop
         B := Byte (V1 and 16#7f#);
         V1 := Shift_Right (V1, 7);
         if V1 /= 0 then
            Gen_8 (B or 16#80#);
         else
            Gen_8 (B);
            exit;
         end if;
      end loop;
   end Gen_Uleb128;

   procedure Set_Line_Stmt (Line : Int32)
   is
      Pc : Pc_Type;
      D_Pc : Pc_Type;
      D_Ln : Int32;
   begin
      if Line = Line_Last then
         return;
      end if;
      Pc := Get_Current_Pc;

      D_Pc := (Pc - Line_Pc) / Min_Insn_Len;
      D_Ln := Line - Line_Last;

      --  Always emit line information, since missing info can distrub the
      --  user.
      --  As an optimization, we could try to emit the highest line for the
      --  same PC, since GDB seems to handle this way.
      if False and D_Pc = 0 then
         return;
      end if;

      Set_Current_Section (Line1_Sect);
      Prealloc (32);

      if Cur_File /= Last_File then
         Gen_8 (Byte (DW_LNS_Set_File));
         Gen_Uleb128 (Unsigned_32 (Cur_File));
         Last_File := Cur_File;
      elsif Cur_File = 0 then
         --  No file yet.
         return;
      end if;

      if D_Ln < Line_Base or D_Ln >= (Line_Base + Line_Range) then
         --  Emit an advance line.
         Gen_8 (Byte (DW_LNS_Advance_Line));
         Gen_Sleb128 (Int32 (D_Ln - Line_Base));
         D_Ln := Line_Base;
      end if;
      if D_Pc >= Line_Max_Addr then
         --  Emit an advance addr.
         Gen_8 (Byte (DW_LNS_Advance_Pc));
         Gen_Uleb128 (Unsigned_32 (D_Pc));
         D_Pc := 0;
      end if;
      Gen_8 (Line_Opcode_Base
              + Byte (D_Pc) * Line_Range
              + Byte (D_Ln - Line_Base));

      Line_Pc := Pc;
      Line_Last := Line;
   end Set_Line_Stmt;


   type String_Acc is access constant String;

   type Dir_Chain;
   type Dir_Chain_Acc is access Dir_Chain;
   type Dir_Chain is record
      Name : String_Acc;
      Next : Dir_Chain_Acc;
   end record;

   type File_Chain;
   type File_Chain_Acc is access File_Chain;
   type File_Chain is record
      Name : String_Acc;
      Dir : Natural;
      Next : File_Chain_Acc;
   end record;

   Dirs : Dir_Chain_Acc := null;
   Files : File_Chain_Acc := null;

   procedure Set_Filename (Dir : String; File : String)
   is
      D : Natural;
      F : Natural;
      D_C : Dir_Chain_Acc;
      F_C : File_Chain_Acc;
   begin
      --  Find directory.
      if Dir = "" then
         --  Current directory.
         D := 0;
      elsif Dirs = null then
         --  First directory.
         Dirs := new Dir_Chain'(Name => new String'(Dir),
                                Next => null);
         D := 1;
      else
         --  Find a directory.
         D_C := Dirs;
         D := 1;
         loop
            exit when D_C.Name.all = Dir;
            D := D + 1;
            if D_C.Next = null then
               D_C.Next := new Dir_Chain'(Name => new String'(Dir),
                                          Next => null);
               exit;
            else
               D_C := D_C.Next;
            end if;
         end loop;
      end if;

      --  Find file.
      F := 1;
      if Files = null then
         --  first file.
         Files := new File_Chain'(Name => new String'(File),
                                  Dir => D,
                                  Next => null);
      else
         F_C := Files;
         loop
            exit when F_C.Name.all = File and F_C.Dir = D;
            F := F + 1;
            if F_C.Next = null then
               F_C.Next := new File_Chain'(Name => new String'(File),
                                           Dir => D,
                                           Next => null);
               exit;
            else
               F_C := F_C.Next;
            end if;
         end loop;
      end if;
      Cur_File := F;
   end Set_Filename;

   procedure Gen_Abbrev_Header (Tag : Unsigned_32; Child : Byte) is
   begin
      Gen_Uleb128 (Tag);
      Gen_8 (Child);
   end Gen_Abbrev_Header;

   procedure Gen_Abbrev_Tuple (Attr : Unsigned_32; Form : Unsigned_32) is
   begin
      Gen_Uleb128 (Attr);
      Gen_Uleb128 (Form);
   end Gen_Abbrev_Tuple;

   procedure Init is
   begin
      --  Generate type names.
      Flags.Flag_Type_Name := True;

      --  Symbols for the start/end of .text
      Orig_Sym := Create_Local_Symbol;
      Set_Symbol_Pc (Orig_Sym, False);  --  Set its address
      End_Sym := Create_Local_Symbol;

      Create_Section (Line1_Sect, ".debug_line-1", Section_Debug);
      Set_Current_Section (Line1_Sect);

      --  Write Address.
      Gen_8 (0); -- extended opcode
      Gen_8 (1 + Pc_Type_Sizeof); -- length
      Gen_8 (Byte (DW_LNE_Set_Address));
      Gen_Ua_Addr (Orig_Sym, 0);

      Line_Last := 1;

      Create_Section (Line_Sect, ".debug_line", Section_Debug);
      Set_Section_Info (Line_Sect, null, 0, 0);
      Set_Current_Section (Line_Sect);
      Line_Sym := Create_Local_Symbol;
      Set_Symbol_Pc (Line_Sym, False);

      --  Abbrevs.
      Create_Section (Abbrev_Sect, ".debug_abbrev", Section_Debug);
      Set_Section_Info (Abbrev_Sect, null, 0, 0);
      Set_Current_Section (Abbrev_Sect);

      Abbrev_Sym := Create_Local_Symbol;
      Set_Symbol_Pc (Abbrev_Sym, False);

      Gen_Uleb128 (1);
      Gen_Abbrev_Header (DW_TAG_Compile_Unit, DW_CHILDREN_Yes);

      Gen_Abbrev_Tuple (DW_AT_Stmt_List, DW_FORM_Data4);
      Gen_Abbrev_Tuple (DW_AT_Low_Pc, DW_FORM_Addr);
      Gen_Abbrev_Tuple (DW_AT_High_Pc, DW_FORM_Addr);
      Gen_Abbrev_Tuple (DW_AT_Producer, DW_FORM_String);
      Gen_Abbrev_Tuple (DW_AT_Comp_Dir, DW_FORM_String);
      Gen_Abbrev_Tuple (0, 0);

      Abbrev_Last := 1;

      --  Info.
      Create_Section (Info_Sect, ".debug_info", Section_Debug);
      Set_Section_Info (Info_Sect, null, 0, 0);
      Set_Current_Section (Info_Sect);
      Info_Sym := Create_Local_Symbol;
      Set_Symbol_Pc (Info_Sym, False);

      Gen_32 (7);  --  Length: to be patched.
      Gen_16 (2);  --  version
      Gen_Ua_32 (Abbrev_Sym); --  Abbrev offset
      Gen_8 (Pc_Type_Sizeof);  --  Ptr size.

      --  Compile_unit.
      Gen_Uleb128 (1);
      Gen_Ua_32 (Line_Sym);
      Gen_Ua_Addr (Orig_Sym, 0);
      Gen_Ua_Addr (End_Sym, 0);
      Gen_String_Nul ("T.Gingold ortho_mcode (2004)");
      Gen_String_Nul (GNAT.Directory_Operations.Get_Current_Dir);
   end Init;

   procedure Emit_Decl (Decl : O_Dnode);

   --  Next node to be emitted.
   Last_Decl : O_Dnode := O_Dnode_First;

   procedure Emit_Decls_Until (Last : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      while Last_Decl < Last loop
         Emit_Decl (Last_Decl);
         Last_Decl := Get_Decl_Chain (Last_Decl);
      end loop;
   end Emit_Decls_Until;

   procedure Finish
   is
      Length : Pc_Type;
      Last : O_Dnode;
   begin
      Set_Symbol_Pc (End_Sym, False);
      Length := Get_Current_Pc;

      Last := Decls.Get_Decl_Last;
      Emit_Decls_Until (Last);
      if Last_Decl <= Last then
         Emit_Decl (Last);
      end if;

      --  Finish abbrevs.
      Set_Current_Section (Abbrev_Sect);
      Gen_Uleb128 (0);

      --  Emit header.
      Set_Current_Section (Line_Sect);
      Prealloc (32);

      --  Unit_Length (to be patched).
      Gen_32 (0);
      --  version
      Gen_16 (2);
      --  header_length (to be patched).
      Gen_32 (5 + 12 + 1);
      --  minimum_instruction_length.
      Gen_8 (Min_Insn_Len);
      --  default_is_stmt
      Gen_8 (1);
      --  line base
      Gen_8 (Line_Base);
      --  line range
      Gen_8 (Line_Range);
      --  opcode base
      Gen_8 (Line_Opcode_Base);
      --  standard_opcode_length.
      Gen_8 (0); --  copy
      Gen_8 (1); --  advance pc
      Gen_8 (1); --  advance line
      Gen_8 (1); --  set file
      Gen_8 (1); --  set column
      Gen_8 (0); --  negate stmt
      Gen_8 (0); --  set basic block
      Gen_8 (0); --  const add pc
      Gen_8 (1); --  fixed advance pc
      Gen_8 (0); --  set prologue end
      Gen_8 (0); --  set epilogue begin
      Gen_8 (1); --  set isa
      --if Line_Opcode_Base /= 13 then
      --   raise Program_Error;
      --end if;

      --  include directories
      declare
         D : Dir_Chain_Acc;
      begin
         D := Dirs;
         while D /= null loop
            Gen_String_Nul (D.Name.all);
            D := D.Next;
         end loop;
         Prealloc (1);
         Gen_8 (0); -- last entry.
      end;

      --  file_names.
      declare
         F : File_Chain_Acc;
      begin
         F := Files;
         while F /= null loop
            Gen_String_Nul (F.Name.all);
            Prealloc (8);
            Gen_Uleb128 (Unsigned_32 (F.Dir));
            Gen_8 (0);  --  time
            Gen_8 (0);  --  length
            F := F.Next;
         end loop;
         Gen_8 (0);  --  last entry.
      end;

      --  Set prolog length
      Patch_32 (6, Unsigned_32 (Get_Current_Pc - 6));

      Merge_Section (Line_Sect, Line1_Sect);
      Prealloc (4);

      --  Emit end of sequence.
      Gen_8 (0); -- extended opcode
      Gen_8 (1); -- length: 1
      Gen_8 (Byte (DW_LNE_End_Sequence));

      --  Set total length.
      Patch_32 (0, Unsigned_32 (Get_Current_Pc - 4));

      --  Info.
      Set_Current_Section (Info_Sect);
      Prealloc (8);
      --  Finish child.
      Gen_Uleb128 (0);
      --  Set total length.
      Patch_32 (0, Unsigned_32 (Get_Current_Pc - 4));

      --  Aranges
      Create_Section (Aranges_Sect, ".debug_aranges", Section_Debug);
      Set_Section_Info (Aranges_Sect, null, 0, 0);
      Set_Current_Section (Aranges_Sect);

      Prealloc (32);
      Gen_32 (24 + Pc_Type_Sizeof);  --  Length.
      Gen_16 (2);  --  version
      Gen_Ua_32 (Info_Sym); --  info offset
      Gen_8 (Pc_Type_Sizeof);  --  Ptr size.
      Gen_8 (0);  --  seg desc size.
      Gen_32 (0);  --  pad
      Gen_Ua_Addr (Orig_Sym, 0); --  text offset
      Gen_32 (Unsigned_32 (Length));
      Gen_32 (0); --  End
      Gen_32 (0);
   end Finish;

   procedure Generate_Abbrev (Abbrev : out Unsigned_32) is
   begin
      Abbrev_Last := Abbrev_Last + 1;
      Abbrev := Abbrev_Last;

      Set_Current_Section (Abbrev_Sect);
      --  FIXME: should be enough ?
      Prealloc (128);
      Gen_Uleb128 (Abbrev);
   end Generate_Abbrev;

   procedure Gen_Info_Header (Abbrev : Unsigned_32) is
   begin
      Set_Current_Section (Info_Sect);
      Gen_Uleb128 (Abbrev);
   end Gen_Info_Header;

   function Gen_Info_Sibling return Pc_Type
   is
      Pc : Pc_Type;
   begin
      Pc := Get_Current_Pc;
      Gen_32 (0);
      return Pc;
   end Gen_Info_Sibling;

   procedure Patch_Info_Sibling (Pc : Pc_Type) is
   begin
      Patch_32 (Pc, Unsigned_32 (Get_Current_Pc));
   end Patch_Info_Sibling;

   Abbrev_Base_Type : Unsigned_32 := 0;
   Abbrev_Base_Type_Name : Unsigned_32 := 0;
   Abbrev_Pointer : Unsigned_32 := 0;
   Abbrev_Pointer_Name : Unsigned_32 := 0;
   Abbrev_Uncomplete_Pointer : Unsigned_32 := 0;
   Abbrev_Uncomplete_Pointer_Name : Unsigned_32 := 0;
   Abbrev_Ucarray : Unsigned_32 := 0;
   Abbrev_Ucarray_Name : Unsigned_32 := 0;
   Abbrev_Uc_Subrange : Unsigned_32 := 0;
   Abbrev_Subarray : Unsigned_32 := 0;
   Abbrev_Subarray_Name : Unsigned_32 := 0;
   Abbrev_Subrange : Unsigned_32 := 0;
   Abbrev_Struct : Unsigned_32 := 0;
   Abbrev_Struct_Name : Unsigned_32 := 0;
   Abbrev_Uncomplete_Struct : Unsigned_32 := 0;
   Abbrev_Union : Unsigned_32 := 0;
   Abbrev_Union_Name : Unsigned_32 := 0;
   Abbrev_Member : Unsigned_32 := 0;
   Abbrev_Enum : Unsigned_32 := 0;
   Abbrev_Enum_Name : Unsigned_32 := 0;
   Abbrev_Enumerator : Unsigned_32 := 0;

   package TOnodes is new Tables
     (Table_Component_Type => Pc_Type,
      Table_Index_Type => O_Tnode,
      Table_Low_Bound => O_Tnode_First,
      Table_Initial => 16);

   procedure Emit_Type_Ref (Atype : O_Tnode)
   is
      Off : Pc_Type;
   begin
      pragma Assert (Flag_Debug >= Debug_Dwarf);
      Off := TOnodes.Table (Atype);
      pragma Assert (Off /= Null_Pc);
      Gen_32 (Unsigned_32 (Off));
   end Emit_Type_Ref;

   procedure Emit_Ident (Id : O_Ident)
   is
      use Ortho_Ident;
      L : Natural;
   begin
      L := Get_String_Length (Id);
      Prealloc (Pc_Type (L) + 128);
      Gen_String_Nul (Get_String (Id));
   end Emit_Ident;

   procedure Add_Type_Ref (Atype : O_Tnode; Pc : Pc_Type)
   is
      Prev : O_Tnode;
   begin
      if Atype > TOnodes.Last then
         --  Expand.
         Prev := TOnodes.Last;
         TOnodes.Set_Last (Atype);
         TOnodes.Table (Prev + 1 .. Atype - 1) := (others => Null_Pc);
      end if;
      TOnodes.Table (Atype) := Pc;
   end Add_Type_Ref;

   procedure Emit_Decl_Ident (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      Emit_Ident (Get_Decl_Ident (Decl));
   end Emit_Decl_Ident;

   procedure Emit_Decl_Ident_If_Set (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      if Decl /= O_Dnode_Null then
         Emit_Ident (Get_Decl_Ident (Decl));
      end if;
   end Emit_Decl_Ident_If_Set;

   procedure Emit_Type_Maybe (Atype : O_Tnode);

   procedure Emit_Base_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Encoding, DW_FORM_Data1);
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Data1);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;
   begin
      if Decl = O_Dnode_Null then
         if Abbrev_Base_Type = 0 then
            Generate_Abbrev (Abbrev_Base_Type);
            Gen_Abbrev_Header (DW_TAG_Base_Type, DW_CHILDREN_No);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Base_Type);
      else
         if Abbrev_Base_Type_Name = 0 then
            Generate_Abbrev (Abbrev_Base_Type_Name);
            Gen_Abbrev_Header (DW_TAG_Base_Type, DW_CHILDREN_No);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Base_Type_Name);
         Emit_Decl_Ident (Decl);
      end if;

      case Get_Type_Kind (Atype) is
         when OT_Signed =>
            Gen_8 (DW_ATE_Signed);
         when OT_Unsigned =>
            Gen_8 (DW_ATE_Unsigned);
         when OT_Float =>
            Gen_8 (DW_ATE_Float);
         when others =>
            raise Program_Error;
      end case;
      Gen_8 (Byte (Get_Type_Size (Atype)));
   end Emit_Base_Type;

   procedure Emit_Access_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Data1);
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;

      procedure Finish_Gen_Abbrev_Uncomplete is
      begin
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Data1);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev_Uncomplete;

      Dtype : O_Tnode;
      D_Pc : Pc_Type;
   begin
      Dtype := Get_Type_Access_Type (Atype);

      if Dtype = O_Tnode_Null then
         if Decl = O_Dnode_Null then
            if Abbrev_Uncomplete_Pointer = 0 then
               Generate_Abbrev (Abbrev_Uncomplete_Pointer);
               Gen_Abbrev_Header (DW_TAG_Pointer_Type, DW_CHILDREN_No);
               Finish_Gen_Abbrev_Uncomplete;
            end if;
            Gen_Info_Header (Abbrev_Uncomplete_Pointer);
         else
            if Abbrev_Uncomplete_Pointer_Name = 0 then
               Generate_Abbrev (Abbrev_Uncomplete_Pointer_Name);
               Gen_Abbrev_Header (DW_TAG_Pointer_Type, DW_CHILDREN_No);
               Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
               Finish_Gen_Abbrev_Uncomplete;
            end if;
            Gen_Info_Header (Abbrev_Uncomplete_Pointer_Name);
            Emit_Decl_Ident (Decl);
         end if;
         Gen_8 (Byte (Get_Type_Size (Atype)));
      else
         if Decl = O_Dnode_Null then
            if Abbrev_Pointer = 0 then
               Generate_Abbrev (Abbrev_Pointer);
               Gen_Abbrev_Header (DW_TAG_Pointer_Type, DW_CHILDREN_No);
               Finish_Gen_Abbrev;
            end if;
            Gen_Info_Header (Abbrev_Pointer);
         else
            if Abbrev_Pointer_Name = 0 then
               Generate_Abbrev (Abbrev_Pointer_Name);
               Gen_Abbrev_Header (DW_TAG_Pointer_Type, DW_CHILDREN_No);
               Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
               Finish_Gen_Abbrev;
            end if;
            Gen_Info_Header (Abbrev_Pointer_Name);
            Emit_Decl_Ident (Decl);
         end if;
         Gen_8 (Byte (Get_Type_Size (Atype)));
         --  Break possible loops: generate the access entry...
         D_Pc := Get_Current_Pc;
         Gen_32 (0);
         --  ... generate the designated type ...
         Emit_Type_Maybe (Dtype);
         --  ... and write its reference.
         Patch_32 (D_Pc, Unsigned_32 (TOnodes.Table (Dtype)));
      end if;
   end Emit_Access_Type;

   procedure Emit_Array_Type
     (Decl : O_Dnode; El_Type : O_Tnode; Idx_Type : O_Tnode)
   is
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;
   begin
      if Decl = O_Dnode_Null then
         if Abbrev_Ucarray = 0 then
            Generate_Abbrev (Abbrev_Ucarray);
            Gen_Abbrev_Header (DW_TAG_Array_Type, DW_CHILDREN_Yes);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Ucarray);
      else
         if Abbrev_Ucarray_Name = 0 then
            Generate_Abbrev (Abbrev_Ucarray_Name);
            Gen_Abbrev_Header (DW_TAG_Array_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Ucarray_Name);
         Emit_Decl_Ident (Decl);
      end if;
      Emit_Type_Ref (El_Type);

      if Abbrev_Uc_Subrange = 0 then
         Generate_Abbrev (Abbrev_Uc_Subrange);
         Gen_Abbrev_Header (DW_TAG_Subrange_Type, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Gen_Info_Header (Abbrev_Uc_Subrange);
      Emit_Type_Ref (Idx_Type);

      Gen_Uleb128 (0);
   end Emit_Array_Type;

   procedure Emit_Ucarray_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
   begin
      Emit_Array_Type (Decl,
                       Get_Type_Ucarray_Element (Atype),
                       Get_Type_Ucarray_Index (Atype));
   end Emit_Ucarray_Type;

   procedure Emit_Subarray_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Udata);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;

      Base : O_Tnode;
   begin
      if Decl = O_Dnode_Null then
         if Abbrev_Subarray = 0 then
            Generate_Abbrev (Abbrev_Subarray);
            Gen_Abbrev_Header (DW_TAG_Array_Type, DW_CHILDREN_Yes);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Subarray);
      else
         if Abbrev_Subarray_Name = 0 then
            Generate_Abbrev (Abbrev_Subarray_Name);
            Gen_Abbrev_Header (DW_TAG_Array_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Subarray_Name);
         Emit_Decl_Ident (Decl);
      end if;


      Emit_Type_Ref (Get_Type_Subarray_Element (Atype));
      Gen_Uleb128 (Unsigned_32 (Get_Type_Size (Atype)));

      if Abbrev_Subrange = 0 then
         Generate_Abbrev (Abbrev_Subrange);
         Gen_Abbrev_Header (DW_TAG_Subrange_Type, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (DW_AT_Lower_Bound, DW_FORM_Data1);
         Gen_Abbrev_Tuple (DW_AT_Count, DW_FORM_Udata);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Gen_Info_Header (Abbrev_Subrange);
      Base := Get_Type_Subarray_Base (Atype);
      Emit_Type_Ref (Get_Type_Ucarray_Index (Base));
      Gen_8 (0);
      Gen_Uleb128 (Unsigned_32 (Get_Type_Subarray_Length (Atype)));

      Gen_Uleb128 (0);
   end Emit_Subarray_Type;

   procedure Emit_Members (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
      Nbr : Uns32;
      F : O_Fnode;
      Loc_Pc : Pc_Type;
      Sibling_Pc : Pc_Type;
      Sz : Uns32;
   begin
      if Abbrev_Member = 0 then
         Generate_Abbrev (Abbrev_Member);

         Gen_Abbrev_Header (DW_TAG_Member, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (DW_AT_Data_Member_Location, DW_FORM_Block1);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Set_Current_Section (Info_Sect);
      Sibling_Pc := Gen_Info_Sibling;
      Emit_Decl_Ident_If_Set (Decl);
      if Get_Type_Sized (Atype) then
         Sz := Get_Type_Size (Atype);
      else
         Sz := Get_Type_Record_Size (Atype);
      end if;
      Gen_Uleb128 (Unsigned_32 (Sz));

      Nbr := Get_Type_Record_Nbr_Fields (Atype);
      F := Get_Type_Record_Fields (Atype);
      while Nbr > 0 loop
         Gen_Uleb128 (Abbrev_Member);
         Emit_Ident (Get_Field_Ident (F));
         Emit_Type_Ref (Get_Field_Type (F));

         --  Location.
         Loc_Pc := Get_Current_Pc;
         Gen_8 (3);
         Gen_8 (DW_OP_Plus_Uconst);
         Gen_Uleb128 (Unsigned_32 (Get_Field_Offset (F)));
         Patch_8 (Loc_Pc, Unsigned_8 (Get_Current_Pc - (Loc_Pc + 1)));

         F := Get_Field_Chain (F);
         Nbr := Nbr - 1;
      end loop;

      --  end of children.
      Gen_Uleb128 (0);
      Patch_Info_Sibling (Sibling_Pc);
   end Emit_Members;

   procedure Emit_Record_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Udata);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;
   begin
      if Decl = O_Dnode_Null then
         --  Anonymous record.
         if Abbrev_Struct = 0 then
            Generate_Abbrev (Abbrev_Struct);

            Gen_Abbrev_Header (DW_TAG_Structure_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Struct);
      else
         if Abbrev_Struct_Name = 0 then
            Generate_Abbrev (Abbrev_Struct_Name);

            Gen_Abbrev_Header (DW_TAG_Structure_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Struct_Name);
      end if;
      Emit_Members (Atype, Decl);
   end Emit_Record_Type;

   procedure Emit_Uncomplete_Record_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      pragma Unreferenced (Atype);
   begin
      if Abbrev_Uncomplete_Struct = 0 then
         Generate_Abbrev (Abbrev_Uncomplete_Struct);
         Gen_Abbrev_Header (DW_TAG_Structure_Type, DW_CHILDREN_No);
         Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
         Gen_Abbrev_Tuple (DW_AT_Declaration, DW_FORM_Flag);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Gen_Info_Header (Abbrev_Uncomplete_Struct);
      Emit_Decl_Ident (Decl);
      Gen_8 (1);
   end Emit_Uncomplete_Record_Type;

   procedure Emit_Union_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Udata);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;
   begin
      if Decl = O_Dnode_Null then
         if Abbrev_Union = 0 then
            Generate_Abbrev (Abbrev_Union);

            Gen_Abbrev_Header (DW_TAG_Union_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Union);
      else
         if Abbrev_Union_Name = 0 then
            Generate_Abbrev (Abbrev_Union_Name);

            Gen_Abbrev_Header (DW_TAG_Union_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Union_Name);
      end if;
      Emit_Members (Atype, Decl);
   end Emit_Union_Type;

   procedure Emit_Enum_Type (Atype : O_Tnode; Decl : O_Dnode)
   is
      use Ortho_Code.Types;
      use Ortho_Code.Consts;
      procedure Finish_Gen_Abbrev is
      begin
         Gen_Abbrev_Tuple (DW_AT_Byte_Size, DW_FORM_Data1);
         Gen_Abbrev_Tuple (0, 0);
      end Finish_Gen_Abbrev;

      procedure Emit_Enumerator (L : O_Cnode) is
      begin
         Gen_Uleb128 (Abbrev_Enumerator);
         Emit_Ident (Get_Lit_Ident (L));
         Gen_Uleb128 (Unsigned_32 (Get_Lit_Value (L)));
      end Emit_Enumerator;

      Nbr : Uns32;
      L : O_Cnode;
      Sibling_Pc : Pc_Type;
   begin
      if Abbrev_Enumerator = 0 then
         Generate_Abbrev (Abbrev_Enumerator);

         Gen_Abbrev_Header (DW_TAG_Enumerator, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
         Gen_Abbrev_Tuple (DW_AT_Const_Value, DW_FORM_Udata);
         Gen_Abbrev_Tuple (0, 0);
      end if;
      if Decl = O_Dnode_Null then
         if Abbrev_Enum = 0 then
            Generate_Abbrev (Abbrev_Enum);
            Gen_Abbrev_Header (DW_TAG_Enumeration_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Enum);
      else
         if Abbrev_Enum_Name = 0 then
            Generate_Abbrev (Abbrev_Enum_Name);
            Gen_Abbrev_Header (DW_TAG_Enumeration_Type, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Finish_Gen_Abbrev;
         end if;
         Gen_Info_Header (Abbrev_Enum_Name);
      end if;

      Sibling_Pc := Gen_Info_Sibling;
      Emit_Decl_Ident_If_Set (Decl);
      Gen_8 (Byte (Get_Type_Size (Atype)));
      case Get_Type_Kind (Atype) is
         when OT_Enum =>
            Nbr := Get_Type_Enum_Nbr_Lits (Atype);
            L := Get_Type_Enum_Lits (Atype);
            while Nbr > 0 loop
               Emit_Enumerator (L);

               L := Get_Lit_Chain (L);
               Nbr := Nbr - 1;
            end loop;
         when OT_Boolean =>
            Emit_Enumerator (Get_Type_Bool_False (Atype));
            Emit_Enumerator (Get_Type_Bool_True (Atype));
         when others =>
            raise Program_Error;
      end case;

      --  End of children.
      Gen_Uleb128 (0);
      Patch_Info_Sibling (Sibling_Pc);
   end Emit_Enum_Type;

   procedure Emit_Type_Force (Atype : O_Tnode; Completer : Boolean)
   is
      use Ortho_Code.Types;
      Kind : OT_Kind;
      Decl : O_Dnode;
   begin
      Kind := Get_Type_Kind (Atype);

      --  First step: emit inner types (if any).
      case Kind is
         when OT_Signed
            | OT_Unsigned
            | OT_Float
            | OT_Boolean
            | OT_Enum =>
            null;
         when OT_Access =>
            null;
         when OT_Ucarray =>
            Emit_Type_Maybe (Get_Type_Ucarray_Index (Atype));
            Emit_Type_Maybe (Get_Type_Ucarray_Element (Atype));
         when OT_Subarray =>
            Emit_Type_Maybe (Get_Type_Subarray_Base (Atype));
            Emit_Type_Maybe (Get_Type_Subarray_Element (Atype));
         when OT_Record
            | OT_Subrecord
            | OT_Union =>
            declare
               Nbr : Uns32;
               F : O_Fnode;
            begin
               Nbr := Get_Type_Record_Nbr_Fields (Atype);
               F := Get_Type_Record_Fields (Atype);
               while Nbr > 0 loop
                  Emit_Type_Maybe (Get_Field_Type (F));
                  F := Get_Field_Chain (F);
                  Nbr := Nbr - 1;
               end loop;
            end;
      end case;

      Set_Current_Section (Info_Sect);
      Add_Type_Ref (Atype, Get_Current_Pc);

      Decl := Decls.Get_Type_Decl (Atype);

      --  Second step: emit info.
      case Kind is
         when OT_Signed
            | OT_Unsigned
            | OT_Float =>
            Emit_Base_Type (Atype, Decl);
            -- base types.
         when OT_Access =>
            Emit_Access_Type (Atype, Decl);
         when OT_Ucarray =>
            Emit_Ucarray_Type (Atype, Decl);
         when OT_Subarray =>
            Emit_Subarray_Type (Atype, Decl);
         when OT_Record
           | OT_Subrecord =>
            if not Completer and then Get_Type_Deferred (Atype) then
               Emit_Uncomplete_Record_Type (Atype, Decl);
            else
               Emit_Record_Type (Atype, Decl);
            end if;
         when OT_Union =>
            Emit_Union_Type (Atype, Decl);
         when OT_Enum
            | OT_Boolean =>
            Emit_Enum_Type (Atype, Decl);
      end case;
   end Emit_Type_Force;

   procedure Emit_Type_Maybe (Atype : O_Tnode) is
   begin
      --  If already emitted, then return.
      if Atype <= TOnodes.Last
        and then TOnodes.Table (Atype) /= Null_Pc
      then
         return;
      end if;

      Emit_Type_Force (Atype, False);
   end Emit_Type_Maybe;

   procedure Emit_Decl_Type (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      Emit_Type_Ref (Get_Decl_Type (Decl));
   end Emit_Decl_Type;

   Abbrev_Variable : Unsigned_32 := 0;
   Abbrev_Const : Unsigned_32 := 0;

   procedure Emit_Local_Location (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
      Pc : Pc_Type;
   begin
      Pc := Get_Current_Pc;
      Gen_8 (2);
      Gen_8 (DW_OP_Fbreg);
      Gen_Sleb128 (Get_Decl_Info (Decl));
      Patch_8 (Pc, Unsigned_8 (Get_Current_Pc - (Pc + 1)));
   end Emit_Local_Location;

   procedure Emit_Global_Location (Decl : O_Dnode)
   is
      use Ortho_Code.Binary;
   begin
      Gen_8 (1 + Pc_Type_Sizeof);
      Gen_8 (DW_OP_Addr);
      Gen_Ua_Addr (Get_Decl_Symbol (Decl), 0);
   end Emit_Global_Location;

   procedure Emit_Variable (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
      Dtype : O_Tnode;
   begin
      if Get_Decl_Ident (Decl) = O_Ident_Nul then
         return;
      end if;

      if Abbrev_Variable = 0 then
         Generate_Abbrev (Abbrev_Variable);
         Gen_Abbrev_Header (DW_TAG_Variable, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (DW_AT_Location, DW_FORM_Block1);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Dtype := Get_Decl_Type (Decl);
      Emit_Type_Maybe (Dtype);

      Gen_Info_Header (Abbrev_Variable);
      Emit_Decl_Ident (Decl);
      Emit_Type_Ref (Dtype);
      case Get_Decl_Kind (Decl) is
         when OD_Local =>
            Emit_Local_Location (Decl);
         when OD_Var =>
            Emit_Global_Location (Decl);
         when others =>
            raise Program_Error;
      end case;
   end Emit_Variable;

   procedure Emit_Const (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
      Dtype : O_Tnode;
   begin
      if Abbrev_Const = 0 then
         Generate_Abbrev (Abbrev_Const);
         --  FIXME: should be a TAG_Constant, however, GDB does not support it.
         --  work-around: could use a const_type.
         Gen_Abbrev_Header (DW_TAG_Variable, DW_CHILDREN_No);

         Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
         Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
         Gen_Abbrev_Tuple (DW_AT_Location, DW_FORM_Block1);
         Gen_Abbrev_Tuple (0, 0);
      end if;

      Dtype := Get_Decl_Type (Decl);
      Emit_Type_Maybe (Dtype);
      Gen_Info_Header (Abbrev_Const);
      Emit_Decl_Ident (Decl);
      Emit_Type_Ref (Dtype);
      Emit_Global_Location (Decl);
   end Emit_Const;

   procedure Emit_Type_Decl (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      if Flag_Debug < Debug_Dwarf then
         return;
      end if;

      Emit_Type_Maybe (Get_Decl_Type (Decl));
   end Emit_Type_Decl;

   procedure Emit_Type_Completer (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
   begin
      if Flag_Debug < Debug_Dwarf then
         return;
      end if;

      Emit_Type_Force (Get_Decl_Type (Decl), True);
   end Emit_Type_Completer;

   Subprg_Sym : Symbol;

   Abbrev_Block : Unsigned_32 := 0;

   procedure Emit_Block_Decl (Decl : O_Dnode)
   is
      use Ortho_Code.Decls;
      Last : O_Dnode;
      Sdecl : O_Dnode;
      Sibling_Pc : Pc_Type;
   begin
      if Flag_Debug >= Debug_Dwarf then
         if Abbrev_Block = 0 then
            Generate_Abbrev (Abbrev_Block);

            Gen_Abbrev_Header (DW_TAG_Lexical_Block, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
            Gen_Abbrev_Tuple (DW_AT_Low_Pc, DW_FORM_Addr);
            Gen_Abbrev_Tuple (DW_AT_High_Pc, DW_FORM_Addr);
            Gen_Abbrev_Tuple (0, 0);
         end if;

         Gen_Info_Header (Abbrev_Block);
         Sibling_Pc := Gen_Info_Sibling;

         Gen_Ua_Addr (Subprg_Sym, Integer_32 (Get_Block_Info1 (Decl)));
         Gen_Ua_Addr (Subprg_Sym, Integer_32 (Get_Block_Info2 (Decl)));
      end if;

      --  Emit decls for children.
      Last := Get_Block_Last (Decl);
      Sdecl := Decl + 1;
      while Sdecl <= Last loop
         Emit_Decl (Sdecl);
         Sdecl := Get_Decl_Chain (Sdecl);
      end loop;

      if Flag_Debug >= Debug_Dwarf then
         --  End of children.
         Set_Current_Section (Info_Sect);
         Gen_Uleb128 (0);

         Patch_Info_Sibling (Sibling_Pc);
      end if;
   end Emit_Block_Decl;

   Abbrev_Function : Unsigned_32 := 0;
   Abbrev_Procedure : Unsigned_32 := 0;
   Abbrev_Interface : Unsigned_32 := 0;

   procedure Emit_Subprg_Body (Bod : O_Dnode)
   is
      use Ortho_Code.Decls;
      Decl : constant O_Dnode := Get_Body_Decl (Bod);
      Kind : constant OD_Kind := Get_Decl_Kind (Decl);
      Idecl : O_Dnode;
      Prev_Subprg_Sym : Symbol;
      Sibling_Pc : Pc_Type;
   begin
      --  Emit interfaces type.
      if Flag_Debug >= Debug_Dwarf then
         Idecl := Get_Subprg_Interfaces (Decl);
         while Idecl /= O_Dnode_Null loop
            Emit_Type_Maybe (Get_Decl_Type (Idecl));
            Idecl := Get_Interface_Chain (Idecl);
         end loop;
         if Kind = OD_Function then
            Emit_Type_Maybe (Get_Decl_Type (Decl));
         end if;
      end if;

      if Kind = OD_Function then
         if Abbrev_Function = 0 then
            Generate_Abbrev (Abbrev_Function);

            Gen_Abbrev_Header (DW_TAG_Subprogram, DW_CHILDREN_Yes);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Gen_Abbrev_Tuple (DW_AT_Low_Pc, DW_FORM_Addr);
            Gen_Abbrev_Tuple (DW_AT_High_Pc, DW_FORM_Addr);

            if Flag_Debug >= Debug_Dwarf then
               Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
               Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
               Gen_Abbrev_Tuple (DW_AT_Frame_Base, DW_FORM_Block1);
            end if;
            --Gen_Abbrev_Tuple (DW_AT_Return_Addr, DW_FORM_Block1);
            Gen_Abbrev_Tuple (0, 0);
         end if;
         Gen_Info_Header (Abbrev_Function);
      else
         if Abbrev_Procedure = 0 then
            Generate_Abbrev (Abbrev_Procedure);

            Gen_Abbrev_Header (DW_TAG_Subprogram, DW_CHILDREN_Yes);

            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Gen_Abbrev_Tuple (DW_AT_Low_Pc, DW_FORM_Addr);
            Gen_Abbrev_Tuple (DW_AT_High_Pc, DW_FORM_Addr);
            if Flag_Debug >= Debug_Dwarf then
               Gen_Abbrev_Tuple (DW_AT_Sibling, DW_FORM_Ref4);
               Gen_Abbrev_Tuple (DW_AT_Frame_Base, DW_FORM_Block1);
            end if;
            --Gen_Abbrev_Tuple (DW_AT_Return_Addr, DW_FORM_Block1);
            Gen_Abbrev_Tuple (0, 0);
         end if;
         Gen_Info_Header (Abbrev_Procedure);
      end if;

      --  Name.
      Emit_Decl_Ident (Decl);

      --  Low, High.
      Prev_Subprg_Sym := Subprg_Sym;
      Subprg_Sym := Binary.Get_Decl_Symbol (Decl);
      Gen_Ua_Addr (Subprg_Sym, 0);
      Gen_Ua_Addr (Subprg_Sym, Integer_32 (Get_Body_Info (Bod)));

      if Flag_Debug >= Debug_Dwarf then
         --  Type.
         if Kind = OD_Function then
            Emit_Decl_Type (Decl);
         end if;

         --  Sibling.
         Sibling_Pc := Gen_Info_Sibling;

         --  Frame base.
         Gen_8 (1);
         case Arch is
            when Arch_X86 =>
               Gen_8 (DW_OP_Reg5); --  ebp
            when Arch_X86_64 =>
               Gen_8 (DW_OP_Reg6); --  rbp
            when others =>
               raise Program_Error;
         end case;
      end if;

      --  Interfaces.
      Idecl := Get_Subprg_Interfaces (Decl);
      if Idecl /= O_Dnode_Null
        and then Flag_Debug >= Debug_Dwarf
      then
         if Abbrev_Interface = 0 then
            Generate_Abbrev (Abbrev_Interface);

            Gen_Abbrev_Header (DW_TAG_Formal_Parameter, DW_CHILDREN_No);
            Gen_Abbrev_Tuple (DW_AT_Type, DW_FORM_Ref4);
            Gen_Abbrev_Tuple (DW_AT_Name, DW_FORM_String);
            Gen_Abbrev_Tuple (DW_AT_Location, DW_FORM_Block1);
            Gen_Abbrev_Tuple (0, 0);
         end if;

         loop
            Gen_Info_Header (Abbrev_Interface);
            Emit_Decl_Type (Idecl);
            Emit_Decl_Ident (Idecl);

            Emit_Local_Location (Idecl);

            Idecl := Get_Interface_Chain (Idecl);
            exit when Idecl = O_Dnode_Null;
         end loop;
      end if;

      --  Internal declarations.
      Emit_Block_Decl (Bod + 1);

      --  End of children.
      Gen_Uleb128 (0);

      if Flag_Debug >= Debug_Dwarf then
         Patch_Info_Sibling (Sibling_Pc);
      end if;

      Subprg_Sym := Prev_Subprg_Sym;
   end Emit_Subprg_Body;

   procedure Emit_Decl (Decl : O_Dnode)
   is
      use Ada.Text_IO;
      use Ortho_Code.Decls;
   begin
      if Flag_Debug = Debug_Dwarf then
         case Get_Decl_Kind (Decl) is
            when OD_Type =>
               Emit_Type_Decl (Decl);
            when OD_Completer =>
               Emit_Type_Completer (Decl);
            when OD_Local
              | OD_Var =>
               Emit_Variable (Decl);
            when OD_Const =>
               Emit_Const (Decl);
            when OD_Function
              | OD_Procedure
              | OD_Interface =>
               null;
            when OD_Body =>
               Emit_Subprg_Body (Decl);
            when OD_Block =>
               Emit_Block_Decl (Decl);
            when others =>
               Put_Line ("dwarf.emit_decl: emit "
                           & OD_Kind'Image (Get_Decl_Kind (Decl)));
         end case;
      elsif Flag_Debug = Debug_Line then
         if Get_Decl_Kind (Decl) = OD_Body then
            Emit_Subprg_Body (Decl);
         end if;
      end if;
   end Emit_Decl;

   procedure Emit_Subprg (Bod : O_Dnode) is
   begin
      Emit_Decls_Until (Bod);
      Emit_Decl (Bod);
      Last_Decl := Decls.Get_Decl_Chain (Bod);
   end Emit_Subprg;

   procedure Mark (M : out Mark_Type) is
   begin
      M.Last_Decl := Last_Decl;
      M.Last_Tnode := TOnodes.Last;
   end Mark;

   procedure Release (M : Mark_Type) is
   begin
      Last_Decl := M.Last_Decl;
      TOnodes.Set_Last (M.Last_Tnode);
   end Release;

end Ortho_Code.Dwarf;
