Re: Subroutines...

2002-04-29 Thread Joe Wilson

I'd have to agree with Andrew.  With only 32 registers of each type in 
Parrot (the last time I checked) using most of them for function arguments 
would cause much needless register copying within each function.  
Surely 8 registers of each type would be more than sufficient for 
function parameters, and the rest could be local scratch registers 
for each function to use as it pleased without saving.

It might be beneficial to look at the code in register rich CPU 
architectures in GCC to see what calling conventions are used for
such chips.

This Google thread discusses this topic:

http://groups.google.com/groups?hl=enselm=85hhsi%24lkj%241%40nnrp1.deja.com

On Mon, 29 Apr 2002 09:50:12 +1000 Andrew J Bromage wrote:
On Sun, Apr 28, 2002 at 11:44:04AM -0400, Dan Sugalski wrote:

 We're going caller-save. I think I made this declaration before,  but 
 now it's backed up with pure PDD goodness. :)

The first thing to realise is that this violates the principle of
callee does everything important to it. :-)

More seriously, this is the opposite extreme, and IMO it's also not
a good idea.



__
Do You Yahoo!?
Yahoo! Health - your guide to health and wellness
http://health.yahoo.com



Re: Subroutines...

2002-04-29 Thread Piers Cawley

Andrew J Bromage [EMAIL PROTECTED] writes:

 G'day all.

 On Sun, Apr 28, 2002 at 09:49:35PM -0400, Melvin Smith wrote:

 I don't think I and Andrew were saying we shouldn't do caller-save,
 we were just discussing that the calling convention (read
 activation record of a subroutine) should support the common
 optimization of passing args in scratch registers when it might
 make sense. Having scratch registers and/or allowing passing in
 registers says nothing about whether you can implement co-routines
 or continuations how you see fit nor whether a caller saves its
 environment correctly or not.

 Actually, I _was_ saying we shouldn't do caller-save (except
 possibly for a small number of registers designated for the
 purpose).

Remember that the caller only has to save the stuff that is important
to it, after all, anything that might be of importance to *its*
callers will already have been saved. If the callee saves then it's
going to be doing belt and braces save/restores on registers that may
be completely irrelevant.

 My main reason for objecting is that it slows down trivial leaf
 subs, which are very common in OO programming.  Ideally neither the
 caller nor the callee should need to save registers when calling
 such a sub.

Any half decent compiler, given enough information from the original
source and/or linkage information on precompiled modules will be able
to do the appropriate register saving optimizations at compile
time. It should even be possible to have automagic inlining
happening. 

 I also can't see that it speeds anything up in the case of
 non-trivial/ non-leaf subs.  If the caller isn't trivial, it will
 still have to save lots of registers, so it really doesn't matter
 who does it.

Well factored code tends to have plenty of small, trivial
subroutines... And in the case of non trivial subs then the register
saving overhead will be the same whereever it happens. The precise
breakdown will be different, but I claim that the average case will
stay roughly the same.

 There's got to be a reason for making caller-save the default, but I
 can't think of it. :-)

You've still not shown how you're going to make a first class
continuation when the callee saves. And no, using branch instead of
bsr is *not* a good enough answer, you need to be able to bundle up a
continuation that can be called from anywhere, even after that
continuation is no longer in the current caller chain, which means you
need to be able to restore the register state and continuation chain.

-- 
Piers

   It is a truth universally acknowledged that a language in
possession of a rich syntax must be in need of a rewrite.
 -- Jane Austen?




Hashes, symbol tables and other fun stuph.

2002-04-29 Thread Piers Cawley

So, I was thinking about how symbol tables are going to work, and I
remembered that Dan had said that he wanted hashes to preserve their
insertion order on print out.

Which led me to think that it'd be really nice if there was some way
of addressing hashes with integers to get the 'nth thing'
inserted. This could lead to some useful compiler optimization
possibilities. Consider:


  my $foo = 10;

  sub thing($arg) {
return { $arg + $foo++ }
  }

Which is ugly and does nothing, but will serve for now. Now, if this
is run by an interpreter, you'd probably see the internal 'return {
$arg + $foo++ }' becoming (this is pseudo code, not parrot code).

  $register{SCRATCH1} = $register{ENVT}-lookup('$arg');
  $register{SCRATCH2} = $register{ENVT}-lookup('$foo');
  $register{SCRATCH1} = $register{SCRATCH1} + $register{SCRATCH2};
  $register{SCRATCH2} = $register{SCRATCH2} + 1;
  $register{ENVT}-bind('$foo' = $register{SCRATCH2});

But, if we assume that the first thing that gets inserted into an
environment hash is its parent environment, then a compiler could turn
this into:

  $register{SCRATCH1} = $register{ENVT}[1] + $register{ENVT}[0][1];
  $register{ENVT}[0][1] = $register{ENVT}[0][1] + 1;

Which takes advantage of the compilers knowledge of the current
compilation state to avoid the runtime cost of the environment
search. 

The question then becomes how does one distinguish within parrot
between %HASH{1} and %HASH[1]. I confess that I wouldn't be
desperately keen on having to do that using an approach along the
lines of '%HASH.as_array.[1]', unless the array returned by .as_array
could be used lvaluably to change the values held in the hash...

