develooper 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
 



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About