
# Generates C++ include files for the SPIR-V instructions.

import sys
import json
import re

HEADER = "#ifndef %s\n#define %s\n\n// Automatically generated by generate_ops.py. DO NOT EDIT.\n\n"
FOOTER = "\n#endif // %s\n"
IMPL_RE = re.compile(r"void Interpreter::step(.*)\(const Insn(.*)& insn\)")
EMIT_RE = re.compile(r"void Insn(.*)::emit\(Compiler \*compiler\)")

# Hard-code these, they're not marked as label IDs in the JSON.
TARGET_LABEL_IDS = {
        "OpBranch": ["targetLabelId"],
        "OpBranchConditional": ["trueLabelId", "falseLabelId"],
        "OpSwitch": ["targetId"],
}

# Instruction operand (with some of our own info).
class Operand:
    def __init__(self, json_operand, operand_kind_map, opname):
        kind = json_operand["kind"]
        name = json_operand.get("name")
        quantifier = json_operand.get("quantifier")

        cpp_type = "uint32_t"
        cpp_subtype = ""
        cpp_name = cleanUpName(name) + "Id" if name else None
        decode_function = "nextu"
        default_value = ""
        is_result = False
        is_argument = False
        is_label = False

        if kind == "IdLabel": # Our own pseudo-kind.
            cpp_comment = "source labels"
        elif kind == "PairIdRefIdRef" and opname == "OpPhi":
            is_argument = True
            cpp_comment = "source ref IDs"
        elif kind == "IdResultType":
            assert(not name)
            cpp_name = "type"
            cpp_comment = "result type"
        elif kind == "IdResult":
            assert(not name)
            cpp_name = "resultId"
            cpp_comment = "SSA register for result value"
            is_result = True
        elif kind == "IdRef":
            assert(name)
            cpp_comment = "operand from register"
            is_argument = True

            # Special cases.
            if opname == "OpFunctionCall" and cpp_name == "functionId":
                is_argument = False
        elif kind == "LiteralString":
            cpp_type = "std::string"
            cpp_comment = "literal string"
            decode_function = "nexts"
        else:
            operand_kind = operand_kind_map[kind]
            category = operand_kind["category"]
            if category in ["Literal", "Composite", "Id"]:
                # We've already handled literal strings above. All
                # composites and IDs are integers.
                assert(cpp_name)
                cpp_comment = kind
            elif category in ["ValueEnum", "BitEnum"]:
                # Keep uint32_t.
                if not cpp_name:
                    cpp_name = CamelCase_to_camelCase(kind)
                cpp_comment = kind
            else:
                sys.stderr.write("Unhandled kind \"%s\" for operator \"%s\".\n" % (kind, opname))
                sys.exit(1)

        # Varargs.
        if quantifier == "*":
            cpp_subtype = cpp_type
            cpp_type = "std::vector<" + cpp_type + ">"
            decode_function = "restv"

        if kind == "MemoryAccess":
            default_value = "NO_MEMORY_ACCESS_SEMANTIC"

        # Should have a default if it's optional.
        if quantifier == "?" and not default_value:
            # Disable this warning, we don't know what to do with it anyway.
            if False:
                sys.stderr.write("Warning: No default for optional operand %s of %s.\n"
                        % (cpp_name, opname))

        # Target labels of this instruction.
        targetLabelIds = TARGET_LABEL_IDS.get(opname, [])
        if cpp_name in targetLabelIds:
            is_argument = False
            is_label = True

        self.kind = kind
        self.name = name
        self.quantifier = quantifier
        self.cpp_type = cpp_type
        self.cpp_subtype = cpp_subtype
        self.cpp_name = cpp_name
        self.cpp_comment = cpp_comment
        self.decode_function = decode_function
        self.default_value = default_value
        self.is_result = is_result
        self.is_argument = is_argument
        self.is_label = is_label

def CamelCase_to_camelCase(name):
    return name[0].lower() + name[1:]

# Clean up a name like "'Sampled Image'" to "sampledImage".
def cleanUpName(name):
    if "," in name:
        return "operand"
    else:
        return CamelCase_to_camelCase(name[1:-1].replace(" ", "").replace("~", ""))

# Returns the set of instructions implemented in the interpreter.
def get_interpreted_instructions(source_pathname):
    lines = open(source_pathname).readlines()

    interpreter_instructions = set()

    for line in lines:
        m = IMPL_RE.match(line.strip())
        if m:
            # Make sure the function name and parameter match.
            name1 = m.group(1)
            name2 = m.group(2)
            assert(name1 == name2)

            interpreter_instructions.add(name1)

    return interpreter_instructions

# Returns the set of instructions in the compiler.
def get_compiled_instructions(source_pathname):
    lines = open(source_pathname).readlines()

    compiled_instructions = set()

    for line in lines:
        m = EMIT_RE.match(line.strip())
        if m:
            name = m.group(1)
            compiled_instructions.add(name)

    return compiled_instructions