Thoughts?

-- 
Piers

   It is a truth universally acknowledged that a language in
possession of a rich syntax must be in need of a rewrite.
 -- Jane Austen?




Re: Subroutines...

2002-04-29 Thread Mike Lambert

  Actually, I _was_ saying we shouldn't do caller-save (except
  possibly for a small number of registers designated for the
  purpose).

 Remember that the caller only has to save the stuff that is important
 to it, after all, anything that might be of importance to *its*
 callers will already have been saved. If the callee saves then it's
 going to be doing belt and braces save/restores on registers that may
 be completely irrelevant.

I'm not familiar with current literature on the subject, so it's possible
that the ideas I'm about to suggest haven't been previously suggested for
a very good reason. Hopefully no one will mind. :)

In each function, store a list of the registers it uses as a bistring.
Should be four integers per function for the four register sets.

We store four integers in the interpter, consisting of what has been used
in the call stack up until this point. When we call function A, we AND
it's used bits with the call stack used bits. Then we go through them,
pushing only those values onto the stack for which the corresponding bits
are on.

Might cause worse performance in some situations, but would be
pushing/popping less overall. Not sure if the time spent iterating through
the bits would screw up any speed benefit, however.



Another idea is to store a list of register frames, which don't get GC'ed
out from under us. Every time a function is called, instead of memcpy'ing
the current register frame, we merely adjust the pointer farther down the
list. Upon returning from a function, we move the pointer back up to the
previous register frame.

Not sure how this will interact with the GC system, such that a
highly-recursive function doesn't cause a bunch of memory to get stuck
hanging around, while not freeing memory as soon as we return from a
function (since it's very useful for the next time we enter any function)

It makes function calls as simple as a couple pointer updates (unless we
have to create a register frame, a one-time expense), at the expense of
possibly hurting cache coherency.  Tail-recursion calls would just leave
the current register frame on the stack. Not sure how it would interact
with continuations.


Just some random, hopefully helpful, ideas.
Mike Lambert




Re: Subroutines...

2002-04-29 Thread Andrew J Bromage

G'day all.

On Mon, Apr 29, 2002 at 07:57:08AM +0100, Piers Cawley wrote:

 Remember that the caller only has to save the stuff that is important
 to it, after all, anything that might be of importance to *its*
 callers will already have been saved. If the callee saves then it's
 going to be doing belt and braces save/restores on registers that may
 be completely irrelevant.

That's true, it's a tradeoff.  However, I think it's much more likely
that complex subs call simpler subs, not the other way around, and
when it is the other way around, it's probably less time-critical, and
even if it is the other way around and it is time critical, I still
think that a small number of caller-save registers is a good idea.

 Any half decent compiler, given enough information from the original
 source and/or linkage information on precompiled modules will be able
 to do the appropriate register saving optimizations at compile
 time. It should even be possible to have automagic inlining
 happening. 

Intermodule optimization requires a compiler that's significantly
better than half decent, if only to handle the recompilation
dependencies.  After all, we don't want existing code to break every
time someone does a CPAN upgrade.

Of course the ultimate solution is the old MIPS register allocation
at link time approach, but I assume that for a dynamic language like
Perl this is a bad idea. :-)

 Well factored code tends to have plenty of small, trivial
 subroutines... And in the case of non trivial subs then the register
 saving overhead will be the same whereever it happens. The precise
 breakdown will be different, but I claim that the average case will
 stay roughly the same.

I agree that in the case of non-trivial subs the overhead will be
roughly the same.  Modern code has lots of trivial subs.  I remember
reading this somewhere but I can't for the life of me remember where.

Until I find a proper reference, I can only point to the large number
of callee-save registers currently found in real world ABIs. :-)

 You've still not shown how you're going to make a first class
 continuation when the callee saves. And no, using branch instead of
 bsr is *not* a good enough answer, you need to be able to bundle up a
 continuation that can be called from anywhere, even after that
 continuation is no longer in the current caller chain, which means you
 need to be able to restore the register state and continuation chain.

I'm in a bit of a hurry at the moment, so I can't do this justice right
now, however, just a thought: If you want a continuation that can be
called from everywhere, and it has registers to be loaded, don't you
really want a closure?

(I might be wrong on that; it's been a while since I last read Appel's
Compiling with Continuations.)

Cheers,
Andrew Bromage



Parrot and external libraries

2002-04-29 Thread Alberto Manuel Brandão Simões


Hi, again!

Sorry but I didn't have the time to read all I should about parrot, but
this question can be usefull to know if I should continue looking at it,
or not... is parrot ready (or will be, soon) to use external libraries?

And another simple... can parrot be used as a filter? assembler x.pasm |
parrot?

Thanks,
Alberto
-- 
Alberto Manuel B. Simoes
Departamento de Informática - Universidade do Minho
http://alfarrabio.di.uminho.pt/~albie - http://numexp.sf.net




[PATCH] Typo in core.ops

2002-04-29 Thread Ilya Martynov


