Re: Subroutines...
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...
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.
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...
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...
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
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
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
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...
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...
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
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?
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...
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
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
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
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...
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...
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
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
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...
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...
[ 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
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
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...
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