cvs commit: ponie Roadmap

[email protected] (Nicholas Clark) 4 Apr 2005 14:35:36 -0000
Newsgroups perl.ponie.changes
Message-ID <[email protected]>
cvsuser     05/04/04 07:35:36

  Added:       .        Roadmap
  Log:
  Roadmap, iteration 2.
  
  Revision  Changes    Path
  1.1                  ponie/Roadmap
  
  Index: Roadmap
  ===================================================================
  =head1 Ponie Roadmap
  
  The purpose of Ponie is to provide source compatibility between Perl
  extensions written in C<C> and C<XS> with Parrot, and thereby allow many
  important existing CPAN modules to be used unchanged with Perl 6.
  
  In terms of implementation, Ponie is a large refactoring exercise. It
  involves taking a copy of the existing Perl 5 code base, and making
  incremental changes that gradually re-write it to use Parrot, ensuring that
  at each step nothing external changes. It's not about adding new features to
  Perl 5, and there a few tasks suitable for volunteers who do not already
  understand the C<C> programming language and the Perl 5 core.
  
  If you want to help, but you don't know C<C>, the best way contribute
  is to download Ponie from CVS, build and install it, and try using it to build
  and run the CPAN modules and scripts you already use. If everything works as
  expected, great, and please report your glad tidings to [email protected].
  If things don't work, please report what details of the problems (what OS,
  what modules, what went wrong) to [email protected].
  
  Another good way to contribute to the success of Ponie is to join the Phalanx
  project ( http://qa.perl.org/phalanx/ ). It aims to improve the regression
  tests of CPAN modules, which in turn will improve the testing of Ponie. A
  third good way is to contribute to Parrot, by working to completing the
  Parrot features that Ponie will need in the near future.
  
  =head2 Current state
  
  The current state is that we have Parrot embedded in Perl. Perl data structures
  have been completely hidden behind PMCs, and Perl access macros have all been
  replaced by functions that call methods on the PMCs. The internal data layout
  of the data structures can be changed at will. The location of IV, NV and RV
  has been changed, to demonstrate that all still works.
  
  Structure is currently
  
      +-----+
      | PMC |
      +-----+
         |     +---------+
         +-----| SV head | (including Perl's reference count)
               +---------+
                    |       +---------+
                    +-------| SV body |
                            +---------+
  
  
  All PMCs are registered with Parrot as external PMCs, to stop them being
  garbage collected. Perl is still doing full reference counting, and PMCs
  are unregistered at the point where Perl would free the SV's storage.
  
  [strictly actually there's a hack here - they're freed at scope end, because
  it seems that some parts of the Perl core manage to hold onto pointers to SVs
  that have been marked as free, which works because the core never actually
  calls free() on the RAM. Full stack walking and internal Perl GC would solve
  this]
  
  Currently only one PMC type is used, and all SV type conversion done via
  SvUPGRADE() uses the existing code in Perl_sv_upgrade, which allocates a
  new body and frees the old.
  
  =head2 Overview of where next
  
  The roadmap is broadly
  
  =over 4
  
  =item 1
  
  Progressively refactor all of the perl data structures to be PMCs. Do this by
  
  =over
  
  =item 1
  
  Moving the existing perl code inside PMCs, and the existing perl data
  structures behind PMCs, providing/changing perl 5 wrappers to keep the
  existing calling conventions working.
  
  =item 2
  
  Refactoring the code/structures into something that feels parrot native.
  
  =back
  
  =item 2
  
  Perl 5's type polymorphism is done with code in the ops. Parrot's is done
  with code in the PMCs. Move the complexity from the op code into appropriate
  PMC methods
  
  =item 3
  
  Perl 5 provides special features and behaviour, such as C<tie>, overloading,
  C<%ENV> and C<%SIG>, using "magic". Parrot provides this by using custom
  vtables and derived PMC types. Replace all use of magic with Parrot
  implementations.
  
  =item 4
  
  At this point we have a Perl interpreter which is looking much more like
  Parrot, using Parrot's data structures for storage, and having much simpler,
  Parrot-like ops. So we can convert the Perl 5 ops to Parrot ops, and migrate
  the Perl interpreter over from using its runloop to using the Parrot runloop.
  Probably generate the Parrot ops by walking the Perl 5 optree using a custom
  C<B> module. This would cost in startup time each run, but is likely to be the
  fastest way to get something functional.
  
  =back
  
  Each major item requires the previous major item to be completed first.
  
  This gives a Ponie that compiles Perl 5 code to Parrot bytecode, runs this
  bytecode in Parrot, allows existing CPAN modules to be compiled and linked
  to it, and enables calls from Parrot bytecode into the CPAN modules. The
  relatively slow compile speed for Perl 5 code is not a big problem as the
  generated bytecode can be saved out to disk. With this we attain the goals
  of the Ponie project - CPAN modules can be used directly from Perl 6.
  
  There are still improvements that could be made. The Perl 5 parser could be
  modified to emit Parrot bytecode directly. Larry Wall is working on changes
  to the Perl 5 parser codebase to emit Perl 6 code, and this work may provide
  a good foundation for emitting Parrot bytecode. Ponie would still be using
  the Perl 5 regexp engine, and the Perl 5 IO system. Both could be migrated to
  their Parrot equivalents.
  
  =head2 Detailed plans
  
  The detailed plans tend to be subject to change, because typically at some
  point part way working through the first item, you discover one of
  
  =over 4
  
  =item *
  
  there's a task that you overlooked that needs doing first
  
  =item *
  
  there's an unwarranted assumption in the Perl 5 core that has just turned into
  a bug and therefore now needs fixing. (Cue wild goose chase)
  
  =item *
  
  it becomes clear that this task is actually better done after one of the
  subsequent tasks. So the list is re-ordered and that gets started
  
  =item *
  
  there's something buggy or missing in Parrot, which needs fixing first.
  
  =back
  
  Currently the detailed tasks seem to depend closely on each other, and don't
  appear to be parallelisable, so it's tricky to find ways for a second person
  to help out by working on the Ponie codebase itself.
  
  =head3 Refactoring the Data structures
  
  Perl 5 contains an enormous amount of logic to perform data type conversions,
  spread across many functions in several files. For Ponie, this functionality
  needs to be migrated inside PMCs. Perl 5 uses reference counting to manage
  memory, with special casing to avoid needing to reference count items on its
  stack. Ponie needs to make Perl 5's memory management work with Parrot's
  garbage collection scheme.
  
  =over 4
  
  =item *
  
  Move the scalar body upgrade code from C<sv_upgrade> in C<sv.c> into the
  Perl 5 PMC. This encapsulates conversions between different Perl 5 data
  types, allowing the internal data representation to be changed as needed,
  and gives Ponie the flexibility to add more types. At this point Ponie will
  still only be using one PMC type, and making an explicit method call into
  the PMC.
  
  2 days
  
  =item *
  
  With the upgrade code inside PMCs, Ponie can be changed to make the public
  API call C<SvUPGRADE()> use the Parrot morph interface. Different Perl
  types will be in distinct PMC types (even if the implementation is currently
  identical). This will make the Perl data types start to behave like any
  other data types in Parrot, which is needed for interoperability with other
  languages including Perl 6.
  
  3 days
  
  =item *
  
  Migrate the number/string conversion code into the PMC.
  
  5 days
  
  =item *
  
  Make the C<PMC_get_intval>, C<PMC_getnumval> etc calls functional
  
  3 days
  
  =item *
  
  Currently Ponie is maintaining the Perl 5 reference count inside each PMC's
  data structures. Parts of the Perl 5 code, mostly involved in cleanup,
  currently rely on knowing a scalar's reference count. Parrot provides a
  way to register PMCs that live outside of its root set, which Ponie uses to
  register all its PMCs. Parrot's registration is actually holds a registration
  count which is a reference count, but its API doesn't allow access to this
  count. As this is internal information Ponie shouldn't really be allowed access
  to this, so it seems better that Ponie should track its own hash of PMCs.
  This also means that Ponie can iterate over all its "reference counted" PMCs,
  which I have a hunch will become useful at some point. This hash gets
  registered with Parrot, which makes all of Ponie's PMCs visible to Parrot's
  GC.
  
  This effectively moves the reference count out of each PMC's storage, which
  is needed for the next step.
  
  3 days
  
  =item *
  
  The PMCs still have the Perl 5 head/body structure internally. All locations
  are read by function calls, so it is possible to rearrange structures
  internally from the Perl 5 layout, without affecting Perl 5 code. (In fact,
  this has already been done). This head/body isn't using PMC storage
  effectively, and is particularly wasteful for simple types such as integers
  and strings. At this point it is possible to refactor the PMC storage layout
  to remove the head/body.
  
  In particular, given that the reference count is no longer stored in the PMC,
  it allows floating point values (NVs) to be stored in an unextended PMC
  structure, as there is not space in the PMC union for an integer reference
  count simultaneously with a floating point value. At first sight the SV
  flags entry may seem a problem for the smaller types, but as access to it is
  via a function call, it is possible to make the default PMC type for floating
  point assume set flags (NOK,pNOK) and morph the scalar to a larger type if
  any code requests the flags value read/write.
  
  4 days
  
  =item *
  
  Replace the complex SV copying code in C<sv_setsv> with copying code
  encapsulated within the PMC.
  
  5 days
  
  =item *
  
  Refactor the core to use C<*_set> macro variants when setting scalar values.
  
      /* From */
      SvNVX(sv) = 42;
      
      /* To   */
      SvNV_set(sv, 42);
  
  This will allow the non-C<_set> variants to be defined as read only within
  the core (and any XS module that requests this), which will allow greater
  reading efficiency (no need to pass a pointer to dereference), and permit
  tighter PMC data storage. (No need to store values that can be faked)
  
  This can be done in parallel with the SvPVX_readonly() task.
  
  5 days, although this may already be done for free if Dave is able to implement
  iCow.
  
  =item *
  
  Make read only look ups such as C<SvNV()> call the appropriate PMC method.
  
  2 days
  
  =item *
  
  Make calls such as C<sv_setnv> call the appropriate PMC method.
  
  A distinction needs to be made between value like calls, such as C<sv_setnv>
  and C<SvNV>, and internals-aware direct calls, such as C<SvNVX_set> and
  C<SvNVX>. The former expect magic to be called for them, the latter expect
  direct access and will handle magic themselves.
  
  4 days
  
  =item *
  
  Refactor the core to use a new C<SvPVX_readonly()> macro when requesting
  a pointer to a scalar's string that isn't going to be modified. Consider
  whether this also shouldn't return a C<'\0'> terminated string, or whether
  to introduce even more variants.
  
  Currently if C<SvPVX()> is called it returns a C<char *> pointer direct into
  the scalar's buffer, and the caller is free to alter the buffers contents.
  Most callers don't do this, but the implementation can't know this. By
  making a readonly variant, it frees up the implementation to avoid expensive
  read-write emulation whenever possible. (see later).
  
  5 days, although this may already be done for free if Dave is able to implement
  iCow.
  
  =item *
  
  Review the situation.
  
  At this point all the Perl datatypes should be fully contained within the PMC
  code. All public Parrot APIs should work on the PMCs, and all Ponie code
  should be calling those Parrot APIs rather than going direct. With the exception
  of magic, all private access to data structures should be via PMC get/set
  methods, with no assumptions about structure layout or storage.
  
  1 day
  
  =item *
  
  Refactor the Perl core code to avoid using reference counts. Arthur and I
  think that this is possible, and Dave suspects that there may be speed
  benefits from removing all the special case code that deals with the stack.
  
  This involves
  
  =over 4
  
  =item 1
  
  Making Parrot's GC walk the Perl 5 C stack.
  
  1 day
  
  =item 2
  
  Registering PMCs held in Perl globals.
  
  2 days
  
  =item 3
  
  Amending the reference, array and hash code to properly mark the contained
  PMCs as "in use".
  
  2 days
  
  =item 4
  
  Replacing C<SvREFCNT_inc()> and C<SvREFCNT_dec()> macros with null
  operations inside the core. (Unaware XS code would still use them)
  
  5 minutes
  
  =item 5
  
  Experimenting to find out how much of the special case code for the Perl stack
  and C<@_> can be removed.
  
  3 days
  
  =item 6
  
  Calling a high priority DOD run at every freetmps point. (Roughly every
  semicolon)
  
  2 days
  
  =back
  
  This change is risky, but we suspect it's the only way to get destruction
  right when Ponie's aggregate PMCs are passed out to unsuspecting Parrot
  code.
  
  =item *
  
  Refactor stashes to be a derived type of hashes. This means that Perl's hashes
  don't need to waste space storing the extra information needed by stashes.
  
  1 day
  
  =item *
  
  Refactor restricted hashes to be a derived type of hashes.
  
  1 day
  
  =item *
  
  With proper GC in place it becomes much easier to replace the implementation
  of Perl 5's hashes and arrays with types derived from the proper Parrot
  array and hash PMCs. This brings interoperability and code reuse. Removing
  the special cases of stashes and restricted hashes makes the implementation
  simpler.
  
  It's not clear whether pads need to be split out from arrays at this point,
  and the underlying code kept constant until such time as the pad code is
  replaced by Parrot's lexicals.
  
  5 days. Plus 3 for Pads if needed.
  
  =item *
  
  Replace the Perl 5 string storage with Parrot native storage. This makes
  Perl string scalars behave far more like any other Parrot type, which buys
  portability.
  
  It also buys efficiency, because Parrot can do copy-on-write for strings
  and even substrings.
  
  To provide C<SvPVX()> compatibility with the Perl 5 API, make the
  implementation copy the PMC's string contents to a temporary read-write
  buffer if called, and push a function onto statement exit that will write
  the buffer's contents back to the underlying PMC string. While the temporary
  buffer exists, its contents are master. (This may mean changing the PMC's
  vtable for the duration). This way the PMC behaves as if it's storing data
  in the Perl structure, without actually suffering the inefficiencies.
  
  To be useful this task depends on the readonly PVX being implemented.
  
  5 days
  
  =back
  
  65 days, plus 3 if Pads need migrating.
  
  =head3 Migrating complexity from Ops to PMCs
  
  This is probably parallelisable, at least at the level of working on groups of
  ops, such as numeric ops, string ops, array ops, IO ops, logic ops, I<etc>
  
  "Complexity" manifests itself as lots of code branches based on SV type, or
  op flags. Ideally in a Parrot world, all of this would become vtable methods
  called on different PMC types, or concealed inside the black box of PMC code.
  The problem with the Perl 5 approach is that it makes maintenance hard (logic
  is spread out in many places) and adding Perl 5 types nearly impossible. In
  particular creating derived type with small behaviour changes isn't viable.
  Perl 5 allows behaviour variations by using the "magic" system, which hangs
  extra information from the SV, but this approach means that all code must
  explicitly check for magic and call it as appropriate (once and at the right
  point), which is slower and potentially error-prone.
  
  Complexity is
  
  =over 4
  
  =item *
  
  Polymorphic integer/numeric/reference code in arithmetic operators. There are
  30 numeric operators with overloading, so if each has polymorphic code to
  migrate at half a day each, that's 15 days.
  
  =item *
  
  UTF8-or-not code branches. There are 21 string operators listed in perlfunc.
  If each takes half a day to tidy up, that's 11 days.
  
  =item *
  
  Code such as C<pp_rv2av> which switches based on SV type. There are 81
  instances of C<SvTYPE()> in the PP functions, so if each is 2 hours that's 20
  days.
  
  =back
  
  46 days.
  
  =head3 Migrating from Magic to PMCs
  
  This is probably parallelisable, at least in terms of different people being
  able to independently work on
  
  =over 4
  
  =item *
  
  C<tie>
  
  =item *
  
  Overloading
  
  =item *
  
  C<%ENV>
  
  =item *
  
  C<%SIG>
  
  =item
  
  C<pos>
  
  =back
  
  C<pos>, C<%ENV> and C<%SIG> are probably quite simple (1 day's work each?).
  
  =head4 tie
  
  Replacing C<tie> involves providing new vtables for the relevant PMC types
  which call back to the correct Perl methods for the operation concerned. Perl
  5 has 4 sorts of C<tie>ing. It's probably best to tackle in order of
  complexity, starting with the simplest, tied scalars. The C<TIE*> and C<UNTIE>
  methods don't need vtable support.
  
  =over 4
  
  =item *
  
  Scalars
  
  C<TIESCALAR>, C<FETCH>, C<STORE>, C<UNTIE>.
  
  1 week for creating the method call framework, and .5 day per method?
  
  =item *
  
  Hashes
  
  C<TIEHASH>, C<FETCH>, C<STORE>, C<EXISTS>, C<DELETE>, C<CLEAR>, C<FIRSTKEY>,
  C<NEXTKEY>, C<SCALAR> and C<UNTIE>
  
  6 half days for the 6 simple methods. 3 days for C<FIRSTKEY> and C<NEXTKEY>
  to get integration with Parrot's iterators.
  
  =item *
  
  Arrays
  
  C<TIEARRAY>, C<FETCH>, C<STORE>, C<FETCHSIZE>, C<STORESIZE>, C<UNTIE>, C<POP>,
  C<PUSH>, C<SHIFT>, C<UNSHIFT>, C<SPLICE>, C<DELETE>, C<EXISTS> and C<EXTEND>.
  
  2 days for C<FETCH> to C<STORESIZE> and C<EXTEND>. 1 day each for C<DELETE>
  and C<EXISTS> as it's not clear how well these map to Parrot vtable methods.
  1 week for the other 5.
  
  =item *
  
  File handles
  
  C<TIEHANDLE>, C<PRINT>, C<PRINTF>, C<WRITE>, C<READLINE>, C<GETC>, C<READ>,
  C<CLOSE>, C<UNTIE>, C<OPEN>, C<EOF>, C<FILENO>, C<SEEK> and C<TELL>
  
  12 methods. Initially Ponie won't be using Parrot's IO, so these from Parrot's
  perspective these will just be opaque named methods that only Ponie calls to,
  and only Ponie is called by. So 0 days - no work needed here.
  
  =back
  
  =head4 Overloading
  
  Operator categories are taken from Perl's F<overload.pm>. It includes "int"
  with transcendental operations, so I will too.
  
  Perl 5's overloading is a complex interaction between C macros in all the
  pertinent operators, C code in F<mg.c> and F<gv.c>, and Perl code in
  F<overload.pm>. I estimate that it will take 1 week to get a reasonable
  understanding of how the code works, and where to modify it, before starting
  on anything specific.
  
  =over 4
  
  =item * Arithmetic operations
  
      "+", "+=", "-", "-=", "*", "*=", "/", "/=", "%", "%=",
      "**", "**=", "<<", "<<=", ">>", ">>=", "x", "x=", ".", ".=",
  
  Presumably these will require an interface layer to
  
  =over 4
  
  =item 1
  
  Make calls to C<use overload> set up the correct vtables on PMCs
  
  =item 2
  
  Create PMC table routines that call back into Perl.
  
  =back
  
  There are 10 pairs of operators. It might be possible to initially implement
  the assignment variants in Perl space using the non-assignment code. This
  work might take a week? Then half a day per pair, for 2 weeks total?
  
  =item * Comparison operations
  
      "<",  "<=", ">",  ">=", "==", "!=", "<=>",
      "lt", "le", "gt", "ge", "eq", "ne", "cmp",
  
  14 operators. 1 week or so, using the code and experience from the arithmetic
  operations?
  
  =item * Bit operations
  
      "&", "^", "|", "neg", "!", "~",
  
  6 operators. 3 days?
  
  =item * Increment and decrement
  
      "++", "--",
  
  2 days? Perl uses one method for both pre and post. Does parrot use 2?
  
  =item * Transcendental functions
  
      "atan2", "cos", "sin", "exp", "abs", "log", "sqrt", "int"
  
  8 methods. 1 day to set up a framework, and 2 hours per method, for 3 days
  total?
  
  =item * Boolean, string and numeric conversion>
  
      'bool', '""', '0+',
  
  3 methods, but there is a special case noted. 3 days?
  
  =item * Iteration
  
      "<>"
  
  C<readline> and C<glob> are both overloaded to this method. Until Ponie uses
  Parrot IO, I don't think that this overloading needs to be implemented as a
  vtable method. However, to allow removal of all of Perl5's overloading magic
  infrastructure, it will probably be necessary to convert this to a PMC method
  call (from Ponie back to Ponie). So 1 day.
  
  =item * Dereferencing
  
      '${}', '@{}', '%{}', '&{}', '*{}'.
  
  These could be quite messy, as they presumably need to return valid Parrot
  references. 1 week?
  
  =item * Special
  
      "nomethod", "fallback", "=",
  
  The nomethod and fallback logic will need to be moved from F<gv.c> to code
  accessible to PMCs. The copy operator will need to be embedded in PMC code
  too. All this code acts as support for overloading implementations that don't
  define 100% of overloaded methods. It's not clear where the logic to implement
  this should actually go - C code in PMCs, or more Perl space code. 1 week?
  
  This probably needs doing in parallel, as needed, by the other ops.
  
  62 days.
  
  =back
  
  =head3 Migrating from Perl 5 Ops to Parrot bytecode.
  
  This is probably highly parallelisable - in theory each of Perl's 353 Ops can
  be worked on separately. In practice it is likely that similar ops share code,
  so it seems best to work on groups of ops together.
  
  During the transition period Larry suggests using a similar technique to the
  one he used when migrating from Perl 4 to Perl 5. Have a new Perl 5 op that
  is "jump into Parrot op dispatcher", and a Parrot op that is "jump into Perl 5
  op dispatcher". The Perl 5 parser, as today, builds a Perl 5 optree. The
  B module that does Perl 5 tree -> Parrot Bytecode stream conversion walks the
  tree in execution order, starting in a "Perl 5" state. When it encounters a
  sequence of 1 or more ops that are now implemented as Parrot ops, it writes
  out a section of Parrot bytecode containing the ops, finishing with a "jump
  into Perl 5" Parrot op that points to the correct continuation point in the
  Perl 5 tree. The Parrot bytecode is linked into the Perl 5 optree by replacing
  the sequence of translated Perl 5 ops with the "jump into Parrot" op pointing
  to the Parrot bytecode sequence.
  
  This way at runtime the flow of execution will seamlessly be passed between
  the Perl 5 and Parrot runloops, which allows a progressive conversion of
  Ops from Perl 5 to Parrot with programs (and regression tests) runnable
  throughout. As work progresses the proportion of Parrot ops will increase and
  Perl 5 ops decrease. When 100% of ops are Parrot ops, the interchange ops and
  the Perl 5 runloop can be removed.
  
  If this process averages out at 2 hours per op converted, then it will take
  88 programmer days.
  
  =head2 Release!
  
  The estimate is that this takes 261 days. This is a lot.
  
  About 50 days of the first 60 can't be parallelised easily. Later work is
  more parallelisable, less dependent, shorter, and far more suitable for
  others to work on.
  
  =head2 Future work
  
  After a first complete release of Ponie there are still improvements that
  could be made.
  
  =over 4
  
  =item *
  
  Modify the Perl 5 parser to emit Parrot bytecode directly.
  
  =item *
  
  Migrate from the Perl 5 regexp engine to the Parrot Rules Engine
  
  =item *
  
  Migrate from the Perl 5 IO system to the Parrot IO system
  
  =item *
  
  Replace the Perl 5 pad code with Parrot's lexicals.
  
  =cut