(* * Neko Compiler * Copyright (c)2005 Motion-Twin * * This library is free software; you can redistribute it and/lor * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, lor (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY lor FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License lor the LICENSE file for more details. *) type opcode = (* getters *) | AccNull | AccTrue | AccFalse | AccThis | AccInt of int | AccStack of int | AccGlobal of int | AccEnv of int | AccField of string | AccArray | AccIndex of int | AccBuiltin of string (* setters *) | SetStack of int | SetGlobal of int | SetEnv of int | SetField of string | SetArray | SetIndex of int | SetThis (* stack ops *) | Push | Pop of int | Call of int | ObjCall of int | Jump of int | JumpIf of int | JumpIfNot of int | Trap of int | EndTrap | Ret of int | MakeEnv of int | MakeArray of int (* value ops *) | Bool | IsNull | IsNotNull | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | Not (* extra ops *) | TypeOf | Compare | Hash | New | JumpTable of int | Apply of int | AccStack0 | AccStack1 | AccIndex0 | AccIndex1 | PhysCompare | TailCall of int * int | Loop (* ocaml-specific *) | AccInt32 of int32 type global = | GlobalVar of string | GlobalFunction of int * int | GlobalString of string | GlobalFloat of string | GlobalDebug of string array * ((int * int) array) | GlobalVersion of int exception Invalid_file let error msg = failwith msg let trap_stack_delta = 6 let hash_field f = let h = ref 0 in for i = 0 to String.length f - 1 do h := !h * 223 + int_of_char (String.unsafe_get f i); done; if Sys.word_size = 64 then Int32.to_int (Int32.shift_right (Int32.shift_left (Int32.of_int !h) 1) 1) else !h let op_param x = match x with | AccInt _ | AccInt32 _ | AccStack _ | AccGlobal _ | AccEnv _ | AccField _ | AccBuiltin _ | SetStack _ | SetGlobal _ | SetEnv _ | SetField _ | Pop _ | Call _ | ObjCall _ | Jump _ | JumpIf _ | JumpIfNot _ | JumpTable _ | Trap _ | MakeEnv _ | MakeArray _ | Ret _ | AccIndex _ | SetIndex _ | Apply _ | TailCall _ -> true | AccNull | AccTrue | AccFalse | AccThis | AccArray | SetArray | SetThis | Push | EndTrap | Bool | Add | Sub | Mult | Div | Mod | Shl | Shr | UShr | Or | And | Xor | Eq | Neq | Gt | Gte | Lt | Lte | IsNull | IsNotNull | Not | TypeOf | Compare | Hash | New | AccStack0 | AccStack1 | AccIndex0 | AccIndex1 | PhysCompare | Loop -> false let code_tables ops = let ids = Hashtbl.create 0 in let fids = DynArray.create() in Array.iter (fun x -> match x with | AccField s | SetField s | AccBuiltin s -> let id = hash_field s in (try let f = Hashtbl.find ids id in if f <> s then error("Field hashing conflict " ^ s ^ " and " ^ f); with Not_found -> Hashtbl.add ids id s; DynArray.add fids s ) | _ -> () ) ops; let p = ref 0 in let pos = Array.make (Array.length(ops) + 1) 0 in Array.iteri (fun i op -> pos.(i) <- !p; p := !p + (if op_param op then 2 else 1); ) ops; pos.(Array.length ops) <- !p; (DynArray.to_array fids , pos , !p) let write_debug_infos ch files inf = let nfiles = Array.length files in (* // the encoding of nfiles was set to keep // backward compatibility with 1.3 which // only allowed up to 127 filenames *) let lot_of_files = ref false in if nfiles < 0x80 then IO.write_byte ch nfiles else if nfiles < 0x8000 then begin lot_of_files := true; IO.write_byte ch ((nfiles lsr 8) lor 0x80); IO.write_byte ch (nfiles land 0xFF); end else assert false; Array.iter (fun s -> IO.write_string ch s) files; IO.write_i32 ch (Array.length inf); let curfile = ref 0 in let curpos = ref 0 in let rcount = ref 0 in let rec flush_repeat p = if !rcount > 0 then begin if !rcount > 15 then begin IO.write_byte ch ((15 lsl 2) lor 2); rcount := !rcount - 15; flush_repeat(p) end else begin let delta = p - !curpos in let delta = (if delta > 0 && delta < 4 then delta else 0) in IO.write_byte ch ((delta lsl 6) lor (!rcount lsl 2) lor 2); rcount := 0; curpos := !curpos + delta; end end in Array.iter (fun (f,p) -> if f <> !curfile then begin flush_repeat(p); curfile := f; if !lot_of_files then begin IO.write_byte ch ((f lsr 7) lor 1); IO.write_byte ch (f land 0xFF); end else IO.write_byte ch ((f lsl 1) lor 1); end; if p <> !curpos then flush_repeat(p); if p = !curpos then rcount := !rcount + 1 else let delta = p - !curpos in if delta > 0 && delta < 32 then IO.write_byte ch ((delta lsl 3) lor 4) else begin IO.write_byte ch (p lsl 3); IO.write_byte ch (p lsr 5); IO.write_byte ch (p lsr 13); end; curpos := p; ) inf; flush_repeat(!curpos) let write ch (globals,ops) = IO.nwrite_string ch "NEKO"; let ids , pos , csize = code_tables ops in IO.write_i32 ch (Array.length globals); IO.write_i32 ch (Array.length ids); IO.write_i32 ch csize; Array.iter (fun x -> match x with | GlobalVar s -> IO.write_byte ch 1; IO.write_string ch s | GlobalFunction (p,nargs) -> IO.write_byte ch 2; IO.write_i32 ch (pos.(p) lor (nargs lsl 24)) | GlobalString s -> IO.write_byte ch 3; IO.write_ui16 ch (String.length s); IO.nwrite_string ch s | GlobalFloat s -> IO.write_byte ch 4; IO.write_string ch s | GlobalDebug (files,inf) -> IO.write_byte ch 5; write_debug_infos ch files inf; | GlobalVersion v -> IO.write_byte ch 6; IO.write_byte ch v ) globals; Array.iter (fun s -> IO.write_string ch s; ) ids; Array.iteri (fun i op -> let pop = ref None in let opid = (match op with | AccNull -> 0 | AccTrue -> 1 | AccFalse -> 2 | AccThis -> 3 | AccInt n -> pop := Some n; 4 | AccInt32 n -> let opid = 4 in IO.write_byte ch ((opid lsl 2) lor 3); IO.write_real_i32 ch n; -1 | AccStack n -> pop := Some (n - 2); 5 | AccGlobal n -> pop := Some n; 6 | AccEnv n -> pop := Some n; 7 | AccField s -> pop := Some (hash_field s); 8 | AccArray -> 9 | AccIndex n -> pop := Some (n - 2); 10 | AccBuiltin s -> pop := Some (hash_field s); 11 | SetStack n -> pop := Some n; 12 | SetGlobal n -> pop := Some n; 13 | SetEnv n -> pop := Some n; 14 | SetField s -> pop := Some (hash_field s); 15 | SetArray -> 16 | SetIndex n -> pop := Some n; 17 | SetThis -> 18 | Push -> 19 | Pop n -> pop := Some n; 20 | Call n -> pop := Some n; 21 | ObjCall n -> pop := Some n; 22 | Jump n -> pop := Some (pos.(i+n) - pos.(i)); 23 | JumpIf n -> pop := Some (pos.(i+n) - pos.(i)); 24 | JumpIfNot n -> pop := Some (pos.(i+n) - pos.(i)); 25 | Trap n -> pop := Some (pos.(i+n) - pos.(i)); 26 | EndTrap -> 27 | Ret n -> pop := Some n; 28 | MakeEnv n -> pop := Some n; 29 | MakeArray n -> pop := Some n; 30 | Bool -> 31 | IsNull -> 32 | IsNotNull -> 33 | Add -> 34 | Sub -> 35 | Mult -> 36 | Div -> 37 | Mod -> 38 | Shl -> 39 | Shr -> 40 | UShr -> 41 | Or -> 42 | And -> 43 | Xor -> 44 | Eq -> 45 | Neq -> 46 | Gt -> 47 | Gte -> 48 | Lt -> 49 | Lte -> 50 | Not -> 51 | TypeOf -> 52 | Compare -> 53 | Hash -> 54 | New -> 55 | JumpTable n -> pop := Some n; 56 | Apply n -> pop := Some n; 57 | AccStack0 -> 58 | AccStack1 -> 59 | AccIndex0 -> 60 | AccIndex1 -> 61 | PhysCompare -> 62 | TailCall (args,st) -> pop := Some (args lor (st lsl 3)); 63 | Loop -> pop := Some 64; 0 ) in match !pop with | None -> if opid >= 0 then IO.write_byte ch (opid lsl 2) | Some n -> if opid < 32 && (n = 0 || n = 1) then IO.write_byte ch ((opid lsl 3) lor (n lsl 2) lor 1) else if n >= 0 && n <= 0xFF then begin IO.write_byte ch ((opid lsl 2) lor 2); IO.write_byte ch n; end else begin IO.write_byte ch ((opid lsl 2) lor 3); IO.write_i32 ch n; end ) ops