blob: d11e60d053ec811a36a3157f4e5eeaa90f8ca071 [file] [log] [blame]
Andreas Wundsam76db0062013-11-15 13:34:41 -08001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
28import sys
29
30import re
31import string
32import os
33import glob
34import copy
35import collections
36import c_gen.of_g_legacy as of_g
37import c_gen.type_maps as type_maps
38import c_gen.loxi_utils_legacy as loxi_utils
39import loxi_globals
40import c_gen.identifiers as identifiers
41import pyparsing
42import loxi_front_end.parser as parser
43import c_gen.translation as translation
44import loxi_front_end.frontend as frontend
45from loxi_ir import *
46from generic_utils import *
47
48root_dir = os.path.dirname(os.path.realpath(__file__))
49
50versions = {}
51# TODO: Put these in a class so they get documented
52
53## Dict indexed by version giving all info related to version
54#
55# This is local; after processing, the information is stored in
56# of_g variables.
57
58def add_class(wire_version, cls, members):
59 """
60 Process a class for the given version and update the unified
61 list of classes as needed.
62
63 @param wire_version The wire version for this class defn
64 @param cls The name of the class being added
65 @param members The list of members with offsets calculated
66 """
67 memid = 0
68
69 sig = loxi_utils.class_signature(members)
70 if cls in of_g.unified:
71 uc = of_g.unified[cls]
72 if wire_version in uc:
73 debug("Error adding %s to unified. Wire ver %d exists" %
74 (cls, wire_version))
75 sys.exit(1)
76 uc[wire_version] = {}
77 # Check for a matching signature
78 for wver in uc:
79 if type(wver) != type(0): continue
80 if wver == wire_version: continue
81 if not "use_version" in uc[wver]:
82 if sig == loxi_utils.class_signature(uc[wver]["members"]):
83 log("Matched %s, ver %d to ver %d" %
84 (cls, wire_version, wver))
85 # have a match with existing version
86 uc[wire_version]["use_version"] = wver
87 # What else to do?
88 return
89 else: # Haven't seen this entry before
90 log("Adding %s to unified list, ver %d" % (cls, wire_version))
91 of_g.unified[cls] = dict(union={})
92 uc = of_g.unified[cls]
93
94 # At this point, need to add members for this version
95 uc[wire_version] = dict(members = members)
96
97 # Per member processing:
98 # Add to union list (I'm sure there's a better way)
99 # Check if it's a list
100 union = uc["union"]
101 if not cls in of_g.ordered_members:
102 of_g.ordered_members[cls] = []
103 for member in members:
104 m_name = member["name"]
105 m_type = member["m_type"]
106 if m_name.find("pad") == 0:
107 continue
108 if m_name in union:
109 if not m_type == union[m_name]["m_type"]:
110 debug("ERROR: CLASS: %s. VERSION %d. MEMBER: %s. TYPE: %s" %
111 (cls, wire_version, m_name, m_type))
112 debug(" Type conflict adding member to unified set.")
113 debug(" Current union[%s]:" % m_name)
114 debug(union[m_name])
115 sys.exit(1)
116 else:
117 union[m_name] = dict(m_type=m_type, memid=memid)
118 memid += 1
119 if not m_name in of_g.ordered_members[cls]:
120 of_g.ordered_members[cls].append(m_name)
121
Andreas Wundsam76db0062013-11-15 13:34:41 -0800122def order_and_assign_object_ids():
123 """
124 Order all classes and assign object ids to all classes.
125
126 This is done to promote a reasonable order of the objects, putting
127 messages first followed by non-messages. No assumptions should be
128 made about the order, nor about contiguous numbering. However, the
129 numbers should all be reasonably small allowing arrays indexed by
130 these enum values to be defined.
131 """
132
133 # Generate separate message and non-message ordered lists
134 for cls in of_g.unified:
135 if loxi_utils.class_is_message(cls):
136 of_g.ordered_messages.append(cls)
137 elif loxi_utils.class_is_list(cls):
138 of_g.ordered_list_objects.append(cls)
139 else:
140 of_g.ordered_non_messages.append(cls)
141
142 of_g.ordered_messages.sort()
143 of_g.ordered_pseudo_objects.sort()
144 of_g.ordered_non_messages.sort()
145 of_g.ordered_list_objects.sort()
146 of_g.standard_class_order.extend(of_g.ordered_messages)
147 of_g.standard_class_order.extend(of_g.ordered_non_messages)
148 of_g.standard_class_order.extend(of_g.ordered_list_objects)
149
150 # This includes pseudo classes for which most code is not generated
151 of_g.all_class_order.extend(of_g.ordered_messages)
152 of_g.all_class_order.extend(of_g.ordered_non_messages)
153 of_g.all_class_order.extend(of_g.ordered_list_objects)
154 of_g.all_class_order.extend(of_g.ordered_pseudo_objects)
155
156 # Assign object IDs
157 for cls in of_g.ordered_messages:
158 of_g.unified[cls]["object_id"] = of_g.object_id
159 of_g.object_id += 1
160 for cls in of_g.ordered_non_messages:
161 of_g.unified[cls]["object_id"] = of_g.object_id
162 of_g.object_id += 1
163 for cls in of_g.ordered_list_objects:
164 of_g.unified[cls]["object_id"] = of_g.object_id
165 of_g.object_id += 1
Andreas Wundsam76db0062013-11-15 13:34:41 -0800166
167
168def initialize_versions():
169 """
170 Create an empty datastructure for each target version.
171 """
172
173 for version in loxi_globals.OFVersions.target_versions:
174 wire_version = version.wire_version
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800175 long_constant = version.constant_version('OF_VERSION_')
176 of_g.wire_ver_map[wire_version] = long_constant
177 of_g.short_version_names[wire_version] = version.short_constant
178 of_g.of_version_range.append(wire_version)
179 of_g.of_version_wire2name[wire_version] = long_constant
180 versions[long_constant] = dict(
181 version_name = version.constant,
Andreas Wundsam76db0062013-11-15 13:34:41 -0800182 wire_version = wire_version,
183 classes = {})
184 of_g.ordered_classes[wire_version] = []
185
186 of_g.target_version_list = [ v.wire_version for v in loxi_globals.OFVersions.target_versions ]
187
188def build_ordered_classes():
189 """
190 Read in from files given on command line and update global state
191
192 @fixme Should select versions to support from command line
193 """
194
195 for version, protocol in loxi_globals.ir.items():
196 wire_version = version.wire_version
197 # Populate global state
198 version_name = of_g.of_version_wire2name[wire_version]
199
200 for ofclass in protocol.classes:
Andreas Wundsam76db0062013-11-15 13:34:41 -0800201 of_g.ordered_classes[wire_version].append(ofclass.name)
202 legacy_members = []
203 pad_count = 0
204 for m in ofclass.members:
205 if type(m) == OFPadMember:
Rich Lane2fe8e912014-10-16 14:28:26 -0700206 continue
Andreas Wundsam76db0062013-11-15 13:34:41 -0800207 else:
Rich Lanec247ade2014-11-07 09:33:24 -0800208 if m.oftype.find("list(") == 0:
Rich Lane2fe8e912014-10-16 14:28:26 -0700209 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
210 m_type = list_name + "_t"
Andreas Wundsam76db0062013-11-15 13:34:41 -0800211 else:
212 enum = find(lambda e: e.name == m.oftype, protocol.enums)
213 if enum and "wire_type" in enum.params:
214 m_type = enum.params["wire_type"]
215 else:
216 m_type = m.oftype
Rich Lane2fe8e912014-10-16 14:28:26 -0700217
218 if m.offset is None:
219 m_offset = -1
220 else:
221 m_offset = m.offset
222
223 legacy_members.append(dict(m_type=m_type, name=m.name, offset=m_offset))
Andreas Wundsam76db0062013-11-15 13:34:41 -0800224 versions[version_name]['classes'][ofclass.name] = legacy_members
225
Rich Lane2fe8e912014-10-16 14:28:26 -0700226 of_g.base_length[(ofclass.name, version.wire_version)] = ofclass.base_length
227 if ofclass.is_fixed_length:
228 of_g.is_fixed_length.add((ofclass.name, version.wire_version))
229
Andreas Wundsam76db0062013-11-15 13:34:41 -0800230 for enum in protocol.enums:
231 for entry in enum.entries:
232 identifiers.add_identifier(
233 translation.loxi_name(entry.name),
234 entry.name, enum.name, entry.value, wire_version,
235 of_g.identifiers, of_g.identifiers_by_group)
236
237def populate_type_maps():
238 """
239 Use the type members in the IR to fill out the legacy type_maps.
240 """
Andreas Wundsam76db0062013-11-15 13:34:41 -0800241 type_maps.generate_maps()
242
243def analyze_input():
244 """
245 Add information computed from the input, including offsets and
246 lengths of struct members and the set of list and action_id types.
247 """
Rich Lane2fe8e912014-10-16 14:28:26 -0700248 # Create lists
249 for version, protocol in loxi_globals.ir.items():
250 lists = set()
251 classes = versions[of_g.of_version_wire2name[version.wire_version]]['classes']
252
253 for ofclass in protocol.classes:
254 for m in ofclass.members:
255 if isinstance(m, OFDataMember) and m.oftype.find("list(") == 0:
256 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
257 lists.add(list_name)
258
259 for list_type in lists:
260 classes[list_type] = []
261 of_g.ordered_classes[version.wire_version].append(list_type)
262 of_g.base_length[(list_type, version.wire_version)] = 0
263
264 # Find special offsets
265 # These are defined as members (except padding) that don't have a fixed
266 # offset. The special_offsets map stores the name of the previous member.
267 for version, protocol in loxi_globals.ir.items():
268 for ofclass in protocol.classes:
269 prev_member = None
270 for m in ofclass.members:
271 if isinstance(m, OFPadMember):
272 continue
273 if m.offset == None:
274 old = of_g.special_offsets.get((ofclass.name, m.name))
275 if old and old != prev_member.name:
276 raise Exception("Error: special offset changed: version=%s cls=%s member=%s old=%s new=%s" %
277 (version, ofclass.name, m.name, old, prev_member.name))
278 of_g.special_offsets[(ofclass.name, m.name)] = prev_member.name
279 prev_member = m
Andreas Wundsam76db0062013-11-15 13:34:41 -0800280
281def unify_input():
282 """
283 Create Unified View of Objects
284 """
285
286 global versions
287
288 # Add classes to unified in wire-format order so that it is easier
289 # to generate things later
290 keys = versions.keys()
291 keys.sort(reverse=True)
292 for version in keys:
293 wire_version = versions[version]["wire_version"]
294 classes = versions[version]["classes"]
295 for cls in of_g.ordered_classes[wire_version]:
296 add_class(wire_version, cls, classes[cls])