|
@@ -1,4 +1,30 @@
|
|
|
|
|
|
+# 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.
|
|
|
+
|
|
|
NOP_LITERAL = None
|
|
|
"""A literal that results in a nop during which execution may be interrupted
|
|
|
when yielded."""
|