Skip to content

Commit 60c28d2

Browse files
authored
Fix float() ordering problem for DDI tables (#488)
+ Add class information for new graph APIs to ensure ordinals are correctly used + Fix for bug that used float() to order based on versions. Hence causing 1.17 to be less than 1.4. + Permanently simplify version ordering within the spec + Use Ordinal to return zet ddi table to original order, this fix will ensure despite the bad order due to past bugs. The order is harmless but it needs to stay in this manner. Signed-off-by: Russell McGuire <russell.w.mcguire@intel.com>
1 parent 0d98c2f commit 60c28d2

5 files changed

Lines changed: 75 additions & 114 deletions

File tree

scripts/core/graph.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,4 +398,28 @@ details:
398398
params:
399399
- type: $x_graph_handle_t
400400
name: hGraph
401-
desc: "[in][release] handle of the graph to destroy"
401+
desc: "[in][release] handle of the graph to destroy"
402+
--- #--------------------------------------------------------------------------
403+
type: class
404+
desc: "C++ wrapper for a recorded graph object"
405+
name: $xGraph
406+
owner: $xContext
407+
members:
408+
- type: $x_graph_handle_t
409+
name: handle
410+
desc: "[in] handle of graph object"
411+
- type: $xContext*
412+
name: pContext
413+
desc: "[in] pointer to owner object"
414+
--- #--------------------------------------------------------------------------
415+
type: class
416+
desc: "C++ wrapper for an executable graph object"
417+
name: $xExecutableGraph
418+
owner: $xGraph
419+
members:
420+
- type: $x_executable_graph_handle_t
421+
name: handle
422+
desc: "[in] handle of executable graph object"
423+
- type: $xGraph*
424+
name: pGraph
425+
desc: "[in] pointer to owner object"

scripts/parse_specs.py

Lines changed: 15 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import json
1212
import yaml
1313
import copy
14-
from templates.helper import param_traits, type_traits, value_traits, get_tag
14+
from templates.helper import param_traits, type_traits, value_traits, get_tag, version_key
1515

1616
default_version = "1.0"
1717
all_versions = ["1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "2.0"]
@@ -526,120 +526,22 @@ def _validate_struct_enum_mapping(specs, tags):
526526
"""
527527
filters object by version
528528
"""
529+
# Version comparisons route through helper.version_key (single source of truth
530+
# for major/minor decomposition). Do NOT reintroduce float(version) here.
529531
def _version_compare_greater(a, b):
530-
a_major = int(a.split('.')[0])
531-
a_minor = int(a.split('.')[1])
532-
533-
b_major = int(b.split('.')[0])
534-
b_minor = int(b.split('.')[1])
535-
536-
if (a_major > b_major):
537-
# print("DEBUG: greater(%d.%d, %d.%d) -> True (major)" % (a_major, a_minor, b_major, b_minor))
538-
return True
539-
540-
if (a_major < b_major):
541-
# print("DEBUG: greater(%d.%d, %d.%d) -> False (major)" % (a_major, a_minor, b_major, b_minor))
542-
return False
543-
544-
# a_major == b_major
545-
546-
if (a_minor > b_minor):
547-
# print("DEBUG: greater(%d.%d, %d.%d) -> True (minor)" % (a_major, a_minor, b_major, b_minor))
548-
return True
549-
550-
# a_minor <= b_minor
551-
552-
# print("DEBUG: greater(%d.%d, %d.%d) -> False (minor)" % (a_major, a_minor, b_major, b_minor))
553-
return False
532+
return version_key(a) > version_key(b)
554533

555534
def _version_compare_equal(a, b):
556-
a_major = int(a.split('.')[0])
557-
a_minor = int(a.split('.')[1])
558-
559-
b_major = int(b.split('.')[0])
560-
b_minor = int(b.split('.')[1])
561-
562-
ret_val = ((a_major == b_major) and (a_minor == b_minor))
563-
# print("DEBUG: equal(%d.%d, %d.%d) -> %s" % (a_major, a_minor, b_major, b_minor, ret_val))
564-
return ret_val
535+
return version_key(a) == version_key(b)
565536

566537
def _version_compare_less(a, b):
567-
a_major = int(a.split('.')[0])
568-
a_minor = int(a.split('.')[1])
569-
570-
b_major = int(b.split('.')[0])
571-
b_minor = int(b.split('.')[1])
572-
573-
if a_major > b_major:
574-
# print("DEBUG: less(%d.%d, %d.%d) -> False (major)" % (a_major, a_minor, b_major, b_minor))
575-
return False
576-
577-
if a_major < b_major:
578-
# print("DEBUG: less(%d.%d, %d.%d) -> True (major)" % (a_major, a_minor, b_major, b_minor))
579-
return True
580-
581-
# a_major == b_major
582-
583-
if a_minor < b_minor:
584-
# print("DEBUG: less(%d.%d, %d.%d) -> True (minor)" % (a_major, a_minor, b_major, b_minor))
585-
return True
586-
587-
# a_minor >= b_minor
588-
589-
# print("DEBUG: less(%d.%d, %d.%d) -> False (minor)" % (a_major, a_minor, b_major, b_minor))
590-
return False
538+
return version_key(a) < version_key(b)
591539

592540
def _version_compare_lequal(a, b):
593-
a_major = int(a.split('.')[0])
594-
a_minor = int(a.split('.')[1])
595-
596-
b_major = int(b.split('.')[0])
597-
b_minor = int(b.split('.')[1])
598-
599-
if a_major > b_major:
600-
# print("DEBUG: lequal(%d.%d, %d.%d) -> False (major)" % (a_major, a_minor, b_major, b_minor))
601-
return False
602-
603-
if a_major < b_major:
604-
# print("DEBUG: lequal(%d.%d, %d.%d) -> True (major)" % (a_major, a_minor, b_major, b_minor))
605-
return True
606-
607-
# a_major == b_major
608-
609-
if a_minor <= b_minor:
610-
# print("DEBUG: lequal(%d.%d, %d.%d) -> True (minor)" % (a_major, a_minor, b_major, b_minor))
611-
return True
612-
613-
# a_minor > b_minor
614-
615-
# print("DEBUG: lequal(%d.%d, %d.%d) -> False (minor)" % (a_major, a_minor, b_major, b_minor))
616-
return False
541+
return version_key(a) <= version_key(b)
617542

618543
def _version_compare_gequal(a, b):
619-
a_major = int(a.split('.')[0])
620-
a_minor = int(a.split('.')[1])
621-
622-
b_major = int(b.split('.')[0])
623-
b_minor = int(b.split('.')[1])
624-
625-
if a_major > b_major:
626-
# print("DEBUG: gequal(%d.%d, %d.%d) -> True (major)" % (a_major, a_minor, b_major, b_minor))
627-
return True
628-
629-
if a_major < b_major:
630-
# print("DEBUG: gequal(%d.%d, %d.%d) -> False (major)" % (a_major, a_minor, b_major, b_minor))
631-
return False
632-
633-
# a_major == b_major
634-
635-
if a_minor >= b_minor:
636-
# print("DEBUG: gequal(%d.%d, %d.%d) -> True (minor)" % (a_major, a_minor, b_major, b_minor))
637-
return True
638-
639-
# a_minor < b_minor
640-
641-
# print("DEBUG: gequal(%d.%d, %d.%d) -> False (minor)" % (a_major, a_minor, b_major, b_minor))
642-
return False
544+
return version_key(a) >= version_key(b)
643545

644546
def _filter_version(d, max_ver):
645547
ver = d.get('version', default_version)
@@ -662,7 +564,7 @@ def __filter_detail(det):
662564
detail = None
663565
for k, v in det.items():
664566
try:
665-
version = float(k)
567+
version_key(k)
666568
except:
667569
return det
668570
if _version_compare_lequal(k, max_ver):
@@ -1122,7 +1024,12 @@ def parse(section, version, tags, meta, ref):
11221024
# extract header from objects
11231025
if re.match(r"header", d['type']):
11241026
header = d
1125-
header['ordinal'] = int(int(header.get('ordinal',"1000")) * float(header.get('version',"1.0")))
1027+
# Header ordinal drives DDI table/class order (and the global
1028+
# spec emission sort below). version_key() decomposes major/minor
1029+
# so e.g. "1.17" sorts after "1.4"; a naive float(version) would
1030+
# treat "1.17" as 1.17 < "1.4"==1.4 and misplace sub-tables,
1031+
# breaking N-1 ABI. See helper.version_key.
1032+
header['ordinal'] = int(int(header.get('ordinal',"1000")) * version_key(header.get('version',"1.0")))
11261033
header['ordinal'] *= 1000 if re.match(r"extension", header.get('desc',"").lower()) else 1
11271034
header['ordinal'] *= 1000 if re.match(r"experimental", header.get('desc',"").lower()) else 1
11281035

scripts/templates/helper.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@
66
"""
77
import re
88

9+
"""
10+
Single source of truth for version ordering.
11+
12+
Maps a "major.minor" version string to a sortable/comparable integer
13+
("1.10" -> 10010, "1.4" -> 10004) via major/minor decomposition.
14+
15+
NEVER use float(version) for version comparison or sorting:
16+
float("1.10") == 1.1, which is *less* than float("1.4") == 1.4, so any
17+
two-digit minor version silently sorts before lower real versions. That
18+
misplaces DDI sub-tables in the container struct and breaks N-1 ABI.
19+
"""
20+
_VERSION_MINOR_BASE = 10000 # major*BASE + minor; a minor >= BASE aliases (major+1).0
21+
22+
def version_key(version):
23+
major, _, minor = str(version).partition(".")
24+
minor = int(minor) if minor else 0
25+
# Loud failure beats a silent wrap that would reorder a DDI table and break ABI.
26+
assert 0 <= minor < _VERSION_MINOR_BASE, \
27+
"minor version %d exceeds version_key headroom (%d); raise _VERSION_MINOR_BASE" % (minor, _VERSION_MINOR_BASE)
28+
return int(major) * _VERSION_MINOR_BASE + minor
29+
30+
"""
31+
Stable ordering key for a function/object within a DDI table: order by
32+
version first, then by the object's explicit 'ordinal' (default 100).
33+
"""
34+
def function_sort_key(obj):
35+
return (version_key(obj.get('version', "1.0")), int(obj.get('ordinal', "100")))
36+
937
"""
1038
Extracts traits from a spec object
1139
"""
@@ -913,14 +941,14 @@ def get_class_function_objs(specs, cname, version = None, includeExt = False):
913941
is_function = obj_traits.is_function(obj)
914942
match_cls = cname == obj_traits.class_name(obj)
915943
if is_function and match_cls:
916-
if version is None or (float(obj.get('version',"1.0")) <= version):
944+
if version is None or (version_key(obj.get('version',"1.0")) <= version_key(version)):
917945
if obj_traits.is_extension(obj):
918946
if includeExt:
919947
objects.append(obj)
920948
else:
921949
objects.append(obj)
922950

923-
return sorted(objects, key=lambda obj: (float(obj.get('version',"1.0").split(".")[0]) * 10000000) + (float(obj.get('version',"1.0").split(".")[1])*10000) + int(obj.get('ordinal',"100")))
951+
return sorted(objects, key=function_sort_key)
924952

925953
"""
926954
Public:
@@ -938,8 +966,8 @@ def get_class_function_objs_exp(specs, cname):
938966
exp_objects.append(obj)
939967
else:
940968
objects.append(obj)
941-
objects = sorted(objects, key=lambda obj: (float(obj.get('version',"1.0").split(".")[0]) * 10000000) + (float(obj.get('version',"1.0").split(".")[1]) * 10000) + int(obj.get('ordinal',"100")))
942-
exp_objects = sorted(exp_objects, key=lambda obj: (float(obj.get('version',"1.0").split(".")[0]) * 10000000) + (float(obj.get('version',"1.0").split(".")[1])* 10000) + int(obj.get('ordinal',"100")))
969+
objects = sorted(objects, key=function_sort_key)
970+
exp_objects = sorted(exp_objects, key=function_sort_key)
943971
return objects, exp_objects
944972

945973
"""
@@ -1035,7 +1063,7 @@ def get_pfntables(specs, meta, namespace, tags):
10351063
def get_pfncbtables(specs, meta, namespace, tags):
10361064
tables = []
10371065
for cname in sorted(meta['class'], key=lambda x: meta['class'][x]['ordinal']):
1038-
objs = get_class_function_objs(specs, cname, 1.0)
1066+
objs = get_class_function_objs(specs, cname, "1.0")
10391067
if len(objs) > 0:
10401068
name = get_table_name(namespace, tags, {'class': cname})
10411069
table = "%s_%s_callbacks_t"%(namespace, _camel_to_snake(name))

scripts/tools/concurrentMetricGroup.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ desc: "Get sets of metric groups which could be collected concurrently."
3030
version: "1.10"
3131
class: $tDevice
3232
name: GetConcurrentMetricGroupsExp
33+
ordinal: "1"
3334
decl: static
3435
details:
3536
- "Re-arrange the input metric groups to provide sets of concurrent metric groups."

scripts/tools/metricProgrammable.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ desc: "Create multiple metric group handles from metric handles."
446446
version: "1.10"
447447
class: $tDevice
448448
name: CreateMetricGroupsFromMetricsExp
449+
ordinal: "2"
449450
decl: static
450451
details:
451452
- "Creates multiple metric groups from metrics which were created using $tMetricCreateFromProgrammableExp2()."

0 commit comments

Comments
 (0)