Index: core.ops
===
RCS file: /cvs/public/parrot/core.ops,v
retrieving revision 1.130
diff -u -d -u -r1.130 core.ops
--- core.ops24 Apr 2002 20:31:39 -  1.130
+++ core.ops29 Apr 2002 14:00:28 -
 -2823,7 +2823,7 
 
 Re-enable GC
 
-=back
+=cut
 
 op collecton() {
   if (interpreter-GC_block_level) {


-- 
Ilya Martynov (http://martynov.org/)



First patch to memory allocation routines

2002-04-29 Thread Peter Gibbs

Herewith the first set of patches to the memory allocation routines.

There is no new functionality here yet; basically I have been working on
trying to remove some of the code that is duplicated between the various
pools, before even more copies get made for the new stuff.

The result is some fairly major changes, but I believe it will make adding
the new features easier. I have tested the end product using the standard
test suite, 5000 generations of life, loading Clint's basic wumpus, and
reversing resources.c using Melvin's reverse.cola. We don't have much in the
way of PMC-intensive test code at present.

A summary of the changes:
struct free_pool has new member 'replenish'; this is a function to be called
when the pool is empty
Initialisation of the free pools moved from memory.c to resources.c - mainly
to avoid exporting more functions from resources.c
New function new_free_pool to create a free pool - just to stop doing the
same thing several times
New function add_to_free_pool - replaces existing add_pmc_to_free and
add_header_to_free
New function get_from_free_pool - called by the new_*_header functions to
avoid duplicating the code involved in extracting an entry from the free
pool; also incorporates the calling of do_dod_run and the replenishment of
the pools
[Mike - in the process, I have removed some of your GC_DEBUG stuff; if this
patch is applied, feel free to decide where you want to reinstate this]

Essentially, the free pools have become the centre of the allocation
process. If anybody has any problems with this basic concept, please let me
know asap.

The next stage will be the creation of a header arena and memory pool for
constant strings.

Files changed: resources.h, resources.c, memory.c

--
Peter Gibbs
EmKel Systems


Index: include/parrot/resources.h
===
RCS file: /home/perlcvs/parrot/include/parrot/resources.h,v
retrieving revision 1.27
diff -u -r1.27 resources.h
--- include/parrot/resources.h 23 Apr 2002 19:41:15 - 1.27
+++ include/parrot/resources.h 29 Apr 2002 14:27:21 -
 -44,6 +44,8 

 void buffer_lives(Buffer *);

+void Parrot_initialize_free_pools(struct Parrot_Interp *);
+
 #define STRING_HEADERS_PER_ALLOC 128
 #define PMC_HEADERS_PER_ALLOC 128
 #define BUFFER_HEADERS_PER_ALLOC 128
 -77,6 +79,7 
 struct free_pool {
 Buffer pool_buffer;
 size_t entries_in_pool;
+void (*replenish)(struct Parrot_Interp *, struct free_pool *);
 };

 struct Memory_Pool {
Index: resources.c
===
RCS file: /home/perlcvs/parrot/resources.c,v
retrieving revision 1.48
diff -u -r1.48 resources.c
--- resources.c 27 Apr 2002 19:31:08 - 1.48
+++ resources.c 29 Apr 2002 14:25:19 -
 -14,39 +14,77 

 #include parrot/parrot.h

-static void add_header_to_free(struct Parrot_Interp *interpreter,
-   struct free_pool *pool, void *to_add);
-
-/* Add a string header to the free string header pool */
-static void add_pmc_to_free(struct Parrot_Interp *interpreter,
-struct free_pool *pool, void
-*to_add) {
-  PMC **temp_ptr;
-  /* First, check and see if there's enough space in the free pool. If
-   we're within the size of a STRING pointer, we make it bigger */
-  if (pool-entries_in_pool * sizeof(PMC *) =
-  pool-pool_buffer.buflen - sizeof(PMC *)) {
-/* If not, make the free pool bigger. We enlarge it by 20% */
-Parrot_reallocate(interpreter,
-  pool-pool_buffer,
-  (UINTVAL)(pool-pool_buffer.buflen * 1.2));
-
-  }
+/* Create a new free pool */
+static struct free_pool *
+new_free_pool(struct Parrot_Interp *interpreter, size_t entries,
+  void (*replenish)(struct Parrot_Interp *, struct free_pool
*)) {
+struct free_pool *pool;
+size_t temp_len;
+
+pool = mem_sys_allocate(sizeof(struct free_pool));
+temp_len = entries * sizeof(void *);
+pool-pool_buffer.bufstart = mem_allocate(interpreter, temp_len);
+pool-pool_buffer.buflen = temp_len;
+pool-pool_buffer.flags = BUFFER_live_FLAG;
+pool-entries_in_pool = 0;
+pool-replenish = replenish;
+return pool;
+}
+
+/* Add entry to free pool
+ * Requires that any object-specific processing (eg flag setting,
statistics)
+ * has already been done by the caller
+ */
+static void
+add_to_free_pool(struct Parrot_Interp *interpreter,
+ struct free_pool *pool, void *to_add) {
+void **temp_ptr;
+
+/* First, check and see if there's enough space in the free pool. If
+ we're within the size of a pointer, we make it bigger */
+if (pool-entries_in_pool * sizeof(void *) =
+pool-pool_buffer.buflen - sizeof(void *)) {
+  /* If not, make the free pool bigger. We enlarge it by 20% */
+  Parrot_reallocate(interpreter,
+pool-pool_buffer,
+

Re: Subroutines...

2002-04-29 Thread Dan Sugalski

At 9:50 AM +1000 4/29/02, Andrew J Bromage wrote:
G'day all.

On Sun, Apr 28, 2002 at 11:44:04AM -0400, Dan Sugalski wrote:

  We're going caller-save. I think I made this declaration before,  but
  now it's backed up with pure PDD goodness. :)

The first thing to realise is that this violates the principle of
callee does everything important to it. :-)

Sure, but saving registers isn't something the callee finds 
important. The saved registers aren't important to it.

More seriously, this is the opposite extreme, and IMO it's also not
a good idea.

Welcome to my world. They're all bad ideas in some way or, rather, 
they're all equally as good.

OO code is full of subs which a) are called very often and b) look
like this:

   sub code
   {
 my $self = shift;
 return $self-{CODE};
   }

   sub body
   {
 my $self = shift;
 if (@_) {
   $self-{BODY} = shift;
 }

 return $self-{BODY};
   }

Yup, caller save doesn't necessarily favor code like this. On the 
other hand, it does favor recursive code and code with heavier-weight 
bodies. It's always a toss-up. Nothing's free, and all decisions have 
consequences of some sort.

Maybe half a dozen registers used in each one at the most.  A caller is
obliged to save _all_ its registers in order to call these trivial
subs.

Nope, just the registers it needs preserved across calls. It needs to 
save the data that *is* relevant, where on callee save the data that 
*might* be relevant needs saving. The question, of course, is whether 
there's as much potentially relevant data as actually relevant data.

Assuming that the caller and callee spend about the same amount of 
time saving data (An assumption that's a bit dodgy there, as it's 
based on the coding style the language encourages) caller-save is 
ultimately a bit less expensive, as making tail calls avoids a 
push/pop pair, which callee-save can't avoid.

Now admittedly we have a single op for this, so at least in the
interpreter it may not cost as much as a whole bunch of individual
register saves.  For other targets (JIT), unnecessary caller saves will
almost certainly cause a measurable performance hit on OO-like code.

Just for chuckles, I went and benchmarked things, as meaningless 
numbers are useful to have. With my current GCC3/-O3 build of parrot, 
the cost to do 10,000,000 sets of saves is as follows:

save all: 5.3 sec usertime
   save 1: 2.6 sec user
   save 2: 4.7 sec user
   save 3: 6.8 sec user

Saving all of them was done with pushp. The empty loop took .48 
seconds if you want to factor that out. JITting, at the moment, 
doesn't do much for the time, but that's mainly because the push and 
pop routines are relatively heavyweight as these things go.
-- 
 Dan

--it's like this---
Dan Sugalski  even samurai
[EMAIL PROTECTED] have teddy bears and even
   teddy bears get drunk



Re: Subroutines...

2002-04-29 Thread Melvin Smith

   
  
  Dan Sugalski 
  
  [EMAIL PROTECTED]  To:   Andrew J Bromage 
[EMAIL PROTECTED], [EMAIL PROTECTED] 
   cc: 
  
  04/29/2002 10:53 Subject:  Re: Subroutines...
  
  AM   
  
   
  
   
  




Just for chuckles, I went and benchmarked things, as meaningless
numbers are useful to have. With my current GCC3/-O3 build of parrot,
the cost to do 10,000,000 sets of saves is as follows:

save all: 5.3 sec usertime
   save 1: 2.6 sec user
   save 2: 4.7 sec user
   save 3: 6.8 sec user

Wow, if you got this on your VMS box, It should do about 20,000,000
ops on my 386? :)

J/K I love VMS but couldn't resist.

What CPU is this benchmark on, seriously? I've been watching
the Athlon XP chips drop in price and want to rebuild my single
CPU Linux box with one soon.
-Melvin Smith

IBM :: Atlanta Innovation Center
[EMAIL PROTECTED] :: 770-835-6984




Re: First patch to memory allocation routines

2002-04-29 Thread Dan Sugalski

At 4:44 PM +0200 4/29/02, Peter Gibbs wrote:

Herewith the first set of patches to the memory allocation routines.

There is no new functionality here yet; basically I have been working on
trying to remove some of the code that is duplicated between the various
pools, before even more copies get made for the new stuff.

The result is some fairly major changes, but I believe it will make adding
the new features easier. I have tested the end product using the standard
test suite, 5000 generations of life, loading Clint's basic wumpus, and
reversing resources.c using Melvin's reverse.cola. We don't have much in the
way of PMC-intensive test code at present.

Cool. We're getting closer to the 0% Dan-code limit that signals a 
1.0 release. :)

Few questions:

1) Has the external interface changed, and are you planning on having 
it change?
2) How's the performance relative to the old stuff?
3) Do you want the memory manager/GC hat?
-- 
 Dan

