aboutsummaryrefslogtreecommitdiffstats
path: root/src/synth/synth-aggr.adb
diff options
context:
space:
mode:
Diffstat (limited to 'src/synth/synth-aggr.adb')
-rw-r--r--src/synth/synth-aggr.adb433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/synth/synth-aggr.adb b/src/synth/synth-aggr.adb
new file mode 100644
index 000000000..25f32cacd
--- /dev/null
+++ b/src/synth/synth-aggr.adb
@@ -0,0 +1,433 @@
+-- Aggregates synthesis.
+-- Copyright (C) 2020 Tristan Gingold
+--
+-- This file is part of GHDL.
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+-- MA 02110-1301, USA.
+
+with Types; use Types;
+
+with Netlists; use Netlists;
+with Netlists.Utils; use Netlists.Utils;
+
+with Vhdl.Errors; use Vhdl.Errors;
+
+with Synth.Errors; use Synth.Errors;
+with Synth.Expr; use Synth.Expr;
+with Synth.Stmts; use Synth.Stmts;
+with Synth.Decls; use Synth.Decls;
+
+package body Synth.Aggr is
+ type Stride_Array is array (Dim_Type range <>) of Nat32;
+
+ function Get_Index_Offset
+ (Index : Int64; Bounds : Bound_Type; Expr : Iir) return Uns32
+ is
+ Left : constant Int64 := Int64 (Bounds.Left);
+ Right : constant Int64 := Int64 (Bounds.Right);
+ begin
+ case Bounds.Dir is
+ when Iir_To =>
+ if Index >= Left and then Index <= Right then
+ -- to
+ return Uns32 (Index - Left);
+ end if;
+ when Iir_Downto =>
+ if Index <= Left and then Index >= Right then
+ -- downto
+ return Uns32 (Left - Index);
+ end if;
+ end case;
+ Error_Msg_Synth (+Expr, "index out of bounds");
+ return 0;
+ end Get_Index_Offset;
+
+ function Get_Index_Offset
+ (Index : Valtyp; Bounds : Bound_Type; Expr : Iir) return Uns32 is
+ begin
+ return Get_Index_Offset (Read_Discrete (Index), Bounds, Expr);
+ end Get_Index_Offset;
+
+ function Fill_Stride (Typ : Type_Acc) return Stride_Array is
+ begin
+ case Typ.Kind is
+ when Type_Vector =>
+ return (1 => 1);
+ when Type_Array =>
+ declare
+ Bnds : constant Bound_Array_Acc := Typ.Abounds;
+ Res : Stride_Array (1 .. Bnds.Ndim);
+ Stride : Nat32;
+ begin
+ Stride := 1;
+ for I in reverse 2 .. Bnds.Ndim loop
+ Res (Dim_Type (I)) := Stride;
+ Stride := Stride * Nat32 (Bnds.D (I).Len);
+ end loop;
+ Res (1) := Stride;
+ return Res;
+ end;
+ when others =>
+ raise Internal_Error;
+ end case;
+ end Fill_Stride;
+
+ procedure Fill_Array_Aggregate (Syn_Inst : Synth_Instance_Acc;
+ Aggr : Node;
+ Res : Valtyp_Array_Acc;
+ Typ : Type_Acc;
+ First_Pos : Nat32;
+ Strides : Stride_Array;
+ Dim : Dim_Type;
+ Const_P : out Boolean)
+ is
+ Bound : constant Bound_Type := Get_Array_Bound (Typ, Dim);
+ El_Typ : constant Type_Acc := Get_Array_Element (Typ);
+ Stride : constant Nat32 := Strides (Dim);
+ Value : Node;
+ Assoc : Node;
+
+ procedure Set_Elem (Pos : Nat32)
+ is
+ Sub_Const : Boolean;
+ Val : Valtyp;
+ begin
+ if Dim = Strides'Last then
+ Val := Synth_Expression_With_Type (Syn_Inst, Value, El_Typ);
+ Val := Synth_Subtype_Conversion (Val, El_Typ, False, Value);
+ pragma Assert (Res (Pos) = No_Valtyp);
+ Res (Pos) := Val;
+ if Const_P and then not Is_Static (Val.Val) then
+ Const_P := False;
+ end if;
+ else
+ Fill_Array_Aggregate
+ (Syn_Inst, Value, Res, Typ, Pos, Strides, Dim + 1, Sub_Const);
+ if not Sub_Const then
+ Const_P := False;
+ end if;
+ end if;
+ end Set_Elem;
+
+ procedure Set_Vector (Pos : Nat32; Len : Nat32; Val : Valtyp) is
+ begin
+ pragma Assert (Dim = Strides'Last);
+ if Len = 0 then
+ return;
+ end if;
+ pragma Assert (Res (Pos) = No_Valtyp);
+ Res (Pos) := Val;
+
+ -- Mark following slots as busy so that 'others => x' won't fill
+ -- them.
+ for I in 2 .. Len loop
+ Res (Pos + I - 1).Typ := Val.Typ;
+ end loop;
+
+ if Const_P and then not Is_Static (Val.Val) then
+ Const_P := False;
+ end if;
+ end Set_Vector;
+
+ Pos : Nat32;
+ begin
+ Assoc := Get_Association_Choices_Chain (Aggr);
+ Pos := First_Pos;
+ Const_P := True;
+ while Is_Valid (Assoc) loop
+ Value := Get_Associated_Expr (Assoc);
+ loop
+ case Get_Kind (Assoc) is
+ when Iir_Kind_Choice_By_None =>
+ if Get_Element_Type_Flag (Assoc) then
+ if Pos >= First_Pos + Stride * Nat32 (Bound.Len) then
+ Error_Msg_Synth (+Assoc, "element out of array bound");
+ else
+ Set_Elem (Pos);
+ Pos := Pos + Stride;
+ end if;
+ else
+ declare
+ Val : Valtyp;
+ Val_Len : Uns32;
+ begin
+ Val := Synth_Expression_With_Basetype
+ (Syn_Inst, Value);
+ Val_Len := Get_Bound_Length (Val.Typ, 1);
+ pragma Assert (Stride = 1);
+ if Pos - First_Pos > Nat32 (Bound.Len - Val_Len) then
+ Error_Msg_Synth
+ (+Assoc, "element out of array bound");
+ else
+ Set_Vector (Pos, Nat32 (Val_Len), Val);
+ Pos := Pos + Nat32 (Val_Len);
+ end if;
+ end;
+ end if;
+ when Iir_Kind_Choice_By_Others =>
+ pragma Assert (Get_Element_Type_Flag (Assoc));
+ declare
+ Last_Pos : constant Nat32 :=
+ First_Pos + Nat32 (Bound.Len) * Stride;
+ begin
+ while Pos < Last_Pos loop
+ if Res (Pos) = No_Valtyp then
+ -- FIXME: the check is not correct if there is
+ -- an array.
+ Set_Elem (Pos);
+ end if;
+ Pos := Pos + Stride;
+ end loop;
+ end;
+ when Iir_Kind_Choice_By_Expression =>
+ pragma Assert (Get_Element_Type_Flag (Assoc));
+ declare
+ Ch : constant Node := Get_Choice_Expression (Assoc);
+ Idx : Valtyp;
+ Off : Nat32;
+ begin
+ Idx := Synth_Expression (Syn_Inst, Ch);
+ if not Is_Static (Idx.Val) then
+ Error_Msg_Synth (+Ch, "choice is not static");
+ else
+ Off := Nat32 (Get_Index_Offset (Idx, Bound, Ch));
+ Set_Elem (First_Pos + Off * Stride);
+ end if;
+ end;
+ when Iir_Kind_Choice_By_Range =>
+ declare
+ Ch : constant Node := Get_Choice_Range (Assoc);
+ Rng : Discrete_Range_Type;
+ Val : Valtyp;
+ Rng_Len : Width;
+ Off : Nat32;
+ begin
+ Synth_Discrete_Range (Syn_Inst, Ch, Rng);
+ if Get_Element_Type_Flag (Assoc) then
+ Val := Create_Value_Discrete
+ (Rng.Left,
+ Get_Subtype_Object (Syn_Inst,
+ Get_Base_Type (Get_Type (Ch))));
+ while In_Range (Rng, Read_Discrete (Val)) loop
+ Off := Nat32 (Get_Index_Offset (Val, Bound, Ch));
+ Set_Elem (First_Pos + Off * Stride);
+ Update_Index (Rng, Val);
+ end loop;
+ else
+ -- The direction must be the same.
+ if Rng.Dir /= Bound.Dir then
+ Error_Msg_Synth
+ (+Assoc, "direction of range does not match "
+ & "direction of array");
+ end if;
+ -- FIXME: can the expression be unbounded ?
+ Val := Synth_Expression_With_Basetype
+ (Syn_Inst, Value);
+ -- The length must match the range.
+ Rng_Len := Get_Range_Length (Rng);
+ if Get_Bound_Length (Val.Typ, 1) /= Rng_Len then
+ Error_Msg_Synth
+ (+Value, "length doesn't match range");
+ end if;
+ pragma Assert (Stride = 1);
+ Off := Nat32 (Get_Index_Offset (Rng.Left, Bound, Ch));
+ Set_Vector (First_Pos + Off, Nat32 (Rng_Len), Val);
+ end if;
+ end;
+ when others =>
+ Error_Msg_Synth
+ (+Assoc, "unhandled association form");
+ end case;
+ Assoc := Get_Chain (Assoc);
+ exit when Is_Null (Assoc);
+ exit when not Get_Same_Alternative_Flag (Assoc);
+ end loop;
+ end loop;
+ end Fill_Array_Aggregate;
+
+ procedure Fill_Record_Aggregate (Syn_Inst : Synth_Instance_Acc;
+ Aggr : Node;
+ Rec : Valtyp_Array_Acc;
+ Const_P : out Boolean)
+ is
+ El_List : constant Node_Flist :=
+ Get_Elements_Declaration_List (Get_Type (Aggr));
+ Value : Node;
+ Assoc : Node;
+ Pos : Nat32;
+
+ procedure Set_Elem (Pos : Nat32)
+ is
+ Val : Valtyp;
+ El_Type : Type_Acc;
+ begin
+ El_Type := Get_Subtype_Object
+ (Syn_Inst, Get_Type (Get_Nth_Element (El_List, Natural (Pos))));
+ Val := Synth_Expression_With_Type (Syn_Inst, Value, El_Type);
+ if Const_P and not Is_Static (Val.Val) then
+ Const_P := False;
+ end if;
+ Val := Synth_Subtype_Conversion (Val, El_Type, False, Value);
+ Rec (Nat32 (Pos + 1)) := Val;
+ end Set_Elem;
+ begin
+ Assoc := Get_Association_Choices_Chain (Aggr);
+ Pos := 0;
+ Const_P := True;
+ while Is_Valid (Assoc) loop
+ Value := Get_Associated_Expr (Assoc);
+ loop
+ case Get_Kind (Assoc) is
+ when Iir_Kind_Choice_By_None =>
+ Set_Elem (Pos);
+ Pos := Pos + 1;
+ when Iir_Kind_Choice_By_Others =>
+ for I in Rec'Range loop
+ if Rec (I) = No_Valtyp then
+ Set_Elem (I - 1);
+ end if;
+ end loop;
+ when Iir_Kind_Choice_By_Name =>
+ Pos := Nat32 (Get_Element_Position
+ (Get_Named_Entity
+ (Get_Choice_Name (Assoc))));
+ Set_Elem (Pos);
+ when others =>
+ Error_Msg_Synth
+ (+Assoc, "unhandled association form");
+ end case;
+ Assoc := Get_Chain (Assoc);
+ exit when Is_Null (Assoc);
+ exit when not Get_Same_Alternative_Flag (Assoc);
+ end loop;
+ end loop;
+ end Fill_Record_Aggregate;
+
+ function Valtyp_Array_To_Net (Tab : Valtyp_Array) return Net
+ is
+ Res : Net;
+ Arr : Net_Array_Acc;
+ Idx : Nat32;
+ begin
+ Arr := new Net_Array (Tab'Range);
+ Idx := 0;
+ for I in Arr'Range loop
+ if Tab (I).Val /= null then
+ Idx := Idx + 1;
+ Arr (Idx) := Get_Net (Tab (I));
+ end if;
+ end loop;
+ Concat_Array (Arr (1 .. Idx), Res);
+ Free_Net_Array (Arr);
+ return Res;
+ end Valtyp_Array_To_Net;
+
+ function Synth_Aggregate_Array (Syn_Inst : Synth_Instance_Acc;
+ Aggr : Node;
+ Aggr_Type : Type_Acc) return Valtyp
+ is
+ Strides : constant Stride_Array := Fill_Stride (Aggr_Type);
+ Flen : constant Iir_Index32 := Get_Array_Flat_Length (Aggr_Type);
+ Tab_Res : Valtyp_Array_Acc;
+ Const_P : Boolean;
+ Res : Valtyp;
+ begin
+ Tab_Res := new Valtyp_Array'(1 .. Nat32 (Flen) => No_Valtyp);
+
+ Fill_Array_Aggregate
+ (Syn_Inst, Aggr, Tab_Res, Aggr_Type, 1, Strides, 1, Const_P);
+
+ -- TODO: check all element types have the same bounds ?
+
+ if Const_P then
+ declare
+ Off : Size_Type;
+ begin
+ Res := Create_Value_Memory (Aggr_Type);
+ Off := 0;
+ for I in Tab_Res'Range loop
+ if Tab_Res (I).Val /= null then
+ -- There can be holes due to sub-arrays.
+ Write_Value (Res.Val.Mem + Off, Tab_Res (I));
+ Off := Off + Tab_Res (I).Typ.Sz;
+ end if;
+ end loop;
+ pragma Assert (Off = Aggr_Type.Sz);
+ end;
+ else
+ Res := Create_Value_Net
+ (Valtyp_Array_To_Net (Tab_Res.all), Aggr_Type);
+ end if;
+
+ Free_Valtyp_Array (Tab_Res);
+
+ return Res;
+ end Synth_Aggregate_Array;
+
+ function Synth_Aggregate_Record (Syn_Inst : Synth_Instance_Acc;
+ Aggr : Node;
+ Aggr_Type : Type_Acc) return Valtyp
+ is
+ Tab_Res : Valtyp_Array_Acc;
+ Res : Valtyp;
+ Const_P : Boolean;
+ begin
+ -- Allocate the result.
+ Tab_Res :=
+ new Valtyp_Array'(1 .. Nat32 (Aggr_Type.Rec.Len) => No_Valtyp);
+
+ Fill_Record_Aggregate (Syn_Inst, Aggr, Tab_Res, Const_P);
+
+ if Const_P then
+ Res := Create_Value_Memory (Aggr_Type);
+ for I in Aggr_Type.Rec.E'Range loop
+ Write_Value (Res.Val.Mem + Aggr_Type.Rec.E (I).Moff,
+ Tab_Res (Nat32 (I)));
+ end loop;
+ else
+ Res := Create_Value_Net
+ (Valtyp_Array_To_Net (Tab_Res.all), Aggr_Type);
+ end if;
+
+ Free_Valtyp_Array (Tab_Res);
+
+ return Res;
+ end Synth_Aggregate_Record;
+
+ -- Aggr_Type is the type from the context.
+ function Synth_Aggregate (Syn_Inst : Synth_Instance_Acc;
+ Aggr : Node;
+ Aggr_Type : Type_Acc) return Valtyp is
+ begin
+ case Aggr_Type.Kind is
+ when Type_Unbounded_Array | Type_Unbounded_Vector =>
+ declare
+ Res_Type : Type_Acc;
+ begin
+ Res_Type := Decls.Synth_Array_Subtype_Indication
+ (Syn_Inst, Get_Type (Aggr));
+ return Synth_Aggregate_Array (Syn_Inst, Aggr, Res_Type);
+ end;
+ when Type_Vector | Type_Array =>
+ return Synth_Aggregate_Array (Syn_Inst, Aggr, Aggr_Type);
+ when Type_Record =>
+ return Synth_Aggregate_Record (Syn_Inst, Aggr, Aggr_Type);
+ when others =>
+ raise Internal_Error;
+ end case;
+ end Synth_Aggregate;
+
+end Synth.Aggr;