summary refs log tree commit diff
path: root/source/mod_student/translator.ml
blob: c755cdb0fdf8da55ecd7036d72ed32f0a6e4ea0e (plain) (blame)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
open Xi_lib
open Ir

let i32_0 = Int32.of_int 0
let i32_1 = Int32.of_int 1

(* --------------------------------------------------- *)

module Make() = struct

  module Environment = struct

    type env = Env of
      { procmap: procid Ast.IdMap.t
      ; varmap: reg Ast.IdMap.t
      }

    let empty =
      let procmap = Ast.IdMap.empty in
      let varmap = Ast.IdMap.empty in
      Env {procmap; varmap}


    let add_proc id procid (Env {procmap; varmap}) =
      let procmap = Ast.IdMap.add id procid procmap in
      Env {procmap; varmap}

    let add_var id reg (Env {procmap; varmap}) =
      let varmap = Ast.IdMap.add id reg varmap in
      Env {procmap; varmap}

    let lookup_proc id (Env {procmap; _}) =
      try
        Ast.IdMap.find id procmap
      with Not_found ->
        failwith @@ Format.sprintf "Unknown procedure identifier: %s" (Ast.string_of_identifier id)

    let lookup_var id (Env {varmap; _}) =
      try
        Ast.IdMap.find id varmap
      with Not_found ->
        failwith @@ Format.sprintf "Unknown variable identifier: %s" (Ast.string_of_identifier id)

  end


(* --------------------------------------------------- *)
  module Scanner = struct

    let mangle_id id =
      Format.sprintf "_I_%s" (Ast.string_of_identifier id)

    let rec mangle_texpr = function
      | Ast.TEXPR_Int _ -> "i"
      | Ast.TEXPR_Bool _ -> "b"
      | Ast.TEXPR_Array {sub;_} -> "a" ^ mangle_texpr sub


    let mangle_var_declaration v = mangle_texpr @@ Ast.type_expression_of_var_declaration v

    let mangle_formal_parameters xs = String.concat "" (List.map mangle_var_declaration xs)

    let mangle_return_types xs = String.concat "" (List.map mangle_texpr xs)

    let scan_global_declaration (env, symbols) = function
      | Ast.GDECL_Function {id; formal_parameters; return_types; _} ->
        let name = Format.sprintf "%s_%s_%s" 
          (mangle_id id) (mangle_formal_parameters formal_parameters) (mangle_return_types return_types)
          in
        
        Environment.add_proc id (Procid name) env, Procid name :: symbols

    let scan_module env (Ast.ModuleDefinition {global_declarations; _}) = 
      List.fold_left scan_global_declaration (env, []) global_declarations

  end
(* --------------------------------------------------- *)

  module type SContext = sig

    val cfg : ControlFlowGraph.t

    val node2type: (Ast.node_tag, Types.normal_type) Hashtbl.t

    val allocate_register: unit -> reg
  end