--it's like this---
Dan Sugalski  even samurai
[EMAIL PROTECTED] have teddy bears and even
   teddy bears get drunk



entrytype OP is broken?

2002-04-29 Thread Ilya Martynov


I just started playing with Parrot assembly and it looks like I have
found a bug with entrytype OP.

This test case causes segfault:

save I1
entrytype I2, 0

Another test case

save I1
entrytype I2, -1

This one causes 'Stack Depth wrong' error instead of getting type for
entry from the bottom of stack.

BTW from POD docs in core.ops it is not very clear what second
parameter of this OP is for.  I rewrote documentation for this OP
basing on comments from stack.c for stack_entry():

Index: core.ops
===
RCS file: /cvs/public/parrot/core.ops,v
retrieving revision 1.130
diff -u -d -u -r1.130 core.ops
--- core.ops24 Apr 2002 20:31:39 -  1.130
+++ core.ops29 Apr 2002 14:48:49 -
 -2506,7 +2506,10 
 
 =item Bentrytype(out INT, in INT)
 
-Gets the type of entry $2 of the stack and puts it in $1
+If $2 = 0, gets the type of entry at that depth from the top of the
+stack and puts it in $1, with 0 being the top entry.  If $2  0, then
+gets the type of entry |$2| entries from the bottom of the stack and
+puts it in $1.
 
 =cut
 



