On 11/13/2017 02:01 PM, Dylan Baker wrote: > Quoting Ian Romanick (2017-11-10 14:32:50) > [snip] >> + >> +def can_have_extra_operands(name, enums): >> + """Determine whether an enum can have extra operands. >> + >> + Some enums, like SpvDecoration, can have extra operands with some >> values. >> + Other enums, like SpvMemorySemantics, cannot. >> + """ >> + >> + if name not in enums: >> + return False >> + >> + for parameter in enums[name][1]: >> + if len(parameter[2]) > 0: > > Not using len() here would be faster: > if parameter[2]: > return True > > Or, if you like a more functional approach you could do something like: > return any(p[2] for p in enums[name][1]) > >> + return True >> + >> + return False >> + >> +def prototype_for_instruction_emit(inst, all_enums, bit_enums): >> + parameters = ["spirv_program *prog"] >> + >> + if "operands" in inst: >> + parameters = parameters + [declare_parameter(operand, all_enums, >> bit_enums) for operand in inst["operands"]] >> + >> + return textwrap.dedent("""\ >> + static inline void >> + emit_Spv{name}({parameters})""".format(name=inst["opname"], >> + parameters=",\n >> ".join(parameters))) >> + >> + >> +def default_value(parameter, bit_enums): >> + """Determine whether an enum has a value that is the default when the >> enum is >> + not specified. >> + >> + Some enums are almost always marked as optional on the instructions that >> + use them. Some of these, like SpvMemoryAccess, have a value that is >> + assumed when the value is not specificed in the instruction. >> + """ >> + >> + pass > > This function appears to be unused. > >> + >> +def instruction_size(instruction, enums): >> + """Determine the size of an instruction based on its operands >> + >> + Instructions that have only operands without ? or * quantifiers are >> sized >> + by the number of operands. In addition, instructions that have certain >> + BitEnum and ValueEnum parameters also have varying sizes. >> + """ >> + >> + if "operands" not in instruction: >> + # Instructions like OpNop that have no operands. Handle these with >> a >> + # trivial special case. >> + return 1 >> + >> + for operand in instruction["operands"]: >> + if "quantifier" in operand: >> + return 0 >> + elif operand["kind"] == "LiteralString": >> + return 0 >> + elif operand["kind"] in enums and enums[operand["kind"]][0]: >> + return 0 >> + >> + return len(instruction["operands"]) + 1 >> + >> +def optional_parameter_flag(operand, bit_enums): >> + """Return the name of the flag for an optional parameter, None otherwise >> + >> + Optional parameters are noted in the JSON with a "?" quantifier. For >> some >> + types, it is possible to infer that a value does not exist by examining >> + the value. For example, if a optional LiteralString parameter is NULL, >> it >> + is not included. The same applies for some BitEnum kinds such as >> + ImageOperands. For cases where the existence cannot be infered from the >> + value, a "has_foo" flag is added to the parameter list. >> + >> + This function returns either the has_foo name as a string or None. >> + """ >> + >> + if operand["kind"] == "LiteralString": >> + return None >> + >> + if operand["kind"] == "ImageOperands": >> + return None >> + >> + if operand["kind"] in bit_enums: >> + return None >> + >> + if "quantifier" not in operand or operand["quantifier"] != "?": >> + return None >> + >> + return "has_" + operand_name(operand) >> + >> +def operand_name(operand): >> + if operand["kind"] == "IdResult": >> + return "result" >> + elif operand["kind"] == "IdResultType": >> + return "result_type" >> + elif "name" in operand: >> + if "quantifier" in operand and "+" in operand["name"]: >> + return operand["kind"] >> + elif operand["name"] == "'D~ref~'": >> + return "deref" >> + else: >> + name=operand["name"].replace("'", "").replace(" ", >> "_").replace(".", "").replace("~","_").lower() >> + return name if name not in ["default"] else "_" + name >> + else: >> + return operand["kind"] >> + >> +def list_is_same(a, b): >> + if len(a) != len(b): >> + return False >> + >> + for x in a: >> + if x not in b: >> + return False >> + >> + return True > > is there a reason you canot just do `a == b`?
That's what I had first, and it did not work in some cases. I believe that list==list is only True if the elements have the same order. This function only requires that both lists have the same contents without regard for order. I will add a comment to that effect. >> + >> +def capabilities_are_same(enums): >> + for parameter in enums[1:]: >> + if not list_is_same(enums[0][1], parameter[1]): >> + return False >> + >> + return True >> + >> +def render_enable_capability(capability_list, indent=" "): >> + if len(capability_list) == 0: >> + return "" >> + >> + execution_modes = ["Kernel", "Shader", "Geometry", "Tessellation"] >> + >> + if set(capability_list) <= set(execution_modes): >> + # If all of the capabilities are execution mode capabilities, then >> one >> + # of the must already be set. >> + >> + if len(capability_list) == 1: >> + text = >> "assert(prog->capabilities->is_enabled(SpvCapability{}));".format(capability_list[0]) >> + else: >> + template = Template("""\ >> + >> assert(prog->capabilities->is_enabled(SpvCapability${capability_list[0]}) || >> + % for cap in capability_list[1:-1]: >> + prog->capabilities->is_enabled(SpvCapability${cap}) || >> + % endfor >> + >> prog->capabilities->is_enabled(SpvCapability${capability_list[-1]}));""") >> + >> + text = template.render(capability_list=capability_list) >> + elif len(capability_list) == 2 and capability_list[0] in >> execution_modes: >> + # If there are two capabilities listed and one is either >> + # SpvCapabilityKernel or SpvCapabilityShader, then enable the other >> + # capability if the non-SpvCapabilityKernel-or-SpvCapabilityShader >> + # capablity is not enabled. >> + >> + text = """\ >> + if (!prog->capabilities->is_enabled(SpvCapability{})) >> + >> prog->capabilities->enable(SpvCapability{});""".format(capability_list[0], >> + >> capability_list[1]) >> + elif len(capability_list) == 1: >> + # If there is a single capability listed that is neither >> + # SpvCapabilityKernel nor SpvCapabilityShader, then enable it. >> + >> + text = >> "prog->capabilities->enable(SpvCapability{});".format(capability_list[0]) >> + else: >> + # If there are multiple capabilities listed, things become >> + # complicated. There is generally not enough information available >> to >> + # know which capability should be enabled. >> + >> + template = Template("""\ >> + if >> (!prog->capabilities->is_enabled(SpvCapability${capability_list[0]}) && >> + % for cap in capability_list[1:-1]: >> + !prog->capabilities->is_enabled(SpvCapability${cap}) && >> + % endfor >> + >> !prog->capabilities->is_enabled(SpvCapability${capability_list[-1]})) { >> + // FINISHME: Do something smart. >> + }""") >> + >> + text = template.render(capability_list=capability_list) >> + >> + text = textwrap.dedent(text) >> + return "\n".join([(indent + l).rstrip() for l in text.splitlines()]) >> + >> +TEMPLATE = Template(COPYRIGHT + """\ >> +#ifndef SPIRV_BUILDER_FUNCTIONS_H >> +#define SPIRV_BUILDER_FUNCTIONS_H >> + >> +% for name in bit_enums: >> + >> +static inline void >> +Spv${name}_validate_and_set_capabilities(MAYBE_UNUSED spirv_program *prog, >> MAYBE_UNUSED ${type_map[name]} bits${", MAYBE_UNUSED const uint32_t >> *parameters" if bit_enums[name][0] else ""}) >> +{ >> + assert((bits & ~(${bit_enums[name][1][0][0]} | >> + % for parameter in bit_enums[name][1][1:]: >> + ${parameter[0]}${" |" if parameter != >> bit_enums[name][1][-1] else ")) == 0);"} >> + % endfor >> + % for parameter in bit_enums[name][1]: >> + % if len(parameter[1]) == 1 and parameter[1][0] in ["Shader", >> "Kernel"]: >> + assert((bits & ${parameter[0]}) == 0 || >> + prog->capabilities->is_enabled(SpvCapability${parameter[1][0]})); >> + % elif len(parameter[1]) > 0: >> + >> + if ((bits & ${parameter[0]})) >> + prog->capabilities->enable(SpvCapability${parameter[1][0]}); >> + % endif >> + % endfor >> +} >> + % if bit_enums[name][0]: >> + >> +static inline unsigned >> +${type_map[name]}_parameter_count(${type_map[name]} bits) >> +{ >> + unsigned count = 0; >> + % for parameter in bit_enums[name][1]: >> + % if len(parameter[2]) > 0: >> + >> + if ((bits & ${parameter[0]}) != 0) >> + count += ${len(parameter[2])}; >> + % endif >> + % endfor >> + >> + return count; >> +} >> + % endif >> +% endfor >> +% for name in value_enums: >> + >> +static inline void >> +Spv${name}_validate_and_set_capabilities(MAYBE_UNUSED spirv_program *prog, >> ${type_map[name]} value${", MAYBE_UNUSED const uint32_t *parameters" if >> value_enums[name][0] else ""}) >> +{ >> + switch (value) { >> + % if name == "Capability": >> + % for parameter in value_enums[name][1]: >> + case ${type_map[name]}${parameter[0]}: >> + % endfor >> + break; >> + % else: >> + % for parameter in value_enums[name][1]: >> + case ${type_map[name]}${parameter[0]}: >> + % for i in range(len(parameter[2])): >> + % if parameter[2][i]["kind"] in all_enums: >> + Spv${parameter[2][i]["kind"]}_validate_and_set_capabilities(prog, >> (${type_map[parameter[2][i]["kind"]]}) parameters[${i}]); >> + % endif >> + % endfor >> + % if not capabilities_are_same(value_enums[name][1]) or >> parameter == value_enums[name][1][-1]: >> + % if len(parameter[1]) > 0: >> +${render_enable_capability(parameter[1], " ")} >> + % endif >> + break; >> + % endif >> + % endfor >> + % endif >> + default: >> + unreachable("Invalid ${type_map[name]} value."); >> + } >> +} >> + % if value_enums[name][0]: >> + >> +static inline unsigned >> +Spv${name}_parameter_count(${type_map[name]} value) >> +{ >> + switch (value) { >> + % for parameter in value_enums[name][1]: >> + case ${type_map[name]}${parameter[0]}: return ${len(parameter[2])}; >> + % endfor >> + default: unreachable("Invalid ${type_map[name]} value."); >> + } >> +} >> + % endif >> +% endfor >> + >> +static inline void >> +emit_SpvHeader(spirv_program *prog, uint32_t magic, uint32_t id_bound) >> +{ >> + prog->write_word(SpvMagicNumber); >> + prog->write_word(SpvVersion); >> + prog->write_word(magic); >> + prog->write_word(id_bound); >> + prog->write_word(0); >> +} >> +% for inst in instructions: >> + >> + % if "operands" in inst and "quantifier" in inst["operands"][-1] and >> inst["operands"][-1]["quantifier"] == "*": >> +static inline void >> +begin_Spv${inst["opname"]}( >> + ${", ".join(["spirv_program *prog"] + [declare_parameter(operand, >> all_enums, bit_enums) for operand in inst["operands"][:-1]])}) >> +{ >> + % if "capabilities" in inst: >> +${render_enable_capability(inst["capabilities"], " ")} >> + >> + % endif >> + >> + % if len(inst["operands"]) < 2: >> + /* Write the instruction header now, even though it will be rewritten in >> + * end_Spv${inst["opname"]}, to aid debugging. Having something in the >> + * output stream can be helpful. >> + */ >> + % endif >> + prog->start_instruction(Spv${inst["opname"]}); >> + % for operand in inst["operands"][:-1]: >> + % if operand["kind"] == "LiteralString": >> + prog->write_string(${operand_name(operand)}); >> + % else: >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + % endif >> + % endfor >> +} >> + >> +static inline void >> +emit_Spv${inst["opname"]}_data( >> + spirv_program *prog, >> + % if inst["operands"][-1]["kind"] in composite_types: >> + % for i in >> range(len(composite_types[inst["operands"][-1]["kind"]]) - 1): >> + ${type_map[composite_types[inst["operands"][-1]["kind"]][i]]} p${i}, >> + % endfor >> + ${type_map[composite_types[inst["operands"][-1]["kind"]][-1]]} >> p${len(composite_types[inst["operands"][-1]["kind"]]) - 1}) >> + % else: >> + ${declare_parameter(inst["operands"][-1], all_enums, bit_enums)}) >> + % endif >> +{ >> + //assert(prog->begin != NULL); >> + % if inst["operands"][-1]["kind"] in composite_types: >> + % for i in >> range(len(composite_types[inst["operands"][-1]["kind"]])): >> + prog->write_word((uint32_t) p${i}); >> + % endfor >> + % else: >> + prog->write_word((uint32_t) ${operand_name(inst["operands"][-1])}); >> + % endif >> +} >> + >> +static inline void >> +end_Spv${inst["opname"]}(spirv_program *prog) >> +{ >> + prog->finish_instruction(Spv${inst["opname"]}); >> +} >> + % elif instruction_size(inst, all_enums) == 0: >> +${prototype_for_instruction_emit(inst, all_enums, bit_enums)} >> +{ >> + % if "capabilities" in inst: >> +${render_enable_capability(inst["capabilities"], " ")} >> + >> + % endif >> + % if "operands" in inst: >> + % for operand in inst["operands"]: >> + % if operand["kind"] in all_enums: >> + % if optional_parameter_flag(operand, bit_enums) is not >> None: >> + if (${optional_parameter_flag(operand, bit_enums)}) >> + Spv${operand["kind"]}_validate_and_set_capabilities(prog, >> ${operand_name(operand)}${", (const uint32_t >> *){}_parameters".format(operand_name(operand)) if >> can_have_extra_operands(operand["kind"], all_enums) else ""}); >> + % else: >> + Spv${operand["kind"]}_validate_and_set_capabilities(prog, >> ${operand_name(operand)}${", (const uint32_t >> *){}_parameters".format(operand_name(operand)) if >> can_have_extra_operands(operand["kind"], all_enums) else ""}); >> + % endif >> + % endif >> + % endfor >> + >> + % endif >> + % if instruction_size(inst, all_enums) == 0: >> + prog->start_instruction(Spv${inst["opname"]}); >> + % else: >> + prog->start_instruction(Spv${inst["opname"]}, ${instruction_size(inst, >> all_enums)}); >> + % endif >> + % if "operands" in inst: >> + % for operand in inst["operands"]: >> + % if optional_parameter_flag(operand, bit_enums) is not >> None: >> + >> + if (${optional_parameter_flag(operand, bit_enums)}) >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + % elif operand["kind"] == "LiteralString" and "quantifier" >> in operand and operand["quantifier"] == "?": >> + >> + if (${operand_name(operand)} != NULL) >> + prog->write_string(${operand_name(operand)}); >> + % elif operand["kind"] == "LiteralString": >> + prog->write_string(${operand_name(operand)}); >> + % elif operand["kind"] in bit_enums and >> bit_enums[operand["kind"]][0]: >> + % if "quantifier" in operand and operand["quantifier"] >> == "?": >> + >> + if (${operand_name(operand)} != ${type_map[operand["kind"]]}None) { >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + prog->write(${operand_name(operand)}_parameters, >> + >> ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)})); >> + } >> + % else: >> + >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + % if operand["kind"] == "ImageOperands": >> + assert(${operand_name(operand)} != ${type_map[operand["kind"]]}None); >> + >> + prog->write(${operand_name(operand)}_parameters, >> + >> ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)})); >> + % else: >> + if (${operand_name(operand)} != ${type_map[operand["kind"]]}None) { >> + prog->write(${operand_name(operand)}_parameters, >> + >> ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)})); >> + } >> + % endif >> + % endif >> + % elif operand["kind"] in value_enums and >> value_enums[operand["kind"]][0]: >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + prog->write(${operand_name(operand)}_parameters, >> + >> ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)})); >> + % else: >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + % endif >> + % endfor >> + % endif >> + >> + /* Fix the size based on what was actually emitted. */ >> + prog->finish_instruction(Spv${inst["opname"]}); >> +} >> + % else: >> +${prototype_for_instruction_emit(inst, all_enums, bit_enums)} >> +{ >> + % if "capabilities" in inst: >> +${render_enable_capability(inst["capabilities"], " ")} >> + >> + % endif >> + % if inst["opname"] == "OpTypeInt": >> + switch (width) { >> + case 8: >> + assert(prog->capabilities->is_enabled(SpvCapabilityKernel)); >> + prog->capabilities->enable(SpvCapabilityInt8); >> + break; >> + case 16: >> + prog->capabilities->enable(SpvCapabilityInt16); >> + break; >> + case 32: >> + break; >> + case 64: >> + prog->capabilities->enable(SpvCapabilityInt64); >> + break; >> + default: >> + assert(!"Invalid integer width."); >> + } >> + >> + % elif inst["opname"] == "OpTypeFloat": >> + switch (width) { >> + case 16: >> + prog->capabilities->enable(SpvCapabilityFloat16); >> + break; >> + case 32: >> + break; >> + case 64: >> + prog->capabilities->enable(SpvCapabilityFloat64); >> + break; >> + default: >> + assert(!"Invalid float width."); >> + } >> + >> + % endif >> + % if "operands" in inst: >> + % for operand in inst["operands"]: >> + % if operand["kind"] in all_enums: >> + Spv${operand["kind"]}_validate_and_set_capabilities(prog, >> ${operand_name(operand)}${", (const uint32_t >> *){}_parameters".format(operand_name(operand)) if >> can_have_extra_operands(operand["kind"], all_enums) else ""}); >> + % endif >> + % endfor >> + >> + % endif >> + prog->start_instruction(Spv${inst["opname"]}, ${instruction_size(inst, >> all_enums)}); >> + % if "operands" in inst: >> + % for operand in inst["operands"]: >> + prog->write_word((uint32_t) ${operand_name(operand)}); >> + % endfor >> + % endif >> + prog->finish_instruction(Spv${inst["opname"]}); >> +} >> + % endif >> +% endfor >> + >> +#endif /* SPIRV_BUILDER_FUNCTIONS_H */ >> +""") >> + >> +if __name__ == "__main__": >> + pargs = parse_args() >> + >> + spirv_info = json.JSONDecoder().decode(open(pargs.json, "r").read()) >> + >> + type_map = {"LiteralContextDependentNumber": "int", >> + "LiteralExtInstInteger": "int", >> + "LiteralInteger": "int", >> + "LiteralString": "const char *", >> + "LiteralSpecConstantOpInteger": "int" >> + } >> + >> + composite_types = {} >> + bit_enums = {} >> + value_enums = {} >> + for operand_kind in spirv_info["operand_kinds"]: >> + kind = operand_kind["kind"] >> + >> + if operand_kind["category"] == "BitEnum": >> + type_map[kind] = "Spv" + kind + "Mask" >> + >> + values = [] >> + has_parameters = False >> + for enum in operand_kind["enumerants"]: >> + # For some reason, spirv.h does not include names for >> 0-values >> + # of BitEnums that are not None. For example, there is no >> + # SpvMemorySemanticsRelaxedMask even though it appears in >> the >> + # JSON. >> + if int(enum["value"], 16) == 0 and enum["enumerant"] != >> "None": >> + continue >> + >> + capabilities = enum["capabilities"] if "capabilities" in >> enum else [] >> + parameters = enum["parameters"] if "parameters" in enum >> else [] >> + >> + if enum["enumerant"] == "None": >> + enum_name = "Spv" + kind + "MaskNone" >> + else: >> + enum_name = "Spv" + kind + enum["enumerant"] + "Mask" >> + >> + values.append((enum_name, capabilities, parameters)) >> + >> + if len(parameters) > 0: > > just `if parameters:` > >> + has_parameters = True >> + >> + bit_enums[kind] = (has_parameters, values) >> + elif operand_kind["category"] == "ValueEnum": >> + type_map[kind] = "Spv" + kind >> + >> + values = [] >> + has_parameters = False >> + last_value = -1 >> + for enum in operand_kind["enumerants"]: >> + if enum["value"] == last_value: >> + continue >> + >> + last_value = enum["value"] >> + capabilities = enum["capabilities"] if "capabilities" in >> enum else [] >> + parameters = enum["parameters"] if "parameters" in enum >> else [] >> + values.append((enum["enumerant"], capabilities, parameters)) >> + >> + if len(parameters) > 0: >> + has_parameters = True >> + >> + value_enums[kind] = (has_parameters, values) >> + elif operand_kind["category"] == "Id": >> + type_map[kind] = "SpvId" >> + elif operand_kind["category"] == "Composite": >> + type_map[kind] = "struct Spv" + kind >> + composite_types[kind] = operand_kind["bases"] >> + elif operand_kind["category"] != "Literal" or kind not in type_map: >> + print("Don't know how to handle {} in category {}".format(kind, >> operand_kind["category"])) >> + exit(1) >> + >> + all_enums = dict(value_enums); >> + all_enums.update(bit_enums); >> + >> + with open(pargs.out, 'w') as f: >> + f.write(TEMPLATE.render(instructions=spirv_info["instructions"], >> + composite_types=composite_types, >> + type_map=type_map, >> + operand_name=operand_name, >> + instruction_size=instruction_size, >> + >> optional_parameter_flag=optional_parameter_flag, >> + declare_parameter=declare_parameter, >> + >> can_have_extra_operands=can_have_extra_operands, >> + >> prototype_for_instruction_emit=prototype_for_instruction_emit, >> + bit_enums=bit_enums, >> + value_enums=value_enums, >> + all_enums=all_enums, >> + capabilities_are_same=capabilities_are_same, >> + >> render_enable_capability=render_enable_capability)) >> -- >> 2.9.5 >> >> _______________________________________________ >> mesa-dev mailing list >> mesa-dev@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
signature.asc
Description: OpenPGP digital signature
_______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev