the created code has currently (amongst other) a line:
newsub Px, .Sub, foo
where the label "foo" is a relative branch offset.
This is suboptimal for several reasons:
- it creates a new PMC for every call albeit in 99.99% of cases the PMC constant for the sub could be used directly [1]
- the created subroutine PMC lacks information: only the start label is known, when the PMC is created. The subroutine's name and the end of the opcodes for that sub isn't in Px. Obtaining that information would be a costy O(n) lookup in the fixup segement of the bytecode (or in the constants, which is probably still larger). Subroutine length and name information is needed for introspection and for bounds checking in safe run cores.
So I think, we should do instead something like this:
get_sub Px, foo # find the PMC with label "foo" in constants # at compile time and # replace "foo" with the index in constants clone Py, Px # if Px would be modified, clone it first [1]
find_global Px, "foo" # we have that already, but hash lookup!
The current syntax:
newsub Px, .Closure, foo
could remain unchanged, except that again under the hood, the label foo is replaced with the index in the constant table. For closures it's probably best to actually return a new object per default, as a closure might have different state in the lexical pad in each invocation.
>the created code has currently (amongst other) a line:
> newsub Px, .Sub, foo
>where the label "foo" is a relative branch offset.
>This is suboptimal for several reasons: [snip] >So I think, we should do instead something like this:
> get_sub Px, foo # find the PMC with label "foo" in constants > # at compile time and > # replace "foo" with the index in constants > clone Py, Px # if Px would be modified, clone it first [1]
> find_global Px, "foo" # we have that already, but hash lookup! [snip] >Is that reasonable?
Yeah, but I think I've a better approach. Instead of doing this, let's just get PMC constants implemented. (I know -- "just" he says :) Each sub in a bytecode segment can get a slot in the constant table, and we can map in sub fetches to the (as of now nonexistent) set_p_pc op.
While we're at it we should see about adding in an integer constant table that can be fixed up on load (to take care of those pesky "what number did my PMC class map to" problems more quickly than the hash lookup) but we can put that off a bit. -- Dan
--------------------------------------it's like this------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
>> get_sub Px, foo # find the PMC with label "foo" in constants > Yeah, but I think I've a better approach. Instead of doing this, > let's just get PMC constants implemented.
Well, they are implemented, at least partly. Sub PMCs are in the constant table. The funny C<get_sub> "opcode" is actually a ...
> set_p_pc op.
... with the small difference, that at compile time, the integer argument is a label (offset).
What about the other part: closures - should they be created via "new" always?
> While we're at it we should see about adding in an integer constant > table that can be fixed up on load (to take care of those pesky "what > number did my PMC class map to" problems more quickly than the hash > lookup) but we can put that off a bit.
You are thinking of a 2-stage lookup?
I1 = dynamic_type I0 # lookup runtime type mapping $P0 = new I1
>>> get_sub Px, foo # find the PMC with label "foo" in constants
>> Yeah, but I think I've a better approach. Instead of doing this, >> let's just get PMC constants implemented.
>Well, they are implemented, at least partly. Sub PMCs are in the >constant table. The funny C<get_sub> "opcode" is actually a ...
>> set_p_pc op.
>... with the small difference, that at compile time, the integer >argument is a label (offset).
Then we should toss the difference and have a single op to access the PMC constant table. We're going to need to do this for real PMC constants, and I don't see any point to have two ways to do the identical same thing.
>What about the other part: closures - should they be created via "new" >always?
Yeah, I think so. They always need to capture a lexical scope, so I think they're going to have to.
> > While we're at it we should see about adding in an integer constant >> table that can be fixed up on load (to take care of those pesky "what >> number did my PMC class map to" problems more quickly than the hash >> lookup) but we can put that off a bit.
>You are thinking of a 2-stage lookup?
> I1 = dynamic_type I0 # lookup runtime type mapping > $P0 = new I1
More like what we do right now with all the other constant types. Integers aren't in a constant table since we just inline them, but the nice thing about a constant table is you can do fixups on it while not touching the actual bytecode, leaving it readonly and mmapped and all that.
While I'd prefer to leave integers inlined in general, having an integer section of the constant table that can be accessed when necessary makes the things that need integer fixup easier.
And yeah, this imples that our constant table isn't necesasrily constant. I'm OK with that, though. :)
>A normal IntList Array can do that too.
Sure, it could. But we're trying to make sure we provide all the standard facilities in one place so all the different compiler writers don't have to bother. Fixed-up integer constants is a reasonable foundation piece for us to provide. -- Dan
--------------------------------------it's like this------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
Dan Sugalski wrote: > At 4:36 PM +0200 10/29/04, Leopold Toetsch wrote: >> Well, they are implemented, at least partly. Sub PMCs are in the >> constant table. The funny C<get_sub> "opcode" is actually a ...
>>> set_p_pc op.
>> ... with the small difference, that at compile time, the integer >> argument is a label (offset).
> Then we should toss the difference and have a single op to access the > PMC constant table.
I'm all for that. We just need set_p_pc. OTOH we need some syntax bits, what this PMC constant in "_pc" denotes and how it's constructed. For subroutine PMCs it's quite simple: the subroutine label is defining the Sub PMC. So its probably something like:
.pmc_constant .Sub, "foo"
A complex PMC could be
.pmc_constant .Complex, "2+3i"
Putting PMC constants into the constant table isn't the problem here, nor changing the format on disc to use freeze/thaw, the construction of more or less arbitrary PMC constants needs some thoughts.
We could probably just define that the C<new_extended> vtable of a class is responsible for constructing an appropriate object from a given string. And that get's frozen to bytecode.
[ integer constants ]
> More like what we do right now with all the other constant types.
Ah, sounds good.
> Integers aren't in a constant table since we just inline them, but the > nice thing about a constant table is you can do fixups on it while not > touching the actual bytecode, leaving it readonly and mmapped and all that.
> While I'd prefer to leave integers inlined in general, having an integer > section of the constant table that can be accessed when necessary makes > the things that need integer fixup easier.
Well, we can't have both schemes coexisting. When running "add_i_ic" or "new_p_ic". we have to know, whether the integer is inlined or in the constant table. For RISC cpus JIT code a constant table is better even for integers.
And having integer constants in the constant table would open the path to compile an INTVAL=64bit configuration on a 32-bit machine, where opcode_t is 32 bits.
That leads again to my warnocked proposal to just toss all variants of opcodes that have constants too. With all possible PMC constants in the constants table, we get another (estimated) times two opcode count increase.
When we now have only: add_p_p_p we'd get: add_p_p_pc add_p_pc_p add_p_pc_pc additionally.
We'll start blowing caches. The code get's too big (think compile problems with CGoto). JIT maintainers have to support all these opcodes.
Please consider to reduce constant usage to 4 opcodes:
set_i_ic set_n_nc set_s_sc set_p_pc
(yes, that imposes a bit more pressure on the register allocator, but these constants are reloadable all the time and don't need spilling)
> And yeah, this imples that our constant table isn't necesasrily > constant. I'm OK with that, though. :)
Well, constant Sub PMCs have already offsets relative to their code segment in the PBC. On loading the segment, this gets converted to absolute code addresses. Forget the constantness of constant segments ;)
We'll do yet another fixup and I really like the idea WRT PMC types.
>>>Well, they are implemented, at least partly. Sub PMCs are in the >>>constant table. The funny C<get_sub> "opcode" is actually a ...
>>>> set_p_pc op.
>>>... with the small difference, that at compile time, the integer >>>argument is a label (offset).
>>Then we should toss the difference and have a single op to access >>the PMC constant table.
>I'm all for that. We just need set_p_pc. OTOH we need some syntax >bits, what this PMC constant in "_pc" denotes and how it's >constructed. For subroutine PMCs it's quite simple: the subroutine >label is defining the Sub PMC. So its probably something like:
> .pmc_constant .Sub, "foo"
For now I'm fine with restricting it to sub PMCs and opening it up to more later. :)
>>More like what we do right now with all the other constant types.
>Ah, sounds good.
>>Integers aren't in a constant table since we just inline them, but >>the nice thing about a constant table is you can do fixups on it >>while not touching the actual bytecode, leaving it readonly and >>mmapped and all that.
>>While I'd prefer to leave integers inlined in general, having an >>integer section of the constant table that can be accessed when >>necessary makes the things that need integer fixup easier.
>Well, we can't have both schemes coexisting.
Right, and I don't think it's a good idea to switch out from what we have now. For integer constants I think we ought to have an explicit op for fetching them:
getconstant Ix, Iconstantnumber
or something like that. We can have a corresponding setconstant op for constants that... aren't. And for code to set up the constant table in the first place.
>And having integer constants in the constant table would open the >path to compile an INTVAL=64bit configuration on a 32-bit machine, >where opcode_t is 32 bits.
>That leads again to my warnocked proposal to just toss all variants >of opcodes that have constants too. With all possible PMC constants >in the constants table, we get another (estimated) times two opcode >count increase.
>Please consider to reduce constant usage to 4 opcodes:
I have, and no. (though we can toss all the two-constant I, S, and N forms) We leave things as-is. When we run up to our 1.0 release we can run some analysis on the different compilers to see what ops aren't being used and pare out the list at that point
>>And yeah, this imples that our constant table isn't necesasrily >>constant. I'm OK with that, though. :)
>Well, constant Sub PMCs have already offsets relative to their code >segment in the PBC. On loading the segment, this gets converted to >absolute code addresses. Forget the constantness of constant >segments ;)
:) Works for me. I want to revisit packfile formats and whatnot again soon anyway -- I want us to start adding in source line number & source lines to the packfiles and have it available as we run so we can start throwing more informative error messages and making the debugger more useful. -- Dan
--------------------------------------it's like this------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
Dan Sugalski <d...@sidhe.org> wrote: > At 10:17 PM +0200 10/29/04, Leopold Toetsch wrote: > >That leads again to my warnocked proposal to just toss all variants > >of opcodes that have constants too. With all possible PMC constants > >in the constants table, we get another (estimated) times two opcode > >count increase.
> >Please consider to reduce constant usage to 4 opcodes:
> I have, and no. (though we can toss all the two-constant I, S, and N > forms) We leave things as-is. When we run up to our 1.0 release we > can run some analysis on the different compilers to see what ops > aren't being used and pare out the list at that point
As a compromise, it strikes me that we're using 32-bit numbers to encode registers, but only using five bits of that 32. Could we not have constants indicated by numbers starting at 32 or something similar? We could probably do something very clever to abstract it, like load all the constants into a reserved, dynamically-sized set of registers starting at [INSP]32.
A scheme like this would allow us to consolidate the constant and register variants of all the ops, while still allowing us to use constants whenever we wanted. I'm not sure if the cost--allocating more register banks and loading the constants into those registers--is worth it, but it might be worth thinking about at least.
-- Brent 'Dax' Royal-Gordon <br...@brentdax.com> Perl and Parrot hacker
> ... We could probably do something very clever to abstract it, > like load all the constants into a reserved, dynamically-sized set of > registers starting at [INSP]32.
That doesn't work. Registers are accessed per interpreter/thread and now per subroutine (with the frame pointer). Constants are always the same (modulo initialization/fixup phase) during program execution.
> A scheme like this would allow us to consolidate the constant and > register variants of all the ops,
I'd like to, but the meaning of these 2 kinds is too different. Have a look at JIT code:
fldl 0x87652468 # get number from const_table stpl 0x88(%ebx) # store it in Parrot reg N1 mov 4(%ebx), %eax # get I1