-- 
Ilya Martynov (http://martynov.org/)



Re: As promised...

2002-04-29 Thread Steve Fink

On Sun, Apr 28, 2002 at 10:08:01PM -0400, Jeff wrote:
 And a long time coming...
 
 An assembler supporting keyed aggregates. Because of the current lack of
 macro support (that will be added in a few days), it's not built by
 default and isn't invoked in the test suite.

Yay! Thank you.

 Since keyed aggregate support was waiting on this step, key constructs
 will not yet be supported, but this is the first major step in getting
 there. Macro support will be added over the next few days, along with a
 different backend that the language compilers will be able to use that
 will support compilation straight to Parrot bytecode, without a .pasm
 file as an intermediary. Hopefully we'll be able to use this for the
 tests as well, which should speed things up by quite a bit.

Well, one problem -- there's no way to create an aggregate right now,
or any other PMC. The assembler needs to be taught about the PMC
constants:

  ./newasm t/pmc/perlhash6.pasm  ph6.pbc
  Undefined label PerlHash used at line 1

That comes from the line

  new P0, PerlHash

I know, I can replace that with a constant integer, or stop whining
and add the support myself, but my parrot time right now is going
towards finishing off the memory management problems in the hashtable
implementation.



Re: First patch to memory allocation routines

2002-04-29 Thread Mike Lambert

 I suspect the bug may be in my understanding of the memory
 management API, though. If I want to maintain a linked-list of my own
 objects, how do I do it? If I carve out my objects (hash buckets) from
 a Buffer, then GC would keep moving them around and breaking the
 -next link pointers. Setting the immobile flag doesn't seem to do it,
 probably for the same reason that the GC PDD says don't set this on
 Parrot_allocated memory. I guess if there were a way of registering a
 callback for when the memory gets moved, I could use that to fix
 things up, but that would bloat up the Buffers.

I think the only way to accomplish this with the current GC is with PMCs.
Buffers almost work, but they don't allow for custom mark routines, and
thus can't do intelligent stuff on their data. Not sure if this is by
design, or something that just needs implementing. You could write a PMC
which points to a buffer, and in the buffer, store two things. First, is a
'next' pointer, which points to a PMC header. The second is the contents,
be it the node data itself, or a pointer to a header (buffer or PMC,
doesn't matter).

Then write a custom 'mark' method which correctly mark_used's the next
pointer, and may ignore, mark_used, or buffer_lives the contents of the
node data, depending upon whether it's struct data, a PMC pointer, or a
buffer ptr.

As far as a hashtable implementation goes, from a conversation I had with
Dan about hashtables and GC. I think it's quite similar to what you're
already doing, but there might be some differences:
- Make an array of buffer data, in order of insertion into the hashtable.
set pmc_pointer and buffer_ptr and let the GC rip through it.
- The hashtable itself just uses indices into this array. Each
linked-list node would be a PMC header, that follows the logic mentioned
above, where the linked-list node data would simply be an integer lookup
into the array of buffer data.

 For now, is it the case that if you want to store pointers in memory,
 you have to use mem_sys_allocate() for anything pointed to?

Yes. And luckily, PMC headers and Buffer headers are allocated in pools
which were allocated with mem_sys_allocate. It's always safe to point to
them...never to their contents.

 Btw, this is only a weak guess about what's going on, because the
 corruption I'm seeing isn't even in the linked list nodes. It only
 happens with GC_DEBUG, but it's not an infant mortality bug.

GC_DEBUG adds extra calls to do_dod_run (infant mortality), and
do_collect. You're likely running into problems where you are referencing
memory which is getting moved out from under you. Instead of blindly
referencing the data in the buffer itself, you should be referencing the
buffer header.

Looking forward to seeing a hashtable implementation sometime soon.. :)

Mike Lambert




Re: First patch to memory allocation routines

2002-04-29 Thread Steve Fink

On Mon, Apr 29, 2002 at 01:41:56PM -0400, Mike Lambert wrote:
  I suspect the bug may be in my understanding of the memory
  management API, though. If I want to maintain a linked-list of my own
  objects, how do I do it? If I carve out my objects (hash buckets) from
  a Buffer, then GC would keep moving them around and breaking the
  -next link pointers. Setting the immobile flag doesn't seem to do it,
  probably for the same reason that the GC PDD says don't set this on
  Parrot_allocated memory. I guess if there were a way of registering a
  callback for when the memory gets moved, I could use that to fix
  things up, but that would bloat up the Buffers.
 
 I think the only way to accomplish this with the current GC is with PMCs.
 Buffers almost work, but they don't allow for custom mark routines, and
 thus can't do intelligent stuff on their data. Not sure if this is by
 design, or something that just needs implementing. You could write a PMC
 which points to a buffer, and in the buffer, store two things. First, is a
 'next' pointer, which points to a PMC header. The second is the contents,
 be it the node data itself, or a pointer to a header (buffer or PMC,
 doesn't matter).
 
 Then write a custom 'mark' method which correctly mark_used's the next
 pointer, and may ignore, mark_used, or buffer_lives the contents of the
 node data, depending upon whether it's struct data, a PMC pointer, or a
 buffer ptr.

This is similar to what I'm doing. I have a PMC (a PerlHash) whose
-data member is a Buffer (actually a subclass HASH of Buffer with
extra fields.) The PMC's mark() vtable entry correctly traverses
through the HASH's PMCs and Buffers.

 As far as a hashtable implementation goes, from a conversation I had with
 Dan about hashtables and GC. I think it's quite similar to what you're
 already doing, but there might be some differences:
 - Make an array of buffer data, in order of insertion into the hashtable.
 set pmc_pointer and buffer_ptr and let the GC rip through it.
 - The hashtable itself just uses indices into this array. Each
 linked-list node would be a PMC header, that follows the logic mentioned
 above, where the linked-list node data would simply be an integer lookup
 into the array of buffer data.

I don't want to pay for a whole PMC for each bucket, but that would
work.

I've considered using indexes instead of pointers, since it's a
trivial change to the current code. But it makes the code a little bit
uglier and it just seems wrong to me that you can't safely use
parrot's memory routines for data structures with self-referential
pointers. It all needs to be GC-aware to some degree, but I guess I'd
prefer to write an update_self_on_move() routine just so the rest of
the code can live in a make-believe world where I can implement things
exactly the same as I would without GC.

And maybe this is unfounded, but from a performance standpoint using
bufstart[link_index] all the time instead of *link feels slower -- not
because of the extra arithmetic, but because the CPU's code scheduler
has to assume that the memory location you're referencing can conflict
with any other memory address in the pipeline. And there should be a
lot more hashtable lookups than garbage collections, so I'd rather
penalize the latter.

  Btw, this is only a weak guess about what's going on, because the
  corruption I'm seeing isn't even in the linked list nodes. It only
  happens with GC_DEBUG, but it's not an infant mortality bug.
 
 GC_DEBUG adds extra calls to do_dod_run (infant mortality), and
 do_collect. You're likely running into problems where you are referencing
 memory which is getting moved out from under you. Instead of blindly
 referencing the data in the buffer itself, you should be referencing the
 buffer header.

Doesn't look like it. I've instrumented every place where memory gets
moved around, and it appears that even though it is correctly marking
my Buffer as live, it's still copying something else onto its memory.
Which is in itself not a problem, because that's what I thought should
happen -- but it should be copying my stuff to somewhere else first! I
feel discriminated against. My memory isn't as important as someone
else's.

 Looking forward to seeing a hashtable implementation sometime soon.. :)

Well, if you're anxious, I sent out a working one last week. It just
relied on mem_sys_allocate() for everything and nothing ever got
collected. :-)