(* --------------------------------------------------- *)
  module Translator(M:SContext) = struct
    open M

    (* dodaj instrukcje do bloku *)
    let append_instruction l i =
      let block = ControlFlowGraph.block cfg l in
      ControlFlowGraph.set_block cfg l (block @ [i])

    (* ustaw terminator na skok bezwarunkowy *)
    let set_jump l_from l_to =
      ControlFlowGraph.set_terminator cfg l_from @@ T_Jump l_to;
      ControlFlowGraph.connect cfg l_from l_to

    (* ustaw terminator na powrót-z-procedury *)
    let set_return l_from xs =
      ControlFlowGraph.set_terminator cfg l_from @@ T_Return xs;
      ControlFlowGraph.connect cfg l_from (ControlFlowGraph.exit_label cfg)

    (* ustaw terminator na skok warunkowy *)
    let set_branch cond a b l_from l_to1 l_to2 =
      ControlFlowGraph.set_terminator cfg l_from @@ T_Branch (cond, a, b, l_to1, l_to2);
      ControlFlowGraph.connect cfg l_from l_to1;
      ControlFlowGraph.connect cfg l_from l_to2

    let allocate_block () = ControlFlowGraph.allocate_block cfg

    let i32_0 = Int32.of_int 0
    let i32_1 = Int32.of_int 1
    let int32_of_bool b = Int32.of_int (
                            if b then 1 else 0 )

    (* --------------------------------------------------- *)
    let rec translate_expression env current_bb = function
      | Ast.EXPR_Char {value; _} ->
        current_bb, E_Int (Int32.of_int @@ Char.code value)

      | Ast.EXPR_Id {id; _} ->
         
        current_bb, E_Reg (Environment.lookup_var id env)
      | Ast.EXPR_Int {value;_} ->
         current_bb, E_Int value

      | Ast.EXPR_Bool {value; _} ->
         current_bb, E_Int (int32_of_bool value)

      | Ast.EXPR_Binop {lhs;rhs;op=Ast.BINOP_Add;tag;_} ->
         let reg= allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         begin
           match Hashtbl.find node2type tag with
           | TP_Int -> append_instruction current_bb @@ I_Add ( reg, lhs, rhs)
           | TP_Array _ -> append_instruction current_bb @@ I_Concat ( reg, lhs, rhs)
           | TP_Bool -> failwith "Internal error"
         end;
         current_bb, E_Reg reg

      | Ast.EXPR_Binop {lhs;rhs;op=Ast.BINOP_Sub;_} ->
         let reg= allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         append_instruction current_bb @@ I_Sub ( reg, lhs, rhs);
         current_bb, E_Reg reg

      | Ast.EXPR_Binop {lhs;rhs;op=Ast.BINOP_Mult;_} ->
         let reg= allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         append_instruction current_bb @@ I_Mul ( reg, lhs, rhs);
         current_bb, E_Reg reg

      | Ast.EXPR_Binop {lhs;rhs;op=Ast.BINOP_Div;_} ->
         let reg= allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         append_instruction current_bb @@ I_Div ( reg, lhs, rhs);
         current_bb, E_Reg reg

      | Ast.EXPR_Binop {lhs;rhs;op=Ast.BINOP_Rem;_} ->
         let reg= allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         append_instruction current_bb @@ I_Rem ( reg, lhs, rhs);
         current_bb, E_Reg reg
      | Ast.EXPR_Call call ->
         let reg =allocate_register() in
         let current_bb,env = translate_call env current_bb call [reg] in
         current_bb, E_Reg reg
      | Ast.EXPR_Relation {op;lhs;rhs;_} ->
         begin
         let convert_rel = function
           | Ast.RELOP_Eq -> COND_Eq
           | Ast.RELOP_Ne -> COND_Ne
           | Ast.RELOP_Lt -> COND_Lt
           | Ast.RELOP_Gt -> COND_Gt
           | Ast.RELOP_Le -> COND_Le
           | Ast.RELOP_Ge -> COND_Ge
         in
         let reg = allocate_register() in
         let current_bb,lhs = translate_expression env current_bb lhs in
         let current_bb,rhs = translate_expression env current_bb rhs in
         append_instruction current_bb @@ I_Set(reg,convert_rel op,lhs,rhs);
         current_bb, E_Reg reg
      
         end
      | _ ->
        failwith "not yet implemented"

    (* --------------------------------------------------- *)
    and translate_condition env current_bb else_bb = function 
      | Ast.EXPR_Bool {value=true; _} ->
        current_bb

      | Ast.EXPR_Bool {value=false; _} ->
        set_jump current_bb else_bb;
        allocate_block ()
      | Ast.EXPR_Binop {op=Ast.BINOP_And;lhs;rhs;_} ->
         let current_bb = translate_condition env current_bb else_bb lhs in
         let current_bb = translate_condition env current_bb else_bb rhs in
         current_bb
         
         

      | Ast.EXPR_Binop {op=Ast.BINOP_Or;lhs;rhs;_} ->
         let bb = allocate_block() in
         let current_bb = translate_condition env current_bb bb lhs in
         let current_bb = translate_condition env bb else_bb in
         bb
         
         
      (* Zaimplementuj dodatkowe przypadki *)
      
      | e ->
        let current_bb, res = translate_expression env current_bb e in
        let next_bb = allocate_block () in 
        set_branch COND_Ne res (E_Int i32_0) current_bb next_bb else_bb;
        next_bb


    (* --------------------------------------------------- *)
    and translate_call env current_bb (Ast.Call {callee;arguments;tag;_}) returns =
      begin
           let rec aux current_bb = function
             | [] -> []
             | x::xs -> let current_bb,r =
                          translate_expression env current_bb x
                        in r::(aux current_bb xs)
           in
           let arguments = aux current_bb arguments in
           append_instruction current_bb @@ I_Call ( returns, Environment.lookup_proc callee env, arguments, []);
           current_bb,env
      end
    let rec translate_statement env current_bb = function
      | Ast.STMT_Return {values;_} ->
         begin
           let rec aux current_bb = function
             | [] -> []
             | x::xs -> let current_bb,r =
                          translate_expression env current_bb x
                        in r::(aux current_bb xs)
           in
           set_return current_bb (aux current_bb values);
           current_bb,env
         end
      | STMT_Assign {lhs=Ast.LVALUE_Id{id;_};rhs;_} ->
         let current_bb, reg = translate_expression env current_bb rhs in
         let my_reg = allocate_register() in
         append_instruction current_bb @@ I_Move (my_reg, reg);
         current_bb, (Environment.add_var id my_reg env)           
      | STMT_Call call ->
         translate_call env current_bb call []
      | STMT_If {cond;then_branch;else_branch;_} ->
         let end_bb =allocate_block() in
         let else_bb = allocate_block() in
         let current_bb = translate_condition env current_bb else_bb cond in
         set_jump current_bb end_bb;
         set_jump else_bb end_bb;
         end_bb,env
      | _ ->
        failwith "not yet implemented"


    and translate_block env current_bb (Ast.STMTBlock {body; _}) =
      begin
           let rec aux env current_bb = function
             | [] -> env,current_bb
             | x::xs -> let current_bb,env =
                          translate_statement env current_bb x
                        in aux env current_bb xs
             in aux env current_bb body
      end
      

    let bind_var_declaration env vardecl =
      let r = allocate_register () in
      let env = Environment.add_var (Ast.identifier_of_var_declaration vardecl) r env in
      env, r

    let bind_formal_parameters env xs =
      let f env x = fst (bind_var_declaration env x) in 
      List.fold_left f env xs

  let translate_global_definition env = function
    | Ast.GDECL_Function {id; body=Some body; formal_parameters;_} ->
      let procid = Environment.lookup_proc id env in 
      let frame_size = 0 in
      let env = bind_formal_parameters env formal_parameters in
      let formal_parameters = List.length formal_parameters in
      let proc = Procedure {procid; cfg; frame_size; allocate_register; formal_parameters} in
      let first_bb = allocate_block () in
      let _, last_bb = translate_block env first_bb body in 
      ControlFlowGraph.connect cfg  last_bb (ControlFlowGraph.exit_label cfg);
      ControlFlowGraph.connect cfg  (ControlFlowGraph.entry_label cfg) first_bb;
      [proc]
    
    | _ ->
      []

  end

  let make_allocate_register () = 
    let counter = ref 0 in
    fun () ->
      let i = !counter in
      incr counter;
      REG_Tmp i


    let translate_global_definition node2type env gdef = 
      let cfg = ControlFlowGraph.create () in
      let module T = Translator(struct
        let cfg = cfg
        let node2type = node2type
        let allocate_register = make_allocate_register ()
      end) in
      T.translate_global_definition env gdef

    let translate_module node2type env (Ast.ModuleDefinition {global_declarations; _}) =
      List.flatten @@ List.map (translate_global_definition node2type env) global_declarations

    let translate_module mdef node2type =
      let env = Environment.empty in
      let env, symbols = Scanner.scan_module env mdef in
      let procedures = translate_module node2type env mdef in
      Program {procedures; symbols}
  end