import re
import requests
import textwrap
+import yaml
SPIRV_HTML_SPEC_URL = 'https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html'
SPIRV_JSON_SPEC_URL = 'https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json'
return spirv['operand_kinds'], spirv['instructions']
-def split_list_into_sublists(items, offset):
+def split_list_into_sublists(items):
"""Split the list of items into multiple sublists.
This is to make sure the string composed from each sublist won't exceed
Arguments:
- items: a list of strings
- - offset: the offset in calculating each sublist's length
"""
chuncks = []
chunk = []
for case in kind_cases]
# Split them into sublists and concatenate into multiple lines
- case_names = split_list_into_sublists(case_names, 6)
+ case_names = split_list_into_sublists(case_names)
case_names = ['{:6}'.format('') + ', '.join(sublist)
for sublist in case_names]
case_names = ',\n'.join(case_names)
opcode_list = [
decl_fmt_str.format(name=inst['opname']) for inst in instructions
]
- opcode_list = split_list_into_sublists(opcode_list, 6)
+ opcode_list = split_list_into_sublists(opcode_list)
opcode_list = [
'{:6}'.format('') + ', '.join(sublist) for sublist in opcode_list
]
' ]>;'.format(name='Opcode', lst=opcode_list)
return opcode_str + '\n\n' + enum_attr
+def map_cap_to_opnames(instructions):
+ """Maps capabilities to instructions enabled by those capabilities
+
+ Arguments:
+ - instructions: a list containing a subset of SPIR-V instructions' grammar
+ Returns:
+ - A map with keys representing capabilities and values of lists of
+ instructions enabled by the corresponding key
+ """
+ cap_to_inst = {}
+
+ for inst in instructions:
+ caps = inst['capabilities'] if 'capabilities' in inst else ['0_core_0']
+ for cap in caps:
+ if cap not in cap_to_inst:
+ cap_to_inst[cap] = []
+ cap_to_inst[cap].append(inst['opname'])
+
+ return cap_to_inst
+
+def gen_instr_coverage_report(path, instructions):
+ """Dumps to standard output a YAML report of current instruction coverage
+
+ Arguments:
+ - path: the path to SPIRBase.td
+ - instructions: a list containing all SPIR-V instructions' grammar
+ """
+ with open(path, 'r') as f:
+ content = f.read()
+
+ content = content.split(AUTOGEN_OPCODE_SECTION_MARKER)
+
+ existing_opcodes = [k[11:] for k in re.findall('def SPV_OC_\w+', content[1])]
+ existing_instructions = list(
+ filter(lambda inst: (inst['opname'] in existing_opcodes),
+ instructions))
+
+ instructions_opnames = [inst['opname'] for inst in instructions]
+
+ remaining_opcodes = list(set(instructions_opnames) - set(existing_opcodes))
+ remaining_instructions = list(
+ filter(lambda inst: (inst['opname'] in remaining_opcodes),
+ instructions))
+
+ rem_cap_to_instr = map_cap_to_opnames(remaining_instructions)
+ ex_cap_to_instr = map_cap_to_opnames(existing_instructions)
+
+ rem_cap_to_cov = {}
+
+ # Calculate coverage for each capability
+ for cap in rem_cap_to_instr:
+ if cap not in ex_cap_to_instr:
+ rem_cap_to_cov[cap] = 0.0
+ else:
+ rem_cap_to_cov[cap] = \
+ (len(ex_cap_to_instr[cap]) / (len(ex_cap_to_instr[cap]) \
+ + len(rem_cap_to_instr[cap])))
+
+ report = {}
+
+ # Merge the 3 maps into one report
+ for cap in rem_cap_to_instr:
+ report[cap] = {}
+ report[cap]['Supported Instructions'] = \
+ ex_cap_to_instr[cap] if cap in ex_cap_to_instr else []
+ report[cap]['Unsupported Instructions'] = rem_cap_to_instr[cap]
+ report[cap]['Coverage'] = '{}%'.format(int(rem_cap_to_cov[cap] * 100))
+
+ print(yaml.dump(report))
def update_td_opcodes(path, instructions, filter_list):
"""Updates SPIRBase.td with new generated opcode cases.
"""Extracts potentially manually specified sections in op's definition.
Arguments: - A string containing the op's TableGen definition
- - doc: the instruction's SPIR-V HTML doc
Returns:
- A dict containing potential manually specified sections
default='Op',
help='SPIR-V instruction category used for choosing '\
'the TableGen base class to define this op')
+ cli_parser.add_argument('--gen-inst-coverage', dest='gen_inst_coverage', action='store_true')
+ cli_parser.set_defaults(gen_inst_coverage=False)
args = cli_parser.parse_args()
print('Done. Note that this script just generates a template; ', end='')
print('please read the spec and update traits, arguments, and ', end='')
print('results accordingly.')
+
+ if args.gen_inst_coverage:
+ gen_instr_coverage_report(args.base_td_path, instructions)