summaryrefslogtreecommitdiff
path: root/bytecomp/metacomp.ml
blob: 64730886299902777e77e9d321acb0fc568ebfe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
open Asttypes
open Lambda
open Obj

exception Unsupported

exception Not_constant
let extract_constant = function
    Lconst sc -> sc
  | _ -> raise Not_constant

(*
let rec transl_constant v =
  if Obj.is_int v then
    Const_base (Const_int (Obj.obj v))
  else if Obj.is_block v then 
    let tag = Obj.tag v in
    if tag >= no_scan_tag then 
      if tag = string_tag then 
	Const_base (Const_string (Obj.obj v))
      else if tag = double_tag then
	Const_base (Const_float (Obj.obj v))
      else raise Unsupported
    else
      let len = Obj.size v in
      let rec transl_args pos =
	if pos = len then []
	else 
	  transl_constant (Obj.field v pos) :: transl_args (pos+1)
      in
      Const_block (tag, transl_args 0)
  else raise Unsupported
*)

let scan v =
  let blocks = ref [] in
  let rec scan v =
    if Obj.is_int v then ()
    else if Obj.is_block v then 
      try 
        let numref = List.assq v !blocks in
        incr numref
      with
      | Not_found ->
  	blocks := (v, ref 1) :: !blocks;
  	let tag = Obj.tag v in
  	if tag >= no_scan_tag then 
  	  if tag = string_tag then ()
  	  else if tag = double_tag then ()
  	  else raise Unsupported
  	else
  	  let len = Obj.size v in
  	  let rec scan_args pos =
  	    if pos = len then ()
  	    else begin
  	      scan (Obj.field v pos);
  	      scan_args (pos+1)
  	    end
  	  in
  	  scan_args 0
    else raise Unsupported
  in
  scan v;
  !blocks

(* Only for immutable constant without functions *)
let transl_constant overrides v =
  let shared_blocks = 
    List.map (fun (v,_) -> v, Ident.create "share")
      (List.filter (fun (v,numref) -> !numref > 1) (scan v))
  in
  let rec transl share v =
    if Obj.is_int v then
      Lconst (Const_base (Const_int (Obj.obj v)))
    else if Obj.is_block v then 
      try
	let find_overrides () = List.assq v overrides in
	if share then begin
	  try
	    Lvar (List.assq v shared_blocks)
	  with
	  | Not_found -> find_overrides ()
	end else find_overrides ()
      with
      | Not_found ->
	let tag = Obj.tag v in
	if tag >= no_scan_tag then 
	  if tag = string_tag then 
	    Lconst (Const_base (Const_string (Obj.obj v)))
	  else if tag = double_tag then
	    Lconst (Const_base (Const_float (Obj.obj v)))
	  else raise Unsupported
	else
	  let len = Obj.size v in
	  let rec transl_args pos =
	    if pos = len then []
	    else transl true (Obj.field v pos) :: transl_args (pos+1)
	  in
	  let args = transl_args 0 in
	  try
	    Lconst (Const_block (tag, List.map extract_constant args))
	  with
	  | Not_constant ->
	      Lprim(Pmakeblock(tag, Immutable), args)
    else raise Unsupported
  in
  let defs = List.map (fun (v,id) -> id, transl false v) shared_blocks in
  let body = transl false v in
  match defs with
  | [] -> body
  | _ -> Lletrec (defs, body)