| #!/usr/bin/env python |
| # |
| # Copyright (C) 2017 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| import getopt |
| import json |
| import string |
| import sys |
| |
| def generate_header_file(filename, with_guard, printer): |
| f = open(filename, 'w') |
| orig_stdout = sys.stdout |
| sys.stdout = f |
| print '// DO NOT MODIFY. AUTO-GENERATED.\n' |
| if with_guard: |
| print "#pragma once" |
| print |
| print "namespace android {" |
| print "namespace spirit {" |
| print |
| printer() |
| if with_guard: |
| print "} // namespace spirit" |
| print "} // namespace android" |
| f.close() |
| sys.stdout = orig_stdout |
| |
| |
| |
| ################################################################################ |
| # |
| # Generate Builder class: the .h and .cpp files. |
| # |
| ################################################################################ |
| |
| def factory_method_name(opname, outlined): |
| if outlined: |
| return "Builder::Make%s" % opname_noprefix(opname) |
| else: |
| return "Make%s" % opname_noprefix(opname) |
| |
| |
| |
| def factory_method_prototype(inst, outlined): |
| opname = inst['opname'] |
| operands = inst.get('operands') |
| str = "%s *%s(" % (class_name(opname), factory_method_name(opname, outlined)) |
| first = True; |
| for type, var, quantifier, comment in generate_member_list(operands): |
| if var != "mResult": |
| param = var[1:] |
| if first: |
| first = False |
| else: |
| str += ', ' |
| if quantifier == '?': |
| str += '%s *%s=nullptr' % (type, param) |
| elif quantifier == '*': |
| vecTy = "std::vector<%s>" % type |
| str += '%s %s=%s()' % (vecTy, param, vecTy) |
| else: |
| str += '%s %s' % (type, param) |
| str += ')' |
| return str |
| |
| |
| |
| def factory_method_body(inst): |
| opname = inst['opname'] |
| operands = inst.get('operands') |
| clazz = class_name(opname) |
| str = "%s *ret = new %s(" % (clazz, clazz) |
| first = True |
| for type, var, quantifier, comment in generate_member_list(operands): |
| if var != "mResult" and quantifier != '*' and quantifier != '?': |
| param = var[1:] |
| if first: |
| first = False |
| else: |
| str += ', ' |
| str += param |
| str += """); |
| if (!ret) { |
| return nullptr; |
| } |
| """ |
| str += """ |
| if (ret->hasResult()) { |
| ret->setId(Module::getCurrentModule()->nextId()); |
| } |
| """ |
| for type, var, quantifier, comment in generate_member_list(operands): |
| param = var[1:] |
| # TODO: use vector::swap() or move instead of copy |
| if quantifier == '?' or quantifier == '*': |
| str += " ret->%s = %s;\n" % (var, param) |
| str += " return ret;" |
| return str |
| |
| |
| |
| def print_factory_method(inst): |
| print """%s { |
| %s |
| }""" % (factory_method_prototype(inst, False), |
| factory_method_body(inst)) |
| |
| |
| |
| def print_factory_methods(insts): |
| for inst in insts: |
| print_factory_method(inst) |
| |
| |
| |
| ################################################################################ |
| # |
| # Generate type defintions |
| # |
| ################################################################################ |
| |
| def enum_enumerants(ty, enumerants): |
| str = "" |
| for enumerant in enumerants: |
| name = enumerant['enumerant'] |
| val = enumerant['value'] |
| if name[0].isalpha(): |
| str += " %s = %sU,\n" % (name, val) |
| else: |
| str += " %s%s = %sU,\n" % (ty, name, val) |
| return str |
| |
| |
| def generate_enum(ty): |
| typeName = ty['kind'] |
| print """enum class %s : uint32_t {\n%s}; |
| """ % (typeName, |
| enum_enumerants(typeName, ty['enumerants'])) |
| |
| |
| def generate_composite_fields(bases): |
| str = "" |
| i = 0 |
| for field in bases: |
| str += " %s mField%d;\n" % (field, i) |
| i = i + 1 |
| return str |
| |
| |
| |
| def print_type_definitions(operand_kinds): |
| for ty in operand_kinds: |
| category = ty['category'] |
| if category == 'BitEnum' or category == 'ValueEnum': |
| generate_enum(ty) |
| elif category == 'Composite': |
| print "struct %s {\n%s};\n" % (ty['kind'], |
| generate_composite_fields(ty['bases'])) |
| |
| |
| |
| ################################################################################ |
| # |
| # Generate class defintions for all instructions |
| # |
| ################################################################################ |
| |
| def opname_noprefix(opname): |
| return opname[2:] |
| |
| def class_name(opname): |
| return "%sInst" % opname_noprefix(opname) |
| |
| |
| |
| def generate_member_list(operands): |
| members = [] |
| if operands is None: |
| return members |
| index = 1 |
| for operand in operands: |
| type = operand['kind'] |
| if type == 'IdResultType' or type == 'IdResult': |
| varName = "m%s" % type[2:] |
| else: |
| varName = "mOperand%d" % index |
| index = index + 1 |
| quantifier = operand.get('quantifier') |
| comment = operand.get('name'); |
| members.append((type, varName, quantifier, comment)) |
| return members |
| |
| def fixed_word_count(member_list): |
| wc = 1 # for the first word of opcode and word count |
| for type, var, quant, comment in member_list: |
| if quant != '?' and quant != '*': |
| wc += 1 |
| return wc |
| |
| def string_for_members(opname, member_list): |
| if member_list == []: |
| return "" |
| member_str = "\n static constexpr OpCode mOpCode=%s;\n" % opname |
| for type, var, quantifier, comment in member_list: |
| if comment is not None and comment.find('\n') != -1: |
| member_str += " /* %s\n */\n" % comment |
| member_str += " " |
| if quantifier == '?': |
| type = type + '*'; |
| elif quantifier == '*': |
| type = 'std::vector<%s>' % type; |
| member_str += "%s %s;" % (type, var) |
| if comment is not None and comment.find('\n') == -1: |
| member_str += " // %s" % comment |
| member_str += "\n" |
| return member_str |
| |
| def string_for_constructor(opname, opcode, members): |
| # Default constructor |
| initializer = "Instruction(%s, %d)" % (opname, fixed_word_count(members)) |
| first = True |
| for type, var, quantifier, comment in members: |
| if quantifier == '?': |
| initializer += ", %s(nullptr)" % var |
| str = "%s() : %s {}" % (class_name(opname), initializer) |
| |
| # Constructor with values for members |
| if members == [] or (len(members) == 1 and members[0][0]=='IdResult'): |
| return str |
| nonOptionalOperandExists = False |
| for type, var, quantifier, comment in members: |
| if quantifier is None: |
| nonOptionalOperandExists = True |
| if not nonOptionalOperandExists: |
| return str |
| params = "" |
| initializer = "Instruction(%s, %d)" % (opname, fixed_word_count(members)) |
| first = True |
| for type, var, quantifier, comment in members: |
| if var != "mResult" and quantifier != '*': |
| initializer += ", " |
| if quantifier == '?': |
| initializer += "%s(nullptr)" % var |
| else: |
| if first: |
| first = False |
| else: |
| params += ", " |
| param = var[1:] # remove the prefix "m" |
| params += "%s %s" % (type, param) |
| initializer += "%s(%s)" % (var, param) |
| if params != "": |
| str += "\n %s(%s) :\n %s {}" % (class_name(opname), params, initializer) |
| str += "\n virtual ~%s() {}" % class_name(opname) |
| return str |
| |
| def string_for_serializer_body(opcode, members): |
| body = "setWordCount();\n" |
| body += " OS << mCodeAndCount;\n" |
| for type, var, quantifier, comment in members: |
| if quantifier == '?': |
| body += " if (%s!=nullptr) { OS << *%s; }\n" % (var, var) |
| elif quantifier == '*': |
| body += """ for (auto val : %s) { OS << val; }\n""" % var |
| else: |
| body += " OS << %s;\n" % var |
| body += " SerializeExtraOperands(OS);\n" |
| return body |
| |
| def string_for_deserializer_body(name, members): |
| body = "return DeserializeFirstWord(IS, %s)" % name |
| for type, var, quantifier, comment in members: |
| body += " &&\n " |
| if quantifier == '?': |
| body += "DeserializeOptionallyOne(IS, &%s)" % var |
| elif quantifier == '*': |
| body += "DeserializeZeroOrMoreOperands(IS, &%s)" % var |
| else: |
| body += "DeserializeExactlyOne(IS, &%s)" % var |
| body += " &&\n DeserializeExtraOperands(IS);\n" |
| return body |
| |
| def string_for_get_word_count(members): |
| str = """uint16_t getWordCount() const override { |
| uint16_t count = mFixedWordCount;\n""" |
| for type, var, quantifier, comment in members: |
| if quantifier == '?': |
| str += " if (%s) count += WordCount(*%s);\n" % (var, var) |
| elif quantifier == '*': |
| str += " if (!%s.empty()) count += WordCount(%s[0]) * %s.size();\n" % (var, var, var) |
| elif type == 'LiteralString': |
| str += " count += WordCount(%s) - 1;\n" % var |
| |
| str += """ count += mExtraOperands.size(); |
| return count; |
| }""" |
| return str |
| |
| def string_for_accept(): |
| return """ |
| void accept(IVisitor *v) override { v->visit(this); } |
| """ |
| |
| def has_result(members): |
| for type, val, quantifier, comment in members: |
| if type == 'IdResult': |
| return True |
| return False |
| |
| |
| def string_for_has_result(hasResult): |
| if hasResult: |
| retVal = "true" |
| else: |
| retVal = "false" |
| return "bool hasResult() const override { return %s; }" % retVal |
| |
| def string_for_get_all_refs(members): |
| str = """std::vector<const IdRef*> getAllIdRefs() const override { |
| std::vector<const IdRef*> ret = {""" |
| first = True |
| # TODO: what if references are in * operands? |
| for type, var, quantifier, comment in members: |
| if type == 'IdRef' or type == 'IdResultType' or type == 'IdMemorySemantics' or type == 'IdScope': |
| if quantifier == '*': |
| pass |
| else: |
| if first: |
| first = False |
| else: |
| str += ", " |
| if quantifier == '?': |
| str += "%s" % var |
| else: |
| str += "&%s" % var |
| str += """}; |
| """ |
| for type, var, quantifier, comment in members: |
| if type == 'IdRef' or type == 'IdResultType' or type == 'IdMemorySemantics' or type == 'IdScope': |
| if quantifier == '*': |
| str+=""" |
| for(const auto &ref : %s) { |
| ret.push_back(&ref); |
| } |
| """ % var |
| str += """ |
| return ret; |
| }""" |
| return str |
| |
| def string_for_get_set_id(hasResult): |
| if hasResult: |
| return """IdResult getId() const override { return mResult; } |
| void setId(IdResult id) override { mResult = id; }""" |
| else: |
| retVal = "0" |
| return """IdResult getId() const override { return 0; } |
| void setId(IdResult) override {}""" |
| |
| |
| def print_instruction_class(inst): |
| opname = inst['opname'] |
| opcode = inst['opcode'] |
| operands = inst.get('operands') |
| members = generate_member_list(operands) |
| hasResult = has_result(members) |
| print """class %s : public Instruction { |
| public: |
| %s |
| |
| void Serialize(OutputWordStream &OS) const override { |
| %s } |
| |
| bool DeserializeInternal(InputWordStream &IS) override { |
| %s } |
| |
| void accept(IVisitor *v) override { v->visit(this); } |
| |
| %s |
| |
| %s |
| |
| %s |
| |
| %s |
| %s}; |
| """ % (class_name(opname), |
| string_for_constructor(opname, opcode, members), |
| string_for_serializer_body(opcode, members), |
| string_for_deserializer_body(opname, members), |
| string_for_get_word_count(members), |
| string_for_has_result(hasResult), |
| string_for_get_set_id(hasResult), |
| string_for_get_all_refs(members), |
| string_for_members(opname, members)) |
| |
| def print_all_instruction_classes(insts): |
| for inst in insts: |
| print_instruction_class(inst) |
| |
| ################################################################################ |
| # |
| # Generate opcode enum |
| # |
| ################################################################################ |
| |
| def print_opcode_enum(insts): |
| print "enum OpCode {" |
| for inst in insts: |
| opname = inst['opname'] |
| opcode = inst['opcode'] |
| print " %s = %d," % (opname, opcode) |
| print "};" |
| |
| |
| |
| ################################################################################ |
| # |
| # Generate dispatching code |
| # |
| ################################################################################ |
| |
| def print_dispatches(insts): |
| for inst in insts: |
| opname = inst['opname'] |
| print "HANDLE_INSTRUCTION(%s,%s)" % (opname, class_name(opname)) |
| |
| def print_type_inst_dispatches(insts): |
| for inst in insts: |
| opname = inst['opname'] |
| if opname[:6] == "OpType": |
| print "HANDLE_INSTRUCTION(%s, %s)" % (opname, class_name(opname)) |
| |
| def print_const_inst_dispatches(insts): |
| for inst in insts: |
| opname = inst['opname'] |
| if opname[:10] == "OpConstant": |
| print "HANDLE_INSTRUCTION(%s, %s)" % (opname, class_name(opname)) |
| |
| def print_enum_dispatches(operand_kinds): |
| for ty in operand_kinds: |
| category = ty['category'] |
| if category == 'BitEnum' or category == 'ValueEnum': |
| print "HANDLE_ENUM(%s)" % ty['kind'] |
| |
| |
| |
| ################################################################################ |
| # |
| # main |
| # |
| ################################################################################ |
| |
| def main(): |
| try: |
| opts, args = getopt.getopt(sys.argv[2:],"h",["instructions=", |
| "types=", |
| "opcodes=", |
| "instruction_dispatches=", |
| "enum_dispatches=", |
| "type_inst_dispatches=", |
| "const_inst_dispatches=", |
| "factory_methods="]) |
| except getopt.GetoptError: |
| print sys.argv[0], '' |
| sys.exit(2) |
| |
| with open(sys.argv[1]) as grammar_file: |
| grammar = json.load(grammar_file) |
| |
| instructions = grammar['instructions'] |
| |
| for opt, arg in opts: |
| if opt == "--instructions": |
| generate_header_file(arg, True, lambda: print_all_instruction_classes(instructions)) |
| elif opt == "--types": |
| kinds = grammar['operand_kinds'] |
| generate_header_file(arg, True, lambda: print_type_definitions(kinds)) |
| elif opt == "--opcodes": |
| generate_header_file(arg, True, lambda: print_opcode_enum(instructions)) |
| elif opt == "--instruction_dispatches": |
| generate_header_file(arg, False, lambda: print_dispatches(instructions)) |
| elif opt == "--enum_dispatches": |
| kinds = grammar['operand_kinds'] |
| generate_header_file(arg, False, lambda: print_enum_dispatches(kinds)) |
| elif opt == "--type_inst_dispatches": |
| generate_header_file(arg, False, lambda: print_type_inst_dispatches(instructions)) |
| elif opt == "--const_inst_dispatches": |
| generate_header_file(arg, False, lambda: print_const_inst_dispatches(instructions)) |
| elif opt == "--factory_methods": |
| generate_header_file(arg, False, lambda: print_factory_methods(instructions)) |
| |
| if __name__ == '__main__': |
| main() |