Re: First patch to memory allocation routines

2002-04-29 Thread Steve Fink

   Btw, this is only a weak guess about what's going on, because the
   corruption I'm seeing isn't even in the linked list nodes. It only
   happens with GC_DEBUG, but it's not an infant mortality bug.
  
  GC_DEBUG adds extra calls to do_dod_run (infant mortality), and
  do_collect. You're likely running into problems where you are referencing
  memory which is getting moved out from under you. Instead of blindly
  referencing the data in the buffer itself, you should be referencing the
  buffer header.
 
 Doesn't look like it. I've instrumented every place where memory gets
 moved around, and it appears that even though it is correctly marking
 my Buffer as live, it's still copying something else onto its memory.
 Which is in itself not a problem, because that's what I thought should
 happen -- but it should be copying my stuff to somewhere else first! I
 feel discriminated against. My memory isn't as important as someone
 else's.

Doh! Never mind. The problem was that my buffer was coming from
new_tracked_header, and those aren't scanned. Basically, I didn't
finish implementing it. (And I probably won't until Peter Gibbs's
stuff lands.)

stoopid sfink.



Re: As promised...

2002-04-29 Thread Simon Cozens

Steve Fink:
 The assembler needs to be taught about the PMC constants:

I disagree. Once you start adding assembler functions to make it easier 
for humans to use, you won't stop. I consider the assembler a tool for
machines to use. But if you want to do it, here's a patch.

