123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929 |
- # NOTE: NOP_LITERAL abuses a mechanic of the modelverse kernel. Specifically,
- # whenever the `ModelverseKernel.execute_yields` method returns `None`, then
- # the server built around it takes that as a hint that an instruction's phase
- # has been completed. The server interrupts the kernel's thread of execution
- # when it remarks that an instruction has completed a phase (i.e., when `None`
- # is returned by `ModelverseKernel.execute_yields`) and proceeds to check for
- # input and output.
- #
- # In assembly language, a nop is usually used as a point at which a thread of
- # execution can be terminated. It follows from the paragraph above that what
- # the interpreter does is more or less equivalent to placing nops after every
- # instruction. It is worthwhile to remark that JIT-compiled code cannot rely
- # on the kernel to interrupt the thread of execution automatically during a
- # jitted function's execution -- jitted functions are considered equivalent
- # to a single instruction as far as the kernel is concerned. A nop will be
- # inserted _after_ the function call (if it is called from interpreted code)
- # but that does not suffice for IO, which needs the input/output processing
- # to be performed during function execution.
- #
- # For this reason, the JIT must strategically interrupt the execution of the
- # functions it compiles. In other words, it must insert its own nops.
- # Here comes the interesting part: a nop is equivalent to `yield None`,
- # because that will persuade `ModelverseKernel.execute_yields` to relay the
- # `None` marker value to the server, without terminating the current
- # generator.
- # Let's just agree to disagree on map vs list comprehensions, pylint.
- # pylint: disable=I0011,W0141
- import modelverse_jit.source_map as source_map
- NOP_LITERAL = None
- """A literal that results in a nop during which execution may be interrupted
- when yielded."""
- UNKNOWN_RESULT_TYPE = 'unknown'
- """The result type for instructions that produce either primitive values or
- references to nodes."""
- PRIMITIVE_RESULT_TYPE = 'primitive'
- """The result type for instructions that produce primitive values."""
- NODE_RESULT_TYPE = 'node'
- """The result type for instructions that produce references to nodes."""
- NO_RESULT_TYPE = 'nothing'
- """The result type for instructions that no result."""
- def result_type_intersection(first_type, second_type):
- """Computes the intersection of the given result types."""
- if first_type == second_type:
- return first_type
- elif ((first_type == PRIMITIVE_RESULT_TYPE and second_type == UNKNOWN_RESULT_TYPE)
- or (first_type == UNKNOWN_RESULT_TYPE and second_type == PRIMITIVE_RESULT_TYPE)):
- return PRIMITIVE_RESULT_TYPE
- elif ((first_type == NODE_RESULT_TYPE and second_type == UNKNOWN_RESULT_TYPE)
- or (first_type == UNKNOWN_RESULT_TYPE and second_type == NODE_RESULT_TYPE)):
- return NODE_RESULT_TYPE
- else:
- return NO_RESULT_TYPE
- def result_type_union(first_type, second_type):
- """Computes the union of the given result types."""
- if first_type == second_type:
- return first_type
- elif ((first_type == PRIMITIVE_RESULT_TYPE and second_type == NO_RESULT_TYPE)
- or (first_type == NO_RESULT_TYPE and second_type == PRIMITIVE_RESULT_TYPE)):
- return PRIMITIVE_RESULT_TYPE
- elif ((first_type == NODE_RESULT_TYPE and second_type == NO_RESULT_TYPE)
- or (first_type == NO_RESULT_TYPE and second_type == NODE_RESULT_TYPE)):
- return NODE_RESULT_TYPE
- else:
- return UNKNOWN_RESULT_TYPE
- class Instruction(object):
- """A base class for instructions. An instruction is essentially a syntax
- node that must first be defined, and can only then be used."""
- def __init__(self):
- self.result_type_cache = None
- self.has_definition_cache = None
- def get_result_type(self):
- """Gets this instruction's result type."""
- if self.result_type_cache is None:
- self.result_type_cache = self.get_result_type_impl()
- return self.result_type_cache
- def has_result(self):
- """Tells if this instruction computes a result."""
- return self.get_result_type() != NO_RESULT_TYPE
- def has_definition(self):
- """Tells if this instruction requires a definition."""
- if self.has_definition_cache is None:
- self.has_definition_cache = self.has_definition_impl()
- return self.has_definition_cache
- def get_result_type_impl(self):
- """Gets this instruction's result type."""
- return PRIMITIVE_RESULT_TYPE
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return True
- def get_result_name_override(self, code_generator):
- """Gets a value that overrides the code generator's result name for this
- instruction if it is not None."""
- return None
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return self.has_result()
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- if self.has_definition():
- raise NotImplementedError()
- else:
- code_generator.append_line('pass')
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- if self.has_result():
- return code_generator.get_result_name(self)
- else:
- return 'None'
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- raise NotImplementedError()
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- raise NotImplementedError()
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- return self
- def simplify(self):
- """Applies basic simplification to this instruction and all of its children."""
- # This fairly convoluted one-liner first simplifies all children, then creates
- # an instruction from the simplified children, and finally simplifies that top-level
- # instruction.
- return self.create([c.simplify() for c in self.get_children()]).simplify_node()
- def __str__(self):
- code_generator = PythonGenerator()
- self.generate_python_def(code_generator)
- return str(code_generator)
- class PythonGenerator(object):
- """Generates Python code from instructions."""
- def __init__(self, combine_state_definitions=True):
- self.code = []
- self.state_definitions = []
- self.state_definition_names = set()
- self.indentation_string = ' ' * 4
- self.indentation = 0
- self.result_name_dict = {}
- self.combine_state_definitions = combine_state_definitions
- self.source_map_builder = source_map.SourceMapBuilder()
- def append(self, text):
- """Appends the given string to this code generator."""
- self.flush_state_definitions()
- self.code.append(text)
- for _ in xrange(text.count('\n')):
- self.source_map_builder.append_line()
- def append_indentation(self):
- """Appends indentation to the code generator."""
- self.append(self.indentation_string * self.indentation)
- def append_line(self, line=None):
- """Appends the indentation string followed by the given string (if any)
- and a newline to the code generator."""
- self.append_indentation()
- if line is not None:
- self.append(line)
- self.append('\n')
- def increase_indentation(self):
- """Increases the code generator's indentation by one indent."""
- self.flush_state_definitions()
- self.indentation += 1
- def decrease_indentation(self):
- """Decreases the code generator's indentation by one indent."""
- self.flush_state_definitions()
- self.indentation -= 1
- def get_result_name(self, instruction, advised_name=None):
- """Gets the name of the given instruction's result variable."""
- if instruction not in self.result_name_dict:
- override_name = instruction.get_result_name_override(self)
- if override_name is not None:
- self.result_name_dict[instruction] = override_name
- elif advised_name is not None:
- self.result_name_dict[instruction] = advised_name
- else:
- self.result_name_dict[instruction] = \
- 'tmp' + str(len(self.result_name_dict))
- result = self.result_name_dict[instruction]
- if result in self.state_definition_names:
- # This might mean that we're trying to access a name that is
- # defined by the current block of state definitions. So we need
- # to flush the state definitions now.
- self.flush_state_definitions()
- return result
- def append_definition(self, lhs, rhs):
- """Defines the first instruction's result variable as the second
- instruction's result."""
- self.append_line(
- self.get_result_name(lhs) + ' = ' + rhs.generate_python_use(self))
- def append_move_definition(self, lhs, rhs):
- """First defines the second instruction, then defines the first
- instruction as the result of the second."""
- if rhs.has_definition():
- # Retrieve the result name for the lhs.
- lhs_result_name = self.get_result_name(lhs)
- # Encourage the rhs to take on the same result name as the lhs.
- rhs_result_name = self.get_result_name(rhs, lhs_result_name)
- # Generate the rhs' definition.
- rhs.generate_python_def(self)
- # Only perform an assignment if it's truly necessary.
- if lhs_result_name != rhs_result_name or not rhs.has_result_temporary():
- self.append_definition(lhs, rhs)
- else:
- self.append_definition(lhs, rhs)
- def append_state_definition(self, lhs, opcode, args):
- """Appends a definition that queries the modelverse state."""
- result_name = self.get_result_name(lhs)
- request_tuple = '(%s, [%s])' % (
- repr(opcode),
- ', '.join([arg_i.generate_python_use(self) for arg_i in args]))
- self.state_definitions.append((result_name, request_tuple))
- self.state_definition_names.add(result_name)
- if not self.combine_state_definitions:
- self.flush_state_definitions()
- def flush_state_definitions(self):
- """Flushes the list of state-access definitions to the generated code."""
- state_defs = self.state_definitions
- if len(state_defs) > 0:
- # Clear state_definitions _before_ calling append_line, because append_line
- # will call flush_state_definitions. Clearing state_definitions afterward
- # will result in infinite recursion.
- self.state_definitions = []
- self.state_definition_names = set()
- if len(state_defs) == 1:
- self.append_line('%s, = yield [%s]' % state_defs[0])
- else:
- self.append_line(
- "%s = yield [%s]" % (
- ', '.join([name for name, _ in state_defs]),
- ', '.join([def_val for _, def_val in state_defs])))
- def __str__(self):
- self.flush_state_definitions()
- return ''.join(self.code)
- class VoidInstruction(Instruction):
- """A base class for instructions that do not return a value."""
- def get_result_type_impl(self):
- """Gets this instruction's result type."""
- return NO_RESULT_TYPE
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- class EmptyInstruction(VoidInstruction):
- """Represents the empty instruction, which does nothing."""
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return False
- def __repr__(self):
- return "EmptyInstruction()"
- class SelectInstruction(Instruction):
- """Represents a select-instruction: an instruction that defines one of two
- child instructions, and sets its result to the defined child's result."""
- def __init__(self, condition, if_clause, else_clause):
- Instruction.__init__(self)
- self.condition = condition
- self.if_clause = if_clause
- self.else_clause = else_clause
- def get_result_type_impl(self):
- """Gets this instruction's result type."""
- return result_type_intersection(
- self.if_clause.get_result_type(),
- self.else_clause.get_result_type())
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- if isinstance(self.condition, LiteralInstruction):
- return self.if_clause if self.condition.literal else self.else_clause
- else:
- return SelectInstruction(self.condition, self.if_clause, self.else_clause)
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.condition, self.if_clause, self.else_clause]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- condition, if_clause, else_clause = new_children
- return SelectInstruction(condition, if_clause, else_clause)
- def generate_python_if(self, code_generator, is_elif=False):
- """Generates Python code for this instruction."""
- if_has_result = self.has_result()
- if self.condition.has_definition():
- self.condition.generate_python_def(code_generator)
- code_generator.append_line(
- ('elif ' if is_elif else 'if ') +
- self.condition.generate_python_use(code_generator) + ':')
- code_generator.increase_indentation()
- if if_has_result:
- code_generator.append_move_definition(self, self.if_clause)
- else:
- self.if_clause.generate_python_def(code_generator)
- code_generator.decrease_indentation()
- else_has_def = self.else_clause.has_definition()
- if else_has_def or if_has_result:
- if (isinstance(self.else_clause, SelectInstruction) and
- not self.else_clause.condition.has_definition()):
- self.else_clause.generate_python_if(code_generator, True)
- if if_has_result:
- code_generator.increase_indentation()
- code_generator.append_definition(self, self.else_clause)
- code_generator.decrease_indentation()
- else:
- code_generator.append_line('else:')
- code_generator.increase_indentation()
- if if_has_result:
- code_generator.append_move_definition(self, self.else_clause)
- else:
- self.else_clause.generate_python_def(code_generator)
- code_generator.decrease_indentation()
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- return self.generate_python_if(code_generator)
- def __repr__(self):
- return "SelectInstruction(%r, %r, %r)" % (self.condition, self.if_clause, self.else_clause)
- class SwitchInstruction(VoidInstruction):
- """An instruction that evaluates an instruction based on which condition matches."""
- def __init__(self, conditions_and_clauses):
- VoidInstruction.__init__(self)
- self.conditions_and_clauses = conditions_and_clauses
- assert not any([cond.has_definition() for cond, _ in self.conditions_and_clauses])
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- if len(self.conditions_and_clauses) == 0:
- return EmptyInstruction()
- elif len(self.conditions_and_clauses) == 1:
- cond, clause = next(iter(self.conditions_and_clauses))
- return SelectInstruction(cond, IgnoreInstruction(clause), EmptyInstruction())
- else:
- return self
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- results = []
- for cond, body in self.conditions_and_clauses:
- results.append(cond)
- results.append(body)
- return results
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- new_pairs = []
- for i in xrange(len(self.conditions_and_clauses)):
- new_pairs.append((new_children[2 * i], new_children[2 * i + 1]))
- return SwitchInstruction(new_pairs)
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if_keyword = 'if '
- for condition, clause in self.conditions_and_clauses:
- code_generator.append_line(
- if_keyword + condition.generate_python_use(code_generator) + ':')
- code_generator.increase_indentation()
- clause.generate_python_def(code_generator)
- code_generator.decrease_indentation()
- if_keyword = 'elif '
- def __repr__(self):
- return "SwitchInstruction(%r)" % self.conditions_and_clauses
- class ReturnInstruction(VoidInstruction):
- """Represents a return-instruction."""
- def __init__(self, value):
- VoidInstruction.__init__(self)
- self.value = value
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- value, = new_children
- return ReturnInstruction(value)
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- # If we have a return whose value is a call, then we can rewrite
- # that as a tail call and save us a stack frame.
- def rewrite_call(instruction):
- """Rewrites the given instruction in tail-call form, or returns
- None."""
- if isinstance(instruction, RunGeneratorFunctionInstruction):
- return RunTailGeneratorFunctionInstruction(*instruction.get_children())
- elif isinstance(instruction, CompoundInstruction):
- snd_rewritten = rewrite_call(instruction.second)
- if snd_rewritten is not None:
- return CompoundInstruction(instruction.first, snd_rewritten)
- return None
- rewritten_value = rewrite_call(self.value)
- if rewritten_value is None:
- return self
- else:
- # We don't even need to create a return here, because tail calls terminate
- # the current stack frame anyway.
- return rewritten_value
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if self.value.has_definition():
- self.value.generate_python_def(code_generator)
- code_generator.append_line(
- 'raise PrimitiveFinished(' +
- self.value.generate_python_use(code_generator) +
- ')')
- def __repr__(self):
- return "ReturnInstruction(%r)" % self.value
- class RaiseInstruction(VoidInstruction):
- """An instruction that raises an error."""
- def __init__(self, value):
- VoidInstruction.__init__(self)
- self.value = value
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- value, = new_children
- return RaiseInstruction(value)
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- self.value.generate_python_def(code_generator)
- code_generator.append_line(
- 'raise ' + self.value.generate_python_use(code_generator))
- def __repr__(self):
- return "RaiseInstruction(%r)" % self.value
- class CallInstruction(Instruction):
- """An instruction that performs a simple call."""
- def __init__(self, target, argument_list):
- Instruction.__init__(self)
- self.target = target
- self.argument_list = argument_list
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.target] + self.argument_list
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return CallInstruction(new_children[0], new_children[1:])
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if self.target.has_definition():
- self.target.generate_python_def(code_generator)
- for arg in self.argument_list:
- if arg.has_definition():
- arg.generate_python_def(code_generator)
- code_generator.append_line(
- '%s = %s(%s)' % (
- code_generator.get_result_name(self),
- self.target.generate_python_use(code_generator),
- ', '.join([arg.generate_python_use(code_generator) for arg in self.argument_list])))
- def __repr__(self):
- return "CallInstruction(%r, %r)" % (self.target, self.argument_list)
- class PrintInstruction(VoidInstruction):
- """An instruction that prints a value."""
- def __init__(self, argument):
- VoidInstruction.__init__(self)
- self.argument = argument
- assert isinstance(argument, Instruction)
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.argument]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- arg, = new_children
- return PrintInstruction(arg)
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if self.argument.has_definition():
- self.argument.generate_python_def(code_generator)
- code_generator.append_line(
- 'print(%s)' % (
- self.argument.generate_python_use(code_generator)))
- def __repr__(self):
- return "PrintInstruction(%r)" % self.argument
- class DebugInfoInstruction(Instruction):
- """An instruction that defines debug information for its child instruction."""
- def __init__(self, value, debug_info):
- Instruction.__init__(self)
- self.value = value
- self.debug_info = debug_info
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.value.has_definition()
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return self.value.has_result_temporary()
- def get_result_type_impl(self):
- """Gets this instruction's result type."""
- return self.value.get_result_type()
- def get_result_name_override(self, code_generator):
- """Gets a value that overrides the code generator's result name for this
- instruction if it is not None."""
- return self.value.get_result_name_override(code_generator)
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- arg, = new_children
- return DebugInfoInstruction(arg, self.debug_info)
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if self.has_definition():
- code_generator.source_map_builder.push_debug_info(self.debug_info)
- code_generator.append_move_definition(self, self.value)
- code_generator.source_map_builder.pop_debug_info()
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return self.value.generate_python_use(code_generator)
- def __repr__(self):
- return "DebugInfoInstruction(%r, %r)" % (self.value, self.debug_info)
- class BinaryInstruction(Instruction):
- """An instruction that performs a binary operation."""
- def __init__(self, lhs, operator, rhs):
- Instruction.__init__(self)
- self.lhs = lhs
- self.operator = operator
- self.rhs = rhs
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.lhs.has_definition() or self.rhs.has_definition()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.lhs, self.rhs]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- lhs, rhs, = new_children
- return BinaryInstruction(lhs, self.operator, rhs)
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- if isinstance(self.lhs, LiteralInstruction) and isinstance(self.rhs, LiteralInstruction):
- # TODO: there's probably a better way to do this than with eval.
- return LiteralInstruction(
- eval('%s %s %s' % (repr(self.lhs.literal), self.operator, repr(self.rhs.literal))))
- else:
- return self
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return '(%s %s %s)' % (
- self.lhs.generate_python_use(code_generator),
- self.operator,
- self.rhs.generate_python_use(code_generator))
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- lhs_has_def, rhs_has_def = self.lhs.has_definition(), self.rhs.has_definition()
- if lhs_has_def:
- self.lhs.generate_python_def(code_generator)
- if rhs_has_def:
- self.rhs.generate_python_def(code_generator)
- elif rhs_has_def:
- self.rhs.generate_python_def(code_generator)
- else:
- code_generator.append_line('pass')
- def __repr__(self):
- return "BinaryInstruction(%r, %r, %r)" % (self.lhs, self.operator, self.rhs)
- class UnaryInstruction(Instruction):
- """An instruction that performs a unary operation."""
- def __init__(self, operator, operand):
- Instruction.__init__(self)
- self.operator = operator
- self.operand = operand
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.operand.has_definition()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.operand]
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- operand, = new_children
- return UnaryInstruction(self.operator, operand)
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- if isinstance(self.operand, LiteralInstruction):
- # TODO: there's probably a better way to do this than with eval.
- return LiteralInstruction(
- eval('%s %s' % (self.operator, repr(self.operand.literal))))
- else:
- return self
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return '(%s %s)' % (
- self.operator,
- self.operand.generate_python_use(code_generator))
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- if self.operand.has_definition():
- self.operand.generate_python_def(code_generator)
- else:
- code_generator.append_line('pass')
- def __repr__(self):
- return "UnaryInstruction(%r, %r)" % (self.operator, self.operand)
- class LoopInstruction(VoidInstruction):
- """Represents a loop-instruction, which loops until broken."""
- def __init__(self, body):
- VoidInstruction.__init__(self)
- self.body = body
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.body]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- body, = new_children
- return LoopInstruction(body)
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- code_generator.append_line('while 1:')
- code_generator.increase_indentation()
- self.body.generate_python_def(code_generator)
- code_generator.decrease_indentation()
- def __repr__(self):
- return "LoopInstruction(%r)" % self.body
- class BreakInstruction(VoidInstruction):
- """Represents a break-instruction."""
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- code_generator.append_line('break')
- def __repr__(self):
- return "BreakInstruction()"
- class ContinueInstruction(VoidInstruction):
- """Represents a continue-instruction."""
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- code_generator.append_line('continue')
- def __repr__(self):
- return "ContinueInstruction()"
- class IgnoreInstruction(VoidInstruction):
- """Represents an instruction that evaluates its operand, and then discards
- the result."""
- def __init__(self, value):
- VoidInstruction.__init__(self)
- self.value = value
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.value.has_definition()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- value, = new_children
- return IgnoreInstruction(value)
- def simplify_node(self):
- """Applies basic simplification to this instruction and its children."""
- if not self.value.has_result():
- return self.value
- else:
- return self
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- self.value.generate_python_def(code_generator)
- def __repr__(self):
- return "IgnoreInstruction(%r)" % self.value
- class CompoundInstruction(Instruction):
- """Represents an instruction that evaluates two other instructions
- in order, and returns the second instruction's result."""
- def __init__(self, first, second):
- Instruction.__init__(self)
- self.first = first
- self.second = second
- def get_result_type_impl(self):
- """Gets this instruction's result type."""
- if self.second.has_result():
- return self.second.get_result_type()
- else:
- return self.first.get_result_type()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.first, self.second]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- first, second = new_children
- return CompoundInstruction(first, second)
- def simplify_node(self):
- """Applies basic simplification to this instruction and its children."""
- if not self.first.has_definition() and (
- not self.first.has_result() or self.second.has_result()):
- return self.second
- elif (not self.second.has_definition()) and (not self.second.has_result()):
- return self.first
- else:
- return self
- def generate_python_def(self, code_generator):
- """Generates Python code for this instruction."""
- if self.second.has_result():
- self.first.generate_python_def(code_generator)
- code_generator.append_move_definition(self, self.second)
- elif self.first.has_result():
- code_generator.append_move_definition(self, self.first)
- self.second.generate_python_def(code_generator)
- else:
- self.first.generate_python_def(code_generator)
- self.second.generate_python_def(code_generator)
- def __repr__(self):
- return "CompoundInstruction(%r, %r)" % (self.first, self.second)
- class LiteralInstruction(Instruction):
- """Represents an integer, floating-point, string or Boolean literal."""
- def __init__(self, literal):
- Instruction.__init__(self)
- self.literal = literal
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return False
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return repr(self.literal)
- def __repr__(self):
- return "LiteralInstruction(%r)" % self.literal
- class DictionaryLiteralInstruction(Instruction):
- """Constructs a dictionary literal."""
- def __init__(self, key_value_pairs):
- Instruction.__init__(self)
- self.key_value_pairs = key_value_pairs
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- results = []
- for key, val in self.key_value_pairs:
- results.append(key)
- results.append(val)
- return results
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- new_kv_pairs = []
- for i in xrange(len(self.key_value_pairs)):
- new_kv_pairs.append((new_children[2 * i], new_children[2 * i + 1]))
- return DictionaryLiteralInstruction(new_kv_pairs)
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return any(
- [key.has_definition() or val.has_definition()
- for key, val in self.key_value_pairs])
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def simplify(self):
- """Applies basic simplification to this instruction and its children."""
- return DictionaryLiteralInstruction(
- [(key.simplify(), val.simplify()) for key, val in self.key_value_pairs])
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- for key, val in self.key_value_pairs:
- if key.has_definition():
- key.generate_python_def(code_generator)
- if val.has_definition():
- val.generate_python_def(code_generator)
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return '{%s}' % ', '.join(
- ['%s : %s' % (
- key.generate_python_use(code_generator),
- val.generate_python_use(code_generator))
- for key, val in self.key_value_pairs])
- def __repr__(self):
- return "DictionaryLiteralInstruction(%r)" % self.key_value_pairs
- class ListSliceInstruction(Instruction):
- """Slices a list."""
- def __init__(self, seq, start, end, step):
- Instruction.__init__(self)
- self.seq = seq
- self.start = start
- self.end = end
- self.step = step
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- all_items = (self.seq, self.start, self.end, self.step)
- return [item for item in all_items if item is not None]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- # pylint: disable=I0011,E1120
- args = []
- i = 0
- for old_item in (self.seq, self.start, self.end, self.step):
- if old_item is None:
- args.append(None)
- else:
- args.append(new_children[i])
- i += 1
- assert len(new_children) == i
- assert len(args) == 4
- return ListSliceInstruction(*args)
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return any([item.has_definition() for item in self.get_children()])
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- for item in self.get_children():
- if item.has_definition():
- item.generate_python_def(code_generator)
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return '%s[%s:%s:%s]' % (
- self.seq.generate_python_use(code_generator),
- '' if self.start is None else self.start.generate_python_use(code_generator),
- '' if self.end is None else self.end.generate_python_use(code_generator),
- '' if self.step is None else self.step.generate_python_use(code_generator))
- def __repr__(self):
- return "ListSliceInstruction(%r, %r, %r, %r)" % (self.seq, self.start, self.end, self.step)
- class StateInstruction(Instruction):
- """An instruction that accesses the modelverse state."""
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return NODE_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- raise NotImplementedError()
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- raise NotImplementedError()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return self.get_arguments()
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return type(self)(*new_children)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- args = self.get_arguments()
- for arg_i in args:
- if arg_i.has_definition():
- arg_i.generate_python_def(code_generator)
- code_generator.append_state_definition(self, self.get_opcode(), args)
- def __repr__(self):
- return "StateInstruction(%s)" % ', '.join(map(repr, self.get_arguments()))
- class RunGeneratorFunctionInstruction(StateInstruction):
- """An instruction that runs a generator function."""
- def __init__(self, function, argument_dict, result_type=PRIMITIVE_RESULT_TYPE):
- StateInstruction.__init__(self)
- self.function = function
- self.argument_dict = argument_dict
- self.result_type_cache = result_type
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "CALL_KWARGS"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.function, self.argument_dict]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- func, arg_dict = new_children
- return RunGeneratorFunctionInstruction(func, arg_dict, self.get_result_type())
- class RunTailGeneratorFunctionInstruction(StateInstruction):
- """An instruction that runs a generator function."""
- def __init__(self, function, argument_dict, result_type=NO_RESULT_TYPE):
- StateInstruction.__init__(self)
- self.function = function
- self.argument_dict = argument_dict
- self.result_type_cache = result_type
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "TAIL_CALL_KWARGS"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.function, self.argument_dict]
- class RegisterDebugInfoInstruction(StateInstruction):
- """An instruction that sends a DEBUG_INFO request to the request handler."""
- def __init__(self, function_name, function_source_map, function_origin):
- StateInstruction.__init__(self)
- self.function_name = function_name
- self.function_source_map = function_source_map
- self.function_origin = function_origin
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return NO_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "DEBUG_INFO"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.function_name, self.function_source_map, self.function_origin]
- class VariableName(object):
- """A data structure that unifies names across instructions that access the
- same variable."""
- def __init__(self, name):
- self.name = name
- def get_result_name_override(self, _):
- """Gets a value that overrides the code generator's result name for this
- instruction if it is not None."""
- return self.name
- def __str__(self):
- return self.name
- def __repr__(self):
- return 'VariableName(%r)' % self.name
- class VariableInstruction(Instruction):
- """A base class for instructions that access variables."""
- def __init__(self, name):
- Instruction.__init__(self)
- if isinstance(name, str) or isinstance(name, unicode) or name is None:
- self.name = VariableName(name)
- else:
- self.name = name
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- raise NotImplementedError()
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- raise NotImplementedError()
- def get_result_name_override(self, code_generator):
- """Gets a value that overrides the code generator's result name for this
- instruction if it is not None."""
- return code_generator.get_result_name(self.name)
- class LocalInstruction(VariableInstruction):
- """A base class for instructions that access local variables."""
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- raise NotImplementedError()
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- raise NotImplementedError()
- def create_load(self):
- """Creates an instruction that loads the variable referenced by this instruction."""
- return LoadLocalInstruction(self.name)
- def create_store(self, value):
- """Creates an instruction that stores the given value in the variable referenced
- by this instruction."""
- return StoreLocalInstruction(self.name, value)
- class StoreLocalInstruction(LocalInstruction):
- """An instruction that stores a value in a local variable."""
- def __init__(self, name, value):
- LocalInstruction.__init__(self, name)
- self.value = value
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- val, = new_children
- return StoreLocalInstruction(self.name, val)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- code_generator.append_move_definition(self, self.value)
- def __repr__(self):
- return 'StoreLocalInstruction(%r, %r)' % (self.name, self.value)
- class TupleStoreLocalInstruction(VoidInstruction):
- """Assigns a number of values to a number of variables."""
- def __init__(self, name_value_pairs):
- VoidInstruction.__init__(self)
- self.name_value_pairs = name_value_pairs
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [val for _, val in self.name_value_pairs]
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- new_name_value_pairs = [
- (name, new_value) for (name, _), new_value in zip(self.name_value_pairs, new_children)]
- return TupleStoreLocalInstruction(new_name_value_pairs)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- tuple_lhs = []
- tuple_rhs = []
- for name, value in self.name_value_pairs:
- if isinstance(name, str) or isinstance(name, unicode) or name is None:
- variable = VariableName(name)
- else:
- variable = name
- # Retrieve the result name for the variable.
- var_result_name = code_generator.get_result_name(variable)
- # Encourage the value to take on the same result name as the variable.
- value_result_name = code_generator.get_result_name(value, var_result_name)
- if value.has_definition():
- # Generate the value' definition.
- value.generate_python_def(code_generator)
- # Only perform an assignment if it's truly necessary.
- if var_result_name != value_result_name or not value.has_result_temporary():
- tuple_lhs.append(var_result_name)
- tuple_rhs.append(value.generate_python_use(code_generator))
- if len(tuple_lhs) > 0:
- code_generator.append_line(
- '%s = %s' % (', '.join(tuple_lhs), ', '.join(tuple_rhs)))
- def __repr__(self):
- return 'TupleStoreLocalInstruction(%r)' % (self.name_value_pairs)
- class LoadLocalInstruction(LocalInstruction):
- """An instruction that loads a value from a local variable."""
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return False
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- def __repr__(self):
- return 'LoadLocalInstruction(%r)' % self.name
- class DefineFunctionInstruction(VariableInstruction):
- """An instruction that defines a function."""
- def __init__(self, name, parameter_list, body):
- VariableInstruction.__init__(self, name)
- self.parameter_list = parameter_list
- self.body = body
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.body]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- body, = new_children
- return DefineFunctionInstruction(self.name, self.parameter_list, body)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- code_generator.append_line('def %s(%s):' % (
- code_generator.get_result_name(self), ', '.join(self.parameter_list)))
- code_generator.increase_indentation()
- self.body.generate_python_def(code_generator)
- code_generator.decrease_indentation()
- def __repr__(self):
- return 'DefineFunctionInstruction(%r, %r, %r)' % (self.name, self.parameter_list, self.body)
- class LocalExistsInstruction(LocalInstruction):
- """An instruction that checks if a local variable exists."""
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return False
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return "'%s' in locals()" % self.get_result_name_override(code_generator)
- def __repr__(self):
- return 'LocalExistsInstruction(%r)' % self.name
- class LoadGlobalInstruction(VariableInstruction):
- """An instruction that loads a value from a global variable."""
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return False
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- def __repr__(self):
- return 'LoadGlobalInstruction(%r)' % self.name
- class StoreGlobalInstruction(VariableInstruction):
- """An instruction that assigns a value to a global variable."""
- def __init__(self, name, value):
- VariableInstruction.__init__(self, name)
- self.value = value
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- val, = new_children
- return StoreGlobalInstruction(self.name, val)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- code_generator.append_move_definition(self, self.value)
- def __repr__(self):
- return 'StoreGlobalInstruction(%r, %r)' % (self.name, self.value)
- class DeclareGlobalInstruction(VariableInstruction):
- """An instruction that declares a name as a global variable."""
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return []
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return NO_RESULT_TYPE
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- return self
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- code_generator.append_line('global %s' % self.name)
- def __repr__(self):
- return 'DeclareGlobalInstruction(%r)' % self.name
- class LoadIndexInstruction(Instruction):
- """An instruction that produces a value by indexing a specified expression with
- a given key."""
- def __init__(self, indexed, key):
- Instruction.__init__(self)
- self.indexed = indexed
- self.key = key
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.indexed.has_definition() or self.key.has_definition()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.indexed, self.key]
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- indexed, key = new_children
- return LoadIndexInstruction(indexed, key)
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return "%s[%s]" % (
- self.indexed.generate_python_use(code_generator),
- self.key.generate_python_use(code_generator))
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- indexed_has_def, key_has_def = self.indexed.has_definition(), self.key.has_definition()
- if indexed_has_def:
- self.indexed.generate_python_def(code_generator)
- if key_has_def:
- self.key.generate_python_def(code_generator)
- if not indexed_has_def and not key_has_def:
- code_generator.append_line('pass')
- def __repr__(self):
- return 'LoadIndexInstruction(%r, %r)' % (self.indexed, self.key)
- class LoadMemberInstruction(Instruction):
- """An instruction that produces a value by loading a member from a container."""
- def __init__(self, container, member_name):
- Instruction.__init__(self)
- self.container = container
- self.member_name = member_name
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return self.container.has_definition()
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.container]
- def has_result_temporary(self):
- """Tells if this instruction stores its result in a temporary."""
- return False
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- container, = new_children
- return LoadMemberInstruction(container, self.member_name)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- self.container.generate_python_def(code_generator)
- def generate_python_use(self, code_generator):
- """Generates a Python expression that retrieves this instruction's
- result. The expression is returned as a string."""
- return "%s.%s" % (
- self.container.generate_python_use(code_generator),
- self.member_name)
- def __repr__(self):
- return 'LoadMemberInstruction(%r, %r)' % (self.container, self.member_name)
- class StoreMemberInstruction(VoidInstruction):
- """An instruction that stores a value in a container member."""
- def __init__(self, container, member_name, value):
- VoidInstruction.__init__(self)
- self.container = container
- self.member_name = member_name
- self.value = value
- def has_definition_impl(self):
- """Tells if this instruction requires a definition."""
- return True
- def get_children(self):
- """Gets this instruction's sequence of child instructions."""
- return [self.container, self.value]
- def create(self, new_children):
- """Creates a new instruction of this type from the given sequence of child instructions."""
- container, value = new_children
- return StoreMemberInstruction(container, self.member_name, value)
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- if self.container.has_definition():
- self.container.generate_python_def(code_generator)
- code_generator.append_line('%s.%s = %s' % (
- self.container.generate_python_use(code_generator),
- self.member_name,
- self.value.generate_python_use(code_generator)))
- def __repr__(self):
- return 'StoreMemberInstruction(%r, %r, %r)' % (self.container, self.member_name, self.value)
- class NopInstruction(VoidInstruction):
- """A nop instruction, which allows for the kernel's thread of execution to be interrupted."""
- def generate_python_def(self, code_generator):
- """Generates a Python statement that executes this instruction.
- The statement is appended immediately to the code generator."""
- code_generator.append_line('yield %s' % repr(NOP_LITERAL))
- def __repr__(self):
- return 'NopInstruction()'
- class ReadValueInstruction(StateInstruction):
- """An instruction that reads a value from a node."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return PRIMITIVE_RESULT_TYPE
- def simplify_node(self):
- """Applies basic simplification to this instruction only."""
- if isinstance(self.node_id, CreateNodeWithValueInstruction):
- return self.node_id.value
- else:
- return self
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RV"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class ReadDictionaryValueInstruction(StateInstruction):
- """An instruction that reads a dictionary value."""
- def __init__(self, node_id, key):
- StateInstruction.__init__(self)
- self.node_id = node_id
- self.key = key
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RD"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id, self.key]
- class ReadDictionaryNodeInstruction(StateInstruction):
- """An instruction that reads a dictionary node."""
- def __init__(self, node_id, key):
- StateInstruction.__init__(self)
- self.node_id = node_id
- self.key = key
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RDN"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id, self.key]
- class ReadDictionaryEdgeInstruction(StateInstruction):
- """An instruction that reads a dictionary edge."""
- def __init__(self, node_id, key):
- StateInstruction.__init__(self)
- self.node_id = node_id
- self.key = key
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RDE"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id, self.key]
- class ReadDictionaryKeysInstruction(StateInstruction):
- """An instruction that reads all keys from a dictionary."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RDK"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class ReadEdgeInstruction(StateInstruction):
- """An instruction that reads an edge."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RE"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class ReadOutgoingEdgesInstruction(StateInstruction):
- """An instruction that reads all outgoing edges for a node."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return PRIMITIVE_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RO"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class ReadIncomingEdgesInstruction(StateInstruction):
- """An instruction that reads all incoming edges for a node."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return PRIMITIVE_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "RI"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class CreateNodeInstruction(StateInstruction):
- """An instruction that creates an empty node."""
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "CN"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return []
- class CreateNodeWithValueInstruction(StateInstruction):
- """An instruction that creates a node with a given value."""
- def __init__(self, value):
- StateInstruction.__init__(self)
- self.value = value
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "CNV"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.value]
- class CreateEdgeInstruction(StateInstruction):
- """An instruction that creates an edge."""
- def __init__(self, source_id, target_id):
- StateInstruction.__init__(self)
- self.source_id = source_id
- self.target_id = target_id
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "CE"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.source_id, self.target_id]
- class CreateDictionaryEdgeInstruction(StateInstruction):
- """An instruction that creates a dictionary edge."""
- def __init__(self, source_id, key, target_id):
- StateInstruction.__init__(self)
- self.source_id = source_id
- self.key = key
- self.target_id = target_id
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "CD"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.source_id, self.key, self.target_id]
- class DeleteNodeInstruction(StateInstruction):
- """An instruction that deletes a node."""
- def __init__(self, node_id):
- StateInstruction.__init__(self)
- self.node_id = node_id
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return NO_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "DN"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.node_id]
- class DeleteEdgeInstruction(StateInstruction):
- """An instruction that deletes an edge."""
- def __init__(self, edge_id):
- StateInstruction.__init__(self)
- self.edge_id = edge_id
- def get_result_type_impl(self):
- """Gets the type of value produced by this instruction."""
- return NO_RESULT_TYPE
- def get_opcode(self):
- """Gets the opcode for this state instruction."""
- return "DE"
- def get_arguments(self):
- """Gets this state instruction's argument list."""
- return [self.edge_id]
- def create_block(*statements):
- """Creates a block-statement from the given list of statements."""
- length = len(statements)
- if length == 0:
- return EmptyInstruction()
- elif length == 1:
- return statements[0]
- else:
- return CompoundInstruction(
- statements[0],
- create_block(*statements[1:]))
- def create_jit_call(
- target, named_arguments, kwargs,
- create_run_generator=RunGeneratorFunctionInstruction):
- """Creates a call that abides by the JIT's calling convention."""
- # A JIT call looks like this:
- #
- # target = ...
- # arg_dict = { ... }
- # arg_dict.update(kwargs)
- # result, = yield [("CALL_KWARGS", [target, arg_dict])]
- results = []
- if target.has_definition():
- target_tmp = StoreLocalInstruction(None, target)
- results.append(target_tmp)
- target = target_tmp.create_load()
- arg_dict = StoreLocalInstruction(
- None,
- DictionaryLiteralInstruction(
- [(LiteralInstruction(key), val) for key, val in named_arguments]))
- results.append(arg_dict)
- results.append(
- CallInstruction(
- LoadMemberInstruction(arg_dict.create_load(), 'update'),
- [kwargs]))
- return CompoundInstruction(
- create_block(*results),
- create_run_generator(
- target, arg_dict.create_load(), NODE_RESULT_TYPE))
- def evaluate_and_load(value):
- """Creates a statement that evaluates the given tree, and creates
- an expression that loads the result. These instructions are returned
- as a pair of trees."""
- if isinstance(value, (LoadLocalInstruction, LiteralInstruction)):
- return EmptyInstruction(), value
- else:
- store = StoreLocalInstruction(None, value)
- return IgnoreInstruction(store), store.create_load()
- def create_new_local_node(local_variable, connected_node, edge_variable=None):
- """Creates a local node that is the backing storage for a local variable.
- This node is connected to a given node to make sure it's not perceived
- as dead by the GC. The newly created node is stored in the given
- local variable. The edge's id can also optionally be stored in a variable."""
- local_store = StoreLocalInstruction(local_variable, CreateNodeInstruction())
- create_edge = CreateEdgeInstruction(connected_node, local_store.create_load())
- if edge_variable is not None:
- create_edge = StoreLocalInstruction(edge_variable, create_edge)
- return create_block(IgnoreInstruction(local_store), IgnoreInstruction(create_edge))
- def map_instruction_tree_top_down(function, instruction):
- """Applies the given mapping function to every instruction in the tree
- that has the given instruction as root. The map is applied in a top-down
- fashion."""
- mapped_instruction = function(instruction)
- return mapped_instruction.create(
- [map_instruction_tree_top_down(function, child)
- for child in mapped_instruction.get_children()])
- def map_and_simplify(function, instruction):
- """Applies the given mapping function to every instruction in the tree
- that has the given instruction as root, and simplifies it on-the-fly.
- The map is applied in a bottom-up fashion.
- This is at least as powerful as first mapping and then simplifying, as
- maps and simplifications are interspersed."""
- return function(
- instruction.create(
- [map_and_simplify(function, child)
- for child in instruction.get_children()])).simplify_node()
- def iterate_as_stack(instruction, stack_iterator):
- """Iterates over the given instruction and its children in the order in which temporaries are
- 'pushed' on and 'popped' from a virtual evaluation stack."""
- if isinstance(instruction, SelectInstruction):
- iterate_as_stack(instruction.condition, stack_iterator)
- stack_iterator.pop()
- if_iterator, else_iterator = stack_iterator.branch(), stack_iterator.branch()
- iterate_as_stack(instruction.if_clause, if_iterator)
- iterate_as_stack(instruction.else_clause, else_iterator)
- stack_iterator.merge(if_iterator, else_iterator)
- elif isinstance(instruction, CompoundInstruction):
- iterate_as_stack(instruction.first, stack_iterator)
- if instruction.second.has_result():
- stack_iterator.pop()
- iterate_as_stack(instruction.second, stack_iterator)
- if not instruction.second.has_result():
- stack_iterator.pop()
- else:
- children = instruction.get_children()
- for child in children:
- # Push all children onto the stack.
- iterate_as_stack(child, stack_iterator)
- stack_iterator.before_pop_args(instruction)
- for child in children:
- # Pop all children from the stack.
- stack_iterator.pop()
- # Push the instruction.
- stack_iterator.push(instruction)
- class StackIterator(object):
- """A base class for stack iterators."""
- def __init__(self, stack=None):
- self.stack = [] if stack is None else stack
- def before_pop_args(self, instruction):
- """Performs an action before the given instruction's arguments are popped."""
- pass
- def pop(self):
- """Pops an instruction from the stack."""
- self.stack.pop()
- def push(self, instruction):
- """Pushes an instruction onto the stack."""
- self.stack.append(set([instruction]))
- def branch(self):
- """Creates a copy of this stack iterator."""
- return self.create(self.copy_stack())
- def merge(self, *branches):
- """Sets this stack iterator's stack to the union of the given branches."""
- self.__init__(self.merge_stacks(*branches))
- def copy_stack(self):
- """Creates a copy of this stack iterator's stack."""
- return [set(vals) for vals in self.stack]
- def create(self, new_stack):
- """Creates a stack iterator from the given stack"""
- return type(self)(new_stack)
- def merge_stacks(self, *branches):
- """Computes the union of the stacks of the given branches."""
- results = None
- for branch in branches:
- if results is None:
- results = branch.copy_stack()
- else:
- assert len(branch.stack) == len(results)
- results = [set.union(*t) for t in zip(branch.stack, results)]
- return results
- def protect_temporaries_from_gc(instruction, connected_node, fast_jit_compat=False):
- """Protects temporaries from the garbage collector by connecting them to the given node."""
- # # The reasoning behind this function
- #
- # A nop instruction (`yield None`) may trigger the garbage collector, which will delete
- # unreachable ("dead") vertices and edges. Now take into account that a bare temporary node
- # is actually unreachable from the root node. The consequence is that temporary nodes
- # may be garbage-collected if a nop instruction is executed while they are on the evaluation
- # "stack." This is _never_ what we want.
- #
- # To counter this, we can connect temporary nodes to a node that is reachable from the root.
- # However, we only want to create edges between edges and a known reachable node if we really
- # have to, because creating edges incurs some overhead.
- #
- # We will create an edge between a temporary and the known reachable node if and only if the
- # temporary is on the "stack" when either a nop or a call instruction is executed.
- class GCStackIterator(StackIterator):
- """A stack iterator that detects which instructions might be at risk of getting garbage
- collected."""
- def __init__(self, stack=None, gc_temporaries=None):
- StackIterator.__init__(self, stack)
- self.gc_temporaries = set() if gc_temporaries is None else gc_temporaries
- def before_pop_args(self, instruction):
- """Performs an action before the given instruction's arguments are popped."""
- if fast_jit_compat and isinstance(instruction, DictionaryLiteralInstruction):
- for instruction_set in self.stack:
- self.gc_temporaries.update(instruction_set)
- def push(self, instruction):
- """Pushes an instruction onto the stack."""
- if isinstance(instruction, (
- NopInstruction,
- RunGeneratorFunctionInstruction,
- RunTailGeneratorFunctionInstruction)):
- # All values currently on the stack are at risk. Mark them as such.
- for instruction_set in self.stack:
- self.gc_temporaries.update(instruction_set)
- # Proceed.
- StackIterator.push(self, instruction)
- def merge(self, *branches):
- """Sets this stack iterator's stack to the union of the given branches."""
- self.__init__(
- self.merge_stacks(*branches),
- set.union(*[br.gc_temporaries for br in branches]))
- def create(self, new_stack):
- """Creates a stack iterator from the given stack"""
- return GCStackIterator(new_stack, self.gc_temporaries)
- # Find out which instructions are at risk.
- gc_iterator = GCStackIterator()
- iterate_as_stack(instruction, gc_iterator)
- # These temporaries need to be protected from the GC.
- gc_temporaries = gc_iterator.gc_temporaries
- def protect_result(instruction):
- """Protects the given instruction's (temporary) result."""
- if instruction in gc_temporaries and instruction.get_result_type() == NODE_RESULT_TYPE:
- gc_temporaries.remove(instruction)
- store_instr = StoreLocalInstruction(None, instruction)
- return CompoundInstruction(
- store_instr,
- CompoundInstruction(
- SelectInstruction(
- BinaryInstruction(
- store_instr.create_load(), 'is not', LiteralInstruction(None)),
- CreateEdgeInstruction(connected_node, store_instr.create_load()),
- EmptyInstruction()),
- store_instr.create_load()))
- else:
- return instruction
- return map_instruction_tree_top_down(protect_result, instruction)
|