Przeglądaj źródła

Extern variables now use the .exportvar/.importvar directives + some general fixes

Taddeus Kroes 11 lat temu
rodzic
commit
cd575763a0

+ 2 - 2
Makefile

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

BIN
bin32/civas


BIN
bin32/civvm


+ 11 - 6
phases/assemble.ml

@@ -244,10 +244,15 @@ let assemble program =
                                          (should be one-dimensional)")))
 
     | VarUse (dec, Some dims, _) ->
-      let load = match (depthof dec, depthof node) with
-      | (0, _)            -> Load (typeof dec, Glob,        indexof dec)
-      | (a, b) when a = b -> Load (typeof dec, Current,     indexof dec)
-      | (a, b)            -> Load (typeof dec, Rel (b - a), indexof dec)
+      let load =
+        match dec with
+        | GlobalDec (ctype, name, _) ->
+          Load (ctype, Extern, indexof dec)
+        | _ ->
+          match (depthof dec, depthof node) with
+          | (0, _)            -> Load (typeof dec, Glob,        indexof dec)
+          | (a, b) when a = b -> Load (typeof dec, Current,     indexof dec)
+          | (a, b)            -> Load (typeof dec, Rel (b - a), indexof dec)
       in
       (trav_all (List.rev dims)) @          (* push dimensions *)
       [InlineComment (load, nameof dec)] @  (* push array reference *)
@@ -269,7 +274,7 @@ let assemble program =
   let instrs = trav program in
 
   (* Sort aggregated constants and add definitions
-   * If possible, this should be rewritten in the future because it's a little
+   * We might want to rewrite this in the future because it's a little
    * cumbersome right now... *)
   let pairs = ref [] in
   let add_pair value index =
@@ -279,7 +284,7 @@ let assemble program =
   Hashtbl.iter add_pair consts;
   let cmp (_, i) (_, j) = compare i j in
   let sorted_pairs = List.sort cmp !pairs in
-  let const_defs = List.map (fun (d, _) -> d) sorted_pairs in
+  let const_defs = List.map fst sorted_pairs in
 
   instrs @ const_defs
 

+ 39 - 27
phases/constprop.ml

@@ -24,23 +24,32 @@ let is_const = function
   | Var (name, _, _)      -> is_const_id name
   | _                     -> false
 
+(* Only assignments to variables local to this module can be removed, others
+ * must stay for consistency when used by other modules *)
+let is_local_dec = function
+  | VarDec _                       -> true
+  | GlobalDef (export, _, _, _, _) -> not export
+  | _                              -> false
+
 (* Play-it-safe side effect analysis: only return true for variables and
  * constants, since these are targeted in arithmetic simplification (in
  * particular targeting array indices that can be simplified after array
- * dimension reduction). *)
+ * dimension reduction) *)
 let no_side_effect = function
   | Const _ | VarUse _ | Var _ -> true
   | _ -> false
 
-(* Redefine integer operators within this module since they are only used on
- * IntVal values, which have type int32 *)
-let (+) = Int32.add
-let (-) = Int32.sub
-let (/) = Int32.div
-let ( * ) = Int32.mul
+(* Constand folding + arithmetic popagation *)
+let eval node =
+  (* Redefine integer operators within this module since they are only used on
+   * IntVal values, which have type int32 *)
+  let ( + ) = Int32.add in
+  let ( - ) = Int32.sub in
+  let ( / ) = Int32.div in
+  let ( * ) = Int32.mul in
+  let (mod) = Int32.rem in
 
-(* Constand folding *)
-let eval = function
+  match node with
   (* Binop - arithmetic *)
   | Binop (Add, Const (IntVal left, _), Const (IntVal right, _), ann) ->
     Const (IntVal (left + right), ann)