--- newasm~ Mon Apr 29 19:31:42 2002
+++ newasm  Mon Apr 29 19:32:35 2002
 -9,6 +9,7 
 use strict;
 use Parrot::OpLib::core;
 use Parrot::Types;
+use Parrot::PMC qw(%pmc_types);
 
 my %ops;
 my %fullops;
 -121,6 +122,8 
 if ($arg_t eq label) {
 if (defined $labels{$arg}) {
 push args, $labels{$arg} - $ops_pc;
+} elsif (exists $pmc_types{$arg}) {
+push args, $pmc_types{$arg};
 } else {
 die Undefined label $arg used at line $lineno\n;
 }

-- 
Be not anxious about what you have, but about what you are.
-- Pope St. Gregory I



Re: As promised...

2002-04-29 Thread Steve Fink

On Mon, Apr 29, 2002 at 07:34:22PM +0100, Simon Cozens wrote:
 Steve Fink:
  The assembler needs to be taught about the PMC constants:
 
 I disagree. Once you start adding assembler functions to make it easier 
 for humans to use, you won't stop. I consider the assembler a tool for
 machines to use. But if you want to do it, here's a patch.

I half agree. The way I was thinking about adding it was to have
rudimentary macros, and generate a prologue that defined all of the
standard PMC types:

..def PerlArray 1
..def PerlString 2
  .
  .
  .

Then you'd either have a .include directive, or there would be an
assembler driver script that would know to suck in the prologue. I
know you're not crazy about assembler macros, but IMO it's better to
allow compilers to run with minimal knowledge of parrot internals.



Re: First patch to memory allocation routines

2002-04-29 Thread Piers Cawley

Steve Fink [EMAIL PROTECTED] writes:

 On Mon, Apr 29, 2002 at 01:41:56PM -0400, Mike Lambert wrote:
 - Make an array of buffer data, in order of insertion into the hashtable.
 set pmc_pointer and buffer_ptr and let the GC rip through it.
 - The hashtable itself just uses indices into this array. Each
 linked-list node would be a PMC header, that follows the logic mentioned
 above, where the linked-list node data would simply be an integer lookup
 into the array of buffer data.

 A bucket needs at least:
   - key : STRING*

Um... Larry's already said that you're going to be able to use
arbitrary objects as keys. Just to make life even easier for
everyone :)

-- 
Piers

   It is a truth universally acknowledged that a language in
possession of a rich syntax must be in need of a rewrite.
 -- Jane Austen?




Re: First patch to memory allocation routines

2002-04-29 Thread Steve Fink

On Mon, Apr 29, 2002 at 09:42:46PM +0100, Piers Cawley wrote:
 Steve Fink [EMAIL PROTECTED] writes:
 
  On Mon, Apr 29, 2002 at 01:41:56PM -0400, Mike Lambert wrote:
  - Make an array of buffer data, in order of insertion into the hashtable.
  set pmc_pointer and buffer_ptr and let the GC rip through it.
  - The hashtable itself just uses indices into this array. Each
  linked-list node would be a PMC header, that follows the logic mentioned
  above, where the linked-list node data would simply be an integer lookup
  into the array of buffer data.
 
  A bucket needs at least:
- key : STRING*
 
 Um... Larry's already said that you're going to be able to use
 arbitrary objects as keys. Just to make life even easier for
 everyone :)

Yeah, I know. I'm ignoring that for now, because there are a dozen
different things that could mean. The semantics aren't defined yet,
and I think parrot needs hashes sooner than would be possible if we
waited for the discussion of semantics to settle on something.

So my stance is that hashes with only STRING keys will always be
needed anyway, because hashes are used in many performance-critical
places and you can't afford the speed and space hit of using
generalized hashes. It's even possible that Perl6 hashes will have
STRING hashes underlying them, but it really depends on the exact
semantics for whether that makes any sense. And I bet a lot of
languages will be perfectly happy with STRING hashes. (And a STRING
can be any wad of binary data.)

Way back when, I even wrote RFC266, which gives a brief proposal of
one set of semantics, and talks about alternatives. So I'm definitely
in favor of fancier hashes, but not until the details have been
hammered out.



Re: Subroutines...

2002-04-29 Thread Joe Wilson

OO code is full of subs which a) are called very often and b) look
like this:

   sub code
   {
 my $self = shift;
 return $self-{CODE};
   }

   sub body
   {
 my $self = shift;
 if (_) {
   $self-{BODY} = shift;
 }

 return $self-{BODY};
   }

Yup, caller save doesn't necessarily favor code like this. On the 
other hand, it does favor recursive code and code with heavier-weight 
bodies. It's always a toss-up. Nothing's free, and all decisions have 
consequences of some sort.

I think caller save is the way to go.  I am just questioning how
many registers to actually save prior to each call.
The cost of saving registers may be relatively cheap, but it is
not zero.  There must be a good reason why the calling conventions 
on register rich CPUs often call-save just 6 registers.
It must have been backed up by research with hard numbers on hundreds 
of typical programs.


__
Do You Yahoo!?
Yahoo! Health - your guide to health and wellness
http://health.yahoo.com



