|
weekly meeting at 18:30 UTC, Tuesday | logged at irclog.perlgeek.de/parrotsketch/today Set by moderator on 30 September 2008. |
|||
|
14:02
davidfetter joined
15:50
tewk_ joined
16:35
Wknight8111 joined
17:04
jhorwitz joined
17:17
cotto joined
17:48
PacoLinux joined,
kj joined
18:04
pmichaud joined
18:05
moritz joined
18:22
NotFound joined
18:27
allison joined
18:29
rurban joined
|
|||
| moritz | hi | 18:30 | |
| rurban | hi! | ||
| cotto | hi!! | ||
|
18:30
chromatic joined
|
|||
| NotFound | H | 18:30 | |
| particle | hi-o | ||
| Wknight8111 | hello | ||
| kj | hiya | ||
| jhorwitz | hi | 18:31 | |
| chromatic | good $localtime | ||
| allison | hi | 18:32 | |
| Tene | Hi Allison! | 18:33 | |
| allison | hi tene | ||
|
18:33
TimToady joined
|
|||
| chromatic | Shall we begin? | 18:33 | |
| moritz | aye | 18:34 | |
| chromatic | allison? | ||
| chromatic? | 18:35 | ||
| I fixed a couple of bugs, and I increased the warning level locally for type conversions in C code. | |||
| I want to be somewhat stricter about type widths and signedness, so I fixed some conversions in IMCC and am working through PMCs. | 18:36 | ||
| There are some questions about auxiliary functions called from ops that we should eventually resolve. | |||
| I'll poke at any bugs or crashes that block anyone, if you let me know. | |||
| EOR | |||
| cotto? | |||
| cotto | * have PhpArrays mostly implemented | ||
| * working on making iterators play nice | |||
| * iterators, stringification (var_dump, print_r, etc) and general cleanup are all that remain to be done | |||
| * queue 2 questions | |||
| StopIteration | |||
| chromatic | japhb? | ||
| allison? | 18:38 | ||
| allison | - I merged the pdd27mmd branch into trunk. This is a significant cleanup of the multiple dispatch subsystem, merging two (conflicting) MMD subsystems into one. | ||
| - Fixed language test failures before and after the merge. A few more left. | |||
| - Created a branch for a cleanup of the calling conventions. Andrew Whitworth will be doing some work on this, continuing work we started in the multiple dispatch branch. | |||
| - Also starting a branch for the I/O milestone (the next on the list). The primary part of this milestone is shifting I/O over to being a regular object, instead of the old "layers" implementation. | |||
| EOR | |||
| chromatic | jhorwitz? | ||
| jhorwitz | segfault in #58368 continues to be a blocker for mod_perl6. narrowed it down to the PAST generation phase, but can't get much further than that. if we can fix this in time for the workshop this weekend, beer will be distributed. :) | 18:39 | |
| began work on binding mod_parrot interpreters to connections, which is necessary in a threaded apache mpm to preserve state across the entire request lifecycle. | |||
| EOR | |||
| chromatic | kj? | ||
| particle | jhorwitz++ | ||
| kj | == work on pirc: | 18:40 | |
| * fixed some parsing issues; most seem to be resolved now. | |||
| * 3 major tasks left (in pirc/new) are: | |||
| - convert all LABEL operands into offsets (integer constants) | |||
| - convert keylists, such as ['a';'b'] into PMC constants | |||
| .eor | |||
| chromatic | moritz? | ||
| moritz | Nothing interesting to report, just usual testing stuff. | ||
| EOR | |||
| chromatic | NotFound? | 18:41 | |
| NotFound | * more pirric improvements | ||
| * reviewing some old tickets, closed a few | |||
| * fixing some bugs | |||
| * queue one question | |||
| EOR | |||
| chromatic | particle? | ||
| particle | yay! finally some hacking time, after too long without any | ||
| mainly because i got my msdn subscription | |||
| i have the latest tools now running (microsoft++) | |||
| rakudo: | |||
| ~ added <apostrophe> and <identifier> tokens to grammar | |||
| ~ added 'package' declarator | |||
| ~ added $*OS, $*OSVER, $*EXECUTABLE_NAME (azawawi++ for initial code) | |||
| ~ some refactorings and other cleanups | |||
| parrot: | |||
| ~ fixed a few bugs, applied some patches from contributors | |||
| .end | |||
| chromatic | pmichaud? | 18:42 | |
| pmichaud | ** Rakudo spectest_regression: 205 files, 4363 passing, 74 failing | ||
| == PCT stuff | |||
| : Worked with Tene++ to get rid of legacy 'Foo::Bar' classnames in Parrot | |||
| : Everything in PCT (including PGE) is moving to ['Foo';'Bar'] form internally | |||
| == Rakudo stuff | |||
| : answered questions about package, 'is export', compiler variables, etc. | |||
| : 74 failures in spectest_regression are primarily due to Complex PMC issues | |||
| .end | |||
| chromatic | rurban? | ||
| rurban | allison++ | ||
| rurban's report: | |||
| * I'm live at the frankfurt.pm meeting. They are all saying hi! | |||
| * Released parrot-0.7.1 for parrot and updated pdd30_install branch with the new findings. | |||
| * One remaining problem with tools/dev/install_files.pl then I can merge. Simply files overwriting each other. No ticket yet. | |||
| * 1 q | |||
| EOR | |||
| chromatic | Wknight8111? | ||
| Wknight8111 | * Added lots of function-level documentation to files that needed it. | ||
| * Looking over more of the calling convention stuff, preparing to work on that. | |||
| * Not a lot of time for much else yet. | |||
| EOR | |||
| chromatic | Did I miss anyone? | ||
| Tene | Me! | 18:43 | |
| chromatic | My apologies; go ahead. | ||
| Tene | * Blocks are based as named positions instead of positional to functions in cardinal | ||
| * All blocks accept an optional block argument in cardinal | |||
| * Little bit of work on control exceptions in PCT | |||
| * Lots of progress with pmichaud++ on migrating everything to use ['real';'parrot';'namespaces'] instead of 'Fake::Namespaces' | |||
| * Curry is delicious | |||
| KTHXBAI | |||
| chromatic | cotto, you had a question. | ||
| cotto | Allison, you suggested a change to some code that depended on the old mmd_dispatch_* function. I changed some phparray code to use VTABLE_cmp before I saw your post, and it seemed to work fine. When should one vs. the other be used? | ||
| allison | A direct vtable call is fine. | 18:44 | |
| In fact, a direct vtable call is preferable | |||
| because it allows an individual PMC to do single dispatch instead of the default multiple dispatch | |||
| (a good speedup for frequently used vtable functions like 'cmp') | 18:45 | ||
| The multiple dispatch versions are in the 'default' PMC's implementations of the vtable functions. | |||
| So, they'll only be called in PMCs that don't implement the vtable function. | 18:46 | ||
| EOA | |||
| chromatic | cotto, next question. | ||
| cotto | In PHP, arrays of the same size with different keys are not considered comparable, and such a comparison returns null. | ||
| Since the cmp VTABLE only returns an INTVAL, would it be a good idea to throw an exception so the HLL code can catch it and return the expected null? | |||
| If so, should I add something like EXCEPTION_ARRAYS_NOT_COMPARABLE to include/parrot/exception.h | |||
| eoq | |||
| allison | so, arrays of the same size with different keys return a different "not equal" value than other comparisons? | 18:47 | |
| cotto | yes | ||
| I know | 18:49 | ||
| allison | So the PHP comparison operator or function is a wrapper around the bare parrot cmp, and will handle returning the different values. | ||
| cotto | that's what I was thinking | 18:50 | |
| allison | you could do it by throwing an exception from within CMP, but that might cause problems for any other language using PHP's arrays, because PHP doesn't throw an exception on that condition | ||
| cotto | (see example #1 us.php.net/manual/en/language.opera...rison.php) | ||
| allison | we have talked about creating variants of the basic integer-returning 'cmp' vtable functions that return a PMC instead | ||
| cmp_pmc, or something like that | 18:51 | ||
| cotto | That'd solve the problem nicely | ||
| allison | which could then return PMCNULL | ||
| (or if they're equal, a PMC boolean value) | |||
| write a patch for that, and we'll review it on the list | 18:52 | ||
| cotto | ok. Thanks. | ||
| allison | (likely pretty simple, touching src/vtable.tbl and src/pmc/default.pmc, plus your PMCs) | 18:53 | |
| chromatic | NotFound, you have the next question. | ||
| NotFound | I'm testing a way to get rid of string_cstring_free by using a pmc that takes care of free the C string when garbage collected. | 18:54 | |
| The question is if this will be a good design. | |||
| allison | what are the advantages of making it a PMC over just using a Parrot string? | ||
| NotFound | This is whay I have done: nopaste.snit.ch/14245 The PMC, a helper function, and some usages | ||
| chromatic | pinnable | 18:55 | |
| allison | strings are pinnable | ||
| NotFound | Parrot string is already too complex IMO | ||
| allison | I'm not arguing against it, it's just a straight question of where and why we'd use the PMC instead of a Parrot string | 18:56 | |
| if there are enough reasons, then it's a worthwhile addition | 18:57 | ||
| NotFound | string have no vtables, use for this purpose will require access to string internals. | ||
| allison | NotFound: right, we'll be simplifying the strings implementation in the strings milestone, but I didn't mean adding more code to strings, I meant just using the existing strings | 18:58 | |
| use for what purpose? | |||
| NotFound | The intended usage is just to store the C string and free it when garbage collected | ||
| Avoid memory leaks on exceptions before the strinc_ctring_free | 18:59 | ||
| chromatic | How is that different from what Parrot_string does now? | ||
| allison | where do you store the PMC that stores the C string? | ||
|
18:59
pyrimidine joined
|
|||
| NotFound | In cases I tested, in any part. They are all short life. | 19:00 | |
| allison | NotFound: It sounds like you've spent some time thinking through this already, while we're just jumping into the idea mid-stream. | 19:01 | |
| NotFound: Take some time to write up an email for parrot-dev. | |||
| NotFound: The you can describe the problem, and why this is a good solution for the problem. | |||
| NotFound | This is another question I have: what will be the way to store temprary PMC when the function call things that possible can enter a runloop? | 19:02 | |
| Pushing a context will be fine? | |||
| chromatic | In theory, stack and register scanning will find it. | ||
| CPU register scanning, that is. | |||
| NotFound | We do that thing? | ||
| allison | NotFound: is this a temporary PMC only used within C code? | 19:03 | |
| Wknight8111 | In theory, I wouldn't rely on our current stack and register scanning capabilities | ||
| NotFound | allison: yes | ||
| allison | NotFound: that's dod_register_pmc and dod_unregister_pmc when you're done | 19:04 | |
| NotFound | allison: but we can unregister if some thing called throws. | ||
| can't | |||
| allison | it's exactly the same problem as freeing C strings, yes | ||
| but, that's unavoidable | 19:05 | ||
| Wknight8111 | What if we maintained a separate registry for objects that are temps and need to be cleared if there is a throw? | ||
| NotFound | I was thinking about using a context: creatng it, frreing it if all goes well, and let the exception system take care otherwise. | ||
| Wknight8111 | like a dod_temp_register_pobj, etc | 19:06 | |
| NotFound | I mean, pushing and popping, not creating and freeing | ||
| chromatic | Attaching temps to a context makes fair sense to me. | ||
| allison | still the same problem, how do you free the context if an exception is thrown and you never return to this bit of code? | ||
| chromatic | Don't non-resumed exceptions unwind the context graph? | ||
| NotFound | chromatic: that is I was wondering. | 19:07 | |
| allison | chromatic: if an exception handler is invoked, it will modify the context stack | ||
| but, presumably this context isn't on the stack, otherwise it would mess up the execution order | 19:08 | ||
| chromatic | As soon as a context is unreachable, it should get cleared. | ||
| NotFound | allison: I explained bad, the ida is to use push_context and pop_context | ||
| allison | but contexts aren't PMCs (yet) | ||
| chromatic | We don't leak contexts now, so I assume that *something* clears them. | 19:09 | |
| allison | NotFound: okay, if you're doing that it will cause problems, because an exception handler only stupidly pops the context, it won't know to pop your extra context | ||
| Wknight8111 | do we know that we don't leak contexts? Maybe they've never been used in the way NotFound is thinking | 19:10 | |
| allison | chromatic: yes, they're pushed and popped through the flow of executions | ||
| ideally (this is a partial patch not yet committed) contexts will be PMCs | |||
| stored in the linked list of continuations for CPS | 19:11 | ||
| and then they will be garbage collected when unreachable | |||
| NotFound | Then we have the same problem of premature mortality if we create a context attached to nothing. | 19:12 | |
| allison | but, that doesn't give leeway for an extra place to store "internal" PMCs | ||
| chromatic | Why? I can see why a context could represent the context into C code. | ||
| allison | but where is the context stored so it'll be reachable for GC mark? | 19:13 | |
| it's not associated with a continuation, because it's not part of the control flow | |||
| chromatic | Why is it not part of the control flow? Control has already flowed to it. | ||
| NotFound | If we use push and pop, is a part of a chain while it exists. I assume this chain is marked. | 19:14 | |
| Wknight8111 | If you want to mark a PMC live, use pobject_lives. That will spare it through the next GC cycle | ||
| allison | it's an extra context, created to store extra variables | ||
| chromatic | If you're calling into non-Parrot C code and you don't have multiple interpreters, you're not going to run GC until that C code returns. | ||
| Wknight8111 | keep it alive when you are using it, and after a throw, nothing will be keeping it alive anymore | ||
| allison | Wknight8111: yes, but that'll only last until the next GC cycle, so not reliable | ||
| chromatic | If you're calling into non-Parrot C code and you do have multiple interpreters, you have to anchor the current context somehow anyway. | 19:15 | |
| NotFound | Wknight8111: but if we call a function that maybe enter another runloop, we don't know how many GC cicles he must survive. | ||
| allison | NotFound: right | ||
| NotFound | And if we have a multithreaded GC; even worse | ||
| allison | okay, <pause> | ||
| let's take this to the mailing list | 19:16 | ||
| definitely important questions here | |||
| chromatic | rurban, you had a question. | ||
| rurban | May I merge pdd30_install? At first with my suggested layout variant 1, which is in the branch and has the least needed changes, but we can change it later. | ||
| and my battery is running out in 15min | |||
| allison | rurban: it needs a code review, but potentially yes | ||
| NotFound | Ups, forget one question (is short, I promise) | 19:17 | |
| chromatic | kj has the next question. | ||
| rurban | who will over it? | ||
| look over it | 19:18 | ||
| allison | rurban: IIRC from looking at it before, it involved some design questions, so I should look it over | ||
| rurban: I can do that quickly tonight, and let you know if there are things we need to talk through further | 19:19 | ||
| rurban | ++ | ||
| allison | has it been kept in sync with trunk? | ||
| or, should I do an update from trunk? | |||
| rurban | tommorrow | ||
| allison | tomorrow? | 19:20 | |
| rurban | promised | ||
| allison | not following. you mean you'll do an update from trunk tomorrow? | ||
| rurban | I'll prepare the merge tomorrow to tomorrows trunk | 19:21 | |
| allison | okay, let's talk tomorrow | 19:22 | |
| chromatic | kj? | 19:23 | |
| kj | alrighty, my question is not so complex: | ||
| My question consists of 2 subquestions, a and b: | |||
| 1.a: Some Parrot ops do not exist, but are faked. For instance, the opcode add_i_ic_ic (e.g. add $I0, 2, 3) is in IMCC replaced by 'set_i_ic'. | |||
| I understand this was a design decision, as this saves # of ops (which is fine), but I wonder how far this should be taken. AFAICT, the same is done | |||
| for 'lt_ic_ic_ic', e.g. 'lt 10, 20, L1': jump to L1 if 10 is less than 20; in other words, always jump to L1. | |||
| While I can see the usefulness of 'add_i_ic_ic', I wonder to what extent the same trick should be used for non-math ops (as it is currently done for lt), | |||
| as it add complexity to the PIR compiler (whether that be IMCC or PIRC) | |||
| allison | maybe just a little complex ;) | 19:24 | |
| kj | well, long, yes; not complex :-) | ||
| pmichaud has a Complex question. :-) | |||
| kj | :-) | ||
| Basically, IMCC (and I'm working to implement that in pirc) fakes some instructions, so that it allows 'instrucitons' that don't exist | 19:25 | ||
| NotFound | BTW, actually the machanic used to implement that things force a change in optimization level | ||
| kj | my question is: to what extent is this desirable? I think, if the instruction does not exist, then the compiler (writer) should not generate it. | 19:26 | |
| (or the PIR programmer should not use it) | |||
| allison | kj: I've just ripped out a large number of "fake" instructions and replaced them with real opcodes in the MMD branch | ||
| NotFound | Or we must create it | ||
| pmichaud | speaking from the code generator perspective.... | ||
| allison | I'm not a huge fan of making fake instructions | 19:27 | |
| pmichaud | assuming I have an AST node where the operation is "add" and the operands are two constant nodes | ||
| kj | so, for instance add_i_ic_ic does not exist. I can see it's a Good Thing from the perspective of Parrot (and number of ops) | ||
| pmichaud: imcc currently precomputes this | |||
| (and pirc does too ;-) ) | |||
| allison | on the other hand, there's a good question whether we even need separate "i" and "ic" variants of the opcodes | ||
| pmichaud | there is some advantage in being able to generate add $I0, 2, 3 and have it work. | ||
| kj | allison: i vs ic: I don't know about that; don't know (yet) about inner workings of ops in so much detail. | 19:28 | |
| pmichaud | on the other hand, given that nearly everything ends up in PMCs from an HLL perspective, the places where one could reasonably expect to see add $I0, 2, 3 being used are pretty small at this point. | ||
| allison | the 'i' variant does a register lookup | 19:29 | |
| pmichaud | right now PAST::Compiler force the first IN operand to always be a register, just to avoid this sort of thing. | ||
| allison | and if we fake up 'add_i_ic_ic', why don't we fake up 'add_i_ic_i' or 'add_i_i_ic'? | 19:30 | |
| kj | anyway, this doesn't need an answer right now.. just wanted to give it some attention. | ||
| why would one do that? | |||
| the faking of _ic_ic is because we can precompute the result, so add_i_ic_ic --> set_i_ic | 19:31 | ||
|
19:31
rdice joined
|
|||
| allison | a basic form of inlining, yes | 19:32 | |
| kj | yes | ||
| exactly | |||
| NotFound | kj: this is another problem, that type of op sintetizing does not involve register creation. | ||
| allison | I don't see the need to require that of all PIR compilers | ||
| kj | well, it's not a problem to do it, but I was working on it, and found it clutters up my neat code :-) | 19:33 | |
| allison | heh :) | ||
| NotFound | The ones that creates register have the additional problem of changing optimization level, in the current implementation. | ||
| allison | so benchmark with and without | ||
| kj | so before I continue and finish it, just wanted to know if it's worht it | ||
| allison | see if the clutter actually buys anything in practical terms | 19:34 | |
| kj | well it won't slow down the pir compiler that much | ||
| chromatic | The problem is that we're optimizing in the wrong place. | ||
| kj | (I suspect) | ||
| I know that at the time it was a design decision to remove (or not add, not sure if it ever existed) add_i_ic_ic | |||
| allison | that's my guess, that the optimization of inlining integer addition won't actually buy us much speed | ||
| pmichaud | I'd be willing to bet that the add_i_ic_ic instruction is very rarely generated. | 19:35 | |
| kj | well, maybe a bit; but on average, how many add_i_ic_ic instruction would a program use? | ||
| allison | add_i_i_ic is far more common | ||
| kj | yes | ||
| allison | opcodes are cheap, code clutter is expensive (over the long term) | 19:36 | |
| pmichaud | my suggestion would be to not support it, and see what breaks. | ||
| kj | how high are the costs of having a larger opset? | 19:37 | |
| ehm, instruction set? | |||
| I mean, there's about 1250 right now I think | |||
| how much would it cost, runtime cycles, to have an opset of, say, 1500 ? | |||
| is it just memory we're talking here? | |||
| allison | kj: depends on the runcore | ||
| kj | let's say the switch | 19:38 | |
| chromatic | The bigger the set, the more cache pressure for ops. | ||
| kj | it's the slow one | ||
| allison | well, for the switch, it potentially has to run through 1500 alternatives before hitting the right one | 19:39 | |
| (that's why it's the slow one) | |||
| kj | oh ok | ||
| well if it runs through 1200 or 1500, shouldn't matter that much | |||
| allison | yes, it's a small percentage addition | ||
| kj | Ok, well. Just so you know how this is done :-) | 19:40 | |
| Then question b: i found that div_i_ic_ic /is/ a valid opcode | |||
| (and same for fdiv) | |||
| allison | yeah, keep them | ||
| kj | I wondered why these are still there. | ||
| allison | add the other i_ic_icz | ||
| i_ic_ic's | 19:41 | ||
| kj | well, we can see whether they're ever used first... | ||
| allison | likely a partial conversion | ||
| well, if they're *never* used, then sure, delete them, and don't add the other i_ic_ic's | |||
| kj | right. | ||
| and 'being used' I take it that tests don't count? | 19:42 | ||
| allison | I'm in favor of deleting the i_ic_i's while we're at it | ||
| (at least for the symmetrical ones like add) | |||
| kj | you mean sub is asymmetrical? | ||
| pmichaud | fwiw, at present PAST can never generate i_ic_* | ||
| NotFound | Non commutative | ||
| pmichaud | or n_nc_* | ||
| kj | NotFound: right. | 19:43 | |
| allison | kj: yes, tests don't count (since they mostly were created because of the features) | ||
| pmichaud | it's possible that PGE or other hand-written code is making use of the i_ic_i opcodes, though. | ||
| allison | for 'add' it's not needed | ||
| kj | pmichaud: I think the variants with only 1 'ic' should be kept, right? | ||
| NotFound | pmichaud: we can cut that hands | 19:44 | |
| allison | for 'div', you might want to divide a constant by a register variable | ||
| kj | I was only talking about _ic_ic variants. | ||
| not at all about i_ic_i or i_i_ic | |||
| allison | yeah, I'm adding to it: while we're getting rid of i_ic_ic, consider getting rid of many i_ic_i variants | 19:45 | |
| kj | .. in favor of using i_i_ic? | ||
| allison | kj: yes, where the order is not semantically significant | 19:46 | |
| kj | right, I get it. | ||
| ok. well I won't get to this on short term, still trying to finish pirc as far as possible... | 19:47 | ||
| allison | (if we're going to work on trimming extra opcodes, trim the really useless ones) | ||
| yup, not urgent | |||
| chromatic | NotFound? | ||
| NotFound | I added support to get the min and max values of INTVAL and make them available from sysinfo | 19:48 | |
| The problem is that it requires availability of limits.h or a platform hint | |||
| What will be the way to enforce this, and we want to enforce it? | 19:49 | ||
| allison | if we don't enforce it, then we can't really use it for tests (which is the driving purpose of it) | ||
| but, how reliable will it be across platforms? | 19:50 | ||
| NotFound | allison: as reliable as the people wirting the platform hint make it. | ||
| allison | well, our platform hints aren't that fine-grained | 19:51 | |
| NotFound | I mean, if the limits.h is not reliable, the hint must be added, the same as if it had not. | ||
| allison | we don't have "linux 64bit" separated from "linux 32 bit" | ||
| NotFound | Yes, but the hint can define the symbols based in other symbols that platform can have. | 19:52 | |
| allison | more reliable would be to detect it during configure | 19:53 | |
| NotFound | Some like: #include <something> #define INT_MAX MAXINT | ||
| allison: I thinked about that, but puts more problems in the way to make parrot cross-compilable | |||
| allison | not any more than the current strategy for picking which hints file to use | 19:54 | |
| NotFound | allison: yes, but that can be easily solved with command-line options. | 19:55 | |
| allison | (and if you make that selectable for cross-compilation, you can make the int size selectable too) | ||
| NotFound | Mmmmm... yes. | ||
| I'll think and test that way, don't lose next episode. | 19:56 | ||
| In the meantime the sysinfo returns 0 when unknown in the current implementation, FYI | 19:57 | ||
| allison | that's reasonable | ||
| NotFound | And there is a test skipped except in linux that checks are not 0 | 19:58 | |
| Unskipping for other platforms well tested well be welcomed. | 19:59 | ||
| will | |||
| allison | does that wrap up the questions? | 20:00 | |
| NotFound | For me yes | 20:01 | |
| kj | me too | ||
| allison | ok, that's a wrap, thanks everybody! | 20:02 | |
| chromatic | Talk to you all next week. | ||
| kj | bye. | ||
| NotFound | Hasta la vista | 20:03 | |
|
20:04
pmichaud left
20:05
NotFound left,
jhorwitz left
20:06
moritz left
20:12
chromatic left
20:13
PacoLinux left
20:17
cotto left
20:29
allison left
20:30
rdice joined
20:54
pyrimidine joined
21:49
pyrimidine joined,
pyrimidine left
22:25
Whiteknight joined
|
|||