# Output all the code for an instruction.
def generate_instruction(instruction, opname_prefix, opcode_prefix, clip_prefix, opcode_namespace,
        interpreter_instructions, compiled_instructions, operand_kind_map,
        opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
        opcode_decl_f, opcode_decode_f):

    opcode = instruction["opcode"]
    opname = opname_prefix + instruction["opname"]
    if not opname.startswith(clip_prefix):
        sys.stderr.write("Instruction name \"%s\" does not start with \"%s\".\n" %
                (opname, clip_prefix))
        sys.exit(1)

    short_opname = opname[len(clip_prefix):]
    struct_opname = "Insn" + short_opname

    if opname_prefix == "GLSLstd450":
        # Set up 2 operands from OpExtInst that are handled by the extension instruction
        extinst_operands = [
                Operand({"kind": "IdResultType"}, operand_kind_map, opname),
                Operand({"kind": "IdResult"}, operand_kind_map, opname)
        ]
    else:
        extinst_operands = []

    # Process the operands, adding our own custom fields to them.
    operands = [Operand(json_operand, operand_kind_map, opname)
            for json_operand in instruction.get("operands", [])]

    # Add pseudo-operand for labels for OpPhi instruction.
    if opname == "OpPhi":
        operands.append(Operand({
                "kind": "IdLabel",
                "name": "\"label\"",
                "quantifier": "*"
            }, operand_kind_map, opname))

    # All operands, including the extension ones.
    all_operands = extinst_operands + operands

    # Convert opcode namespace to hex string.
    if opcode_namespace == 0:
        opcode_namespace = ""
    else:
        opcode_namespace = "0x%x | " % opcode_namespace

    # Opcode to string file.
    opcode_to_string_f.write("    {%s%d, \"%s\"},\n" % (opcode_namespace, opcode, short_opname))

    if short_opname in interpreter_instructions:
        # Struct file.
        opcode_structs_f.write("// %s instruction (code %d).\n" % (opname, opcode))
        opcode_structs_f.write("struct %s : public Instruction {\n" % (struct_opname, ))
        params = ["const LineInfo& lineInfo"]
        inits = ["Instruction(lineInfo)"]
        for operand in all_operands:
            params.append("%s %s" % (operand.cpp_type, operand.cpp_name))
            if not operand.is_result and not operand.is_argument:
                inits.append("%s(%s)" % (operand.cpp_name, operand.cpp_name))
        opcode_structs_f.write("    %s(%s) : %s {\n" %
                (struct_opname,
                    ", ".join(params),
                    ", ".join(inits)))
        for operand in all_operands:
            if operand.is_result:
                opcode_structs_f.write("        addResult(%s);\n" % operand.cpp_name)
            elif operand.is_argument:
                if operand.quantifier == "*":
                    opcode_structs_f.write("        for (auto _argId : %s) {\n" % operand.cpp_name)
                    opcode_structs_f.write("            addParameter(_argId);\n")
                    opcode_structs_f.write("        }\n")
                else:
                    opcode_structs_f.write("        addParameter(%s);\n" % operand.cpp_name)
            if operand.is_label:
                opcode_structs_f.write("        targetLabelIds.insert(%s);\n" % operand.cpp_name)
        opcode_structs_f.write("    }\n")
        result_index = 0
        argument_index = 0
        for operand in all_operands:
            if operand.is_result:
                opcode_structs_f.write("    %s %s() const { return resIdList[%d]; } // %s%s\n" %
                        (operand.cpp_type, operand.cpp_name, result_index,
                            operand.cpp_comment,
                            " (optional)" if operand.quantifier == "?" else ""))
                result_index += 1
            elif operand.is_argument:
                if operand.quantifier == "*":
                    opcode_structs_f.write("    %s %s(size_t i) const { return argIdList[%d + i]; } // %s%s\n" %
                            (operand.cpp_subtype, operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    opcode_structs_f.write("    size_t %sCount() const { return argIdList.size() - %d; } // %s%s\n" %
                            (operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    argument_index = None # Force a fail if it's used again.
                else:
                    opcode_structs_f.write("    %s %s() const { return argIdList[%d]; } // %s%s\n" %
                            (operand.cpp_type, operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    argument_index += 1
            else:
                opcode_structs_f.write("    %s %s; // %s%s\n" % (operand.cpp_type,
                    operand.cpp_name, operand.cpp_comment,
                    " (optional)" if operand.quantifier == "?" else ""))
        opcode_structs_f.write("    virtual void step(Interpreter *interpreter) { interpreter->step%s(*this); }\n" % short_opname)
        opcode_structs_f.write("    virtual uint32_t opcode() const { return %s%s%s; }\n" % (opcode_namespace, opcode_prefix, opname))
        opcode_structs_f.write("    virtual std::string name() const { return \"%s\"; }\n" % opname)
        if short_opname in compiled_instructions:
            opcode_structs_f.write("    virtual void emit(Compiler *compiler);\n")
        is_branch = opname in ["OpBranch", "OpBranchConditional", "OpSwitch",
                "OpReturn", "OpReturnValue"]
        if is_branch:
            opcode_structs_f.write("    virtual bool isBranch() const { return true; }\n")
        if is_branch or opname in ["OpKill", "OpUnreachable"]:
            opcode_structs_f.write("    virtual bool isTermination() const { return true; }\n")
        opcode_structs_f.write("};\n\n")

        # Struct declaration file.
        opcode_struct_decl_f.write("struct %s;\n" % (struct_opname, ))

        # Decode file.
        opcode_decode_f.write("case %s%s: {\n" % (opcode_prefix, opname))
        if opname == "OpPhi":
            # Split up the (operand,label) pairs of the restv().
            opcode_decode_f.write("    uint32_t type = nextu();\n")
            opcode_decode_f.write("    uint32_t resultId = nextu();\n")
            opcode_decode_f.write("    std::vector<uint32_t> pairs = restv();\n")
            opcode_decode_f.write("    std::vector<uint32_t> operandId;\n")
            opcode_decode_f.write("    std::vector<uint32_t> labelId;\n")
            opcode_decode_f.write("    for (size_t i = 0; i < pairs.size(); i += 2) {\n")
            opcode_decode_f.write("        operandId.push_back(pairs[i]);\n")
            opcode_decode_f.write("        labelId.push_back(pairs[i + 1]);\n")
            opcode_decode_f.write("    }\n")
        else:
            for operand in operands:
                opcode_decode_f.write("    %s %s = %s(%s);\n" %
                        (operand.cpp_type, operand.cpp_name,
                            operand.decode_function, operand.default_value))
        opcode_decode_f.write("    pgm->currentBlock->instructions.push_back(std::make_shared<%s>(%s));\n" %
                (struct_opname, ", ".join(["pgm->currentLine"]+[operand.cpp_name for operand in all_operands])))
        if 'IdResultType' in [op.kind for op in all_operands] and 'IdResult' in [op.kind for op in all_operands]:
            opcode_decode_f.write("    pgm->resultTypes[resultId] = type;\n")
        opcode_decode_f.write("    if(pgm->verbose) {\n")
        opcode_decode_f.write("        std::cout << \"%s\";\n" % short_opname)
        for operand in all_operands:
            opcode_decode_f.write("        std::cout << \" %s \";\n" % operand.cpp_name)
            if operand.quantifier == "*":
                opcode_decode_f.write("        for(size_t i = 0; i < %s.size(); i++)\n"
                        % operand.cpp_name)
                opcode_decode_f.write("            std::cout << %s[i] << \" \";\n" % operand.cpp_name)
            else:
                opcode_decode_f.write("        std::cout << %s;\n" % operand.cpp_name)
        opcode_decode_f.write("        std::cout << \"\\n\";\n")
        opcode_decode_f.write("    }\n")
        opcode_decode_f.write("    break;\n")
        opcode_decode_f.write("}\n\n")

        # Declaration file.
        opcode_decl_f.write("void step%s(const %s& insn);\n" % (short_opname, struct_opname))

    # Generate a stub if it's not already implemented in the C++ file.
    if short_opname not in interpreter_instructions:
        opcode_impl_f.write("void Interpreter::step%s(const %s& insn)\n{\n    std::cerr << \"step%s() not implemented\\n\";\n}\n\n"
                % (short_opname, struct_opname, short_opname))

def main():
    # Input file.
    json_pathname = sys.argv[1]
    grammar = json.load(open(json_pathname))

    glsl_std_450_json_pathname = sys.argv[2]
    glsl_std_450_grammar = json.load(open(glsl_std_450_json_pathname))

    # Create map from operand kind name ("FPFastMathMode") to info about the kind.
    operand_kind_map = dict((kind["kind"], kind) for kind in grammar["operand_kinds"])

    # Find what instructions have already been implemented.
    interpreter_instructions = get_interpreted_instructions("interpreter.cpp")
    compiled_instructions = get_compiled_instructions("shade.cpp")

    # Output files.
    opcode_to_string_f = open("opcode_to_string.h", "w")
    opcode_to_string_f.write(HEADER % ("OPCODE_TO_STRING_H", "OPCODE_TO_STRING_H"))

    opcode_structs_f = open("opcode_structs.h", "w")
    opcode_structs_f.write(HEADER % ("OPCODE_STRUCTS_H", "OPCODE_STRUCTS_H"))
    opcode_structs_f.write("#include \"interpreter.h\"\n\n")

    opcode_struct_decl_f = open("opcode_struct_decl.h", "w")
    opcode_struct_decl_f.write(HEADER % ("OPCODE_STRUCT_DECL_H", "OPCODE_STRUCT_DECL_H"))

    opcode_impl_f = open("opcode_impl.h", "w")
    opcode_impl_f.write(HEADER % ("OPCODE_IMPL_H", "OPCODE_IMPL_H"))

    opcode_decl_f = open("opcode_decl.h", "w")
    opcode_decl_f.write(HEADER % ("OPCODE_DECL_H", "OPCODE_DECL_H"))

    opcode_decode_f = open("opcode_decode.h", "w")
    opcode_decode_f.write(HEADER % ("OPCODE_DECODE_H", "OPCODE_DECODE_H"))

    # Output instructions for core SPIR-V
    for instruction in grammar["instructions"]:
        generate_instruction(instruction, "", "Spv", "Op", 0,
                interpreter_instructions, compiled_instructions, operand_kind_map,
                opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
                opcode_decl_f, opcode_decode_f)

    # Emit opcode decode preamble for extinst
    opcode_decode_f.write("case SpvOpExtInst: {\n")
    opcode_decode_f.write("    uint32_t type = nextu();\n")
    opcode_decode_f.write("    uint32_t resultId = nextu();\n")
    opcode_decode_f.write("    uint32_t ext = nextu();\n")
    opcode_decode_f.write("    uint32_t opcode = nextu();\n")
    opcode_decode_f.write("    if(ext == pgm->ExtInstGLSL_std_450_id) {\n")
    opcode_decode_f.write("        switch(opcode) {\n")

    for instruction in glsl_std_450_grammar["instructions"]:
        generate_instruction(instruction, "GLSLstd450", "", "", 0x10000,
                interpreter_instructions, compiled_instructions, operand_kind_map,
                opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
                opcode_decl_f, opcode_decode_f)

    opcode_decode_f.write("            default: {\n")
    opcode_decode_f.write("                if(pgm->throwOnUnimplemented) {\n")
    opcode_decode_f.write("                    throw std::runtime_error(\"unimplemented GLSLstd450 opcode \" + OpcodeToString[0x10000 | opcode] + \" (\" + std::to_string(opcode) + \")\");\n")
    opcode_decode_f.write("                } else {\n")
    opcode_decode_f.write("                    std::cerr << \"unimplemented GLSLstd450 opcode \" << OpcodeToString[0x10000 | opcode] << \" (\" << opcode << \")\\n\";\n")
    opcode_decode_f.write("                    pgm->hasUnimplemented = true;\n")
    opcode_decode_f.write("                }\n")
    opcode_decode_f.write("                break;\n")
    opcode_decode_f.write("            }\n")
    opcode_decode_f.write("        }\n")
    opcode_decode_f.write("    } else {\n")
    opcode_decode_f.write("        throw std::runtime_error(\"unimplemented instruction \" + std::to_string(opcode) + \" from extension set \" + std::to_string(ext));\n")
    opcode_decode_f.write("    }\n")
    opcode_decode_f.write("    break;\n")
    opcode_decode_f.write("}\n")

    opcode_to_string_f.write(FOOTER % "OPCODE_TO_STRING_H")
    opcode_structs_f.write(FOOTER % "OPCODE_STRUCTS_H")
    opcode_struct_decl_f.write(FOOTER % "OPCODE_STRUCT_DECL_H")
    opcode_impl_f.write(FOOTER % "OPCODE_IMPL_H")
    opcode_decl_f.write(FOOTER % "OPCODE_DECL_H")
    opcode_decode_f.write(FOOTER % "OPCODE_DECODE_H")

    opcode_to_string_f.close()
    opcode_structs_f.close()
    opcode_struct_decl_f.close()
    opcode_impl_f.close()
    opcode_decode_f.close()
    opcode_decl_f.close()

if __name__ == "__main__":
    main()



# Generates C++ include files for the SPIR-V instructions.

import sys
import json
import re

HEADER = "#ifndef %s\n#define %s\n\n// Automatically generated by generate_ops.py. DO NOT EDIT.\n\n"
FOOTER = "\n#endif // %s\n"
IMPL_RE = re.compile(r"void Interpreter::step(.*)\(const Insn(.*)& insn\)")
EMIT_RE = re.compile(r"void Insn(.*)::emit\(Compiler \*compiler\)")

# Hard-code these, they're not marked as label IDs in the JSON.
TARGET_LABEL_IDS = {
        "OpBranch": ["targetLabelId"],
        "OpBranchConditional": ["trueLabelId", "falseLabelId"],
        "OpSwitch": ["targetId"],
}

# Instruction operand (with some of our own info).
class Operand:
    def __init__(self, json_operand, operand_kind_map, opname):
        kind = json_operand["kind"]
        name = json_operand.get("name")
        quantifier = json_operand.get("quantifier")

        cpp_type = "uint32_t"
        cpp_subtype = ""
        cpp_name = cleanUpName(name) + "Id" if name else None
        decode_function = "nextu"
        default_value = ""
        is_result = False
        is_argument = False
        is_label = False

        if kind == "IdLabel": # Our own pseudo-kind.
            cpp_comment = "source labels"
        elif kind == "PairIdRefIdRef" and opname == "OpPhi":
            is_argument = True
            cpp_comment = "source ref IDs"
        elif kind == "IdResultType":
            assert(not name)
            cpp_name = "type"
            cpp_comment = "result type"
        elif kind == "IdResult":
            assert(not name)
            cpp_name = "resultId"
            cpp_comment = "SSA register for result value"
            is_result = True
        elif kind == "IdRef":
            assert(name)
            cpp_comment = "operand from register"
            is_argument = True

            # Special cases.
            if opname == "OpFunctionCall" and cpp_name == "functionId":
                is_argument = False
        elif kind == "LiteralString":
            cpp_type = "std::string"
            cpp_comment = "literal string"
            decode_function = "nexts"
        else:
            operand_kind = operand_kind_map[kind]
            category = operand_kind["category"]
            if category in ["Literal", "Composite", "Id"]:
                # We've already handled literal strings above. All
                # composites and IDs are integers.
                assert(cpp_name)
                cpp_comment = kind
            elif category in ["ValueEnum", "BitEnum"]:
                # Keep uint32_t.
                if not cpp_name:
                    cpp_name = CamelCase_to_camelCase(kind)
                cpp_comment = kind
            else:
                sys.stderr.write("Unhandled kind \"%s\" for operator \"%s\".\n" % (kind, opname))
                sys.exit(1)

        # Varargs.
        if quantifier == "*":
            cpp_subtype = cpp_type
            cpp_type = "std::vector<" + cpp_type + ">"
            decode_function = "restv"

        if kind == "MemoryAccess":
            default_value = "NO_MEMORY_ACCESS_SEMANTIC"

        # Should have a default if it's optional.
        if quantifier == "?" and not default_value:
            # Disable this warning, we don't know what to do with it anyway.
            if False:
                sys.stderr.write("Warning: No default for optional operand %s of %s.\n"
                        % (cpp_name, opname))

        # Target labels of this instruction.
        targetLabelIds = TARGET_LABEL_IDS.get(opname, [])
        if cpp_name in targetLabelIds:
            is_argument = False
            is_label = True

        self.kind = kind
        self.name = name
        self.quantifier = quantifier
        self.cpp_type = cpp_type
        self.cpp_subtype = cpp_subtype
        self.cpp_name = cpp_name
        self.cpp_comment = cpp_comment
        self.decode_function = decode_function
        self.default_value = default_value
        self.is_result = is_result
        self.is_argument = is_argument
        self.is_label = is_label

def CamelCase_to_camelCase(name):
    return name[0].lower() + name[1:]

# Clean up a name like "'Sampled Image'" to "sampledImage".
def cleanUpName(name):
    if "," in name:
        return "operand"
    else:
        return CamelCase_to_camelCase(name[1:-1].replace(" ", "").replace("~", ""))

# Returns the set of instructions implemented in the interpreter.
def get_interpreted_instructions(source_pathname):
    lines = open(source_pathname).readlines()

    interpreter_instructions = set()

    for line in lines:
        m = IMPL_RE.match(line.strip())
        if m:
            # Make sure the function name and parameter match.
            name1 = m.group(1)
            name2 = m.group(2)
            assert(name1 == name2)

            interpreter_instructions.add(name1)

    return interpreter_instructions

# Returns the set of instructions in the compiler.
def get_compiled_instructions(source_pathname):
    lines = open(source_pathname).readlines()

    compiled_instructions = set()

    for line in lines:
        m = EMIT_RE.match(line.strip())
        if m:
            name = m.group(1)
            compiled_instructions.add(name)

    return compiled_instructions

# Output all the code for an instruction.
def generate_instruction(instruction, opname_prefix, opcode_prefix, clip_prefix, opcode_namespace,
        interpreter_instructions, compiled_instructions, operand_kind_map,
        opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
        opcode_decl_f, opcode_decode_f):

    opcode = instruction["opcode"]
    opname = opname_prefix + instruction["opname"]
    if not opname.startswith(clip_prefix):
        sys.stderr.write("Instruction name \"%s\" does not start with \"%s\".\n" %
                (opname, clip_prefix))
        sys.exit(1)

    short_opname = opname[len(clip_prefix):]
    struct_opname = "Insn" + short_opname

    if opname_prefix == "GLSLstd450":
        # Set up 2 operands from OpExtInst that are handled by the extension instruction
        extinst_operands = [
                Operand({"kind": "IdResultType"}, operand_kind_map, opname),
                Operand({"kind": "IdResult"}, operand_kind_map, opname)
        ]
    else:
        extinst_operands = []

    # Process the operands, adding our own custom fields to them.
    operands = [Operand(json_operand, operand_kind_map, opname)
            for json_operand in instruction.get("operands", [])]

    # Add pseudo-operand for labels for OpPhi instruction.
    if opname == "OpPhi":
        operands.append(Operand({
                "kind": "IdLabel",
                "name": "\"label\"",
                "quantifier": "*"
            }, operand_kind_map, opname))

    # All operands, including the extension ones.
    all_operands = extinst_operands + operands

    # Convert opcode namespace to hex string.
    if opcode_namespace == 0:
        opcode_namespace = ""
    else:
        opcode_namespace = "0x%x | " % opcode_namespace

    # Opcode to string file.
    opcode_to_string_f.write("    {%s%d, \"%s\"},\n" % (opcode_namespace, opcode, short_opname))

    if short_opname in interpreter_instructions:
        # Struct file.
        opcode_structs_f.write("// %s instruction (code %d).\n" % (opname, opcode))
        opcode_structs_f.write("struct %s : public Instruction {\n" % (struct_opname, ))
        params = ["const LineInfo& lineInfo"]
        inits = ["Instruction(lineInfo)"]
        for operand in all_operands:
            params.append("%s %s" % (operand.cpp_type, operand.cpp_name))
            if not operand.is_result and not operand.is_argument:
                inits.append("%s(%s)" % (operand.cpp_name, operand.cpp_name))
        opcode_structs_f.write("    %s(%s) : %s {\n" %
                (struct_opname,
                    ", ".join(params),
                    ", ".join(inits)))
        for operand in all_operands:
            if operand.is_result:
                opcode_structs_f.write("        addResult(%s);\n" % operand.cpp_name)
            elif operand.is_argument:
                if operand.quantifier == "*":
                    opcode_structs_f.write("        for (auto _argId : %s) {\n" % operand.cpp_name)
                    opcode_structs_f.write("            addParameter(_argId);\n")
                    opcode_structs_f.write("        }\n")
                else:
                    opcode_structs_f.write("        addParameter(%s);\n" % operand.cpp_name)
            if operand.is_label:
                opcode_structs_f.write("        targetLabelIds.insert(%s);\n" % operand.cpp_name)
        opcode_structs_f.write("    }\n")
        result_index = 0
        argument_index = 0
        for operand in all_operands:
            if operand.is_result:
                opcode_structs_f.write("    %s %s() const { return resIdList[%d]; } // %s%s\n" %
                        (operand.cpp_type, operand.cpp_name, result_index,
                            operand.cpp_comment,
                            " (optional)" if operand.quantifier == "?" else ""))
                result_index += 1
            elif operand.is_argument:
                if operand.quantifier == "*":
                    opcode_structs_f.write("    %s %s(size_t i) const { return argIdList[%d + i]; } // %s%s\n" %
                            (operand.cpp_subtype, operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    opcode_structs_f.write("    size_t %sCount() const { return argIdList.size() - %d; } // %s%s\n" %
                            (operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    argument_index = None # Force a fail if it's used again.
                else:
                    opcode_structs_f.write("    %s %s() const { return argIdList[%d]; } // %s%s\n" %
                            (operand.cpp_type, operand.cpp_name, argument_index,
                                operand.cpp_comment,
                                " (optional)" if operand.quantifier == "?" else ""))
                    argument_index += 1
            else:
                opcode_structs_f.write("    %s %s; // %s%s\n" % (operand.cpp_type,
                    operand.cpp_name, operand.cpp_comment,
                    " (optional)" if operand.quantifier == "?" else ""))
        opcode_structs_f.write("    virtual void step(Interpreter *interpreter) { interpreter->step%s(*this); }\n" % short_opname)
        opcode_structs_f.write("    virtual uint32_t opcode() const { return %s%s%s; }\n" % (opcode_namespace, opcode_prefix, opname))
        opcode_structs_f.write("    virtual std::string name() const { return \"%s\"; }\n" % opname)
        if short_opname in compiled_instructions:
            opcode_structs_f.write("    virtual void emit(Compiler *compiler);\n")
        is_branch = opname in ["OpBranch", "OpBranchConditional", "OpSwitch",
                "OpReturn", "OpReturnValue"]
        if is_branch:
            opcode_structs_f.write("    virtual bool isBranch() const { return true; }\n")
        if is_branch or opname in ["OpKill", "OpUnreachable"]:
            opcode_structs_f.write("    virtual bool isTermination() const { return true; }\n")
        opcode_structs_f.write("};\n\n")

        # Struct declaration file.
        opcode_struct_decl_f.write("struct %s;\n" % (struct_opname, ))

        # Decode file.
        opcode_decode_f.write("case %s%s: {\n" % (opcode_prefix, opname))
        if opname == "OpPhi":
            # Split up the (operand,label) pairs of the restv().
            opcode_decode_f.write("    uint32_t type = nextu();\n")
            opcode_decode_f.write("    uint32_t resultId = nextu();\n")
            opcode_decode_f.write("    std::vector<uint32_t> pairs = restv();\n")
            opcode_decode_f.write("    std::vector<uint32_t> operandId;\n")
            opcode_decode_f.write("    std::vector<uint32_t> labelId;\n")
            opcode_decode_f.write("    for (size_t i = 0; i < pairs.size(); i += 2) {\n")
            opcode_decode_f.write("        operandId.push_back(pairs[i]);\n")
            opcode_decode_f.write("        labelId.push_back(pairs[i + 1]);\n")
            opcode_decode_f.write("    }\n")
        else:
            for operand in operands:
                opcode_decode_f.write("    %s %s = %s(%s);\n" %
                        (operand.cpp_type, operand.cpp_name,
                            operand.decode_function, operand.default_value))
        opcode_decode_f.write("    pgm->currentBlock->instructions.push_back(std::make_shared<%s>(%s));\n" %
                (struct_opname, ", ".join(["pgm->currentLine"]+[operand.cpp_name for operand in all_operands])))
        if 'IdResultType' in [op.kind for op in all_operands] and 'IdResult' in [op.kind for op in all_operands]:
            opcode_decode_f.write("    pgm->resultTypes[resultId] = type;\n")
        opcode_decode_f.write("    if(pgm->verbose) {\n")
        opcode_decode_f.write("        std::cout << \"%s\";\n" % short_opname)
        for operand in all_operands:
            opcode_decode_f.write("        std::cout << \" %s \";\n" % operand.cpp_name)
            if operand.quantifier == "*":
                opcode_decode_f.write("        for(size_t i = 0; i < %s.size(); i++)\n"
                        % operand.cpp_name)
                opcode_decode_f.write("            std::cout << %s[i] << \" \";\n" % operand.cpp_name)
            else:
                opcode_decode_f.write("        std::cout << %s;\n" % operand.cpp_name)
        opcode_decode_f.write("        std::cout << \"\\n\";\n")
        opcode_decode_f.write("    }\n")
        opcode_decode_f.write("    break;\n")
        opcode_decode_f.write("}\n\n")

        # Declaration file.
        opcode_decl_f.write("void step%s(const %s& insn);\n" % (short_opname, struct_opname))

    # Generate a stub if it's not already implemented in the C++ file.
    if short_opname not in interpreter_instructions:
        opcode_impl_f.write("void Interpreter::step%s(const %s& insn)\n{\n    std::cerr << \"step%s() not implemented\\n\";\n}\n\n"
                % (short_opname, struct_opname, short_opname))

def main():
    # Input file.
    json_pathname = sys.argv[1]
    grammar = json.load(open(json_pathname))

    glsl_std_450_json_pathname = sys.argv[2]
    glsl_std_450_grammar = json.load(open(glsl_std_450_json_pathname))

    # Create map from operand kind name ("FPFastMathMode") to info about the kind.
    operand_kind_map = dict((kind["kind"], kind) for kind in grammar["operand_kinds"])

    # Find what instructions have already been implemented.
    interpreter_instructions = get_interpreted_instructions("interpreter.cpp")
    compiled_instructions = get_compiled_instructions("shade.cpp")

    # Output files.
    opcode_to_string_f = open("opcode_to_string.h", "w")
    opcode_to_string_f.write(HEADER % ("OPCODE_TO_STRING_H", "OPCODE_TO_STRING_H"))

    opcode_structs_f = open("opcode_structs.h", "w")
    opcode_structs_f.write(HEADER % ("OPCODE_STRUCTS_H", "OPCODE_STRUCTS_H"))
    opcode_structs_f.write("#include \"interpreter.h\"\n\n")

    opcode_struct_decl_f = open("opcode_struct_decl.h", "w")
    opcode_struct_decl_f.write(HEADER % ("OPCODE_STRUCT_DECL_H", "OPCODE_STRUCT_DECL_H"))

    opcode_impl_f = open("opcode_impl.h", "w")
    opcode_impl_f.write(HEADER % ("OPCODE_IMPL_H", "OPCODE_IMPL_H"))

    opcode_decl_f = open("opcode_decl.h", "w")
    opcode_decl_f.write(HEADER % ("OPCODE_DECL_H", "OPCODE_DECL_H"))

    opcode_decode_f = open("opcode_decode.h", "w")
    opcode_decode_f.write(HEADER % ("OPCODE_DECODE_H", "OPCODE_DECODE_H"))

    # Output instructions for core SPIR-V
    for instruction in grammar["instructions"]:
        generate_instruction(instruction, "", "Spv", "Op", 0,
                interpreter_instructions, compiled_instructions, operand_kind_map,
                opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
                opcode_decl_f, opcode_decode_f)

    # Emit opcode decode preamble for extinst
    opcode_decode_f.write("case SpvOpExtInst: {\n")
    opcode_decode_f.write("    uint32_t type = nextu();\n")
    opcode_decode_f.write("    uint32_t resultId = nextu();\n")
    opcode_decode_f.write("    uint32_t ext = nextu();\n")
    opcode_decode_f.write("    uint32_t opcode = nextu();\n")
    opcode_decode_f.write("    if(ext == pgm->ExtInstGLSL_std_450_id) {\n")
    opcode_decode_f.write("        switch(opcode) {\n")

    for instruction in glsl_std_450_grammar["instructions"]:
        generate_instruction(instruction, "GLSLstd450", "", "", 0x10000,
                interpreter_instructions, compiled_instructions, operand_kind_map,
                opcode_to_string_f, opcode_structs_f, opcode_struct_decl_f, opcode_impl_f,
                opcode_decl_f, opcode_decode_f)

    opcode_decode_f.write("            default: {\n")
    opcode_decode_f.write("                if(pgm->throwOnUnimplemented) {\n")
    opcode_decode_f.write("                    throw std::runtime_error(\"unimplemented GLSLstd450 opcode \" + OpcodeToString[0x10000 | opcode] + \" (\" + std::to_string(opcode) + \")\");\n")
    opcode_decode_f.write("                } else {\n")
    opcode_decode_f.write("                    std::cerr << \"unimplemented GLSLstd450 opcode \" << OpcodeToString[0x10000 | opcode] << \" (\" << opcode << \")\\n\";\n")
    opcode_decode_f.write("                    pgm->hasUnimplemented = true;\n")
    opcode_decode_f.write("                }\n")
    opcode_decode_f.write("                break;\n")
    opcode_decode_f.write("            }\n")
    opcode_decode_f.write("        }\n")
    opcode_decode_f.write("    } else {\n")
    opcode_decode_f.write("        throw std::runtime_error(\"unimplemented instruction \" + std::to_string(opcode) + \" from extension set \" + std::to_string(ext));\n")
    opcode_decode_f.write("    }\n")
    opcode_decode_f.write("    break;\n")
    opcode_decode_f.write("}\n")

    opcode_to_string_f.write(FOOTER % "OPCODE_TO_STRING_H")
    opcode_structs_f.write(FOOTER % "OPCODE_STRUCTS_H")
    opcode_struct_decl_f.write(FOOTER % "OPCODE_STRUCT_DECL_H")
    opcode_impl_f.write(FOOTER % "OPCODE_IMPL_H")
    opcode_decl_f.write(FOOTER % "OPCODE_DECL_H")
    opcode_decode_f.write(FOOTER % "OPCODE_DECODE_H")

    opcode_to_string_f.close()
    opcode_structs_f.close()
    opcode_struct_decl_f.close()
    opcode_impl_f.close()
    opcode_decode_f.close()
    opcode_decl_f.close()

if __name__ == "__main__":
    main()


# Generates C++ include files for the SPIR-V instructions. #define %s\n\n// Automatically generated by generate_ops.py. DO NOT EDIT.\n\n" #endif // %s\n" # Hard-code these, they're not marked as label IDs in the JSON. # Instruction operand (with some of our own info). # Our own pseudo-kind. # Special cases. # We've already handled literal strings above. All # composites and IDs are integers. # Keep uint32_t. # Varargs. # Should have a default if it's optional. # Disable this warning, we don't know what to do with it anyway. # Target labels of this instruction. # Clean up a name like "'Sampled Image'" to "sampledImage". # Returns the set of instructions implemented in the interpreter. # Make sure the function name and parameter match. # Returns the set of instructions in the compiler. # Output all the code for an instruction. # Set up 2 operands from OpExtInst that are handled by the extension instruction # Process the operands, adding our own custom fields to them. # Add pseudo-operand for labels for OpPhi instruction. # All operands, including the extension ones. # Convert opcode namespace to hex string. # Opcode to string file. # Struct file. # Force a fail if it's used again. # Struct declaration file. # Decode file. # Split up the (operand,label) pairs of the restv(). # Declaration file. # Generate a stub if it's not already implemented in the C++ file. # Input file. # Create map from operand kind name ("FPFastMathMode") to info about the kind. # Find what instructions have already been implemented. # Output files. # Output instructions for core SPIR-V # Emit opcode decode preamble for extinst