File: Variable.m3 Last Modified On Tue Jun 20 09:58:08 PDT 1995 By kalsow Modified On Thu Jun 15 12:45:02 PDT 1995 By ericv Modified On Thu Dec 5 17:21:40 PST 1991 By muller
MODULE* -- this doesn't work with the current gcc-based backend. It chokes on VAR v: BITS 32 FOR CHAR := 'X' -- 10/9/96 WKK PROCEDURE FindAlignment (align: AlignVal; size: INTEGER): AlignVal = (* Fix the alignment of small local variables and parameters with BITS FOR types; IMPORT M3, M3ID, CG, Value, ValueRep, Type, Expr, Error, RunTyme; IMPORT Scope, AssignStmt, Formal, M3RT, IntegerExpr, TipeMap, M3String; IMPORT OpenArrayType, Target, TInt, Token, Ident, Module, CallExpr; IMPORT Decl, Null, Int, LInt, Fmt, Procedure, Tracer, TextExpr, NamedExpr; IMPORT PackedType, ErrType; FROM Scanner IMPORT GetToken, Match, cur; CONST Big_Local = 8192; (* x Target.Char.size *) Big_Param = 8; (* x Target.Integer.size *) Max_zero_global = 64; (* x Target.Integer.size *) REVEAL T = Value.T BRANDED "Variable.T" OBJECT tipe : Type.T; init : Expr.T; sibling : T; formal : Value.T; alias : T; trace : Tracer.T; bounds : BoundPair; cg_var : CG.Var; bss_var : CG.Var; next_cg_var : T; init_var : INTEGER; offset : INTEGER; size : INTEGER; align : AlignVal; cg_align : AlignVal; mem_type : BITS 4 FOR CG.Type; stk_type : BITS 4 FOR CG.Type; indirect : M3.Flag; open_ok : M3.Flag; need_addr : M3.Flag; no_type : M3.Flag; global : M3.Flag; initDone : M3.Flag; initZero : M3.Flag; initPending : M3.Flag; initStatic : M3.Flag; OVERRIDES typeCheck := Check; set_globals := SetGlobals; load := Load; declare := Declare; const_init := ConstInit; need_init := NeedInit; lang_init := LangInit; user_init := UserInit; toExpr := ValueRep.NoExpr; toType := ValueRep.NoType; typeOf := TypeOf; base := ValueRep.Self; add_fp_tag := AddFPTag; fp_type := TypeOf; END; TYPE AlignVal = [0..255]; TYPE BoundPair = REF RECORD min : Target.Int; max : Target.Int; END; VAR all_cg_vars: T := NIL; (* variables with attached M3CG values *) PROCEDURE Variable Reset () = VAR t, u: T; BEGIN (* release any M3CG nodes that we've created *) t := all_cg_vars; WHILE (t # NIL) DO u := t; t := t.next_cg_var; u.cg_var := NIL; u.bss_var := NIL; u.next_cg_var := NIL; END; all_cg_vars := NIL; END Reset; PROCEDUREParseDecl (READONLY att: Decl.Attributes) = TYPE TK = Token.T; VAR t : T; type : Type.T; expr : Expr.T; j, n : INTEGER; trace : Tracer.T; alias : M3ID.T; BEGIN Match (TK.tVAR); WHILE (cur.token = TK.tIDENT) DO n := Ident.ParseList (); type := NIL; expr := NIL; IF (cur.token = TK.tCOLON) THEN GetToken (); (* : *) type := Type.Parse (); END; IF (cur.token = TK.tEQUAL) THEN Error.Msg ("variable initialization must begin with ':='"); cur.token := TK.tASSIGN; END; IF (cur.token = TK.tASSIGN) THEN GetToken (); (* := *) expr := Expr.Parse (); END; trace := ParseTrace (); IF (expr = NIL) AND (type = NIL) THEN Error.Msg("variable declaration must include a type or initial value"); END; IF att.isExternal AND att.alias # M3ID.NoID AND n > 1 THEN Error.WarnID (2, att.alias, "EXTERNAL alias applies to first variable"); END; alias := att.alias; j := Ident.top - n; FOR i := 0 TO n - 1 DO t := New (Ident.stack[j + i], FALSE); t.origin := Ident.offset[j + i]; t.external := att.isExternal; t.unused := att.isUnused; t.obsolete := att.isObsolete; t.tipe := type; t.init := expr; t.no_type := (type = NIL); IF (att.isExternal) THEN IF (alias # M3ID.NoID) THEN t.extName := alias; alias := M3ID.NoID; ELSE t.extName := t.name; END; END; Scope.Insert (t); BindTrace (t, trace); END; DEC (Ident.top, n); Match (TK.tSEMI); END; END ParseDecl; PROCEDURENew (name: M3ID.T; used: BOOLEAN): T = VAR t: T; BEGIN t := NEW (T); ValueRep.Init (t, name, Value.Class.Var); t.used := used; t.tipe := NIL; t.init := NIL; t.readonly := FALSE; t.indirect := FALSE; t.global := FALSE; t.formal := NIL; t.alias := NIL; t.extName := M3ID.NoID; t.open_ok := FALSE; t.need_addr := FALSE; t.no_type := FALSE; t.initDone := FALSE; t.initZero := FALSE; t.initPending := FALSE; t.initStatic := FALSE; t.bounds := NIL; t.cg_align := 0; t.cg_var := NIL; t.bss_var := NIL; t.init_var := 0; t.offset := 0; t.size := 0; t.align := 0; t.mem_type := CG.Type.Void; t.stk_type := CG.Type.Void; t.trace := NIL; RETURN t; END New; PROCEDURENewFormal (formal: Value.T; name: M3ID.T): T = VAR t: T; f_info: Formal.Info; BEGIN t := New (name, FALSE); Formal.Split (formal, f_info); t.formal := formal; t.tipe := f_info.type; t.origin := formal.origin; t.indirect := (f_info.mode # Formal.Mode.mVALUE); t.readonly := (f_info.mode = Formal.Mode.mCONST); t.unused := f_info.unused; t.initDone := TRUE; t.imported := FALSE; (* in spite of Module.depth *) IF (NOT t.indirect) AND (OpenArrayType.Is (t.tipe)) THEN t.indirect := TRUE; END; t.trace := NIL; (* the caller must call BindTrace after the variable is inserted into a scope *) RETURN t; END NewFormal; PROCEDURESplit (t: T; VAR type: Type.T; VAR global, indirect, traced: BOOLEAN) = BEGIN <* ASSERT t.checked *> type := t.tipe; global := t.global; indirect := t.indirect; traced := t.traced; END Split; PROCEDUREBindType (t: T; type: Type.T; indirect, readonly, open_array_ok, needs_init: BOOLEAN) = BEGIN <* ASSERT t.tipe = NIL *> t.tipe := type; t.readonly := readonly; t.indirect := indirect; t.open_ok := open_array_ok; IF NOT needs_init THEN t.initDone := TRUE END; END BindType; PROCEDURENeedsAddress (t: T) = BEGIN IF (t = NIL) THEN RETURN END; t.need_addr := TRUE; END NeedsAddress; PROCEDUREIsFormal (t: T): BOOLEAN = BEGIN RETURN (t # NIL) AND (t.formal # NIL); END IsFormal; PROCEDUREHasClosure (t: T): BOOLEAN = BEGIN RETURN (t # NIL) AND (t.formal # NIL) AND Formal.HasClosure (t.formal); END HasClosure; PROCEDURETypeOf (t: T): Type.T = BEGIN IF (t.tipe = NIL) THEN IF (t.init # NIL) THEN t.tipe := Expr.TypeOf (t.init) ELSIF (t.formal # NIL) THEN t.tipe := Value.TypeOf (t.formal) END; IF (t.tipe = NIL) THEN Error.ID (t.name, "variable has no type"); t.tipe := ErrType.T; END; END; RETURN t.tipe; END TypeOf; PROCEDURECheck (t: T; VAR cs: Value.CheckState) = VAR dfault: Expr.T; min, max: Target.Int; info: Type.Info; ref: Type.T; BEGIN t.tipe := Type.CheckInfo (TypeOf (t), info); IF (info.class = Type.Class.Packed) AND (t.formal # NIL) AND (NOT t.indirect) THEN EVAL Type.CheckInfo (PackedType.Base (t.tipe), info); END; t.size := info.size; t.align := info.alignment; t.mem_type := info.mem_type; t.stk_type := info.stk_type; IF (info.class = Type.Class.OpenArray) AND (t.formal = NIL) AND (NOT t.open_ok) THEN Error.ID (t.name, "variable cannot be an open array"); END; IF (info.isEmpty) THEN Error.ID (t.name, "variable has empty type"); END; IF (t.no_type) AND (t.tipe # ErrType.T) AND Type.IsEqual (t.tipe, Null.T, NIL) THEN Error.WarnID (1, t.name, "variable has type NULL"); END; t.global := Scope.OuterMost (t.scope); t.checked := TRUE; (* allow recursions through the init expr *) IF (NOT t.indirect) AND (NOT t.global) THEN IF (t.formal # NIL) AND (info.size > Big_Param * Target.Integer.size) THEN Error.WarnID (1, t.name, "large parameter passed by value (" & Fmt.Int (info.size DIV Target.Char.size) & " bytes)"); ELSIF (info.size > Big_Local * Target.Char.size) THEN Error.WarnID (1, t.name, "large local variable (" & Fmt.Int (info.size DIV Target.Char.size) & " bytes)"); END; ELSIF (t.formal # NIL) AND (info.class = Type.Class.OpenArray) AND Formal.RefOpenArray (t.formal, ref) THEN Error.WarnID (1, t.name, "open array passed by value"); END; IF Type.IsStructured (t.tipe) THEN t.need_addr := TRUE; (* every load requires an address *) END; Value.TypeCheck (t.formal, cs); IF (t.external) THEN IF (t.init # NIL) THEN Error.Msg ("<*EXTERNAL*> variables cannot be initialized"); Expr.TypeCheck (t.init, cs); AssignStmt.Check (t.tipe, t.init, cs); END; ELSIF (t.init # NIL) THEN Expr.TypeCheck (t.init, cs); AssignStmt.Check (t.tipe, t.init, cs); dfault := Expr.ConstValue (t.init); IF (dfault = NIL) THEN IF Module.IsInterface () THEN Error.ID (t.name, "initial value is not a constant"); END; IF (t.global) AND (info.size > Max_zero_global * Target.Integer.size) THEN <*ASSERT NOT t.indirect*> t.indirect := TRUE; END; ELSE (* initialize the variable to an explicit constant *) IF NOT t.indirect THEN t.initZero := Expr.IsZeroes (dfault); IF (t.global) THEN IF (t.initZero) THEN t.initDone := TRUE; IF (info.size > Max_zero_global * Target.Integer.size) THEN <*ASSERT NOT t.indirect*> t.indirect := TRUE; END; END; ELSIF (NOT t.initZero) AND Type.IsStructured (t.tipe) THEN t.initStatic := TRUE; END; t.init := dfault; END; END; ELSIF (t.global) THEN (* no explict initialization is given, but the var is global *) IF Type.InitCost (t.tipe, TRUE) <= 0 THEN IF (info.size > Max_zero_global * Target.Integer.size) THEN <*ASSERT NOT t.indirect*> t.indirect := TRUE; END; t.initDone := TRUE; ELSIF Type.GetBounds (t.tipe, min, max) THEN (* synthesize an initialization expression *) IF Type.IsSubtype (t.tipe, LInt.T) THEN t.init := IntegerExpr.New (LInt.T, min); ELSE t.init := IntegerExpr.New (Int.T, min); END; END; END; CheckTrace (t.trace, cs); END Check; PROCEDURELoad (t: T) = BEGIN t.used := TRUE; Value.Declare (t); IF (t.initPending) THEN ForceInit (t); END; IF Type.IsStructured (t.tipe) THEN (* the RunTyme representation is an address *) IF (t.bss_var # NIL) THEN CG.Load_addr_of (t.bss_var, 0, t.cg_align); ELSIF (t.cg_var = NIL) THEN (* => global *) Module.LoadGlobalAddr (Scope.ToUnit (t), t.offset, is_const := FALSE); CG.Boost_alignment (t.align); ELSIF (t.indirect) THEN CG.Load_addr (t.cg_var, t.offset); CG.Boost_alignment (t.align); ELSE CG.Load_addr_of (t.cg_var, t.offset, t.cg_align); END; ELSE (* simple scalar *) IF (t.bss_var # NIL) THEN CG.Load (t.bss_var, 0, t.size, t.cg_align, t.stk_type); ELSIF (t.cg_var = NIL) THEN (* => global *) Module.LoadGlobalAddr (Scope.ToUnit (t), t.offset, is_const := FALSE); IF (t.indirect) THEN CG.Load_indirect (CG.Type.Addr, 0, Target.Address.size); END; CG.Boost_alignment (t.align); CG.Load_indirect (t.stk_type, 0, t.size); ELSIF (t.indirect) THEN CG.Load_addr (t.cg_var, t.offset); CG.Boost_alignment (t.align); CG.Load_indirect (t.stk_type, 0, t.size); ELSE CG.Load (t.cg_var, t.offset, t.size, t.cg_align, t.stk_type); END; END; END Load; PROCEDURELoadLValue (t: T) = BEGIN t.used := TRUE; Value.Declare (t); IF (t.initPending) THEN ForceInit (t); END; IF (t.bss_var # NIL) THEN CG.Load_addr_of (t.bss_var, 0, t.cg_align); ELSIF (t.cg_var = NIL) THEN (* => global variable *) Module.LoadGlobalAddr (Scope.ToUnit (t), t.offset, is_const := FALSE); IF (t.indirect) THEN CG.Load_indirect (CG.Type.Addr, 0, Target.Address.size); END; ELSIF (t.indirect) THEN CG.Load_addr (t.cg_var, t.offset); ELSE CG.Load_addr_of (t.cg_var, t.offset, t.cg_align); END; CG.Boost_alignment (t.align); END LoadLValue; PROCEDURESetLValue (t: T) = VAR v: CG.Var; align: INTEGER; BEGIN t.used := TRUE; Value.Declare (t); IF (t.initPending) THEN t.initPending := FALSE; END; v := t.cg_var; align := t.cg_align; IF (v = NIL) THEN v := Module.GlobalData (is_const := FALSE); align := CG.Max_alignment; END; <*ASSERT t.indirect *> CG.Boost_alignment (align); CG.Store_addr (v, t.offset); END SetLValue; PROCEDURELocalCGName (t: T; VAR unit: CG.Var; VAR offset: INTEGER) = BEGIN t.used := TRUE; Value.Declare (t); IF (t.initPending) THEN ForceInit (t); END; <*ASSERT NOT t.imported*> IF (t.cg_var = NIL) THEN unit := Module.GlobalData (FALSE); offset := t.offset; ELSE unit := t.cg_var; offset := 0; END; END LocalCGName; PROCEDURESetBounds (t: T; READONLY min, max: Target.Int) = BEGIN IF (t.bounds = NIL) THEN t.bounds := NEW (BoundPair) END; t.bounds.min := min; t.bounds.max := max; END SetBounds; PROCEDUREGetBounds (t: T; VAR min, max: Target.Int) = VAR xx := t.bounds; BEGIN EVAL Type.GetBounds (t.tipe, min, max); IF (xx = NIL) THEN RETURN; END; IF TInt.LT (min, xx.min) THEN min := xx.min; END; IF TInt.LT (xx.max, max) THEN max := xx.max; END; END GetBounds; PROCEDURESetGlobals (t: T) = VAR size, align: INTEGER; BEGIN (* Type.SetGlobals (t.tipe); *) (* IF (t.init # NIL) THEN Type.SetGlobals (Expr.TypeOf (t.init)) END; *) IF (t.offset # 0) OR (NOT t.global) OR (t.external) THEN RETURN END; EVAL Type.Check (t.tipe); IF (t.indirect) THEN size := Target.Address.size; align := Target.Address.align; ELSIF OpenArrayType.Is (t.tipe) THEN align := MAX (Target.Address.align, Target.Integer.align); size := Target.Address.pack + OpenArrayType.OpenDepth(t.tipe) * Target.Integer.pack; ELSE size := t.size; align := t.align; END; (* declare the actual variable *) t.offset := Module.Allocate (size, align, FALSE, id := t.name); END SetGlobals; PROCEDUREDeclare (t: T): BOOLEAN = VAR size := t.size; align := t.align; type := Type.GlobalUID (t.tipe); mtype := Type.CGType (t.tipe, in_memory := TRUE); is_struct := Type.IsStructured (t.tipe); name : TEXT; extern_name : M3ID.T; BEGIN Type.Compile (t.tipe); t.cg_var := NIL; t.bss_var := NIL; IF (is_struct) THEN mtype := CG.Type.Struct; END; IF (t.indirect) THEN type := CG.Declare_indirect (type); size := Target.Address.size; align := Target.Address.align; mtype := CG.Type.Addr; END; (* declare the actual variable *) IF (t.external) THEN name := Value.GlobalName (t, dots := FALSE, with_module := FALSE); extern_name := M3ID.Add (name); t.next_cg_var := all_cg_vars; all_cg_vars := t; t.cg_var := CG.Import_global (extern_name, size, align, mtype, 0(*no mangling*)); t.cg_align := align; ELSIF (t.imported) THEN <*ASSERT t.offset # 0*> ELSIF (t.global) THEN <*ASSERT t.offset # 0*> CG.Declare_global_field (t.name, t.offset, size, type, FALSE); IF (t.initZero) THEN t.initDone := TRUE END; t.cg_align := align; IF (t.indirect) THEN t.cg_align := t.align; t.next_cg_var := all_cg_vars; all_cg_vars := t; t.bss_var := CG.Declare_global (M3ID.NoID, t.size, t.cg_align, CG.Type.Struct, Type.GlobalUID (t.tipe), exported := FALSE, init := FALSE); CG.Init_var (t.offset, t.bss_var, 0, FALSE); END; ELSIF (t.formal = NIL) THEN (* simple local variable *) IF (size < 0) THEN (* it's an open array local introduced by a WITH statement *) align := MAX (Target.Address.align, Target.Integer.align); size := Target.Address.pack + OpenArrayType.OpenDepth(t.tipe) * Target.Integer.pack; END; (** align := FindAlignment (align, size); **) t.cg_align := align; t.next_cg_var := all_cg_vars; all_cg_vars := t; t.cg_var := CG.Declare_local (t.name, size, align, mtype, type, t.need_addr, t.up_level, CG.Maybe); ELSIF (t.indirect) THEN (* formal passed by reference => param is an address *) t.cg_align := align; t.next_cg_var := all_cg_vars; all_cg_vars := t; t.cg_var := CG.Declare_param (t.name, size, align, mtype, type, t.need_addr, t.up_level, CG.Maybe); ELSE (* simple parameter *) (** align := FindAlignment (align, size); **) t.cg_align := align; t.next_cg_var := all_cg_vars; all_cg_vars := t; t.cg_var := CG.Declare_param (t.name, size, align, mtype, type, t.need_addr, t.up_level, CG.Maybe); END; RETURN TRUE; END Declare;
BEGIN
    IF    size < 0                  THEN (*don't mess with open array alignments*)
    ELSIF size >= Target.Int_D.size THEN align := MAX (align, Target.Int_D.align);
    ELSIF size <= Target.Int_A.size THEN align := MAX (align, Target.Int_A.align);
    ELSIF size <= Target.Int_B.size THEN align := MAX (align, Target.Int_B.align);
    ELSIF size <= Target.Int_C.size THEN align := MAX (align, Target.Int_C.align);
    ELSE                                 align := MAX (align, Target.Int_D.align);
    END;
    RETURN align;
  END FindAlignment;
**)
PROCEDURE ConstInit  (t: T) =
  VAR
    size      := t.size;
    align     := t.align;
    type      : INTEGER;
    init_expr : Expr.T;
    name      : TEXT;
    init_name : M3ID.T;
  BEGIN
    IF t.external OR t.imported THEN RETURN END;
    IF (NOT t.initStatic) AND (NOT t.global) THEN RETURN END;
    type := Type.GlobalUID (t.tipe);
    IF (t.indirect) THEN
      type  := CG.Declare_indirect (type);
      size  := Target.Address.size;
      align := Target.Address.align;
    END;
    IF (t.initStatic) THEN
      (* declare the holder for the initial value *)
      name := "_INIT_" & M3ID.ToText (t.name);
      init_name := M3ID.Add (name);
      t.init_var := Module.Allocate (size, align, TRUE,"initial value for ",t.name);
      CG.Declare_global_field (init_name, t.init_var, size, type, TRUE);
      CG.Comment (t.init_var, TRUE, "init expr for ",Value.GlobalName(t,TRUE,TRUE));
      init_expr := Expr.ConstValue (t.init);
      Expr.PrepLiteral (init_expr, t.tipe, TRUE);
      Expr.GenLiteral (init_expr, t.init_var, t.tipe, TRUE);
    END;
    IF (t.global) THEN
      (* try to statically initialize the variable *)
      <*ASSERT t.offset # 0*>
      init_expr := NIL;
      IF (t.init # NIL) AND (NOT t.initDone) AND (NOT t.initStatic) THEN
        init_expr := Expr.ConstValue (t.init);
      END;
      IF (init_expr # NIL) THEN
        Expr.PrepLiteral (init_expr, t.tipe, FALSE);
        Expr.GenLiteral (init_expr, t.offset, t.tipe, FALSE);
        t.initDone := TRUE;
      END;
    END;
  END ConstInit;
PROCEDURE NeedInit  (t: T): BOOLEAN =
  VAR ref: Type.T;
  BEGIN
    IF (t.imported) OR (t.external) OR (t.initDone) THEN
      RETURN FALSE;
    ELSIF (t.formal # NIL) THEN
      RETURN (t.indirect) AND Formal.RefOpenArray (t.formal, ref);
    ELSIF (t.indirect) AND (NOT t.global) THEN
      RETURN FALSE;
    ELSIF (t.global) AND (t.init # NIL) AND (NOT t.initStatic)
      AND (Expr.ConstValue (t.init) # NIL) THEN
      RETURN FALSE;
    ELSIF (t.init # NIL) THEN
      RETURN TRUE;
    ELSE
      RETURN Type.InitCost (t.tipe, FALSE) > 0;
    END;
  END NeedInit;
PROCEDURE LangInit  (t: T) =
  VAR ref: Type.T;
  BEGIN
    IF (t.imported) OR (t.external) THEN
      t.initDone := TRUE;
    ELSIF (t.formal # NIL) THEN
      IF (t.indirect) AND Formal.RefOpenArray (t.formal, ref) THEN
        (* a by-value open array! *)
        CG.Gen_location (t.origin);
        CopyOpenArray (t, ref);
      END;
      (* formal parameters don't need any further initialization *)
      Tracer.Schedule (t.trace);
      t.initDone := TRUE;
    ELSIF (t.indirect) AND (NOT t.global) THEN
      (* is a WITH variable bound to a designator *)
      Tracer.Schedule (t.trace);
      t.initDone := TRUE;
    END;
    IF (t.initDone) THEN RETURN END;
    (* initialize the value *)
    IF (t.init # NIL) AND (NOT t.up_level) AND (NOT t.imported) THEN
      (* variable has a user specified init value and isn't referenced
         by any nested procedures => try to avoid the language defined
         init and wait until we get to the user defined initialization. *)
      t.initPending := TRUE;
    ELSE
      IF Type.InitCost (t.tipe, FALSE) > 0 THEN
        CG.Gen_location (t.origin);
        LoadLValue (t);
        Type.InitValue (t.tipe, FALSE);
      END;
      IF (t.trace # NIL) AND (NOT t.imported) THEN
        IF (t.init = NIL) OR (t.initDone) THEN
          (* there's no explicit user init => might as well trace it now *)
          CG.Gen_location (t.origin);
          Tracer.Schedule (t.trace);
        END;
      END;
    END;
  END LangInit;
PROCEDURE ForceInit  (t: T) =
  BEGIN
    t.initPending := FALSE;
    CG.Gen_location (t.origin);
    LoadLValue (t);
    Type.InitValue (t.tipe, FALSE);
  END ForceInit;
PROCEDURE CopyOpenArray  (t: T;  ref: Type.T) =
  VAR
    ptr   : CG.Val;
    depth := OpenArrayType.OpenDepth (t.tipe);
    align := OpenArrayType.EltAlign (t.tipe);
    pack  := OpenArrayType.EltPack (t.tipe);
    sizes := CG.Declare_temp (Target.Address.pack + Target.Integer.pack,
                              Target.Address.align, CG.Type.Struct,
                              in_memory := TRUE);
    proc  : Procedure.T;
  BEGIN
    (* build the dope vector that describes the array *)
    Load (t);
    CG.Add_offset (M3RT.OA_sizes);
    (*** CG.Check_byte_aligned (); ****)
    CG.Store_addr (sizes, M3RT.OA_elt_ptr);
    CG.Load_intt (depth);
    CG.Store_int (Target.Integer.cg_type, sizes, M3RT.OA_size_0);
    (* allocate the storage *)
    proc := RunTyme.LookUpProc (RunTyme.Hook.NewTracedArray);
    Procedure.StartCall (proc);
    IF Target.DefaultCall.args_left_to_right THEN
      Type.LoadInfo (ref, -1);
      CG.Pop_param (CG.Type.Addr);
      CG.Load_addr_of (sizes, 0, Target.Address.align);
      CG.Pop_param (CG.Type.Addr);
    ELSE
      CG.Load_addr_of (sizes, 0, Target.Address.align);
      CG.Pop_param (CG.Type.Addr);
      Type.LoadInfo (ref, -1);
      CG.Pop_param (CG.Type.Addr);
    END;
    ptr := Procedure.EmitValueCall (proc);
    (* load the destination and source addresses *)
    CG.Push (ptr);
    CG.Boost_alignment (t.align);
    CG.Open_elt_ptr (align);
    CG.Force ();
    Load (t);
    CG.Open_elt_ptr (align);
    CG.Force ();
    (* compute the number of elements *)
    FOR i := 0 TO depth - 1 DO
      Load (t); (* CG.Load_addr (sizes, M3RT.OA_elt_ptr); *)
      CG.Open_size (i);
      IF (i # 0) THEN CG.Multiply (Target.Word.cg_type) END;
    END;
    (* copy the actual argument into the new storage *)
    CG.Copy_n (pack, overlap := FALSE);
    (* set the formal parameter to refer to the new storage *)
    CG.Push (ptr);
    CG.Boost_alignment (t.align);
    CG.Store_addr (t.cg_var);
    (* free our temps *)
    CG.Free_temp (sizes);
    CG.Free (ptr);
  END CopyOpenArray;
PROCEDURE UserInit  (t: T) =
  BEGIN
    IF (t.init # NIL) AND (NOT t.initDone) AND (NOT t.imported) THEN
      CG.Gen_location (t.origin);
      IF (t.initZero) THEN
        t.initPending := FALSE;
        LoadLValue (t);
        Type.Zero (t.tipe);
      ELSIF (t.init_var # 0) THEN
        t.initPending := FALSE;
        LoadLValue (t);
        Module.LoadGlobalAddr (Scope.ToUnit (t), t.init_var, is_const := TRUE);
        CG.Copy (t.size, overlap := FALSE);
      ELSE
        t.initPending := FALSE;
        AssignStmt.PrepForEmit (t.tipe, t.init, initializing := TRUE);
        LoadLValue (t);
        AssignStmt.DoEmit (t.tipe, t.init);
      END;
      t.initDone := TRUE;
      Tracer.Schedule (t.trace);
    END;
  END UserInit;
PROCEDURE GenGlobalMap  (s: Scope.T): INTEGER =
  (* generate the garbage collector's map-proc for the variables of s *)
  VAR started := FALSE;  info: Type.Info;  v := Scope.ToList (s);
  BEGIN
    WHILE (v # NIL) DO
      TYPECASE Value.Base (v) OF
      | NULL =>  (* do nothing *)
      | T(t) =>  IF (NOT t.imported)
                   AND (NOT t.external) THEN
                   EVAL Type.CheckInfo (t.tipe, info);
                   IF (info.isTraced) THEN
                     IF (NOT started) THEN
                       TipeMap.Start ();
                       started := TRUE;
                     END;
                     t.used := TRUE;
                     Value.Declare (t);
                     IF (t.indirect) THEN
                       TipeMap.Add (t.offset, TipeMap.Op.PushPtr, 0);
                       Type.GenMap (t.tipe, 0, -1, refs_only := TRUE);
                       TipeMap.Add (t.size, TipeMap.Op.Return, 0);
                       TipeMap.SetCursor (t.offset + Target.Address.size);
                     ELSE
                       Type.GenMap (t.tipe, t.offset, -1, refs_only := TRUE);
                     END;
                   END;
                 END;
      ELSE (* do nothing *)
      END;
      v := v.next;
    END;
    IF (started)
      THEN RETURN TipeMap.Finish ("global type map");
      ELSE RETURN -1;
    END;
  END GenGlobalMap;
PROCEDURE NeedGlobalInit  (t: T): BOOLEAN =
  BEGIN
    RETURN (NOT t.initDone) AND (NOT t.external);
  END NeedGlobalInit;
PROCEDURE InitGlobal  (t: T) =
  BEGIN
    IF (NOT t.initDone) AND (NOT t.external) THEN
      LoadLValue (t);
      Type.InitValue (t.tipe, TRUE);
    END;
  END InitGlobal;
PROCEDURE AddFPTag  (t: T;  VAR x: M3.FPInfo): CARDINAL =
  BEGIN
    ValueRep.FPStart (t, x, "VAR ", t.offset, global := TRUE);
    RETURN 1;
  END AddFPTag;
--------------------------------------------------------- trace support ---
TYPE TraceNode = Tracer.T OBJECT
                   handler : Expr.T := NIL;
                   call    : Expr.T := NIL;
                 OVERRIDES
                   apply := DoTrace;
                 END;
PROCEDURE ParseTrace  (): Tracer.T =
  TYPE TK = Token.T;
  VAR e: Expr.T;
  BEGIN
    IF (cur.token # TK.tTRACE) THEN RETURN NIL END;
    Match (TK.tTRACE);
    e := Expr.Parse ();
    Match (TK.tENDPRAGMA);
    IF (e = NIL) THEN RETURN NIL END;
    RETURN NEW (TraceNode, handler := e);
  END ParseTrace;
PROCEDURE BindTrace  (t: T;  xx: Tracer.T) =
  VAR x: TraceNode := xx;  p: Scope.IDStack;  z: M3String.T;  args: Expr.List;
  BEGIN
    IF (xx = NIL) THEN RETURN END;
    IF (x.call # NIL) THEN
      x := NEW (TraceNode, handler := x.handler);
    END;
    (* get the variable's full name *)
    p.top := 0;
    Scope.NameToPrefix (t, p, dots := TRUE, with_module := TRUE);
    z := M3String.Add (Scope.StackToText (p));
    (* build the trace procedure call *)
    args := NEW (Expr.List, 2);
    args[0] := TextExpr.New8 (z);
    args[1] := NamedExpr.FromValue (t);
    x.call  := CallExpr.New (x.handler, args);
    <*ASSERT t.trace = NIL*>
    t.trace := x;
  END BindTrace;
PROCEDURE DoTrace  (x: TraceNode) =
  BEGIN
    Expr.Prep (x.call);
    Expr.Compile (x.call);
  END DoTrace;
PROCEDURE CheckTrace  (tt: Tracer.T;  VAR cs: Value.CheckState) =
  VAR x: TraceNode := tt;
  BEGIN
    IF (x # NIL) THEN
      Expr.TypeCheck (x.handler, cs);
      Expr.TypeCheck (x.call, cs);
    END;
  END CheckTrace;
PROCEDURE ScheduleTrace  (t: T) =
  BEGIN
    Tracer.Schedule (t.trace);
  END ScheduleTrace;
BEGIN
END Variable.