Jelajahi Sumber

Documented extern variables phase, moved the phase to before dimension reduction, fixed dimension getters/setters for extern array variables

Taddeus Kroes 12 tahun lalu
induk
melakukan
111c1d546c
7 mengubah file dengan 171 tambahan dan 30 penghapusan
  1. 1 1
      Makefile
  2. 2 2
      main.ml
  3. 15 0
      phases/context.ml
  4. 22 14
      phases/desug.ml
  5. 32 10
      phases/extern.ml
  6. 93 0
      phases/extern.mli
  7. 6 3
      test/old/extern_vars.cvc

+ 1 - 1
Makefile

@@ -1,6 +1,6 @@
 RESULT := civicc
 GLOBALS := types globals stringify util
-PHASES := load parse print desug context typecheck dimreduce boolop extern \
+PHASES := load parse print desug context typecheck extern dimreduce boolop \
 	constprop index assemble peephole output
 SOURCES := $(addsuffix .mli,$(GLOBALS)) $(addsuffix .ml,$(GLOBALS)) \
 	lexer.mll parser.mly main.mli \

+ 2 - 2
main.ml

@@ -21,12 +21,12 @@ let phases = [
    "Context analysis");
   ("typecheck", Typecheck.phase, always,
    "Type checking");
+  ("extern", Extern.phase, always,
+   "Create getters and setters for extern variables");
   ("dimreduce", Dimreduce.phase, always,
    "Array dimension reduction");
   ("boolop", Boolop.phase, always,
    "Convert bool operations");
