Front page | perl.cvs.parrot |
Postings from December 2008
[svn:parrot] r33838 - trunk/docs/book
From:
Whiteknight
Date:
December 12, 2008 10:26
Subject:
[svn:parrot] r33838 - trunk/docs/book
Message ID:
20081212182631.5318CCB9AF@x12.develooper.com
Author: Whiteknight
Date: Fri Dec 12 10:26:30 2008
New Revision: 33838
Modified:
trunk/docs/book/ch12_opcodes.pod
Log:
[Book] Some updates to chapter 12 and some code examples to illustrate how some of the runcores operate.
Modified: trunk/docs/book/ch12_opcodes.pod
==============================================================================
--- trunk/docs/book/ch12_opcodes.pod (original)
+++ trunk/docs/book/ch12_opcodes.pod Fri Dec 12 10:26:30 2008
@@ -5,7 +5,7 @@
Z<CHP-11>
The smallest executable component is not the compilation unit or even
-the subroutine, but is in fact the opcode. Opcodes in PASM, like opcodes
+the subroutine, but is actually the opcode. Opcodes in PASM, like opcodes
in other assembly languages, are individual instructions that implement
low-level operations in Parrot N<In the world of microprocessors, the
word "opcode" typically refers to the numeric identifier for each
@@ -32,10 +32,13 @@
typical software maintenance and analysis tasks. We'll talk about all
of these throughout the chapter.
-Different runcores, because of the way they are structured, require the
-opcodes to be compiled into different forms. Because of this,
-understanding opcodes first requires an understanding of the Parrot
-runcores.
+Runcores must pass execution to each opcode in the incoming bytecode
+stream. This is called I<dispatching> the opcodes. Because the different
+runcores are structured in different ways, the opcodes themselves must
+be formated differently. The opcode compiler compiles opcodes into a
+number of separate formats, depending on what runcores are included in
+the compiled Parrot. Because of this, understanding opcodes first
+requires an understanding of the Parrot runcores.
=head3 Types of Runcores
@@ -56,14 +59,55 @@
properly in bounds, and not somewhere random in memory. Because of this
modular approach where opcodes are treated as separate executable
entities many other runcores, especially diagnostic and maintenance
-cores are based on this design.
+cores are based on this design. The program counter C<pc> is the current
+index into the bytecode stream. Here is a pseudocode representation for
+how the slow core works:
+
+ while(1) {
+ pc = NEXT_OPCODE;
+ if(pc < LOW_BOUND || pc > HIGH_BOUND)
+ throw exception;
+ DISPATCH_OPCODE(pc);
+ UPDATE_INTERPRETER();
+ }
=item* Fast Core
The fast core is a bare-bones core that doesn't do any of the
bounds-checking or context updating that the slow core does. The fast
core is the way Parrot should run, and is used to find and debug places
-where execution strays outside of it's normal bounds.
+where execution strays outside of it's normal bounds. In pseudocode, the
+fast core is very much like the slow core except it doesn't do the bounds
+checking between each instruction, and doesn't update the interpreter's
+current context for each dispatch.
+
+ while(1) {
+ pc = NEXT_OPCODE;
+ DISPATCH_OPCODE(pc);
+ }
+
+=item* Switch Core
+
+As it's name implies, the switch core uses a gigantic C C<switch / case>
+structure to execute opcodes. Here's a brief example of how this
+architecture works:
+
+ for( ; ; current_opcode++) {
+ switch(*current_opcode) {
+ case opcode_1:
+ ...
+ case opcode_2:
+ ...
+ case opcode_3:
+ ...
+ }
+ }
+
+This is quite a fast architecture for dispatching opcodes because it all
+happens within a single function. The only operations performed between
+opcodes is a jump back to the top of the loop, incrementing the opcode
+pointer, dereferencing the opcode pointer, and then a jump to the C<case>
+statement for the next opcode.
=item* Computed Goto Core
@@ -78,6 +122,44 @@
As was mentioned earlier, not all compilers support computed goto, which
means that this core will not be built on platforms that don't support it.
+However, it's still an interesting topic to study so we will look at it
+briefly here. For compilers that support it, computed goto labels are
+C<void **> values. In the computed goto core, all the labels represent
+different opcodes, so they are stored in an array:
+
+ void **my_labels[] = {
+ &&label1,
+ &&label2,
+ &&label3
+ };
+
+ label1:
+ ...
+ label2:
+ ...
+ label3:
+ ...
+
+Jumping to one of these labels is done with a command like this:
+
+ goto my_labels[opcode_number];
+
+Actually, opcodes are pointed to by an C<opcode_t *> pointer, and all
+opcodes are stored sequentially in memory, so the actual jump in the
+computed goto core must increment the pointer and then jump to the new
+version. In C it looks something like this:
+
+ goto my_labels[*(current_opcode += 1)];
+
+Each opcode is a label, and at the end of each opcode an instruction like
+this is performed to move to the next opcode in series, or else some
+kind of control flow occurs that moves it to a non-sequential location:
+
+ goto my_lables[*(current_opcode = destination)];
+
+These are simplifications on what really happens in this core, because
+the actual code has been optimized quite a bit from what has been
+presented here.
=item* Precomputed Goto Core
@@ -90,7 +172,18 @@
The profiling core analyzes the performance of Parrot, and helps to
determine where bottlenecks and trouble spots are in the programs that
-run on top of Parrot.
+run on top of Parrot. When Parrot calls a PIR subroutine it sets up the
+environment, allocates storage for the passed parameters and the return
+values, passes the parameters, and calls a new runcore to execute it. To
+calculate the amount of time that each subroutine takes, we need to
+measure the amount of time spent in each runcore from the time the core
+begins to the time the core executes. The profiling core does exactly
+this, acting very similarly to a slow core but also measuring the amount
+of time it takes for the core to complete. The tracing core actually
+keeps track of a few additional values, including the number of GC cycles
+run while in the subroutine, the number of each opcode called and the
+number of calls to each subroutine made. All this information is helpfully
+printed to the STDERR output for later analysis.
=item* GC Debug Core
-
[svn:parrot] r33838 - trunk/docs/book
by Whiteknight