blob: ff2dfe887f2e29ead4571823b756fc9ecce715e6 [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
175 version_name = of_g.of_version_wire2name[wire_version]
176 of_g.wire_ver_map[wire_version] = version_name
177 versions[version_name] = dict(
178 version_name = version_name,
179 wire_version = wire_version,
180 classes = {})
181 of_g.ordered_classes[wire_version] = []
182
183 of_g.target_version_list = [ v.wire_version for v in loxi_globals.OFVersions.target_versions ]
184
185def build_ordered_classes():
186 """
187 Read in from files given on command line and update global state
188
189 @fixme Should select versions to support from command line
190 """
191
192 for version, protocol in loxi_globals.ir.items():
193 wire_version = version.wire_version
194 # Populate global state
195 version_name = of_g.of_version_wire2name[wire_version]
196
197 for ofclass in protocol.classes:
Andreas Wundsam76db0062013-11-15 13:34:41 -0800198 of_g.ordered_classes[wire_version].append(ofclass.name)
199 legacy_members = []
200 pad_count = 0
201 for m in ofclass.members:
202 if type(m) == OFPadMember:
Rich Lane2fe8e912014-10-16 14:28:26 -0700203 continue
Andreas Wundsam76db0062013-11-15 13:34:41 -0800204 else:
205 # HACK the C backend does not yet support of_oxm_t
206 if m.oftype == 'of_oxm_t':
Rich Lane90020b42014-04-07 12:05:45 -0700207 m_type = 'of_oxm_header_t'
Wilson Ngd6181882014-04-14 16:28:35 -0700208 # HACK the C backend does not yet support of_bsn_vport_t
209 elif m.oftype == 'of_bsn_vport_t':
210 m_type = 'of_bsn_vport_header_t'
Rich Lane2fe8e912014-10-16 14:28:26 -0700211 elif m.oftype.find("list(") == 0:
212 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
213 m_type = list_name + "_t"
Andreas Wundsam76db0062013-11-15 13:34:41 -0800214 else:
215 enum = find(lambda e: e.name == m.oftype, protocol.enums)
216 if enum and "wire_type" in enum.params:
217 m_type = enum.params["wire_type"]
218 else:
219 m_type = m.oftype
Rich Lane2fe8e912014-10-16 14:28:26 -0700220
221 if m.offset is None:
222 m_offset = -1
223 else:
224 m_offset = m.offset
225
226 legacy_members.append(dict(m_type=m_type, name=m.name, offset=m_offset))
Andreas Wundsam76db0062013-11-15 13:34:41 -0800227 versions[version_name]['classes'][ofclass.name] = legacy_members
228
Rich Lane2fe8e912014-10-16 14:28:26 -0700229 of_g.base_length[(ofclass.name, version.wire_version)] = ofclass.base_length
230 if ofclass.is_fixed_length:
231 of_g.is_fixed_length.add((ofclass.name, version.wire_version))
232
Andreas Wundsam76db0062013-11-15 13:34:41 -0800233 for enum in protocol.enums:
234 for entry in enum.entries:
235 identifiers.add_identifier(
236 translation.loxi_name(entry.name),
237 entry.name, enum.name, entry.value, wire_version,
238 of_g.identifiers, of_g.identifiers_by_group)
239
240def populate_type_maps():
241 """
242 Use the type members in the IR to fill out the legacy type_maps.
243 """
Andreas Wundsam76db0062013-11-15 13:34:41 -0800244 type_maps.generate_maps()
245
246def analyze_input():
247 """
248 Add information computed from the input, including offsets and
249 lengths of struct members and the set of list and action_id types.
250 """
251
252 # Generate header classes for inheritance parents
253 for wire_version, ordered_classes in of_g.ordered_classes.items():
254 classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
255 for cls in ordered_classes:
Rich Lane8841f352014-10-12 19:18:36 -0700256 if type_maps.class_is_inheritance_root(cls):
Andreas Wundsam76db0062013-11-15 13:34:41 -0800257 new_cls = cls + '_header'
258 of_g.ordered_classes[wire_version].append(new_cls)
259 classes[new_cls] = classes[cls]
Rich Lane2fe8e912014-10-16 14:28:26 -0700260 of_g.base_length[(new_cls, wire_version)] = of_g.base_length[(cls, wire_version)]
261 if (cls, wire_version) in of_g.is_fixed_length:
262 of_g.is_fixed_length.add((cls, wire_version))
Andreas Wundsam76db0062013-11-15 13:34:41 -0800263
Rich Lane2fe8e912014-10-16 14:28:26 -0700264 # Create lists
265 for version, protocol in loxi_globals.ir.items():
266 lists = set()
267 classes = versions[of_g.of_version_wire2name[version.wire_version]]['classes']
268
269 for ofclass in protocol.classes:
270 for m in ofclass.members:
271 if isinstance(m, OFDataMember) and m.oftype.find("list(") == 0:
272 (list_name, base_type) = loxi_utils.list_name_extract(m.oftype)
273 lists.add(list_name)
274
275 for list_type in lists:
276 classes[list_type] = []
277 of_g.ordered_classes[version.wire_version].append(list_type)
278 of_g.base_length[(list_type, version.wire_version)] = 0
279
280 # Find special offsets
281 # These are defined as members (except padding) that don't have a fixed
282 # offset. The special_offsets map stores the name of the previous member.
283 for version, protocol in loxi_globals.ir.items():
284 for ofclass in protocol.classes:
285 prev_member = None
286 for m in ofclass.members:
287 if isinstance(m, OFPadMember):
288 continue
289 if m.offset == None:
290 old = of_g.special_offsets.get((ofclass.name, m.name))
291 if old and old != prev_member.name:
292 raise Exception("Error: special offset changed: version=%s cls=%s member=%s old=%s new=%s" %
293 (version, ofclass.name, m.name, old, prev_member.name))
294 of_g.special_offsets[(ofclass.name, m.name)] = prev_member.name
295 prev_member = m
Andreas Wundsam76db0062013-11-15 13:34:41 -0800296
297def unify_input():
298 """
299 Create Unified View of Objects
300 """
301
302 global versions
303
304 # Add classes to unified in wire-format order so that it is easier
305 # to generate things later
306 keys = versions.keys()
307 keys.sort(reverse=True)
308 for version in keys:
309 wire_version = versions[version]["wire_version"]
310 classes = versions[version]["classes"]
311 for cls in of_g.ordered_classes[wire_version]:
312 add_class(wire_version, cls, classes[cls])