On Sat, Feb 02, 2002 at 05:40:30PM -0300, Daniel Grunblatt wrote:
> 
> On Fri, 1 Feb 2002, Nicholas Clark wrote:
> 
> > On Fri, Feb 01, 2002 at 01:32:13AM +0000, Nicholas Clark wrote:

> > >    Either way, I found I was fighting the current jit which expects (at worst)
> > >    to be able to split a 32 bit constant into 2 (possibly unequal) halves
> > >    stored in two machine instructions. To be more flexible jit would need to
> 
> Wrong, that's not possible on the ALPHA, or may be it is (don't know much
> about ALPHA assembly) but I don't use it.

Ooops. Sorry. I couldn't guess much of what the alpha was up to. I felt
I had a better grasp of what the sparc assembler was doing.

> I think the main problem with ARM is that you don't have intructions like
> the alpha "ldah" or the sparc "sethi" which do something like:

What does ldah do? [Hey, what does sethi do for that matter? :-)]

Oh. You've just said:

>  "ldah $1,2($2)"
> 
>  $1 = (2 * 65536 + $2)
> 
> That why we should use a constant pool.


Was sethi the one I thought that could put some of a 32 bit constant in a
register. So that in combination with another opcode one could load all 32
bits of a register. And if ldah is not loading the bottom 17 bits of the
register, does that mean that there's another alpha opcode to load
(at least) 17 bits of a register?

If so, yes, that's the problem. There is no ARM opcode, well no pair of ARM
opcodes, to load a 32 bit value into a register.
You either have to build the 32 bit value from one or more arithmetic ops,
or arrange to have the 32 value in a data word somewhere "nearby" to do a
PC relative memory load.


> > CONCLUSION:
> >
> > So I start needing to build simple, parameterisable code, but more complex
> > than the current system allows.
> 
> The current system handles object code at build_asm time, if understand
> what you mean, you need it to handle assembler?

Sorry, I think I didn't explain clearly. Probably because it's not totally
clear in my head. I'm quite happy to write C code that generates or mangles
the correct ARM instructions. It's more that I felt it was getting too
tricky to try and specify the parameterisable bits of the assembly code
by using special syntax in core.ops, which the perl code has to recognise as
special syntax, substitute in legal assembler syntax, assemble the substitute
code, disassemble it, remember where the special syntax was, and then note
for build_asm where something was.

I was also getting quite confused trying to remember what all the numbers
were in jit2h.pl. (Well, I know that they are offsets into the entries in
the C struct, but there weren't enough symbolic names to jog my memory.

Also, it was starting to feel like it was getting rather "special cased" for
everything. I wanted some clear way to define

in core.ops:

load r1 with the value of int reg N

or

load r2 with the address of the interpreter

and not have to worry about whether I was getting that via a register load,
or arithmetic from some other register
(this was particularly acute in the "call a function" code.
It might be a good idea to make a special function to make JIT code for
"call a parrot opcode", as this could be more specialised, and hence more
efficient, than the general "call some C function")

I also wanted to be able to define somewhere (in one place, not 3):

Addresses of int reg N are found by offsetting from CPU register 4

and for build_asm be able to define pairs of things, such as:

this relocation is "mangle an LDR instruction"
the value is "address of int reg N - address of int reg 0"

or
this relocation is "mangle a B instruction"
the value is CPU pc - address of function "foo"

> > 1: reads that
> > 2: mangles &INT_REG[1] to something that is syntactically legal
> > 3: calls out to as
> > 4: calls objdump to disassemble it
> > 5: looks for a pattern to spot where the special bit is.
> >    AARGH. The "special bit" is the last 12 bits of the instruction.
> >    If I have to convert the disassembled instruction back to binary, and
> >    then match /^....010...(.)....................$/ to find out if it's
> >    LDR or STR (with $1 determining which) I feel I might as well write my
> >    own ARM assembler in perl
> No, you can leave everything as it is in this stage and then over write
> the necesary bits at build_asm time.
> 
> Anyway steps 3 to 4/5 may disappear as we should write our own assembler
> because of the objdump problems.

Ah right. OK. Writing an ARM assembler should be relatively easy, for the
subset of ARM instructions used the majority of the time. I may even be
able to "phone a friend" and find one pre-written.

[er. then someone is going to want the floating point assembler.]

> > 7: I need to teach jit.c that on this architecture it is doing INT_REG
> >    loads in this way. (There is a forest of #ifdef rapidly growing there, as
> >    it seems every architecture is not as "simple" as x86)
> There are some ways to get rid of the #ifdef forest, but all will make
> build_asm slower, I still need to find the fastest.
> And that's because every architecture is different.

Might it be better to have a jit C file for each architecture?
Or does that result in function calls where inline code would be preferred?

> > And HELL. I'm going to need to do the same hoop jumping for NUM_REG, string
> > REG, INT_CONST, NUM_CONST, current opcode, aargh.
> Did I ever said it was easy?

:-)

No.

And I suspect a lot is being learnt by attempting to make jits on different
CPUs. Well, trying to make 1 jit system that does all CPUs. Maybe that's the
problem.

> > So for set_i_i I'd be passed something like (0xdeadbeef, "set_i_i", 2, 3)
> > and I'd return 8 bytes of ARM assembler that do it.
> May be you can explain all that with a little bit more details.

Yes, but not tonight. It's near bedtime and I'm distracted by fighting a
FreeBSD install that really doesn't want to work, and I need to make the
machine bootable before I turn it off and go to bed.

Well, without code examples:

My gut feeling was that I felt I'd find it easier to write code, even C code,
to output the ops, and have the build_asm JIT call my routines to make ops.
I felt that the current table based substitution system wasn't flexible
enough in the right way.

To be fair, it's actually extensible from the current system.
I was considering that build_asm has a table of function pointers, one for
each opcode.
(or a smaller table and a "default" function that behaves as if there's a
large table)

and when it wants the machine code for a parrot op, it calls the function
in this table.
For x86, it may be that every pointer is to the same function, which is
a function that calls the current jit system, and uses the current system.
Actually, it probably shouldn't return the machine code, but write it
onto the end of the buffer it was passed in.


This isn't thought through. I'm not convinced that I'm not making a
mistake here with some sort of blind alley.

Nicholas Clark
-- 
EMCFT http://www.ccl4.org/~nick/CV.html

Reply via email to