-  ("extern", Extern.phase, always,
-   "Create getters and setters for extern variables");
   ("constprop", Constprop.phase, when_optimize,
    "Constant propagation");
   ("index", Index.phase, always,

+ 15 - 0
phases/context.ml

@@ -45,6 +45,21 @@ let add_to_scope name dec depth (vars, funs) =
 
 let rec analyse scope depth node =
   let rec collect node = match node with
+    (* For extern array declarations, add the dimension names as well *)
+    | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
+      let rec add_dims = function
+        | [] -> []
+        | Dim (name, ann) :: tl ->
+          let dim = Dim (name, Depth depth :: ann) in
+          add_to_scope (Varname name) dim depth scope;
+          dim :: (add_dims tl)
+        | _ -> raise InvalidNode
+      in
+      let t = ArrayDims (ctype, add_dims dims) in
+      let node = GlobalDec (t, name, Depth depth :: ann) in
+      add_to_scope (Varname name) node depth scope;
+      node
+
     (* For variables, add the name (array dimensions are added
      * implicitly, since they have separate VarDec nodes which were added
      * during the desugaring phase *)

+ 22 - 14
phases/desug.ml

@@ -39,20 +39,20 @@ let rec consts_to_vars node =
 
   | node -> transform_children consts_to_vars node
 
-(* Generate new variables for array dimensions, to avoid re-evalutation when
- * array dimensions are used (e.g., after array dimension reduction). *)
-let rec array_dims node =
-  let make_dims basename values make_dec =
-    let make_name i _ = fresh_const (basename ^ "$dim$" ^ string_of_int (i + 1)) in
-    let names = mapi make_name values in
+let make_dims basename values make_dec =
+  let make_dimname i _ = basename ^ "$" ^ string_of_int (i + 1) in
+  let names = mapi make_dimname values in
 
-    let decs = List.map2 make_dec values names in
+  let decs = List.map2 make_dec values names in
 
-    let make_dim value name = Dim (name, annof value) in
-    let dims = List.map2 make_dim values names in
+  let make_dim value name = Dim (name, annof value) in
+  let dims = List.map2 make_dim values names in
 
-    (decs, dims)
-  in
+  (decs, dims)
+
+(* Generate new variables for array dimensions, to avoid re-evalutation when
+ * array dimensions are used (e.g., after array dimension reduction). *)
+let rec array_dims node =
   match node with
   | VarDec (ArrayDims (ctype, values), name, init, ann) ->
     let make_dec value name = VarDec (Int, name, Some value, []) in
@@ -64,14 +64,22 @@ let rec array_dims node =
     let (decs, dims) = make_dims name values make_dec in
     Block (decs @ [GlobalDef (export, ArrayDims (ctype, dims), name, None, ann)])
 
-  | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
+    (*
+  | GlobalDec (ArrayDims (ctype, values), name, ann) ->
+    (*
     let rec make_decs = function
       | [] -> []
       | Dim (name, ann) :: tl -> GlobalDec (Int, name, ann) :: (make_decs tl)
       | _ -> raise InvalidNode
     in
-    let decs = make_decs dims in
+    let decs = make_decs values in
+    Block (decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
+    *)
+
+    let make_dec value name = GlobalDec (Int, name, []) in
+    let (decs, dims) = make_dims name values make_dec in
     Block (decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
+    *)
 
   | node -> transform_children array_dims node
 
@@ -249,7 +257,7 @@ let rec array_init = function
   | Assign (name, None, (ArrayInit _ as value), ann) ->
     array_init (Assign (name, Some [], value, ann))
 
-  | Assign (name, Some indices, ArrayInit (value, dims), ann) as node ->
+  | Assign (name, Some indices, ArrayInit (value, dims), ann) ->
     let rec add_loop indices = function
       | [] ->
         array_init (Assign (name, Some indices, value, ann))

+ 32 - 10
phases/extern.ml

@@ -12,12 +12,12 @@ let call node args depth =
     FunUse (dec, args, [Type ctype; Depth depth])
   | _ -> raise InvalidNode
 
-let process globals = function
-  | GlobalDef (true, Array ctype, name, None, ann) as dec ->
-    (* Getters for array variable: crate getter for given index Note that
+let create_getset globals = function
+  | GlobalDef (true, ArrayDims (ctype, _), name, None, ann) as dec ->
+    (* Getters for array variable: create getter for given index Note that
      * getters and setters for dimensions are automatically generated,
      * because they have been put into new global variables during the
-     * desugarin phase *)
+     * desugaring phase *)
     let (param, index) = create_param Int (fresh_var "index") in
     let var = VarUse (dec, Some [index], [Type ctype; Depth 1]) in
     let body = Block [Return (var, [])] in
@@ -44,9 +44,26 @@ let process globals = function
 
     [getter; setter]
 
-  | GlobalDec (Array ctype, name, ann) ->
-    (* Getters for external array variable: create getter and setter for a
-     * given index *)
+  | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
+    (* External array variable: create getter and setter for a given index. Now
+     * we also need to generate functions for dimensions since they are NOT
+     * added as new variables during desugaring. *)
+    let rec add_dims i = function
+      | [] -> []
+      | Dim (dimname, ann) :: tl ->
+        let newname = name ^ "$" ^ string_of_int i in
+
+        let getter = FunDec (ctype, newname ^ "$get", [], []) in
+
+        let (param, _) = create_param ctype "value" in
+        let setter = FunDec (Void, newname ^ "$set", [param], []) in
+
+        Hashtbl.add globals dimname (call getter, call setter);
+        getter :: setter :: (add_dims (i + 1) tl)
+      | _ -> raise InvalidNode
+    in
+    let dimfuncs = add_dims 1 dims in
+
     let (param, _) = create_param Int "index" in
     let getter = FunDec (ctype, name ^ "$get", [param], []) in
 
@@ -55,7 +72,7 @@ let process globals = function
     let setter = FunDec (Void, name ^ "$set", [param1; param2], []) in
 
     Hashtbl.add globals name (call getter, call setter);
-    [getter; setter]
+    getter :: setter :: dimfuncs
 
   (* Getter for basic variable type: return the variable *)
   | GlobalDec (ctype, name, ann) ->
@@ -69,25 +86,30 @@ let process globals = function
 
   | _ -> raise InvalidNode
 
+(* Create getter/setter functions for exported/imported variables *)
 let rec create_funcs globals = function
   | Program (decls, ann) ->
     let decls = List.map (create_funcs globals) decls in
     Program (flatten_blocks (List.map (create_funcs globals) decls), ann)
 
   | GlobalDef (true, ctype, name, None, ann) as node ->
-    Block (GlobalDef (false, ctype, name, None, ann) :: (process globals node))
+    Block (GlobalDef (false, ctype, name, None, ann) ::
+    (create_getset globals node))
 
   | GlobalDec (ctype, name, ann) as node ->
-    Block (process globals node)
+    Block (create_getset globals node)
 
   | node -> transform_children (create_funcs globals) node
 
+(* Replace uses for imported/exported variabels with getter/setter functions *)
 let rec replace_vars scope depth = function
+  (* Variable names may be redefined in function scopes *)
   | (VarDec (_, name, _, _) as node)
   | (Param (_, name, _) as node) when Hashtbl.mem scope name ->
     Hashtbl.remove scope name;
     node
 
+  (* Copy scope when traversing into function,, and restore afterwards *)
   | FunDef (export, ret_type, name, params, body, ann) ->
     let local_scope = Hashtbl.copy scope in
     let trav = replace_vars local_scope (depth + 1) in

+ 93 - 0
phases/extern.mli

@@ -1 +1,94 @@
+(** Transform extern variables into getter/setter functions. *)
+
+(**
+    In CiviC, a global variable defined in one compilation unit can be used in
+    different compilation units. To do this, the variable definition needs to be
+    marked with the [export] keyword in the first compilation units and other
+    compilation unit need to contain a extern variable declaration.
+
+    Unfortunately, the CiviC VM does not support exporting global variables.
+    This phase solves this problem by replacing exported variables by getter and
+    setter functions. This phase works in three steps.
+
+    In the first step, getter and setter functions are added for all exported
+    variables definitions and the export flag is removed from these variable
+    definition. For example:
+
+
+{v export int foo;
+export int [n] bar; v}
+
+    becomes:
+
+{v int foo;
+int bar$1;
+int [bar$1] bar;
+
+export void foo$set(int new_value) \{
+    foo = new_value;
+\}
+
+export int foo$get() \{
+    return foo;
+\}
+
+export void bar$set(int index, int new_value) \{
+    bar[index] = new_value;
+\}
+
+export int bar$get(int index) \{
+    return bar[index];
+\}
+
+export void bar$1$set(int new_value) \{
+    bar$1 = new_value;
+\}
+
+export int bar$1$get() \{
+  return bar$1;
+\} v}
+
+
+Note that array dimensions are renamed during the desugaring phase, [n] is
+renamed to [bar$1] because it is the first dimension of bar. After that, it is
+handled as a regulary exported integer for which setter and getter functions are
+created.
+
+In the second step, external variable declarations are replaced by function
+declarations for the corresponding getter and setter functions. For example:
+
+
+{v extern int foo;
+extern int[n] bar; v}
+
+    becomes:
+
+{v extern void foo$set(int new_value);
+extern int foo$get();
+extern void bar$set(int index, int new_value);
+extern int bar$get(int index);
+extern int bar$1$get(int new_value);
+extern int bar$1$set(int index, int new_value); v}
+
+Again, note that [n] is renamed to [bar$1]. This is, however, not done during
+the desugaring phase, since the dimensions are not prevented from being
+evaluated more than once (See {!Desug} for an explanation about this).
+
+In the third step, all occurrences of external variables are replaced by
+function call to a getter or setter function. For example:
+
+{v /* extern int foo;     */
+/* extern int [n] bar; */
+export void main() \{
+    bar[foo] = n;
+\} v}
+
+    becomes:
+
+{v export void main() \{
+    bar$set(foo$get(), bar$1$get());
+\} v}
+
+    *)
+(** Main phase function, called by \{!Main\}. *)
 val phase : Main.phase_func

+ 6 - 3
test/old/extern_vars.cvc

@@ -1,6 +1,6 @@
 export bool local = true;
 
-export int[10] localarr;
+export int[10, 11] localarr;
 
 extern float ext;
 
@@ -8,8 +8,11 @@ extern int[n, m] extarr;
 
 export int main() {
     void localfunc(int n) {
-        m = n;
+        m = n;  // should be replaced with setter
     }
-    extarr[2, 3] = n;
+    void localscope() {
+        int m = 1;  // should not be replaced
+    }
+    extarr[2, 3] = n;  // should be replaced with setter/getter
     return 0;
 }