Re: Subroutines...

2002-04-29 Thread Tim Bunce


[ I'm playing devils advocate for a while longer as I'm not 100% convinced ]

On Mon, Apr 29, 2002 at 10:53:40AM -0400, Dan Sugalski wrote:
 At 9:50 AM +1000 4/29/02, Andrew J Bromage wrote:
 G'day all.
 
 On Sun, Apr 28, 2002 at 11:44:04AM -0400, Dan Sugalski wrote:
 
   We're going caller-save. I think I made this declaration before,  but
   now it's backed up with pure PDD goodness. :)
 
 The first thing to realise is that this violates the principle of
 callee does everything important to it. :-)
 
 Sure, but saving registers isn't something the callee finds 
 important. The saved registers aren't important to it.

I don't think that's a good argument. It should be a good citizen.
Not write on memory it shouldn't and not mess with a (non scratchpad)
register without saving it.


 Yup, caller save doesn't necessarily favor code like this. On the 
 other hand, it does favor recursive code

I don't think that's a good argument. Not enough recursive code to matter.

 and code with heavier-weight bodies.

 that don't make lots of calls to leaf code (little methods etc).

As the 'weight' of the code body increases the register saving
becomes insignificant anyway - unless the code is making lots of calls.

 It's always a toss-up. Nothing's free, and all decisions have 
 consequences of some sort.

Sure.

 Maybe half a dozen registers used in each one at the most.  A caller is
 obliged to save _all_ its registers in order to call these trivial
 subs.
 
 Nope, just the registers it needs preserved across calls. It needs to 
 save the data that *is* relevant, where on callee save the data that 
 *might* be relevant needs saving.

If the caller knows what registers it's using and thus need saving,
why can't the callee also know what registers it is going to use
and thus need saving?

Isn't compiler convienience a (the?) major issue here?


 The question, of course, is whether 
 there's as much potentially relevant data as actually relevant data.
 
 Assuming that the caller and callee spend about the same amount of 
 time saving data (An assumption that's a bit dodgy there, as it's 
 based on the coding style the language encourages) caller-save is 
 ultimately a bit less expensive, as making tail calls avoids a 
 push/pop pair, which callee-save can't avoid.

How common do you expect tail calls to be? (Got practical examples?)

Seems to me like compiler convienience and tail call efficiency
are the main issues.

Tim.



Re: [PATCH] Typo in core.ops

2002-04-29 Thread Jeff

Ilya Martynov wrote:
 
 Index: core.ops
 ===
 RCS file: /cvs/public/parrot/core.ops,v
 retrieving revision 1.130
 diff -u -d -u -r1.130 core.ops
 --- core.ops24 Apr 2002 20:31:39 -  1.130
 +++ core.ops29 Apr 2002 14:00:28 -
 @@ -2823,7 +2823,7 @@
 
  Re-enable GC
 
 -=back
 +=cut
 
  op collecton() {
if (interpreter-GC_block_level) {
 
 --
 Ilya Martynov (http://martynov.org/)

Applied, thanks.
--
Jeff [EMAIL PROTECTED]



Re: [PATCH] Typo in core.ops

2002-04-29 Thread Ilya Martynov

 On Mon, 29 Apr 2002 18:58:18 -0400, Jeff [EMAIL PROTECTED] said:

J Ilya Martynov wrote:
 
 Index: core.ops
 [..snip..]

J Applied, thanks.

I've just found other POD bugs in core.ops

Index: core.ops
===
RCS file: /cvs/public/parrot/core.ops,v
retrieving revision 1.131
diff -u -d -u -r1.131 core.ops
--- core.ops29 Apr 2002 23:01:08 -  1.131
+++ core.ops29 Apr 2002 23:15:54 -
@@ -457,6 +457,8 @@
 Make a clone of $2, and put it in $1. Doesn't affect what was in
 $1. Removes the constant flag on the copy, if there was one.
 
+=cut
+
 inline op clone(out STR, in STR) {
   $1 = string_copy(interpreter, $2);
   goto NEXT();
@@ -469,6 +471,8 @@
 
 Sets register $1 to the current address plus the offset $2
 
+=cut
+
 inline op set_addr(out INT, in INT) {
   $1 = (INTVAL) (CUR_OPCODE + $2);
   goto NEXT();
 


-- 
Ilya Martynov (http://martynov.org/)



Re: Subroutines...

2002-04-29 Thread Andrew J Bromage

G'day all.

On Mon, Apr 29, 2002 at 11:59:45PM +0100, Tim Bunce wrote:

 [ I'm playing devils advocate for a while longer as I'm not 100% convinced ]

Understood.

 Isn't compiler convienience a (the?) major issue here?

I wouldn't call it a major issue.  I think of it as a constraint.

The major issue is performance, subject to the constraint that it
should be possible to write a working compiler (possibly generating
inefficient code) without going to a lot of trouble.

 How common do you expect tail calls to be? (Got practical examples?)

In Perl, not common.  When's the last time you used goto-NAME?

In functional languages (e.g. Scheme) and logic languages (e.g.
Prolog), it's usually part of the spec.

Cheers,
Andrew Bromage