-- Mcode back-end for ortho - Binary X86 instructions generator. -- Copyright (C) 2006 Tristan Gingold -- -- GHDL 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, or (at your option) any later -- version. -- -- GHDL 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 GCC; see the file COPYING. If not, write to the Free -- Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. with Ortho_Code.Abi; with Ortho_Code.Decls; with Ortho_Code.Types; with Ortho_Code.Consts; with Ortho_Code.Debug; with Ortho_Code.X86.Insns; with Ortho_Code.X86.Flags; with Ortho_Code.Flags; with Ortho_Code.Dwarf; with Ortho_Code.Binary; use Ortho_Code.Binary; with Ortho_Ident; with Ada.Text_IO; with Interfaces; use Interfaces; package body Ortho_Code.X86.Emits is type Insn_Size is (Sz_8, Sz_16, Sz_32l, Sz_32h); type Fp_Size is (Fp_32, Fp_64); Sect_Text : Binary_File.Section_Acc; Sect_Rodata : Binary_File.Section_Acc; Sect_Bss : Binary_File.Section_Acc; Reg_Helper : O_Reg; Subprg_Pc : Pc_Type; procedure Error_Emit (Msg : String; Insn : O_Enode) is use Ada.Text_IO; begin Put ("error_emit: "); Put (Msg); Put (", insn="); Put (O_Enode'Image (Insn)); Put (" ("); Put (OE_Kind'Image (Get_Expr_Kind (Insn))); Put (")"); New_Line; raise Program_Error; end Error_Emit; procedure Gen_Insn_Sz (B : Byte; Sz : Insn_Size) is begin case Sz is when Sz_8 => Gen_B8 (B); when Sz_16 => Gen_B8 (16#66#); Gen_B8 (B + 1); when Sz_32l | Sz_32h => Gen_B8 (B + 1); end case; end Gen_Insn_Sz; procedure Gen_Insn_Sz_S8 (B : Byte; Sz : Insn_Size) is begin case Sz is when Sz_8 => Gen_B8 (B); when Sz_16 => Gen_B8 (16#66#); Gen_B8 (B + 3); when Sz_32l | Sz_32h => Gen_B8 (B + 3); end case; end Gen_Insn_Sz_S8; function Get_Const_Val (C : O_Enode; Sz : Insn_Size) return Uns32 is begin case Sz is when Sz_8 | Sz_16 | Sz_32l => return Get_Expr_Low (C); when Sz_32h => return Get_Expr_High (C); end case; end Get_Const_Val; function Is_Imm8 (N : O_Enode; Sz : Insn_Size) return Boolean is begin if Get_Expr_Kind (N) /= OE_Const then return False; end if; return Get_Const_Val (N, Sz) <= 127; end Is_Imm8; procedure Gen_Imm8 (N : O_Enode; Sz : Insn_Size) is begin Gen_B8 (Byte (Get_Const_Val (N, Sz))); end Gen_Imm8; -- procedure Gen_Imm32 (N : O_Enode; Sz : Insn_Size) -- is -- use Interfaces; -- begin -- case Get_Expr_Kind (N) is -- when OE_Const => -- Gen_Le32 (Unsigned_32 (Get_Const_Val (N, Sz))); -- when OE_Addrg => -- Gen_X86_32 (Get_Decl_Symbol (Get_Addr_Object (N)), 0); -- when others => -- raise Program_Error; -- end case; -- end Gen_Imm32; procedure Gen_Imm (N : O_Enode; Sz : Insn_Size) is begin case Get_Expr_Kind (N) is when OE_Const => case Sz is when Sz_8 => Gen_B8 (Byte (Get_Expr_Low (N) and 16#FF#)); when Sz_16 => Gen_Le16 (Unsigned_32 (Get_Expr_Low (N) and 16#FF_FF#)); when Sz_32l => Gen_Le32 (Unsigned_32 (Get_Expr_Low (N))); when Sz_32h => Gen_Le32 (Unsigned_32 (Get_Expr_High (N))); end case; when OE_Addrg => if Sz /= Sz_32l then raise Program_Error; end if; Gen_X86_32 (Get_Decl_Symbol (Get_Addr_Object (N)), 0); when OE_Add => declare P : O_Enode; L, R : O_Enode; S, C : O_Enode; Off : Int32; begin Off := 0; P := N; if Sz /= Sz_32l then raise Program_Error; end if; loop L := Get_Expr_Left (P); R := Get_Expr_Right (P); -- Extract the const node. if Get_Expr_Kind (R) = OE_Const then S := L; C := R; elsif Get_Expr_Kind (L) = OE_Const then S := R; C := L; else raise Program_Error; end if; if Get_Expr_Mode (C) /= Mode_U32 then raise Program_Error; end if; Off := Off + To_Int32 (Get_Expr_Low (C)); exit when Get_Expr_Kind (S) = OE_Addrg; P := S; if Get_Expr_Kind (P) /= OE_Add then raise Program_Error; end if; end loop; Gen_X86_32 (Get_Decl_Symbol (Get_Addr_Object (S)), Integer_32 (Off)); end; when others => raise Program_Error; end case; end Gen_Imm; Rm_Base : O_Reg; Rm_Index : O_Reg; Rm_Offset : Int32; Rm_Sym : Symbol; Rm_Scale : Byte; procedure Fill_Sib (N : O_Enode) is use Ortho_Code.Decls; Reg : O_Reg; begin Reg := Get_Expr_Reg (N); if Reg in Regs_R32 then if Rm_Base = R_Nil then Rm_Base := Reg; elsif Rm_Index = R_Nil then Rm_Index := Reg; else raise Program_Error; end if; return; end if; case Get_Expr_Kind (N) is when OE_Indir => Fill_Sib (Get_Expr_Operand (N)); when OE_Addrl => declare Frame : O_Enode; begin Frame := Get_Addrl_Frame (N); if Frame = O_Enode_Null then Rm_Base := R_Bp; else Rm_Base := Get_Expr_Reg (Frame); end if; end; Rm_Offset := Rm_Offset + Get_Local_Offset (Get_Addr_Object (N)); when OE_Addrg => if Rm_Sym /= Null_Symbol then raise Program_Error; end if; Rm_Sym := Get_Decl_Symbol (Get_Addr_Object (N)); when OE_Add => Fill_Sib (Get_Expr_Left (N)); Fill_Sib (Get_Expr_Right (N)); when OE_Const => Rm_Offset := Rm_Offset + To_Int32 (Get_Expr_Low (N)); when OE_Shl => if Rm_Index /= R_Nil then raise Program_Error; end if; Rm_Index := Get_Expr_Reg (Get_Expr_Left (N)); Rm_Scale := Byte (Get_Expr_Low (Get_Expr_Right (N))); when others => Error_Emit ("fill_sib", N); end case; end Fill_Sib; function To_Reg32 (R : O_Reg) return Byte is begin return O_Reg'Pos (R) - O_Reg'Pos (R_Ax); end To_Reg32; pragma Inline (To_Reg32); function To_Reg_Xmm (R : O_Reg) return Byte is begin return O_Reg'Pos (R) - O_Reg'Pos (R_Xmm0); end To_Reg_Xmm; pragma Inline (To_Reg_Xmm); function To_Reg32 (R : O_Reg; Sz : Insn_Size) return Byte is begin case Sz is when Sz_8 => if R in Regs_R8 then return O_Reg'Pos (R) - O_Reg'Pos (R_Ax); else raise Program_Error; end if; when Sz_16 => if R in Regs_R32 then return O_Reg'Pos (R) - O_Reg'Pos (R_Ax); else raise Program_Error; end if; when Sz_32l => case R is when Regs_R32 => return O_Reg'Pos (R) - O_Reg'Pos (R_Ax); when R_Edx_Eax => return 2#000#; when R_Ebx_Ecx => return 2#001#; when R_Esi_Edi => return 2#111#; when others => raise Program_Error; end case; when Sz_32h => case R is when R_Edx_Eax => return 2#010#; when R_Ebx_Ecx => return 2#011#; when R_Esi_Edi => return 2#110#; when others => raise Program_Error; end case; end case; end To_Reg32; function To_Cond (R : O_Reg) return Byte is begin return O_Reg'Pos (R) - O_Reg'Pos (R_Ov); end To_Cond; pragma Inline (To_Cond); procedure Gen_Sib is begin if Rm_Base = R_Nil then Gen_B8 (Rm_Scale * 2#1_000_000# + To_Reg32 (Rm_Index) * 2#1_000# + 2#101#); else Gen_B8 (Rm_Scale * 2#1_000_000# + To_Reg32 (Rm_Index) * 2#1_000# + To_Reg32 (Rm_Base)); end if; end Gen_Sib; -- Generate an R/M (+ SIB) byte. -- R is added to the R/M byte. procedure Gen_Rm_Mem (R : Byte; N : O_Enode; Sz : Insn_Size) is Reg : O_Reg; begin Reg := Get_Expr_Reg (N); Rm_Base := R_Nil; Rm_Index := R_Nil; if Sz = Sz_32h then Rm_Offset := 4; else Rm_Offset := 0; end if; Rm_Scale := 0; Rm_Sym := Null_Symbol; case Reg is when R_Mem | R_Imm | R_Eq | R_B_Off | R_B_I | R_I_Off | R_Sib => Fill_Sib (N); when Regs_R32 => Rm_Base := Reg; when R_Spill => Rm_Base := R_Bp; Rm_Offset := Rm_Offset + Get_Spill_Info (N); when others => Error_Emit ("gen_rm_mem: unhandled reg", N); end case; if Rm_Index /= R_Nil then -- SIB. if Rm_Base = R_Nil then Gen_B8 (2#00_000_100# + R); Rm_Base := R_Bp; Gen_Sib; Gen_X86_32 (Rm_Sym, Integer_32 (Rm_Offset)); elsif Rm_Sym = Null_Symbol and Rm_Offset = 0 and Rm_Base /= R_Bp then Gen_B8 (2#00_000_100# + R); Gen_Sib; elsif Rm_Sym = Null_Symbol and Rm_Offset <= 127 and Rm_Offset >= -128 then Gen_B8 (2#01_000_100# + R); Gen_Sib; Gen_B8 (Byte (To_Uns32 (Rm_Offset) and 16#Ff#)); else Gen_B8 (2#10_000_100# + R); Gen_Sib; Gen_X86_32 (Rm_Sym, Integer_32 (Rm_Offset)); end if; return; end if; case Rm_Base is when R_Sp => raise Program_Error; when R_Nil => Gen_B8 (2#00_000_101# + R); Gen_X86_32 (Rm_Sym, Integer_32 (Rm_Offset)); when R_Ax | R_Bx | R_Cx | R_Dx | R_Bp | R_Si | R_Di => if Rm_Offset = 0 and Rm_Sym = Null_Symbol and Rm_Base /= R_Bp then Gen_B8 (2#00_000_000# + R + To_Reg32 (Rm_Base)); elsif Rm_Sym = Null_Symbol and Rm_Offset <= 127 and Rm_Offset >= -128 then Gen_B8 (2#01_000_000# + R + To_Reg32 (Rm_Base)); Gen_B8 (Byte (To_Uns32 (Rm_Offset) and 16#Ff#)); else Gen_B8 (2#10_000_000# + R + To_Reg32 (Rm_Base)); Gen_X86_32 (Rm_Sym, Integer_32 (Rm_Offset)); end if; when others => raise Program_Error; end case; end Gen_Rm_Mem; procedure Gen_Rm (R : Byte; N : O_Enode; Sz : Insn_Size) is Reg : O_Reg; begin Reg := Get_Expr_Reg (N); if Reg in Regs_R32 or Reg in Regs_R64 then Gen_B8 (2#11_000_000# + R + To_Reg32 (Reg, Sz)); return; else Gen_Rm_Mem (R, N, Sz); end if; end Gen_Rm; procedure Emit_Op (Op : Byte; Stmt : O_Enode; Sz : Insn_Size) is L, R : O_Enode; Lr, Rr : O_Reg; begin L := Get_Expr_Left (Stmt); R := Get_Expr_Right (Stmt); Lr := Get_Expr_Reg (L); Rr := Get_Expr_Reg (R); Start_Insn; case Rr is when R_Imm => if Is_Imm8 (R, Sz) then Gen_Insn_Sz_S8 (16#80#, Sz); Gen_Rm (Op, L, Sz); Gen_Imm8 (R, Sz); elsif Lr = R_Ax then Gen_Insn_Sz (2#000_000_100# + Op, Sz); Gen_Imm (R, Sz); else Gen_Insn_Sz (16#80#, Sz); Gen_Rm (Op, L, Sz); Gen_Imm (R, Sz); end if; when R_Mem | R_Spill | Regs_R32 | Regs_R64 => Gen_Insn_Sz (2#00_000_010# + Op, Sz); Gen_Rm (To_Reg32 (Lr, Sz) * 8, R, Sz); when others => Error_Emit ("emit_op", Stmt); end case; End_Insn; end Emit_Op; procedure Gen_Into is begin Start_Insn; Gen_B8 (2#1100_1110#); End_Insn; end Gen_Into; procedure Gen_Cdq is begin Start_Insn; Gen_B8 (2#1001_1001#); End_Insn; end Gen_Cdq; procedure Gen_Mono_Op (Op : Byte; Val : O_Enode; Sz : Insn_Size) is begin Start_Insn; Gen_Insn_Sz (2#1111_011_0#, Sz); Gen_Rm (Op, Val, Sz); End_Insn; end Gen_Mono_Op; procedure Emit_Mono_Op_Stmt (Op : Byte; Stmt : O_Enode; Sz : Insn_Size) is begin Gen_Mono_Op (Op, Get_Expr_Operand (Stmt), Sz); end Emit_Mono_Op_Stmt; procedure Emit_Load_Imm (Stmt : O_Enode; Sz : Insn_Size) is Tr : O_Reg; begin Tr := Get_Expr_Reg (Stmt); Start_Insn; -- FIXME: handle 0. case Sz is when Sz_8 => Gen_B8 (2#1011_0_000# + To_Reg32 (Tr, Sz)); when Sz_16 => Gen_B8 (16#66#); Gen_B8 (2#1011_1_000# + To_Reg32 (Tr, Sz)); when Sz_32l | Sz_32h => Gen_B8 (2#1011_1_000# + To_Reg32 (Tr, Sz)); end case; Gen_Imm (Stmt, Sz); End_Insn; end Emit_Load_Imm; function Fp_Size_To_Mf (Sz : Fp_Size) return Byte is begin case Sz is when Fp_32 => return 2#00_0#; when Fp_64 => return 2#10_0#; end case; end Fp_Size_To_Mf; procedure Emit_Load_Fp (Stmt : O_Enode; Sz : Fp_Size) is Sym : Symbol; R : O_Reg; begin Set_Current_Section (Sect_Rodata); Gen_Pow_Align (3); Prealloc (8); Sym := Create_Local_Symbol; Set_Symbol_Pc (Sym, False); Gen_Le32 (Unsigned_32 (Get_Expr_Low (Stmt))); if Sz = Fp_64 then Gen_Le32 (Unsigned_32 (Get_Expr_High (Stmt))); end if; Set_Current_Section (Sect_Text); R := Get_Expr_Reg (Stmt); case R is when R_St0 => Start_Insn; Gen_B8 (2#11011_001# + Fp_Size_To_Mf (Sz)); Gen_B8 (2#00_000_101#); Gen_X86_32 (Sym, 0); End_Insn; when Regs_Xmm => Start_Insn; case Sz is when Fp_32 => Gen_B8 (16#F3#); when Fp_64 => Gen_B8 (16#F2#); end case; Gen_B8 (16#0f#); Gen_B8 (16#10#); Gen_B8 (2#00_000_101# + To_Reg_Xmm (R) * 2#1_000#); Gen_X86_32 (Sym, 0); End_Insn; when others => raise Program_Error; end case; end Emit_Load_Fp; procedure Emit_Load_Fp_Mem (Stmt : O_Enode; Sz : Fp_Size) is begin Start_Insn; Gen_B8 (2#11011_001# + Fp_Size_To_Mf (Sz)); Gen_Rm_Mem (2#000_000#, Get_Expr_Operand (Stmt), Sz_32l); End_Insn; end Emit_Load_Fp_Mem; procedure Emit_Load_Mem (Stmt : O_Enode; Sz : Insn_Size) is Tr : O_Reg; Val : O_Enode; begin Tr := Get_Expr_Reg (Stmt); Val := Get_Expr_Operand (Stmt); case Tr is when Regs_R32 | Regs_R64 => -- mov REG, OP Start_Insn; Gen_Insn_Sz (2#1000_101_0#, Sz); Gen_Rm_Mem (To_Reg32 (Tr, Sz) * 8, Val, Sz); End_Insn; when R_Eq => -- Cmp OP, 1 Start_Insn; Gen_Insn_Sz_S8 (2#1000_000_0#, Sz); Gen_Rm_Mem (2#111_000#, Val, Sz); Gen_B8 (1); End_Insn; when others => Error_Emit ("emit_load_mem", Stmt); end case; end Emit_Load_Mem; procedure Emit_Store (Stmt : O_Enode; Sz : Insn_Size) is T, R : O_Enode; Tr, Rr : O_Reg; B : Byte; begin T := Get_Assign_Target (Stmt); R := Get_Expr_Operand (Stmt); Tr := Get_Expr_Reg (T); Rr := Get_Expr_Reg (R); Start_Insn; case Rr is when R_Imm => if False and (Tr in Regs_R32 or Tr in Regs_R64) then B := 2#1011_1_000#; case Sz is when Sz_8 => B := B and not 2#0000_1_000#; when Sz_16 => Gen_B8 (16#66#); when Sz_32l | Sz_32h => null; end case; Gen_B8 (B + To_Reg32 (Tr, Sz)); else Gen_Insn_Sz (2#1100_011_0#, Sz); Gen_Rm_Mem (16#00#, T, Sz); end if; Gen_Imm (R, Sz); when Regs_R32 | Regs_R64 => Gen_Insn_Sz (2#1000_100_0#, Sz); Gen_Rm_Mem (To_Reg32 (Rr, Sz) * 8, T, Sz); when others => Error_Emit ("emit_store", Stmt); end case; End_Insn; end Emit_Store; procedure Emit_Store_Fp (Stmt : O_Enode; Sz : Fp_Size) is begin -- fstp Start_Insn; Gen_B8 (2#11011_00_1# + Fp_Size_To_Mf (Sz)); Gen_Rm_Mem (2#011_000#, Get_Assign_Target (Stmt), Sz_32l); End_Insn; end Emit_Store_Fp; procedure Emit_Push_32 (Val : O_Enode; Sz : Insn_Size) is R : O_Reg; begin R := Get_Expr_Reg (Val); Start_Insn; case R is when R_Imm => if Is_Imm8 (Val, Sz) then Gen_B8 (2#0110_1010#); Gen_Imm8 (Val, Sz); else Gen_B8 (2#0110_1000#); Gen_Imm (Val, Sz); end if; when Regs_R32 | Regs_R64 => Gen_B8 (2#01010_000# + To_Reg32 (R, Sz)); when others => Gen_B8 (2#1111_1111#); Gen_Rm (2#110_000#, Val, Sz); end case; End_Insn; end Emit_Push_32; procedure Emit_Pop_32 (Val : O_Enode; Sz : Insn_Size) is R : O_Reg; begin R := Get_Expr_Reg (Val); Start_Insn; case R is when Regs_R32 | Regs_R64 => Gen_B8 (2#01011_000# + To_Reg32 (R, Sz)); when others => Gen_B8 (2#1000_1111#); Gen_Rm (2#000_000#, Val, Sz); end case; End_Insn; end Emit_Pop_32; procedure Emit_Push_Fp (Op : O_Enode; Sz : Fp_Size) is pragma Unreferenced (Op); begin Start_Insn; -- subl esp, val Gen_B8 (2#100000_11#); Gen_B8 (2#11_101_100#); case Sz is when Fp_32 => Gen_B8 (4); when Fp_64 => Gen_B8 (8); end case; End_Insn; -- fstp st, (esp) Start_Insn; Gen_B8 (2#11011_001# + Fp_Size_To_Mf (Sz)); Gen_B8 (2#00_011_100#); Gen_B8 (2#00_100_100#); End_Insn; end Emit_Push_Fp; function Prepare_Label (Label : O_Enode) return Symbol is Sym : Symbol; begin Sym := Get_Label_Symbol (Label); if Sym = Null_Symbol then Sym := Create_Local_Symbol; Set_Label_Symbol (Label, Sym); end if; return Sym; end Prepare_Label; procedure Emit_Jmp_T (Stmt : O_Enode; Reg : O_Reg) is Sym : Symbol; Val : Pc_Type; Opc : Byte; begin Sym := Prepare_Label (Get_Jump_Label (Stmt)); Val := Get_Symbol_Value (Sym); Start_Insn; Opc := To_Cond (Reg); if Val = 0 then -- Assume long jmp. Gen_B8 (16#0f#); Gen_B8 (16#80# + Opc); Gen_X86_Pc32 (Sym); else if Val + 128 < Get_Current_Pc + 4 then -- Long jmp. Gen_B8 (16#0f#); Gen_B8 (16#80# + Opc); Gen_Le32 (Unsigned_32 (Val - (Get_Current_Pc + 4))); else -- short jmp. Gen_B8 (16#70# + Opc); Gen_B8 (Byte (Val - (Get_Current_Pc + 1))); end if; end if; End_Insn; end Emit_Jmp_T; procedure Emit_Jmp (Stmt : O_Enode) is Sym : Symbol; Val : Pc_Type; begin Sym := Prepare_Label (Get_Jump_Label (Stmt)); Val := Get_Symbol_Value (Sym); Start_Insn; if Val = 0 then -- Assume long jmp. Gen_B8 (16#e9#); Gen_X86_Pc32 (Sym); else if Val + 128 < Get_Current_Pc + 4 then -- Long jmp. Gen_B8 (16#e9#); Gen_Le32 (Unsigned_32 (Val - (Get_Current_Pc + 4))); else -- short jmp. Gen_B8 (16#eb#); Gen_B8 (Byte ((Val - (Get_Current_Pc + 1)) and 16#Ff#)); end if; end if; End_Insn; end Emit_Jmp; procedure Emit_Label (Stmt : O_Enode) is Sym : Symbol; begin Sym := Prepare_Label (Stmt); Set_Symbol_Pc (Sym, False); end Emit_Label; procedure Gen_Call (Sym : Symbol) is begin Start_Insn; Gen_B8 (16#E8#); Gen_X86_Pc32 (Sym); End_Insn; end Gen_Call; procedure Emit_Setup_Frame (Stmt : O_Enode) is Val : constant Int32 := Get_Stack_Adjust (Stmt); begin if Val > 0 then Start_Insn; -- subl esp, val Gen_B8 (2#100000_11#); Gen_B8 (2#11_101_100#); Gen_B8 (Byte (Val)); End_Insn; elsif Val < 0 then Start_Insn; if -Val <= 127 then -- addl esp, val Gen_B8 (2#100000_11#); Gen_B8 (2#11_000_100#); Gen_B8 (Byte (-Val)); else -- addl esp, val Gen_B8 (2#100000_01#); Gen_B8 (2#11_000_100#); Gen_Le32 (Unsigned_32 (-Val)); end if; End_Insn; end if; end Emit_Setup_Frame; procedure Emit_Call (Stmt : O_Enode) is use Ortho_Code.Decls; Subprg : O_Dnode; Sym : Symbol; begin Subprg := Get_Call_Subprg (Stmt); Sym := Get_Decl_Symbol (Subprg); Gen_Call (Sym); end Emit_Call; procedure Emit_Intrinsic (Stmt : O_Enode) is Op : Int32; begin Op := Get_Intrinsic_Operation (Stmt); Start_Insn; Gen_B8 (16#E8#); Gen_X86_Pc32 (Intrinsics_Symbol (Op)); End_Insn; Start_Insn; -- addl esp, val Gen_B8 (2#100000_11#); Gen_B8 (2#11_000_100#); Gen_B8 (16); End_Insn; end Emit_Intrinsic; procedure Emit_Setcc (Dest : O_Enode; Cond : O_Reg) is begin if Cond not in Regs_Cc then raise Program_Error; end if; Start_Insn; Gen_B8 (16#0f#); Gen_B8 (16#90# + To_Cond (Cond)); Gen_Rm (2#000_000#, Dest, Sz_8); End_Insn; end Emit_Setcc; procedure Emit_Setcc_Reg (Reg : O_Reg; Cond : O_Reg) is begin if Cond not in Regs_Cc then raise Program_Error; end if; Start_Insn; Gen_B8 (16#0f#); Gen_B8 (16#90# + To_Cond (Cond)); Gen_B8 (2#11_000_000# + To_Reg32 (Reg, Sz_8)); End_Insn; end Emit_Setcc_Reg; procedure Emit_Tst (Reg : O_Reg; Sz : Insn_Size) is begin Start_Insn; Gen_Insn_Sz (2#1000_0100#, Sz); Gen_B8 (2#11_000_000# + To_Reg32 (Reg, Sz) * 9); End_Insn; end Emit_Tst; procedure Gen_Cmp_Imm (Reg : O_Reg; Val : Int32; Sz : Insn_Size) is B : Byte; begin Start_Insn; if Val <= 127 and Val >= -128 then B := 2#10#; else B := 0; end if; Gen_Insn_Sz (2#1000_0000# + B, Sz); Gen_B8 (2#11_111_000# + To_Reg32 (Reg)); if B = 0 then Gen_Le32 (Unsigned_32 (To_Uns32 (Val))); else Gen_B8 (Byte (To_Uns32 (Val) and 16#Ff#)); end if; End_Insn; end Gen_Cmp_Imm; procedure Emit_Spill (Stmt : O_Enode; Sz : Insn_Size) is Reg : O_Reg; Expr : O_Enode; begin Expr := Get_Expr_Operand (Stmt); Reg := Get_Expr_Reg (Expr); if Reg = R_Spill then if Get_Expr_Kind (Expr) = OE_Conv then return; else raise Program_Error; end if; end if; Start_Insn; Gen_Insn_Sz (2#1000_1000#, Sz); Gen_Rm (To_Reg32 (Reg, Sz) * 8, Stmt, Sz); End_Insn; end Emit_Spill; procedure Emit_Load (Reg : O_Reg; Val : O_Enode; Sz : Insn_Size) is begin Start_Insn; Gen_Insn_Sz (2#1000_1010#, Sz); Gen_Rm (To_Reg32 (Reg, Sz) * 8, Val, Sz); End_Insn; end Emit_Load; procedure Emit_Lea (Stmt : O_Enode) is Reg : O_Reg; begin -- Hack: change the register to use the real address instead of it. Reg := Get_Expr_Reg (Stmt); Set_Expr_Reg (Stmt, R_Mem); Start_Insn; Gen_B8 (2#10001101#); Gen_Rm_Mem (To_Reg32 (Reg) * 8, Stmt, Sz_32l); End_Insn; Set_Expr_Reg (Stmt, Reg); end Emit_Lea; procedure Gen_Umul (Stmt : O_Enode; Sz : Insn_Size) is begin if Get_Expr_Reg (Get_Expr_Left (Stmt)) /= R_Ax then raise Program_Error; end if; Start_Insn; Gen_Insn_Sz (16#F6#, Sz); Gen_Rm (2#100_000#, Get_Expr_Right (Stmt), Sz); End_Insn; end Gen_Umul; procedure Gen_Mul (Stmt : O_Enode; Sz : Insn_Size) is Reg : O_Reg; Right : O_Enode; Reg_R : O_Reg; begin Reg := Get_Expr_Reg (Stmt); Right := Get_Expr_Right (Stmt); if Get_Expr_Reg (Get_Expr_Left (Stmt)) /= Reg or Sz /= Sz_32l then raise Program_Error; end if; Start_Insn; if Reg = R_Ax then Gen_Insn_Sz (16#F6#, Sz); Gen_Rm (2#100_000#, Right, Sz); else Reg_R := Get_Expr_Reg (Right); case Reg_R is when R_Imm => if Is_Imm8 (Right, Sz) then Gen_B8 (16#6B#); Gen_B8 (To_Reg32 (Reg, Sz) * 9 or 2#11_000_000#); Gen_Imm8 (Right, Sz); else Gen_B8 (16#69#); Gen_B8 (To_Reg32 (Reg, Sz) * 9 or 2#11_000_000#); Gen_Imm (Right, Sz); end if; when R_Mem | R_Spill | Regs_R32 => Gen_B8 (16#0F#); Gen_B8 (16#AF#); Gen_Rm (To_Reg32 (Reg, Sz) * 8, Right, Sz); when others => Error_Emit ("gen_mul", Stmt); end case; end if; End_Insn; end Gen_Mul; -- Do not trap if COND is true. procedure Gen_Ov_Check (Cond : O_Reg) is begin -- JXX +2 Start_Insn; Gen_B8 (16#70# + To_Cond (Cond)); Gen_B8 (16#02#); End_Insn; -- INT 4 (overflow). Start_Insn; Gen_B8 (16#CD#); Gen_B8 (16#04#); End_Insn; end Gen_Ov_Check; procedure Emit_Abs (Val : O_Enode; Mode : Mode_Type) is Szh : Insn_Size; Pc_Jmp : Pc_Type; begin case Mode is when Mode_I32 => Szh := Sz_32l; when Mode_I64 => Szh := Sz_32h; when others => raise Program_Error; end case; Emit_Tst (Get_Expr_Reg (Val), Szh); -- JXX + Start_Insn; Gen_B8 (16#70# + To_Cond (R_Sge)); Gen_B8 (0); End_Insn; Pc_Jmp := Get_Current_Pc; -- NEG Gen_Mono_Op (2#011_000#, Val, Sz_32l); if Mode = Mode_I64 then -- Propagate carray. -- Adc reg,0 -- neg reg Start_Insn; Gen_B8 (2#100000_11#); Gen_Rm (2#010_000#, Val, Sz_32h); Gen_B8 (0); End_Insn; Gen_Mono_Op (2#011_000#, Val, Sz_32h); end if; Gen_Into; Patch_B8 (Pc_Jmp - 1, Unsigned_8 (Get_Current_Pc - Pc_Jmp)); end Emit_Abs; procedure Gen_Alloca (Stmt : O_Enode) is Reg : O_Reg; begin Reg := Get_Expr_Reg (Get_Expr_Operand (Stmt)); if Reg not in Regs_R32 or else Reg /= Get_Expr_Reg (Stmt) then raise Program_Error; end if; -- Align stack on word. -- Add reg, (stack_boundary - 1) Start_Insn; Gen_B8 (2#1000_0011#); Gen_B8 (2#11_000_000# + To_Reg32 (Reg)); Gen_B8 (Byte (X86.Flags.Stack_Boundary - 1)); End_Insn; -- and reg, ~(stack_boundary - 1) Start_Insn; Gen_B8 (2#1000_0001#); Gen_B8 (2#11_100_000# + To_Reg32 (Reg)); Gen_Le32 (not (X86.Flags.Stack_Boundary - 1)); End_Insn; if X86.Flags.Flag_Alloca_Call then Gen_Call (Chkstk_Symbol); else -- subl esp, reg Start_Insn; Gen_B8 (2#0001_1011#); Gen_B8 (2#11_100_000# + To_Reg32 (Reg)); End_Insn; end if; -- movl reg, esp Start_Insn; Gen_B8 (2#1000_1001#); Gen_B8 (2#11_100_000# + To_Reg32 (Reg)); End_Insn; end Gen_Alloca; -- Byte/word to long. procedure Gen_Movzx (Reg : Regs_R32; Op : O_Enode; Sz : Insn_Size) is B : Byte; begin Start_Insn; Gen_B8 (16#0f#); case Sz is when Sz_8 => B := 0; when Sz_16 => B := 1; when Sz_32l | Sz_32h => raise Program_Error; end case; Gen_B8 (2#1011_0110# + B); Gen_Rm (To_Reg32 (Reg) * 8, Op, Sz_8); End_Insn; end Gen_Movzx; -- Convert U32 to xx. procedure Gen_Conv_U32 (Stmt : O_Enode) is Op : O_Enode; Reg_Op : O_Reg; Reg_Res : O_Reg; begin Op := Get_Expr_Operand (Stmt); Reg_Op := Get_Expr_Reg (Op); Reg_Res := Get_Expr_Reg (Stmt); case Get_Expr_Mode (Stmt) is when Mode_I32 => if Reg_Res not in Regs_R32 then raise Program_Error; end if; if Reg_Op /= Reg_Res then Emit_Load (Reg_Res, Op, Sz_32l); end if; Emit_Tst (Reg_Res, Sz_32l); Gen_Ov_Check (R_Sge); when Mode_U8 | Mode_B2 => if Reg_Res not in Regs_R32 then raise Program_Error; end if; if Reg_Op /= Reg_Res then Emit_Load (Reg_Res, Op, Sz_32l); end if; -- cmpl VAL, 0xff Start_Insn; Gen_B8 (2#1000_0001#); Gen_Rm (2#111_000#, Op, Sz_32l); Gen_Le32 (16#00_00_00_Ff#); End_Insn; Gen_Ov_Check (R_Ule); when others => Error_Emit ("gen_conv_u32", Stmt); end case; end Gen_Conv_U32; -- Convert I32 to xxx procedure Gen_Conv_I32 (Stmt : O_Enode) is Op : O_Enode; Reg_Op : O_Reg; Reg_Res : O_Reg; begin Op := Get_Expr_Operand (Stmt); Reg_Op := Get_Expr_Reg (Op); Reg_Res := Get_Expr_Reg (Stmt); case Get_Expr_Mode (Stmt) is when Mode_I64 => if Reg_Res /= R_Edx_Eax or Reg_Op /= R_Ax then raise Program_Error; end if; Gen_Cdq; when Mode_U32 => if Reg_Res not in Regs_R32 then raise Program_Error; end if; if Reg_Op /= Reg_Res then Emit_Load (Reg_Res, Op, Sz_32l); end if; Emit_Tst (Reg_Res, Sz_32l); Gen_Ov_Check (R_Sge); when Mode_B2 => if Reg_Op /= Reg_Res then Emit_Load (Reg_Res, Op, Sz_32l); end if; Gen_Cmp_Imm (Reg_Res, 1, Sz_32l); Gen_Ov_Check (R_Ule); when Mode_U8 => if Reg_Op /= Reg_Res then Emit_Load (Reg_Res, Op, Sz_32l); end if; Gen_Cmp_Imm (Reg_Res, 16#Ff#, Sz_32l); Gen_Ov_Check (R_Ule); when Mode_F64 => Emit_Push_32 (Op, Sz_32l); -- fild (%esp) Start_Insn; Gen_B8 (2#11011_011#); Gen_B8 (2#00_000_100#); Gen_B8 (2#00_100_100#); End_Insn; -- addl %esp, 4 Start_Insn; Gen_B8 (2#100000_11#); Gen_B8 (2#11_000_100#); Gen_B8 (4); End_Insn; when others => Error_Emit ("gen_conv_i32", Stmt); end case; end Gen_Conv_I32; -- Convert U8 to xxx procedure Gen_Conv_U8 (Stmt : O_Enode) is Op : O_Enode; Reg_Res : O_Reg; begin Op := Get_Expr_Operand (Stmt); Reg_Res := Get_Expr_Reg (Stmt); case Get_Expr_Mode (Stmt) is when Mode_U32 | Mode_I32 | Mode_U16 | Mode_I16 => if Reg_Res not in Regs_R32 then raise Program_Error; end if; Gen_Movzx (Reg_Res, Op, Sz_8); when others => Error_Emit ("gen_conv_U8", Stmt); end case; end Gen_Conv_U8; -- Convert B2 to xxx procedure Gen_Conv_B2 (Stmt : O_Enode) is Op : O_Enode; Reg_Res : O_Reg; begin Op := Get_Expr_Operand (Stmt); Reg_Res := Get_Expr_Reg (Stmt); case Get_Expr_Mode (Stmt) is when Mode_U32 | Mode_I32 | Mode_U16 | Mode_I16 => Gen_Movzx (Reg_Res, Op, Sz_8); when others => Error_Emit ("gen_conv_B2", Stmt); end case; end Gen_Conv_B2; -- Convert I64 to xxx procedure Gen_Conv_I64 (Stmt : O_Enode) is Op : O_Enode; begin Op := Get_Expr_Operand (Stmt); case Get_Expr_Mode (Stmt) is when Mode_I32 => -- move dx to reg_helper Start_Insn; Gen_B8 (2#1000_1001#); Gen_B8 (2#11_010_000# + To_Reg32 (Reg_Helper)); End_Insn; Gen_Cdq; -- cmp reg_helper, dx Start_Insn; Gen_B8 (2#0011_1001#); Gen_B8 (2#11_010_000# + To_Reg32 (Reg_Helper)); End_Insn; Gen_Ov_Check (R_Eq); when Mode_F64 => Emit_Push_32 (Op, Sz_32h); Emit_Push_32 (Op, Sz_32l); -- fild (%esp) Start_Insn; Gen_B8 (2#11011_111#); Gen_B8 (2#00_101_100#); Gen_B8 (2#00_100_100#); End_Insn; -- addl %esp, 8 Start_Insn; Gen_B8 (2#100000_11#); Gen_B8 (2#11_000_100#); Gen_B8 (8); End_Insn; when others => Error_Emit ("gen_conv_I64", Stmt); end case; end Gen_Conv_I64; -- Convert FP to xxx. procedure Gen_Conv_Fp (Stmt : O_Enode) is begin case Get_Expr_Mode (Stmt) is when Mode_I32 => -- subl %esp, 4 Start_Insn; Gen_B8 (2#100000_11#); Gen_B8 (2#11_101_100#); Gen_B8 (4); End_Insn; -- fistp (%esp) Start_Insn; Gen_B8 (2#11011_011#); Gen_B8 (2#00_011_100#); Gen_B8 (2#00_100_100#); End_Insn; Emit_Pop_32 (Stmt, Sz_32l); when Mode_I64 => -- subl %esp, 8 Start_Insn; Gen_B8 (2#100000_11#); Gen_B8 (2#11_101_100#); Gen_B8 (8); End_Insn; -- fistp (%esp) Start_Insn; Gen_B8 (2#11011_111#); Gen_B8 (2#00_111_100#); Gen_B8 (2#00_100_100#); End_Insn; Emit_Pop_32 (Stmt, Sz_32l); Emit_Pop_32 (Stmt, Sz_32h); when others => Error_Emit ("gen_conv_fp", Stmt); end case; end Gen_Conv_Fp; procedure Gen_Emit_Op (Stmt : O_Enode; Cl : Byte; Ch : Byte) is begin case Get_Expr_Mode (Stmt) is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Op (Cl, Stmt, Sz_32l); when Mode_I64 | Mode_U64 => Emit_Op (Cl, Stmt, Sz_32l); Emit_Op (Ch, Stmt, Sz_32h); when Mode_B2 | Mode_I8 | Mode_U8 => Emit_Op (Cl, Stmt, Sz_8); when others => Error_Emit ("gen_emit_op", Stmt); end case; end Gen_Emit_Op; procedure Gen_Check_Overflow (Mode : Mode_Type) is begin case Mode is when Mode_I32 | Mode_I64 | Mode_I8 => Gen_Into; when Mode_U64 | Mode_U32 | Mode_U8 => -- FIXME: check no carry. null; when Mode_B2 => null; when others => raise Program_Error; end case; end Gen_Check_Overflow; procedure Gen_Emit_Fp_Op (Stmt : O_Enode; B_St1 : Byte; B_Mem : Byte) is Right : O_Enode; Reg : O_Reg; B_Size : Byte; begin Right := Get_Expr_Right (Stmt); Reg := Get_Expr_Reg (Right); Start_Insn; case Reg is when R_St0 => Gen_B8 (2#11011_110#); Gen_B8 (2#11_000_001# or B_St1); when R_Mem => case Get_Expr_Mode (Stmt) is when Mode_F32 => B_Size := 0; when Mode_F64 => B_Size := 2#100#; when others => raise Program_Error; end case; Gen_B8 (2#11011_000# or B_Size); Gen_Rm_Mem (B_Mem, Right, Sz_32l); when others => raise Program_Error; end case; End_Insn; end Gen_Emit_Fp_Op; procedure Emit_Mod (Stmt : O_Enode) is Right : O_Enode; Pc1, Pc2, Pc3: Pc_Type; begin -- a : EAX -- d : EDX -- b : Rm -- d := Rm -- d := d ^ a -- cltd -- if cc < 0 then -- idiv b -- if edx /= 0 then -- edx := edx + b -- end if -- else -- idiv b -- end if Right := Get_Expr_Right (Stmt); -- %edx <- right Emit_Load (R_Dx, Right, Sz_32l); -- xorl %eax -> %edx Start_Insn; Gen_B8 (2#0011_0011#); Gen_B8 (2#11_010_000#); End_Insn; Gen_Cdq; -- js Start_Insn; Gen_B8 (2#0111_1000#); Gen_B8 (0); End_Insn; Pc1 := Get_Current_Pc; -- idiv Gen_Mono_Op (2#111_000#, Right, Sz_32l); -- jmp Start_Insn; Gen_B8 (2#1110_1011#); Gen_B8 (0); End_Insn; Pc2 := Get_Current_Pc; Patch_B8 (Pc1 - 1, Unsigned_8 (Get_Current_Pc - Pc1)); -- idiv Gen_Mono_Op (2#111_000#, Right, Sz_32l); -- tstl %edx,%edx Start_Insn; Gen_B8 (2#1000_0101#); Gen_B8 (2#11_010_010#); End_Insn; -- jz Start_Insn; Gen_B8 (2#0111_0100#); Gen_B8 (0); End_Insn; Pc3 := Get_Current_Pc; -- addl b, %edx Start_Insn; Gen_B8 (2#00_000_011#); Gen_Rm (2#010_000#, Right, Sz_32l); End_Insn; Patch_B8 (Pc2 - 1, Unsigned_8 (Get_Current_Pc - Pc2)); Patch_B8 (Pc3 - 1, Unsigned_8 (Get_Current_Pc - Pc3)); end Emit_Mod; procedure Emit_Insn (Stmt : O_Enode) is use Ortho_Code.Flags; Kind : OE_Kind; Mode : Mode_Type; Reg : O_Reg; begin Kind := Get_Expr_Kind (Stmt); Mode := Get_Expr_Mode (Stmt); case Kind is when OE_Beg => if Flag_Debug /= Debug_None then Decls.Set_Block_Info1 (Get_Block_Decls (Stmt), Int32 (Get_Current_Pc - Subprg_Pc)); end if; when OE_End => if Flag_Debug /= Debug_None then Decls.Set_Block_Info2 (Get_Block_Decls (Get_End_Beg (Stmt)), Int32 (Get_Current_Pc - Subprg_Pc)); end if; when OE_Leave => null; when OE_BB => null; when OE_Add_Ov => if Mode in Mode_Fp then Gen_Emit_Fp_Op (Stmt, 2#000_000#, 2#000_000#); else Gen_Emit_Op (Stmt, 2#000_000#, 2#010_000#); Gen_Check_Overflow (Mode); end if; when OE_Or => Gen_Emit_Op (Stmt, 2#001_000#, 2#001_000#); when OE_And => Gen_Emit_Op (Stmt, 2#100_000#, 2#100_000#); when OE_Xor => Gen_Emit_Op (Stmt, 2#110_000#, 2#110_000#); when OE_Sub_Ov => if Mode in Mode_Fp then Gen_Emit_Fp_Op (Stmt, 2#100_000#, 2#100_000#); else Gen_Emit_Op (Stmt, 2#101_000#, 2#011_000#); Gen_Check_Overflow (Mode); end if; when OE_Mul_Ov | OE_Mul => case Mode is when Mode_U8 => Gen_Umul (Stmt, Sz_8); when Mode_U16 => Gen_Umul (Stmt, Sz_16); when Mode_U32 => Gen_Mul (Stmt, Sz_32l); when Mode_I32 => Gen_Mono_Op (2#101_000#, Get_Expr_Right (Stmt), Sz_32l); when Mode_F32 | Mode_F64 => Gen_Emit_Fp_Op (Stmt, 2#001_000#, 2#001_000#); when others => Error_Emit ("emit_insn: mul_ov", Stmt); end case; when OE_Shl => declare Right : O_Enode; Sz : Insn_Size; Val : Uns32; begin case Mode is when Mode_U32 => Sz := Sz_32l; when others => Error_Emit ("emit_insn: shl", Stmt); end case; Right := Get_Expr_Right (Stmt); if Get_Expr_Kind (Right) = OE_Const then Val := Get_Expr_Low (Right); Start_Insn; if Val = 1 then Gen_Insn_Sz (2#1101000_0#, Sz); Gen_Rm (2#100_000#, Get_Expr_Left (Stmt), Sz); else Gen_Insn_Sz (2#1100000_0#, Sz); Gen_Rm (2#100_000#, Get_Expr_Left (Stmt), Sz); Gen_B8 (Byte (Val and 31)); end if; End_Insn; else if Get_Expr_Reg (Right) /= R_Cx then raise Program_Error; end if; Start_Insn; Gen_Insn_Sz (2#1101001_0#, Sz); Gen_Rm (2#100_000#, Get_Expr_Left (Stmt), Sz); End_Insn; end if; end; when OE_Mod | OE_Rem | OE_Div_Ov => case Mode is when Mode_U32 => -- Xorl edx, edx Start_Insn; Gen_B8 (2#0011_0001#); Gen_B8 (2#11_010_010#); End_Insn; Gen_Mono_Op (2#110_000#, Get_Expr_Right (Stmt), Sz_32l); when Mode_I32 => if Kind = OE_Mod then Emit_Mod (Stmt); else Gen_Cdq; Gen_Mono_Op (2#111_000#, Get_Expr_Right (Stmt), Sz_32l); end if; when Mode_F32 | Mode_F64 => if Kind = OE_Div_Ov then Gen_Emit_Fp_Op (Stmt, 2#111_000#, 2#110_000#); else raise Program_Error; end if; when others => Error_Emit ("emit_insn: mod_ov", Stmt); end case; when OE_Not => case Mode is when Mode_B2 => -- Xor VAL, $1 Start_Insn; Gen_B8 (2#1000_0011#); Gen_Rm (2#110_000#, Stmt, Sz_8); Gen_B8 (16#01#); End_Insn; when Mode_U8 => Emit_Mono_Op_Stmt (2#010_000#, Stmt, Sz_8); when Mode_U16 => Emit_Mono_Op_Stmt (2#010_000#, Stmt, Sz_16); when Mode_U32 => Emit_Mono_Op_Stmt (2#010_000#, Stmt, Sz_32l); when Mode_U64 => Emit_Mono_Op_Stmt (2#010_000#, Stmt, Sz_32l); Emit_Mono_Op_Stmt (2#010_000#, Stmt, Sz_32h); when others => Error_Emit ("emit_insn: not", Stmt); end case; when OE_Neg_Ov => case Mode is when Mode_I8 => Emit_Mono_Op_Stmt (2#011_000#, Stmt, Sz_8); --Gen_Into; when Mode_I16 => Emit_Mono_Op_Stmt (2#011_000#, Stmt, Sz_16); --Gen_Into; when Mode_I32 => Emit_Mono_Op_Stmt (2#011_000#, Stmt, Sz_32l); --Gen_Into; when Mode_I64 => Emit_Mono_Op_Stmt (2#011_000#, Stmt, Sz_32l); -- adcl 0, high Start_Insn; Gen_B8 (2#100000_11#); Gen_Rm (2#010_000#, Get_Expr_Operand (Stmt), Sz_32h); Gen_B8 (0); End_Insn; Emit_Mono_Op_Stmt (2#011_000#, Stmt, Sz_32h); --Gen_Into; when Mode_F32 | Mode_F64 => -- fchs Start_Insn; Gen_B8 (2#11011_001#); Gen_B8 (2#1110_0000#); End_Insn; when others => Error_Emit ("emit_insn: neg_ov", Stmt); end case; when OE_Abs_Ov => case Mode is when Mode_I32 | Mode_I64 => Emit_Abs (Get_Expr_Operand (Stmt), Mode); when Mode_F32 | Mode_F64 => -- fabs Start_Insn; Gen_B8 (2#11011_001#); Gen_B8 (2#1110_0001#); End_Insn; when others => Error_Emit ("emit_insn: abs_ov", Stmt); end case; when OE_Kind_Cmp => case Get_Expr_Mode (Get_Expr_Left (Stmt)) is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Op (2#111_000#, Stmt, Sz_32l); when Mode_B2 | Mode_I8 | Mode_U8 => Emit_Op (2#111_000#, Stmt, Sz_8); when Mode_U64 => declare Pc : Pc_Type; begin Emit_Op (2#111_000#, Stmt, Sz_32h); -- jne Start_Insn; Gen_B8 (2#0111_0101#); Gen_B8 (0); End_Insn; Pc := Get_Current_Pc; Emit_Op (2#111_000#, Stmt, Sz_32l); Patch_B8 (Pc - 1, Unsigned_8 (Get_Current_Pc - Pc)); end; when Mode_I64 => declare Pc : Pc_Type; begin Reg := Get_Expr_Reg (Stmt); Emit_Op (2#111_000#, Stmt, Sz_32h); -- Note: this does not clobber a reg due to care in -- insns. Emit_Setcc_Reg (Reg, Ekind_Signed_To_Cc (Kind)); -- jne Start_Insn; Gen_B8 (2#0111_0101#); Gen_B8 (0); End_Insn; Pc := Get_Current_Pc; Emit_Op (2#111_000#, Stmt, Sz_32l); Emit_Setcc_Reg (Reg, Ekind_Unsigned_To_Cc (Kind)); Patch_B8 (Pc - 1, Unsigned_8 (Get_Current_Pc - Pc)); return; end; when Mode_F32 | Mode_F64 => -- fcomip st, st(1) Start_Insn; Gen_B8 (2#11011_111#); Gen_B8 (2#1111_0001#); End_Insn; -- fstp st, st (0) Start_Insn; Gen_B8 (2#11011_101#); Gen_B8 (2#11_011_000#); End_Insn; when others => Error_Emit ("emit_insn: cmp", Stmt); end case; Reg := Get_Expr_Reg (Stmt); if Reg not in Regs_Cc then Error_Emit ("emit_insn/cmp: not cc", Stmt); end if; when OE_Const | OE_Addrg => case Mode is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Load_Imm (Stmt, Sz_32l); when Mode_B2 | Mode_U8 | Mode_I8 => Emit_Load_Imm (Stmt, Sz_8); when Mode_I64 | Mode_U64 => Emit_Load_Imm (Stmt, Sz_32l); Emit_Load_Imm (Stmt, Sz_32h); when Mode_F32 => Emit_Load_Fp (Stmt, Fp_32); when Mode_F64 => Emit_Load_Fp (Stmt, Fp_64); when others => Error_Emit ("emit_insn: const", Stmt); end case; when OE_Indir => case Mode is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Load_Mem (Stmt, Sz_32l); when Mode_B2 | Mode_U8 | Mode_I8 => Emit_Load_Mem (Stmt, Sz_8); when Mode_U64 | Mode_I64 => Emit_Load_Mem (Stmt, Sz_32l); Emit_Load_Mem (Stmt, Sz_32h); when Mode_F32 => Emit_Load_Fp_Mem (Stmt, Fp_32); when Mode_F64 => Emit_Load_Fp_Mem (Stmt, Fp_64); when others => Error_Emit ("emit_insn: indir", Stmt); end case; when OE_Conv => case Get_Expr_Mode (Get_Expr_Operand (Stmt)) is when Mode_U32 => Gen_Conv_U32 (Stmt); when Mode_I32 => Gen_Conv_I32 (Stmt); when Mode_U8 => Gen_Conv_U8 (Stmt); when Mode_B2 => Gen_Conv_B2 (Stmt); when Mode_I64 => Gen_Conv_I64 (Stmt); when Mode_F32 | Mode_F64 => Gen_Conv_Fp (Stmt); when others => Error_Emit ("emit_insn: conv", Stmt); end case; when OE_Asgn => case Mode is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Store (Stmt, Sz_32l); when Mode_B2 | Mode_U8 | Mode_I8 => Emit_Store (Stmt, Sz_8); when Mode_U64 | Mode_I64 => Emit_Store (Stmt, Sz_32l); Emit_Store (Stmt, Sz_32h); when Mode_F32 => Emit_Store_Fp (Stmt, Fp_32); when Mode_F64 => Emit_Store_Fp (Stmt, Fp_64); when others => Error_Emit ("emit_insn: move", Stmt); end case; when OE_Jump_F => Reg := Get_Expr_Reg (Get_Expr_Operand (Stmt)); if Reg not in Regs_Cc then Error_Emit ("emit_insn/jmp_f: not cc", Stmt); end if; Emit_Jmp_T (Stmt, Inverse_Cc (Reg)); when OE_Jump_T => Reg := Get_Expr_Reg (Get_Expr_Operand (Stmt)); if Reg not in Regs_Cc then Error_Emit ("emit_insn/jmp_t: not cc", Stmt); end if; Emit_Jmp_T (Stmt, Reg); when OE_Jump => Emit_Jmp (Stmt); when OE_Label => Emit_Label (Stmt); when OE_Ret => -- Value already set. null; when OE_Arg => case Mode is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Push_32 (Get_Expr_Operand (Stmt), Sz_32l); when Mode_U64 | Mode_I64 => Emit_Push_32 (Get_Expr_Operand (Stmt), Sz_32h); Emit_Push_32 (Get_Expr_Operand (Stmt), Sz_32l); when Mode_F32 => Emit_Push_Fp (Get_Expr_Operand (Stmt), Fp_32); when Mode_F64 => Emit_Push_Fp (Get_Expr_Operand (Stmt), Fp_64); when others => Error_Emit ("emit_insn: oe_arg", Stmt); end case; when OE_Stack_Adjust => Emit_Setup_Frame (Stmt); when OE_Call => Emit_Call (Stmt); when OE_Intrinsic => Emit_Intrinsic (Stmt); when OE_Move => declare Operand : O_Enode; Op_Reg : O_Reg; begin Reg := Get_Expr_Reg (Stmt); Operand := Get_Expr_Operand (Stmt); Op_Reg := Get_Expr_Reg (Operand); case Mode is when Mode_B2 => if Reg in Regs_R32 and then Op_Reg in Regs_Cc then Emit_Setcc (Stmt, Op_Reg); elsif (Reg = R_Eq or Reg = R_Ne) and then Op_Reg in Regs_R32 then Emit_Tst (Op_Reg, Sz_8); else Error_Emit ("emit_insn: move/b2", Stmt); end if; when Mode_U32 | Mode_I32 => -- mov REG, OP Start_Insn; Gen_Insn_Sz (2#1000_101_0#, Sz_32l); Gen_Rm (To_Reg32 (Reg, Sz_32l) * 8, Operand, Sz_32l); End_Insn; when others => Error_Emit ("emit_insn: move", Stmt); end case; end; when OE_Alloca => if Mode /= Mode_P32 then raise Program_Error; end if; Gen_Alloca (Stmt); when OE_Set_Stack => Emit_Load_Mem (Stmt, Sz_32l); when OE_Add | OE_Addrl => case Mode is when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Lea (Stmt); when others => Error_Emit ("emit_insn: oe_add", Stmt); end case; when OE_Spill => case Mode is when Mode_B2 | Mode_U8 | Mode_I8 => Emit_Spill (Stmt, Sz_8); when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Spill (Stmt, Sz_32l); when Mode_U64 | Mode_I64 => Emit_Spill (Stmt, Sz_32l); Emit_Spill (Stmt, Sz_32h); when others => Error_Emit ("emit_insn: spill", Stmt); end case; when OE_Reload => declare Expr : O_Enode; begin Reg := Get_Expr_Reg (Stmt); Expr := Get_Expr_Operand (Stmt); case Mode is when Mode_B2 | Mode_U8 | Mode_I8 => Emit_Load (Reg, Expr, Sz_8); when Mode_U32 | Mode_I32 | Mode_P32 => Emit_Load (Reg, Expr, Sz_32l); when Mode_U64 | Mode_I64 => Emit_Load (Reg, Expr, Sz_32l); Emit_Load (Reg, Expr, Sz_32h); when others => Error_Emit ("emit_insn: reload", Stmt); end case; end; when OE_Reg => Reg_Helper := Get_Expr_Reg (Stmt); when OE_Case_Expr | OE_Case => null; when OE_Line => if Flag_Debug = Debug_Dwarf then Dwarf.Set_Line_Stmt (Get_Expr_Line_Number (Stmt)); Set_Current_Section (Sect_Text); end if; when others => Error_Emit ("cannot handle insn", Stmt); end case; end Emit_Insn; procedure Push_Reg_If_Used (Reg : Regs_R32) is use Ortho_Code.X86.Insns; begin if Reg_Used (Reg) then Start_Insn; Gen_B8 (2#01010_000# + To_Reg32 (Reg, Sz_32l)); End_Insn; end if; end Push_Reg_If_Used; procedure Pop_Reg_If_Used (Reg : Regs_R32) is use Ortho_Code.X86.Insns; begin if Reg_Used (Reg) then Start_Insn; Gen_B8 (2#01011_000# + To_Reg32 (Reg, Sz_32l)); End_Insn; end if; end Pop_Reg_If_Used; procedure Emit_Prologue (Subprg : Subprogram_Data_Acc) is use Ortho_Code.Decls; use Ortho_Code.Flags; use Ortho_Code.X86.Insns; Sym : Symbol; Subprg_Decl : O_Dnode; Is_Global : Boolean; Frame_Size : Unsigned_32; Saved_Regs_Size : Unsigned_32; begin -- Switch to .text section and align the function (to avoid the nested -- function trick and for performance). Set_Current_Section (Sect_Text); Gen_Pow_Align (2); Subprg_Decl := Subprg.D_Decl; Sym := Get_Decl_Symbol (Subprg_Decl); case Get_Decl_Storage (Subprg_Decl) is when O_Storage_Public | O_Storage_External => -- FIXME: should not accept the external case. Is_Global := True; when others => Is_Global := False; end case; Set_Symbol_Pc (Sym, Is_Global); Subprg_Pc := Get_Current_Pc; Saved_Regs_Size := Boolean'Pos(Reg_Used (R_Di)) * 4 + Boolean'Pos(Reg_Used (R_Si)) * 4 + Boolean'Pos(Reg_Used (R_Bx)) * 4; -- Compute frame size. -- 8 bytes are used by return address and saved frame pointer. Frame_Size := Unsigned_32 (Subprg.Stack_Max) + 8 + Saved_Regs_Size; -- Align. Frame_Size := (Frame_Size + X86.Flags.Stack_Boundary - 1) and not (X86.Flags.Stack_Boundary - 1); -- The 8 bytes are already allocated. Frame_Size := Frame_Size - 8 - Saved_Regs_Size; -- Emit prolog. -- push %ebp Start_Insn; Gen_B8 (2#01010_101#); End_Insn; -- movl %esp, %ebp Start_Insn; Gen_B8 (2#1000100_1#); Gen_B8 (2#11_100_101#); End_Insn; -- subl XXX, %esp if Frame_Size /= 0 then if not X86.Flags.Flag_Alloca_Call or else Frame_Size <= 4096 then Start_Insn; if Frame_Size < 128 then Gen_B8 (2#100000_11#); Gen_B8 (2#11_101_100#); Gen_B8 (Byte (Frame_Size)); else Gen_B8 (2#100000_01#); Gen_B8 (2#11_101_100#); Gen_Le32 (Frame_Size); end if; End_Insn; else -- mov stack_size,%eax Start_Insn; Gen_B8 (2#1011_1_000#); Gen_Le32 (Frame_Size); End_Insn; Gen_Call (Chkstk_Symbol); end if; end if; if Flag_Profile then Gen_Call (Mcount_Symbol); end if; -- Save registers. Push_Reg_If_Used (R_Di); Push_Reg_If_Used (R_Si); Push_Reg_If_Used (R_Bx); end Emit_Prologue; procedure Emit_Epilogue (Subprg : Subprogram_Data_Acc) is use Ortho_Code.Decls; use Ortho_Code.Types; use Ortho_Code.Flags; Decl : O_Dnode; begin -- Restore registers. Pop_Reg_If_Used (R_Bx); Pop_Reg_If_Used (R_Si); Pop_Reg_If_Used (R_Di); Decl := Subprg.D_Decl; if Get_Decl_Kind (Decl) = OD_Function then case Get_Type_Mode (Get_Decl_Type (Decl)) is when Mode_U8 | Mode_B2 => -- movzx %al,%eax Start_Insn; Gen_B8 (16#0f#); Gen_B8 (2#1011_0110#); Gen_B8 (2#11_000_000#); End_Insn; when Mode_U32 | Mode_I32 | Mode_U64 | Mode_I64 | Mode_F32 | Mode_F64 | Mode_P32 => null; when others => raise Program_Error; end case; end if; -- leave Start_Insn; Gen_B8 (2#1100_1001#); End_Insn; -- ret Start_Insn; Gen_B8 (2#1100_0011#); End_Insn; if Flag_Debug = Debug_Dwarf then Set_Body_Info (Subprg.D_Body, Int32 (Get_Current_Pc - Subprg_Pc)); end if; end Emit_Epilogue; procedure Emit_Subprg (Subprg : Subprogram_Data_Acc) is Stmt : O_Enode; begin if Debug.Flag_Debug_Code2 then Abi.Disp_Subprg_Decl (Subprg.D_Decl); end if; Emit_Prologue (Subprg); Stmt := Subprg.E_Entry; loop Stmt := Get_Stmt_Link (Stmt); if Debug.Flag_Debug_Code2 then Abi.Disp_Stmt (Stmt); end if; Emit_Insn (Stmt); exit when Get_Expr_Kind (Stmt) = OE_Leave; end loop; Emit_Epilogue (Subprg); end Emit_Subprg; procedure Emit_Var_Decl (Decl : O_Dnode) is use Decls; use Types; Sym : Symbol; Storage : O_Storage; Dtype : O_Tnode; begin Set_Current_Section (Sect_Bss); Sym := Create_Symbol (Get_Decl_Ident (Decl)); Set_Decl_Info (Decl, To_Int32 (Uns32 (Sym))); Storage := Get_Decl_Storage (Decl); Dtype := Get_Decl_Type (Decl); case Storage is when O_Storage_External => null; when O_Storage_Public | O_Storage_Private => Gen_Pow_Align (Get_Type_Align (Dtype)); Set_Symbol_Pc (Sym, Storage = O_Storage_Public); Gen_Space (Integer_32 (Get_Type_Size (Dtype))); when O_Storage_Local => raise Program_Error; end case; Set_Current_Section (Sect_Text); end Emit_Var_Decl; procedure Emit_Const_Decl (Decl : O_Dnode) is use Decls; use Types; Sym : Symbol; begin Set_Current_Section (Sect_Rodata); Sym := Create_Symbol (Get_Decl_Ident (Decl)); Set_Decl_Info (Decl, To_Int32 (Uns32 (Sym))); Set_Current_Section (Sect_Text); end Emit_Const_Decl; procedure Emit_Const (Val : O_Cnode) is use Consts; use Types; H, L : Uns32; begin case Get_Const_Kind (Val) is when OC_Signed | OC_Unsigned | OC_Float | OC_Null | OC_Lit => Get_Const_Bytes (Val, H, L); case Get_Type_Mode (Get_Const_Type (Val)) is when Mode_U8 | Mode_I8 | Mode_B2 => Gen_B8 (Byte (L)); when Mode_U32 | Mode_I32 | Mode_F32 | Mode_P32 => Gen_Le32 (Unsigned_32 (L)); when Mode_F64 | Mode_I64 | Mode_U64 => Gen_Le32 (Unsigned_32 (L)); Gen_Le32 (Unsigned_32 (H)); when others => raise Program_Error; end case; when OC_Address | OC_Subprg_Address => Gen_X86_32 (Get_Decl_Symbol (Get_Const_Decl (Val)), 0); when OC_Array => for I in 0 .. Get_Const_Aggr_Length (Val) - 1 loop Emit_Const (Get_Const_Aggr_Element (Val, I)); end loop; when OC_Record => declare E : O_Cnode; begin for I in 0 .. Get_Const_Aggr_Length (Val) - 1 loop E := Get_Const_Aggr_Element (Val, I); Gen_Pow_Align (Get_Type_Align (Get_Const_Type (E))); Emit_Const (E); end loop; end; when OC_Sizeof | OC_Alignof | OC_Union => raise Program_Error; end case; end Emit_Const; procedure Emit_Const_Value (Decl : O_Dnode; Val : O_Cnode) is use Decls; use Types; Sym : Symbol; Dtype : O_Tnode; begin Set_Current_Section (Sect_Rodata); Sym := Get_Decl_Symbol (Decl); Dtype := Get_Decl_Type (Decl); Gen_Pow_Align (Get_Type_Align (Dtype)); Set_Symbol_Pc (Sym, Get_Decl_Storage (Decl) = O_Storage_Public); Prealloc (Pc_Type (Get_Type_Size (Dtype))); Emit_Const (Val); Set_Current_Section (Sect_Text); end Emit_Const_Value; procedure Init is use Ortho_Ident; use Ortho_Code.Flags; begin Arch := Arch_X86; Create_Section (Sect_Text, ".text", Section_Exec + Section_Read); Create_Section (Sect_Rodata, ".rodata", Section_Read); Create_Section (Sect_Bss, ".bss", Section_Read + Section_Write + Section_Zero); Set_Current_Section (Sect_Text); if Flag_Profile then Mcount_Symbol := Create_Symbol (Get_Identifier ("mcount")); end if; if X86.Flags.Flag_Alloca_Call then Chkstk_Symbol := Create_Symbol (Get_Identifier ("___chkstk")); end if; Intrinsics_Symbol (Intrinsic_Mul_Ov_U64) := Create_Symbol (Get_Identifier ("__muldi3")); Intrinsics_Symbol (Intrinsic_Div_Ov_U64) := Create_Symbol (Get_Identifier ("__mcode_div_ov_u64")); Intrinsics_Symbol (Intrinsic_Mod_Ov_U64) := Create_Symbol (Get_Identifier ("__mcode_mod_ov_u64")); Intrinsics_Symbol (Intrinsic_Mul_Ov_I64) := Create_Symbol (Get_Identifier ("__muldi3")); Intrinsics_Symbol (Intrinsic_Div_Ov_I64) := Create_Symbol (Get_Identifier ("__divdi3")); Intrinsics_Symbol (Intrinsic_Mod_Ov_I64) := Create_Symbol (Get_Identifier ("__mcode_mod_ov_i64")); Intrinsics_Symbol (Intrinsic_Rem_Ov_I64) := Create_Symbol (Get_Identifier ("__mcode_rem_ov_i64")); if Debug.Flag_Debug_Asm then Dump_Asm := True; end if; if Debug.Flag_Debug_Hex then Debug_Hex := True; end if; if Flag_Debug = Debug_Dwarf then Dwarf.Init; Set_Current_Section (Sect_Text); end if; end Init; procedure Finish is use Ortho_Code.Flags; begin if Flag_Debug = Debug_Dwarf then Set_Current_Section (Sect_Text); Dwarf.Finish; end if; end Finish; end Ortho_Code.X86.Emits;