@@ -62,9 +71,9 @@ let eval = function
   | Binop (Div, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
     Const (FloatVal (left /. right), ann)
 
-  (*| Binop (Mod, Const (IntVal left, _), Const (IntVal right, _), ann) ->
+  | Binop (Mod, Const (IntVal left, _), Const (IntVal right, _), ann) ->
     Const (IntVal (left mod right), ann)
-*)
+
   (* Binop - relational *)
   | Binop (Eq, Const (IntVal left, _), Const (IntVal right, _), ann) ->
     Const (BoolVal (left = right), ann)
@@ -98,14 +107,14 @@ let eval = function
 
   (* Binop - logical *)
   | Binop (And, Const (BoolVal left, _), Const (BoolVal right, _), ann) ->
-    Const (BoolVal (left && right), ann)
+    Const (BoolVal (left & right), ann)
   | Binop (Or, Const (BoolVal left, _), Const (BoolVal right, _), ann) ->
     Const (BoolVal (left || right), ann)
 
   (* Monary operations *)
-  | Monop (Not, Const (BoolVal  value, _), ann) ->
+  | Monop (Not, Const (BoolVal value, _), ann) ->
     Const (BoolVal  (not value), ann)
-  | Monop (Neg, Const (IntVal   value, _), ann) ->
+  | Monop (Neg, Const (IntVal value, _), ann) ->
     Const (IntVal   (Int32.neg value), ann)
   | Monop (Neg, Const (FloatVal value, _), ann) ->
     Const (FloatVal (-.value), ann)
@@ -148,7 +157,10 @@ let rec propagate consts node =
     let value = propagate value in
     if is_const value then begin
       Hashtbl.add consts (nameof dec) value;
-      DummyNode
+      if is_local_dec dec then
+        DummyNode
+      else
+        VarLet (dec, None, value, ann)
     end else
       VarLet (dec, None, value, ann)
 
@@ -172,18 +184,18 @@ let rec propagate consts node =
     eval (Cond (propagate cond, propagate texp, propagate fexp, ann))
 
   | TypeCast (ctype, value, ann) ->
-    let value = propagate value in
-    begin match (ctype, value) with
-    | (Bool,  Const (BoolVal  value, _)) -> Const (BoolVal value, ann)
-    | (Bool,  Const (IntVal   value, _)) -> Const (BoolVal (value != 1l), ann)
-    | (Bool,  Const (FloatVal value, _)) -> Const (BoolVal (value != 1.0), ann)
-    | (Int,   Const (BoolVal  value, _)) -> Const (IntVal (if value then 1l else 0l), ann)
-    | (Int,   Const (IntVal   value, _)) -> Const (IntVal value, ann)
-    | (Int,   Const (FloatVal value, _)) -> Const (IntVal (Int32.of_float value), ann)
-    | (Float, Const (BoolVal  value, _)) -> Const (FloatVal (if value then 1. else 0.), ann)
-    | (Float, Const (IntVal   value, _)) -> Const (FloatVal (Int32.to_float value), ann)
-    | (Float, Const (FloatVal value, _)) -> Const (FloatVal value, ann)
-    | _ -> TypeCast (ctype, value, ann)
+    let c v = Const (v, ann) in
+    begin match ctype, propagate value with
+    | Bool,  (Const (BoolVal  _, _) as v)
+    | Int,   (Const (IntVal   _, _) as v)
+    | Float, (Const (FloatVal _, _) as v) -> v
+    | Bool,  Const (IntVal   v, _) -> c (BoolVal  (v != 1l))
+    | Bool,  Const (FloatVal v, _) -> c (BoolVal  (v != 1.0))
+    | Int,   Const (BoolVal  v, _) -> c (IntVal   (if v then 1l else 0l))
+    | Int,   Const (FloatVal v, _) -> c (IntVal   (Int32.of_float v))
+    | Float, Const (BoolVal  v, _) -> c (FloatVal (if v then 1. else 0.))
+    | Float, Const (IntVal   v, _) -> c (FloatVal (Int32.to_float v))
+    | _, v -> TypeCast (ctype, v, ann)
     end
 
   | _ -> traverse_unit propagate node

+ 1 - 9
phases/context.ml

@@ -54,15 +54,7 @@ let rec analyse scope depth node err =
   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 t = ArrayDims (ctype, List.map (annotate (Depth depth)) dims) in
       let node = GlobalDec (t, name, Depth depth :: ann) in
       add_to_scope (Varname name) node depth scope;
       node

+ 125 - 93
phases/desug.ml

@@ -2,6 +2,45 @@ open Printf
 open Types
 open Util
 
+(* Check if a function defines a variable name *)
+let defines var = function
+  | FunDef (export, ret_type, name, params, Block (VarDecs decs :: tl), ann) ->
+    let rec trav_decs = function
+      | [] -> false
+      | Param (ArrayDims (_, dims), name, _) :: tl ->
+        name = var || trav_decs dims || trav_decs tl
+      | (Dim (name, _) | VarDec (_, name, _, _) | Param (_, name, _)) :: _
+        when name = var -> true
+      | _ :: tl -> trav_decs tl
+    in
+    trav_decs params || trav_decs decs
+  | _ -> raise InvalidNode
+
+(* Replace all occurences of a variable name with another name *)
+let rec replace_var var replacement node =
+  let trav = (replace_var var replacement) in
+  let trav_all = List.map trav in
+  let trav_opt = function None -> None | Some node -> Some (trav node) in
+  match node with
+  (* Replace variable name on match *)
+  | Var (name, ind, ann) when name = var ->
+    let ind = match ind with None -> None | Some ind -> Some (trav_all ind) in
+    Var (replacement, ind, ann)
+
+  (* Don't enter a function body if it redefines the variable *)
+  | FunDef _ when defines var node -> node
+
+  (* Don't traverse into a for-loop body if the loop counter redefines var *)
+  | For (counter, start, stop, step, body, ann) when counter = var ->
+    For (counter, trav start, trav stop, trav step, body, ann)
+
+  (* At this point, array dimension expressions may not have been moved to new
+   * variables yet, so traverse them explicitly *)
+  | VarDec (ArrayDims (ctype, dims), name, init, ann) ->
+    VarDec (ArrayDims (ctype, trav_all dims), name, trav_opt init, ann)
+
+  | node -> traverse_unit trav node
+
 (* Create new constant variables for scalar initialisations on arrays so that
  * they are only evaluated once *)
 let rec move_scalars = function
@@ -21,56 +60,79 @@ let rec move_scalars = function
 
 (* 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 make_dimname values make_dec =
-    let names = mapi make_dimname values in
+let array_dims node =
+  (*
+  let make_dimname basename i = generate_const basename (i + 1) in
+  *)
+  let make_dimname = generate_const in
+  let patch_dims basename values make_decs =
+    let names = mapi (fun i _ -> make_dimname basename i) values in
 
-    let decs = List.map2 make_dec values names in
+    let decs = List.concat (List.map2 make_decs 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
-  match node with
-  | VarDec (ArrayDims (ctype, values), name, init, ann) ->
-    (* Names for VarDec dimensions must be unique to avoid weid errors when
-     * during context analysis, when an array variable is redeclared within the
-     * same scope *)
-    let make_dimname i _ = fresh_const (name ^ "_" ^ string_of_int (i + 1)) in
-
-    let make_dec value name = VarDec (Int, name, Some value, []) in
-    let (decs, dims) = make_dims make_dimname values make_dec in
-    Block (decs @ [VarDec (ArrayDims (ctype, dims), name, init, ann)])
-
-  | GlobalDef (export, ArrayDims (ctype, values), name, init, ann) ->
-    (* For global decs, the name must be derived from the array base name, but
-     * not constant (no trailing _) since the variable must exist for exporting
-     * (and not pruned during constant propagation) *)
-    let make_dimname i _ = generate_id name (i + 1) in
-
-    let make_dec value name = GlobalDef (export, Int, name, Some value, []) in
-    let (decs, dims) = make_dims make_dimname values make_dec in
-    Block (decs @ [GlobalDef (export, ArrayDims (ctype, dims), name, init, ann)])
-
-  (* DISABLED, this is also done in extern.ml
-  | 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 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)])
-  *)
+  (* Save dimension replacements in one global hash table (we are not replacing
+   * local vars, so everything is in the global scope) *)
+  let replacements = Hashtbl.create 10 in
+
+  let rec trav = function
+    | VarDec (ArrayDims (ctype, values), name, init, ann) ->
+      let make_decs value name = [VarDec (Int, name, Some value, [])] in
+      let (decs, dims) = patch_dims name values make_decs in
+      Block (decs @ [VarDec (ArrayDims (ctype, dims), name, init, ann)])
+
+    | GlobalDef (export, ArrayDims (ctype, values), name, init, ann) ->
+      (* Move array dimensions into new variables to avoid double evaluation of
+      * expressions with side effects (i.e. function calls) *)
+      let make_decs value name = [GlobalDef (export, Int, name, Some value, [])] in
+      let (decs, dims) = patch_dims name values make_decs in
+      Block (decs @ [GlobalDef (export, ArrayDims (ctype, dims), name, init, ann)])
+
+    | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
+      (* Create an 'extern int ...' definition for each dimension with a
+      * consistent import name, and replace all local uses with the imported
+      * variable name *)
+      let make_decs dim impname =
+        match dim with
+        | Dim (dimname, _) ->
+          (* Fix name clashes (needed because context analysis has not been done
+           * yet) *)
+          if Hashtbl.mem replacements dimname then begin
+            raise (FatalError (NodeMsg (dim, "duplicate dimension name")))
+          end;
+
+          (* Occurences of dimension names are replaced after the traversal *)
+          Hashtbl.add replacements dimname impname;
+
+          [GlobalDec (Int, impname, [])]
+        | _ -> raise InvalidNode
+      in
+      let (decs, dims) = patch_dims name dims make_decs in
+      Block (decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
+
+      (*
+      let make_decs i = function
+        | Dim (dimname, dimann) ->
+          let impname = generate_id name (i + 1) in
+          let decs = [
+            GlobalDec (Int, impname, []);
+            GlobalDef (false, Int, dimname, Some (Var (impname, None, [])), [])
+          ] in
+          (decs, Dim (impname, dimann))
+        | _ -> raise InvalidNode
+      in
+      let (decs, dims) = List.split (mapi make_decs dims) in
+      Block (List.concat decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
+      *)
 
-  | node -> traverse_unit array_dims node
+    | node -> traverse_unit trav node
+  in
+  Hashtbl.fold replace_var replacements (trav node)
 
 (* Split variable initialisation into declaration and assignment *)
 let rec split_inits = function
@@ -156,24 +218,14 @@ let rec move_inits = function
   | node -> traverse_unit move_inits node
 
 let for_to_while node =
-  let rec replace_var var replacement node =
-    let trav = (replace_var var replacement) in
-    match node with
-    | Var (name, None, ann) when name = var ->
-      Var (replacement, None, ann)
-    | For (counter, start, stop, step, body, ann) when counter = var ->
-      For (replacement, trav start, trav stop, trav step, trav body, ann)
-    | node ->
-      traverse_unit trav node
-  in
-  let rec traverse new_vars = function
+  let rec trav new_vars = function
     | FunDef (export, ret_type, name, params, body, ann) ->
       let rec place_decs decs = function
         | Block (VarDecs lst :: tl) -> Block (VarDecs (decs @ lst) :: tl)
         | _ -> raise InvalidNode
       in
       let new_vars = ref [] in
-      let body = traverse new_vars body in
+      let body = trav new_vars body in
       let create_vardec name = VarDec (Int, name, None, []) in
       let new_vardecs = List.map create_vardec !new_vars in
       let body = place_decs new_vardecs body in
@@ -199,7 +251,7 @@ let for_to_while node =
         Assign (_i, None, start, annof start);
         Assign (_stop, None, stop, annof stop);
         Assign (_step, None, step, annof step);
-        traverse new_vars (While (cond, (Block (
+        trav new_vars (While (cond, (Block (
           block_body (replace_var counter _i body) @
           [Assign (_i, None, Binop (Add, vi, vstep, []), [])]
         )), ann));
@@ -208,41 +260,35 @@ let for_to_while node =
     (* Transform while-loops to do-while loops in if-statements *)
     (* DISABLED, while-loops are explicitly supported by the assembly phase
     | While (cond, body, ann) ->
-      let cond = traverse new_vars cond in
-      let body = traverse new_vars body in
+      let cond = trav new_vars cond in
+      let body = trav new_vars body in
       Block [If (cond, Block [DoWhile (cond, body, ann)], ann)]
     *)
 
-    | node -> traverse_unit (traverse new_vars) node
+    | node -> traverse_unit (trav new_vars) node
   in
-  traverse (ref []) node
-
-let rec sublist n = function
-  | [] when n > 0  -> raise (Invalid_argument "n")
-  | []             -> []
-  | lst when n = 0 -> lst
-  | _ :: tl        -> sublist (n - 1) tl
+  trav (ref []) node
 
 let rec array_init = function
   (* Transform array constant initialisation into separate assign statements
-   * for all entries in the constant array *)
+   * for all entries in the array literal *)
   | Assign (name, None, ArrayInit (ArrayConst _ as value, dims), ann) ->
     let intconst i = Const (IntVal (Int32.of_int i), []) in
     let ndims = List.length dims in
     let rec make_assigns depth i indices = function
       | [] -> []
       | hd :: tl ->
-        let assigns = traverse depth (i :: indices) hd in
+        let assigns = trav depth (i :: indices) hd in
         make_assigns depth (i + 1) indices tl @ assigns
-    and traverse depth indices = function
+    and trav depth indices = function
       | ArrayConst (values, _) ->
         make_assigns (depth + 1) 0 indices values
       | value when depth = ndims ->
         let indices = List.map intconst indices in
         [Assign (name, Some (List.rev indices), value, ann)]
-      (* DISABLED: nesting level must be equal to number of dimensions
+      (* DISABLED: nesting level must now be equal to number of dimensions
       | value when depth < ndims ->
-        (* Use the for-loops constructed for scalar assignment *)
+        (* Use for-loops for scalar assignment on sub-array *)
         let value = ArrayInit (value, dims) in
         let indices = List.map intconst indices in
         [array_init (Assign (name, Some (List.rev indices), value, ann))]
@@ -252,28 +298,7 @@ let rec array_init = function
           "dimension mismatch: expected %d nesting levels, got %d"
           ndims depth)))
     in
-    Block (List.rev (traverse 0 [] value))
-
-    (*
-    let ndims = list_size dims in
-    let rec make_assigns depth i indices = function
-        | [] -> []
-        | hd :: tl ->
-            let assigns = traverse depth (i :: indices) hd in
-            make_assigns depth (i + 1) indices tl @ assigns
-    and traverse depth indices = function
-        | ArrayConst (values, _) ->
-            make_assigns (depth + 1) 0 indices values
-        | value when depth = ndims ->
-            let intconst i = Const (IntVal (Int32.of_int i), []) in
-            [Assign (name, Some (List.rev_map intconst indices), value, loc)]
-        | node ->
-            raise (FatalError (NodeMsg (node, sprintf
-                "dimension mismatch: expected %d nesting levels, got %d"
-                ndims depth)))
-    in
-    Block (List.rev (traverse 0 [] value))
-    *)
+    Block (List.rev (trav 0 [] value))
 
   (* Replace no indices with empty indices to have a list below *)
   | Assign (name, None, (ArrayInit _ as value), ann) ->
@@ -294,6 +319,12 @@ let rec array_init = function
         in
         For (counter, start, stop, step, body, [])
     in
+    let rec sublist n = function
+      | [] when n > 0  -> raise (Invalid_argument "n")
+      | []             -> []
+      | lst when n = 0 -> lst
+      | _ :: tl        -> sublist (n - 1) tl
+    in
     let dims_left = sublist (List.length indices) dims in
     add_loop indices dims_left
 
@@ -301,8 +332,9 @@ let rec array_init = function
 
 let phase = function
   | Ast node ->
-    (* Generate variable declarations for expressions that must be evaluated
-     * once and used multiple times *)
+    (* Move array dimensions and scalar initialisations into new variables as
+     * initialisations, so that they are evaluated exactly once, and so that
+     * dimension names are consistent with the array name *)
     let node = move_scalars (array_dims node) in
 
     (* Split variable initialisations into declarations and assignments, and

+ 6 - 3
phases/dimreduce.ml

@@ -5,6 +5,9 @@ let flatten_type = function
   | GlobalDef (export, ArrayDims (ctype, _), name, None, ann) ->
     GlobalDef (export, Array ctype, name, None, ann)
 
+  | GlobalDec (ArrayDims (ctype, _), name, ann) ->
+    GlobalDec (Array ctype, name, ann)
+
   | VarDec (ArrayDims (ctype, _), name, None, ann) ->
     VarDec (Array ctype, name, None, ann)
 
@@ -24,7 +27,7 @@ let rec expand_dims = function
     let params = flatten_blocks (List.map expand_dims params) in
     FunDec (ret_type, name, params, ann)
 
-  | FunUse (dec, params, ann) as node ->
+  | FunUse (dec, params, ann) ->
     FunUse (dec, flatten_blocks (List.map expand_dims params), ann)
 
   (* Add additional parameters for array dimensions *)
@@ -96,8 +99,8 @@ and dim_reduce depth = function
   | GlobalDef (export, ArrayDims (ctype, _), name, None, ann) ->
     GlobalDef (export, Array ctype, name, None, ann)
 
-  | GlobalDef (export, ArrayDims (ctype, _), name, None, ann) ->
-    GlobalDef (export, Array ctype, name, None, ann)
+  | GlobalDec (ArrayDims (ctype, _), name, ann) ->
+    GlobalDec (Array ctype, name, ann)
 
   | VarDec (ArrayDims (ctype, _), name, None, ann) ->
     VarDec (Array ctype, name, None, ann)

+ 0 - 146
phases/extern.ml

@@ -1,146 +0,0 @@
-open Types
-open Util
-
-let create_param ctype name =
-  let param = Param (ctype, name, [Depth 1]) in
-  let value = VarUse (param, None, [Type ctype; Depth 1]) in
-  (param, value)
-
-let call node args depth =
-  match node with
-  | FunDec (ctype, name, _, _) as dec ->
-    FunUse (dec, args, [Type ctype; Depth depth])
-  | _ -> raise InvalidNode
-
-let generate_name name postfix = "_" ^ name ^ "_" ^ postfix
-let getname name = generate_name name "get"
-let setname name = generate_name name "set"
-
-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
-     * desugaring phase *)
-    let (param, index) = create_param Int (fresh_id "index") in
-    let var = VarUse (dec, Some [index], [Type ctype; Depth 1]) in
-    let body = Block [Return (var, [])] in
-    let getter = FunDef (true, ctype, getname name, [param], body, []) in
-
-    (* Setters for array variable: create setter for given index *)
-    let (param1, index) = create_param Int (fresh_id "index") in
-    let (param2, value) = create_param ctype (fresh_id "value") in
-    let params = [param1; param2] in
-    let body = Block [VarLet (dec, Some [index], value, [])] in
-    let setter = FunDef (true, Void, setname name, params, body, []) in
-
-    [getter; setter]
-
-  | GlobalDef (true, ctype, name, None, ann) as dec ->
-    (* Getter for basic variable type: return the variable *)
-    let var = VarUse (dec, None, [Type ctype; Depth 1]) in
-    let body = [Return (var, [])] in
-    let getter = FunDef (true, ctype, getname name, [], Block body, []) in
-
-    (* Setter for basic variable type: assign the variable *)
-    let (param, value) = create_param ctype (fresh_id "value") in
-    let body = [VarLet (dec, None, value, [])] in
-    let setter = FunDef (true, Void, setname name, [param], Block body, []) in
-
-    [getter; setter]
-
-  | 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 = generate_id name i in
-
-        let getter = FunDec (ctype, getname newname, [], []) in
-
-        let (param, _) = create_param ctype "value" in
-        let setter = FunDec (Void, setname newname, [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, getname name, [param], []) in
-
-    let (param1, index) = create_param Int "index" in
-    let (param2, value) = create_param ctype "value" in
-    let setter = FunDec (Void, setname name, [param1; param2], []) in
-
-    Hashtbl.add globals name (call getter, call setter);
-    getter :: setter :: dimfuncs
-
-  (* Getter for basic variable type: return the variable *)
-  | GlobalDec (ctype, name, ann) ->
-    let getter = FunDec (ctype, getname name, [], []) in
-
-    let (param, _) = create_param ctype "value" in
-    let setter = FunDec (Void, setname name, [param], []) in
-
-    Hashtbl.add globals name (call getter, call setter);
-    [getter; setter]
-
-  | _ -> 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) ::
-    (create_getset globals node))
-
-  | GlobalDec (ctype, name, ann) as node ->
-    Block (create_getset globals node)
-
-  | node -> traverse_unit (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
-    let params = List.map trav params in
-    FunDef (export, ret_type, name, params, trav body, ann)
-
-  (* Use of regular external variable *)
-  | VarUse (dec, None, _) when Hashtbl.mem scope (nameof dec) ->
-    let (get, _) = Hashtbl.find scope (nameof dec) in
-    get [] depth
-
-  (* Dereference of external array *)
-  | VarUse (dec, Some indices, _) when Hashtbl.mem scope (nameof dec) ->
-    let (get, _) = Hashtbl.find scope (nameof dec) in
-    get indices depth
-
-  | VarLet (dec, dims, value, _) when Hashtbl.mem scope (nameof dec) ->
-    let dims = optmapl (replace_vars scope depth) dims in
-    let (_, set) = Hashtbl.find scope (nameof dec) in
-    Expr (set (dims @ [replace_vars scope depth value]) depth)
-
-  | node -> traverse_unit (replace_vars scope depth) node
-
-let phase = function
-  | Ast node ->
-    let globals = Hashtbl.create 20 in
-    let node = create_funcs globals node in
-    Ast (replace_vars globals 0 node)
-  | _ -> raise InvalidInput

+ 0 - 97
phases/extern.mli

@@ -1,97 +0,0 @@
-(** 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[2] bar; v}
-
-    becomes:
-
-{v int foo;
-
-export int _foo_get() \{
-    return foo;
-\}
-
-export void _foo_set(int _value_1) \{
-    foo = _value_1;
-\}
-
-int _bar_1 = 2;
-
-export int __bar_1_get() \{
-    return _bar_1;
-\}
-
-export void __bar_1_set(int _value_2) \{
-    _bar_1 = _value_2;
-\}
-
-int[_bar_1] bar;
-
-export int _bar_get(int _index_3) \{
-    return bar[_index_3];
-\}
-
-export void _bar_set(int _index_4, int _value_5) \{
-    bar[_index_4] = _value_5;
-\} v}
-
-Note that array dimensions are renamed during the desugaring phase, [2] is
-moved to a variable [_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 implicitly.
-
-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 int _foo_get();
-extern void _foo_set(int value);
-extern int _bar_get(int index);
-extern void _bar_set(int index, int value);
-extern int __bar_1_get();
-extern void __bar_1_set(int value); v}
-
-Again, note that [n] is renamed to [_bar_1]. Also note that this is not already
-done during the desugaring phase for external arrays, 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

+ 1 - 1
phases/print.ml

@@ -160,7 +160,7 @@ let rec print_assembly oc instrs =
    * printed at the end of the file here. The directives are sorted by the first
    * 7 characters to group directive opcodes *)
   if List.length !endbuf > 0 then
-    let cmp a b = compare (String.sub b 0 7) (String.sub a 0 7) in
+    let cmp a b = compare (String.sub a 0 7) (String.sub b 0 7) in
     List.iter output_line (List.sort cmp (List.rev !endbuf))
 
 let phase = function

+ 19 - 21
phases/unroll.ml

@@ -21,14 +21,11 @@ let rec get_body_step i rest = function
 
   | [VarLet (
       VarDec (Int, assigned, None, _), None,
-      Binop (
-        Add,
+      Binop (Add,
         VarUse (VarDec (Int, added, None, _), None, _),
         Const (IntVal step, _),
-        _
-      ),
-      _
-    )] when assigned = added -> Some (step, List.rev rest)
+      _),
+    _)] when assigned = added -> Some (step, List.rev rest)
 
   | hd :: tl -> get_body_step i (hd :: rest) tl
 
@@ -55,23 +52,24 @@ let rec unroll_body counters = function
       Block body,
     _) as loop) :: tl
     when is_generated_id i & comp = i ->
-      begin
-        match get_body_step i [] body with
-        | Some (step, rest) ->
-          let rest = flatten_blocks (unroll_body counters rest) in
-          let i_values = range start stop step in
+    begin
+      match get_body_step i [] body with
+      | Some (step, rest) ->
+        (* First unroll inner loops in body before the may_be_unrolled check *)
+        let rest = flatten_blocks (unroll_body counters rest) in
 
-          if may_be_unrolled i_values rest then begin
-            Hashtbl.add counters i true;
-            let dup_body value =
-              replace_var i (Const (IntVal value, [Type Int])) (Block rest)
-            in
-            Block (List.map dup_body i_values) :: (unroll_body counters tl)
-          end else
-            init :: (unroll counters loop) :: (unroll_body counters tl)
+        let i_values = range start stop step in
+        if may_be_unrolled i_values rest then begin
+          Hashtbl.add counters i true;
+          let dup_body value =
+            replace_var i (Const (IntVal value, [Type Int])) (Block rest)
+          in
+          Block (List.map dup_body i_values) :: (unroll_body counters tl)
+        end else
+          init :: (unroll counters loop) :: (unroll_body counters tl)
 
-        | None -> init :: (unroll counters loop) :: (unroll_body counters tl)
-      end
+      | None -> init :: (unroll counters loop) :: (unroll_body counters tl)
+    end
 
   | hd :: tl -> (unroll counters hd) :: (unroll_body counters tl)
 

+ 0 - 0
test/arrays/check_error/extern_array_arg.cvc → test/arrays/check_success/extern_array_arg.cvc


+ 1 - 0
test/arrays/combined_extern_arrayref_arg/defs.cvc

@@ -0,0 +1 @@
+export int[2, 3] foo = [[1, 2, 3], [4, 5, 6]];

+ 4 - 0
test/arrays/combined_extern_arrayref_arg/expected.out

@@ -0,0 +1,4 @@
+2 3
+1 2 3  4 5 6
+2 2
+7 8  9 10

+ 27 - 0
test/arrays/combined_extern_arrayref_arg/main.cvc

@@ -0,0 +1,27 @@
+extern void printInt(int val);
+extern void printSpaces(int num);
+extern void printNewlines(int num);
+
+extern int[n, m] foo;
+int[2, 2] bar = [[7, 8], [9, 10]];
+
+void printArray(int[n, m] a) {
+    printInt(n);
+    printSpaces(1);
+    printInt(m);
+    printNewlines(1);
+    for (int i = 0, n) {
+        for (int j = 0, m) {
+            printInt(a[i, j]);
+            if (j != m - 1) printSpaces(1);
+        }
+        if (i != n - 1) printSpaces(2);
+    }
+}
+
+export int main() {
+    printArray(foo);
+    printNewlines(1);
+    printArray(bar);
+    return 0;
+}

+ 0 - 39
test/basic/functional/nested_funs.cvc

@@ -1,39 +0,0 @@
-extern void printInt(int val);
-extern void printNewlines(int num);
-
-void foo() {
-    void bar() {
-        void bar() {
-            printInt(1);
-            foobar();  // isrn 1
-        }
-
-        printInt(2);
-        bar();     // isrl
-        baz();     // isrg
-        foobar();  // isr
-    }
-
-    void foobar() {}
-
-    printInt(3);    // isrg
-    bar();          // isrl
-    baz();          // isrg
-}
-
-void bar() {
-    printInt(4);
-}
-
-void baz() {
-    printInt(5);
-    bar();
-}
-
-export int main() {
-    foo();
-    bar();
-    baz();
-    printNewlines(1);
-    return 0;
-}

+ 0 - 1
test/basic/functional/nested_funs.out

@@ -1 +0,0 @@
-3215454454

+ 1 - 1
test/run.bash

@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 CIVAS=${CIVAS-../bin32/civas}
 CIVVM=${CIVVM-../bin32/civvm}
-CIVCC=${CIVCC-../bin/civicc}
+CIVCC=${CIVCC-../civicc}
 CFLAGS=${CFLAGS-}
 RUN_FUNCTIONAL=${RUN_FUNCTIONAL-1}
 

+ 1 - 1
types.mli

@@ -162,7 +162,7 @@ type instr =
   | ExportFun of string * ctype * ctype list * string
     (** [.exportfun "<name>" <ret_type> [<arg_type_1> ...]] <label> *)
   | ImportVar of string * ctype
-    (** [.importvar "<name>" <type> *)
+    (** [.importvar "<name>" <type>] *)
   | ImportFun of string * ctype * ctype list
     (** [.importfun "<name>" <ret_type> [<arg_type_1> ...]] *)
   | ConstDef of const

+ 3 - 2
util.ml

@@ -26,8 +26,7 @@ let log_node verbosity node =
 
 (* Variable generation *)
 
-let generate_id id num =
-  "_" ^ id ^ "_" ^ string_of_int num
+let generate_id id num = "_" ^ id ^ "_" ^ string_of_int num
 
 let var_counter = ref 0
 
@@ -44,6 +43,8 @@ let fresh_id base =
 
 (* Constants are marked by a leading _ for recognition during constant
  * propagation *)
+let generate_const id num = generate_id id num ^ "_"
+
 let fresh_const id = fresh_id id ^ "_"
 
 let is_generated_id id = String.length id >= 1 & id.[0] = '_'

+ 6 - 2
util.mli

@@ -20,10 +20,14 @@ val is_generated_id : string -> bool
 (** Check if an identifier is a constant generated by the compiler. *)
 val is_const_id : string -> bool
 
-(** Generate an identifier from a base and a number. E.g., [generate_id "foo"
-    1] returns ["_foo_1"]*)
+(** Generate an identifier from a base and a number.
+    E.g., [generate_id "foo" 1] returns ["_foo_1"]*)
 val generate_id : string -> int -> string
 
+(** Generate a constant identifier from a base and a number.
+    E.g., [generate_id "foo" 1] returns ["_foo_1_"]*)
+val generate_const : string -> int -> string
+
 (** {2 AST traversal} *)
 
 (** Default transformation traversal for AST nodes of arbitrary constructor: