cvsuser 02/08/01 20:25:37
Modified: . jit.c
config/auto jit.pl
jit/sun4 jit_emit.h
Added: jit/arm core.jit jit_emit.h
Log:
ARM JIT
Here goes. This *isn't* functional - it's the least amount of work I could
get away with (before midnight) that gets the inner loop of mops.pasm JITted.
Courtesy of: Nicholas Clark <[EMAIL PROTECTED]>
Revision Changes Path
1.21 +67 -1 parrot/jit.c
Index: jit.c
===================================================================
RCS file: /cvs/public/parrot/jit.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -w -r1.20 -r1.21
--- jit.c 23 Jul 2002 02:09:27 -0000 1.20
+++ jit.c 2 Aug 2002 03:24:02 -0000 1.21
@@ -1,12 +1,18 @@
/*
** jit.c
**
-** $Id: jit.c,v 1.20 2002/07/23 02:09:27 josh Exp $
+** $Id: jit.c,v 1.21 2002/08/02 03:24:02 grunblatt Exp $
*/
#include <parrot/parrot.h>
#include "parrot/jit.h"
+#ifdef ARM
+#ifdef __linux
+#include <asm/unistd.h>
+#endif
+#endif
+
/*
** optimize_jit()
** XXX Don't pay much attention to this yet.
@@ -128,6 +134,63 @@
return optimizer;
}
+#ifdef ARM
+static void
+arm_sync_d_i_cache (void *start, void *end) {
+/* Strictly this is only needed for StrongARM and later (not sure about ARM8)
+ because earlier cores don't have separate D and I caches.
+ However there aren't that many ARM7 or earlier devices around that we'll be
+ running on. */
+#ifdef __linux
+#ifdef __GNUC__
+ int result;
+ /* swi call based on code snippet from Russell King. Description
+ verbatim: */
+ /*
+ * Flush a region from virtual address 'r0' to virtual address 'r1'
+ * _inclusive_. There is no alignment requirement on either address;
+ * user space does not need to know the hardware cache layout.
+ *
+ * r2 contains flags. It should ALWAYS be passed as ZERO until it
+ * is defined to be something else. For now we ignore it, but may
+ * the fires of hell burn in your belly if you break this rule. ;)
+ *
+ * (at a later date, we may want to allow this call to not flush
+ * various aspects of the cache. Passing '0' will guarantee that
+ * everything necessary gets flushed to maintain consistency in
+ * the specified region).
+ */
+
+ /* The value of the SWI is actually available by in
+ __ARM_NR_cacheflush defined in <asm/unistd.h>, but quite how to
+ get that to interpolate as a number into the ASM string is beyond
+ me. */
+ /* I'm actually passing in exclusive end address, so subtract 1 from
+ it inside the assembler. */
+ __asm__ __volatile__ (
+ "mov r0, %1\n"
+ "sub r1, %2, #1\n"
+ "mov r2, #0\n"
+ "swi " __sys1(__ARM_NR_cacheflush) "\n"
+ "mov %0, r0\n"
+ : "=r" (result)
+ : "r" ((long)start), "r" ((long)end)
+ : "r0","r1","r2");
+
+ if (result < 0) {
+ internal_exception(JIT_ERROR,
+ "Synchronising I and D caches failed with errno=%d\n",
+ -result);
+ }
+#else
+#error "ARM needs to sync D and I caches, and I don't know how to embed assmbler on
this C compiler"
+#endif
+#else
+/* Not strictly true - on RISC OS it's OS_SynchroniseCodeAreas */
+#error "ARM needs to sync D and I caches, and I don't know how to on this OS"
+#endif
+}
+#endif
/*
** build_asm()
@@ -214,6 +277,9 @@
}
}
+#ifdef ARM
+ arm_sync_d_i_cache (jit_info.arena_start, jit_info.native_ptr);
+#endif
return (jit_f)jit_info.arena_start;
}
1.5 +3 -0 parrot/config/auto/jit.pl
Index: jit.pl
===================================================================
RCS file: /cvs/public/parrot/config/auto/jit.pl,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -w -r1.4 -r1.5
--- jit.pl 30 Jul 2002 17:04:33 -0000 1.4
+++ jit.pl 2 Aug 2002 03:24:34 -0000 1.5
@@ -42,11 +42,14 @@
$cpuarch = 'i386';
}
+ $cpuarch =~ s/armv[34]l?/arm/i;
+
Configure::Data->set(
archname => $archname,
cpuarch => $cpuarch,
osname => $osname,
);
+
$cpuarch =~ s/i[456]86/i386/i;
my $jitarchname = "$cpuarch-$osname";
1.1 parrot/jit/arm/core.jit
Index: core.jit
===================================================================
;
; arm/core.jit
;
; $Id: core.jit,v 1.1 2002/08/02 03:25:05 grunblatt Exp $
;
Parrot_noop {
jit_info->native_ptr = emit_nop(jit_info->native_ptr);
}
; ldmea fp, {r4, r5, r6, r7, fp, sp, pc
; but K bug Grr if I load pc direct.
Parrot_end {
#ifndef ARM_K_BUG
jit_info->native_ptr = emit_ldmstm (jit_info->native_ptr,
cond_AL, is_load, dir_EA, no_writeback,
REG11_fp,
reg2mask(4) | reg2mask(REG11_fp)
| reg2mask(REG13_sp)
| reg2mask(REG15_pc));
#else
jit_info->native_ptr = emit_ldmstm (jit_info->native_ptr,
cond_AL, is_load, dir_EA, no_writeback,
REG11_fp,
reg2mask(4) | reg2mask(REG11_fp)
| reg2mask(REG13_sp)
| reg2mask(REG14_lr));
jit_info->native_ptr = emit_mov(jit_info->native_ptr, REG15_pc, REG14_lr);
#endif
}
; This shows why it would be nice in the future to have a way to have ops
; broken into 1 to 3 of:
;
; -1) get values from parrot registers into CPU registers
; 0) do stuff
; +1) write values back to parrot registers
;
; that way, a JIT optimiser could punt -1 and +1 outside loops leaving
; intermediate values in CPU registers. It could collate -1 and +1 [leaving
; nothing :-)] and choose how to maximise use of as many real CPU registers as
; possible.
Parrot_set_i_i {
Parrot_jit_int_load(jit_info, interpreter, 2, r0);
Parrot_jit_int_store(jit_info, interpreter, 1, r0);
}
Parrot_add_i_i_i {
Parrot_jit_int_load(jit_info, interpreter, 2, r0);
Parrot_jit_int_load(jit_info, interpreter, 3, r1);
jit_info->native_ptr = emit_arith_reg (jit_info->native_ptr, cond_AL,
ADD, 0, r2, r0, r1);
Parrot_jit_int_store(jit_info, interpreter, 1, r2);
}
Parrot_sub_i_i_i {
Parrot_jit_int_load(jit_info, interpreter, 2, r0);
Parrot_jit_int_load(jit_info, interpreter, 3, r1);
jit_info->native_ptr = emit_arith_reg (jit_info->native_ptr, cond_AL,
SUB, 0, r2, r0, r1);
Parrot_jit_int_store(jit_info, interpreter, 1, r2);
}
Parrot_if_i_ic {
Parrot_jit_int_load(jit_info, interpreter, 1, r0);
jit_info->native_ptr = emit_arith_immediate (jit_info->native_ptr, cond_AL,
CMP, 0, 0, r0, 0, 0);
emit_jump_to_op (jit_info, cond_NE, 0, *INT_CONST[2]);
}
1.1 parrot/jit/arm/jit_emit.h
Index: jit_emit.h
===================================================================
/*
* jit_emit.h
*
* ARM (I think this is all ARM2 or later, although it is APCS-32)
*
* $Id: jit_emit.h,v 1.1 2002/08/02 03:25:05 grunblatt Exp $
*/
/* Registers
*
* r0 Argument/result/scratch register 0.
* r1 Argument/result/scratch register 1.
* r2 Argument/result/scratch register 2.
* r3 Argument/result/scratch register 3.
* r4 Variable register 1.
* r5 Variable register 2.
* r6 Variable register 3.
* r7 Variable register 4.
* r8 Variable register 5.
* r9 ARM State variable register 6. Static Base in PID, re-entrant
* shared-library variants.
* r10 ARM State variable register 7. Stack limit pointer in stack-checked
* variants.
* r11 ARM State variable register 8. ARM state frame pointer.
* r12 The Intra-Procedure call scratch register.
* r13 The Stack Pointer.
* r14 The Link Register.
* r15 The Program Counter.
*
* r0-r3 are used to pass in first 4 arguments, and are not preserved by a
* function. Results (that would fit) are returned in r0
* Other registers are preserved across calls, although (by implication) r14
* and r15 are used by the call process. I don't think that it is mandated
* that r14 on return must hold the link address.
* r12 (ip) is only used on subroutine entry for stack frame calculations -
* after then it is a useful scratch register. If you push r14 you get
* another scratch register quickly.
*
* Most things nowadays are StrongARM or later. StrongARM is v4 of the
* architecture. ARM6 and ARM7 cores are v3, which introduced the 32 bit
* address bus. Earlier cores (which you won't encounter) used a 26 bit address
* bus, with program counter and status register combined in r15
*/
typedef enum {
r0,
r1,
r2,
r3,
r4,
r5,
r6,
r7,
r8,
r9,
r10,
r11,
r12,
r13,
r14,
r15,
REG10_sl = 10, REG11_fp = 11,
REG12_ip = 12,
REG13_sp = 13,
REG14_lr = 14,
REG15_pc = 15
} arm_register_t;
typedef enum {
cond_EQ = 0x00,
cond_NE = 0x10,
cond_CS = 0x20,
cond_CC = 0x30,
cond_MI = 0x40,
cond_PL = 0x50,
cond_VS = 0x60,
cond_VC = 0x70,
cond_HI = 0x80,
cond_LS = 0x90,
cond_GE = 0xA0,
cond_LT = 0xB0,
cond_GT = 0xC0,
cond_LE = 0xD0,
cond_AL = 0xE0,
/* cond_NV = 0xF0, */
/* synonyms for CS and CC: */
cond_HS = 0x20,
cond_LO = 0x30
} arm_cond_t;
/* I've deliberately shifted these right by 1 bit so that I can forcibly
set the status flag on ops such as CMP. It's easy to forget (the assembler
doesn't mandate you explicitly write CMPS, it just sets the bit for you).
I got an illegal instruction trap on a StrongARM for a CMP without S, but
I think some of the other comparison operators have legal weird effects
with no S flag. */
typedef enum {
AND = 0x00,
EOR = 0x02,
SUB = 0x04, /* Subtract rd = rn - op2 */
RSB = 0x06, /* Reverse SUbtract rd = op2 - rn ; op2 is more flexible. */
ADD = 0x08,
ADC = 0x0A, /* ADd with Carry. */
SBC = 0x0C, /* SuBtract with Carry. */
RSC = 0x0E, /* Reverse Subtract with Carry. */
TST = 0x11, /* TeST rn AND op2 (sets flags). */
TEQ = 0x13, /* Test EQuivalence rn XOR op2 (won't set V flag). */
CMP = 0x15, /* CoMPare rn - op2 */
CMN = 0x17, /* CoMpare Negated rn + op2 */
ORR = 0x18,
MOV = 0x1A, /* MOVe rd = op2 */
BIC = 0x1C, /* BIt Clear rd = rn AND NOT op2 */
MVN = 0x1E /* MoV Not rd = NOT op2 */
} alu_op_t;
/* note MVN is move NOT (ie logical NOT, 1s complement), whereas
CMN is compare NEGATIVE (ie arithmetic NEGATION, 2s complement) */
#define arith_sets_S 0x10
#define INTERP_STRUCT_ADDR_REG r4
/* B / BL
*
* +--------------------------------------------------------------------+
* | cond | 1 0 1 | L | signed_immed_24 |
* +--------------------------------------------------------------------+
* 31 28 27 25 24 23 0
*
*
* The L bit
*
* If L == 1 the instruction will store a return address in the link
* register (R14). Otherwise L == 0, the instruction will simply branch without
* storing a return address.
*
* The target address
*
* Specifies the address to branch to. The branch target address is calculated
* by:
*
* - Sign-extending the 24-bit signed (two's complement) immediate to 32 bits.
*
* - Shifting the result left two bits.
*
* - Adding this to the contents of the PC, which contains the address of the
* branch instruction plus 8.
*
* The instruction can therefore specify a branch of approximately ±32MB.
*
* [Not the full 32 bit address range of the v3 and later cores.]
*/
/* IIRC bx is branch into thumb mode, so don't name this back to bx */
char *emit_branch(char *pc,
arm_cond_t cond,
int L,
int imm) {
*(pc++) = imm;
*(pc++) = ((imm) >> 8);
*(pc++) = ((imm) >> 16);
*(pc++) = cond | 0xA | L;
return pc;
}
#define emit_b(pc, cond, imm) \
emit_branch(pc, cond, 0, imm)
#define emit_bl(pc, cond, imm) \
emit_branch(pc, cond, 1, imm)
#define reg2mask(reg) (1<<(reg))
typedef enum {
is_store = 0x00,
is_load = 0x10,
is_writeback = 0x20,
no_writeback = 0,
is_caret = 0x40, /* assembler syntax is ^ - load sets status flags in
USR mode, or load/store use user bank registers
in other mode. IIRC. */
no_caret = 0,
is_byte = 0x40,
no_byte = 0, /* It's a B suffix for a byte load, no suffix for
word load, so this is more natural than is_word */
is_pre = 0x01, /* pre index addressing. */
is_post = 0x00 /* post indexed addressing. ie arithmetic for free */
} transfer_flags;
/* multiple register transfer direction.
D = decrease, I = increase
A = after, B = before
or the stack notation
FD = full descending (the usual)
ED = empty descending
FA = full ascending
FD = full descending
values for stack notation are 0x10 | (ldm type) << 2 | (stm type)
*/
typedef enum {
dir_DA = 0,
dir_IA = 1,
dir_DB = 2,
dir_IB = 3,
dir_FD = 0x10 | (1 << 2) | 2,
dir_FA = 0x10 | (0 << 2) | 3,
dir_ED = 0x10 | (3 << 2) | 0,
dir_EA = 0x10 | (2 << 2) | 1
} ldm_stm_dir_t;
typedef enum {
dir_Up = 0x80,
dir_Down = 0x00
} ldr_str_dir_t;
char *
emit_ldmstm_x(char *pc,
arm_cond_t cond,
int l_s,
ldm_stm_dir_t direction,
int caret,
int writeback,
int base,
int regmask) {
if ((l_s == is_load) && (direction & 0x10))
direction >>= 2;
*(pc++) = regmask;
*(pc++) = regmask >> 8;
/* bottom bit of direction is the up/down flag. */
*(pc++) = ((direction & 1) << 7) | caret | writeback | l_s | base;
/* binary 100x is code for stm/ldm. */
/* Top bit of direction is pre/post increment flag. */
*(pc++) = cond | 0x8 | ((direction >> 1) & 1);
return pc;
}
/* Is is going to be rare to non existent that anyone needs to use the ^
syntax on LDM or STM, so make it easy to generate the normal form: */
#define emit_ldmstm(pc, cond, l_s, direction, writeback, base, regmask) \
emit_ldmstm_x(pc, cond, l_s, direction, 0, writeback, base, regmask)
/* Load / Store
*
* +--------------------------------------------------------------------+
* | cond | 0 1 | I | P | U | B | W | L | Rn | Rd | offset |
* +--------------------------------------------------------------------+
* 31 28 27 26 25 24 23 22 21 20 19 16 15 12 11 0
*
*
* The P bit
*
* P == 0 indicates the use of post-indexed addressing. The base register value
* is used for the memory address, and the offset is then applied to the
* base register and written back to the base register.
*
* P == 1 indicates the use of offset addressing or pre-indexed addressing (the
* W bit determines which). The memory address is generated by applying
* the offset to the base register value.
*
* The U bit
*
* Indicates whether the offset is added to the base (U == 1) or is subtracted
* from the base (U == 0).
*
* The B bit
*
* Distinguishes between an unsigned byte (B == 1) and a word (B == 0) access.
*
* The W bit
*
* P == 0 If W == 0, the instruction is LDR, LDRB, STR or STRB and a normal
* memory access is performed. If W == 1, the instruction is LDRBT,
* LDRT, STRBT or STRT and an unprivileged (User mode) memory access is
* performed.
*
* P == 1 If W == 0, the base register is not updated (offset addressing). If
* W == 1, the calculated memory address is written back to the base
* register (pre-indexed addressing).
*
* The L bit
*
* Distinguishes between a Load (L == 1) and a Store (L == 0).
*
* <Rd> is the destination register.
* <Rn> is the base register.
*
* XXX need to detail addr mode, for I = 0 and I = 1
*
* Note that you can take advantage of post indexed addressing to get a free
* add onto the base register if you need it for some other purpose.
*
* Note that on StrongARM [and later? but not XScale :-(] if you don't use Rd
* next instruction then a load doesn't stall if it is from the cache (ie
* 1 cycle loads). You will want to re-order things where possible to take
* advantage of this.
*
* ARM1 had register shift register as possibilities for the offset (as the
* ALU ops still do. These took 1 more cycle, and were taken out as virtually
* no use was found for them. However, the bit patterns they represent
* certainly didn't used to fault as an illegal instruction on ARM2s, and
* probably later. So beware of generating illegal bit pattern offsets, as
* you'll get silent undefined behaviour.
*/
char *
emit_ldrstr(char *pc,
arm_cond_t cond,
int l_s,
ldr_str_dir_t direction,
int pre,
int writeback,
int byte,
int dest,
int base,
int offset_type,
unsigned int offset) {
*(pc++) = offset;
*(pc++) = ((offset >> 8) & 0xF) | (dest << 4);
*(pc++) = direction | byte | writeback | l_s | base;
*(pc++) = cond | 0x4 | offset_type | pre;
return pc;
}
char *
emit_ldrstr_offset (char *pc,
arm_cond_t cond,
int l_s,
int pre,
int writeback,
int byte,
int dest,
int base,
int offset) {
ldr_str_dir_t direction = dir_Up;
if (offset > 4095 || offset < -4095) {
internal_exception(JIT_ERROR,
"Unable to generate offset %d, larger than 4095\n",
offset);
}
if (offset < 0) {
direction = dir_Down;
offset = -offset;
}
return emit_ldrstr(pc, cond, l_s, direction, pre, writeback, byte, dest,
base, 0, offset);
}
/* Arithmetic
*
* +--------------------------------------------------------------------+
* | cond | 0 0 | I | ALU Opcode | S | Rn | Rd | shifted operand |
* +--------------------------------------------------------------------+
* 31 28 27 26 25 24 23 22 21 20 19 16 15 12 11 0
*
*
* The S bit
*
* Indicates if the CPSR will be updated (S == 1) or not (S == 0).
*
* Two types of CPSR updates can occur:
*
* - If <Rd> is not R15, the N and Z flags are set according to the result of
* of the addition, and C and V flags are set according to whether the
* addition generated a carry (unsigned overflow) and a signed overflow,
* respectively. The rest of the CPSR is unchanged.
*
* XXX shifted immediate values for the second operand can also set the C
* flag (and therefore presumably also clear it) when the S flag is set for
* certain ALU ops. (I think just the logical ops) This is obscure, but
* sometimes useful. No idea where this is documented.
*
* - If <Rd> is R15, the SPSR of the current mode is copied to the CPSR. This
* form of the instruction is UNPREDICTABLE if executed in User mode or
* System mode, because these do not have an SPSR.
*
*
*/
typedef enum {
shift_LSL = 0x00,
shift_LSR = 0x20,
shift_ASR = 0x40,
shift_ROR = 0x60,
shift_ASL = 0x00 /* Synonym - no sign extension (or not) on << */
} barrel_shift_t;
/* RRX (rotate right with extend - a 1 position 33 bit rotate including the
carry flag) is encoded as ROR by 0. */
char *
emit_arith(char *pc,
arm_cond_t cond,
alu_op_t op,
int status,
arm_register_t rd,
arm_register_t rn,
int operand2_type,
int operand2) {
*(pc++) = operand2;
*(pc++) = rd << 4 | ((operand2 >> 8) & 0xF);
*(pc++) = op << 4 | status | rn;
*(pc++) = cond | 0 | operand2_type | op >> 4;
return pc;
}
/* eg add r0, r3, r7 */
#define emit_arith_reg(pc, cond, op, status, rd, rn, rm) \
emit_arith (pc, cond, op, status, rd, rn, 0, rm)
/* eg sub r0, r3, r7 lsr #3 */
#define emit_arith_reg_shift_const(pc, cond, op, status, rd, rn, rm, shift, by) \
emit_arith (pc, cond, op, status, rd, rn, 0, ((by) << 7) | shift | 0 | (rm))
/* eg orrs r1, r2, r1 rrx */
#define emit_arith_reg_rrx(pc, cond, op, status, rd, rn, rm) \
emit_arith (pc, cond, op, status, rd, rn, 0, shift_ROR | 0 | (rm))
/* I believe these take 2 cycles (due to having to access a 4th register. */
#define emit_arith_reg_shift_reg(pc, cond, op, status, rd, rn, rm, shift, rs) \
emit_arith (pc, cond, op, status, rd, rn, 0, ((rs) << 8) | shift | 0x10 | (rm))
#define emit_arith_immediate(pc, cond, op, status, rd, rn, val, rotate) \
emit_arith (pc, cond, op, status, rd, rn, 2, ((rotate) << 8) | (val))
/* I'll use mov r0, r0 as my NOP for now. */
#define emit_nop(pc) emit_mov (pc, r0, r0)
/* MOV ignores rn */
#define emit_mov(pc, dest, src) emit_arith_reg (pc, cond_AL, MOV, 0, dest, 0, src)
#define emit_dcd(pc, word) { \
*((int *)pc) = word; \
pc+=4; }
static void Parrot_jit_int_load(Parrot_jit_info *jit_info,
struct Parrot_Interp *interpreter,
int param,
int hwreg)
{
opcode_t op_type
= interpreter->op_info_table[*jit_info->cur_op].types[param];
int val = jit_info->cur_op[param];
int offset;
switch(op_type){
case PARROT_ARG_I:
offset = ((char *)&interpreter->ctx.int_reg.registers[val])
- (char *)interpreter;
if (offset > 4095) {
internal_exception(JIT_ERROR,
"integer load register %d generates offset %d,
larger than 4095\n",
val, offset);
}
jit_info->native_ptr = emit_ldrstr_offset (jit_info->native_ptr,
cond_AL,
is_load,
is_pre,
0, 0,
hwreg,
INTERP_STRUCT_ADDR_REG,
offset);
break;
case PARROT_ARG_IC:
default:
internal_exception(JIT_ERROR,
"Unsupported op parameter type %d in jit_int_load\n",
op_type);
}
}
static void Parrot_jit_int_store(Parrot_jit_info *jit_info,
struct Parrot_Interp *interpreter,
int param,
int hwreg)
{
opcode_t op_type
= interpreter->op_info_table[*jit_info->cur_op].types[param];
int val = jit_info->cur_op[param];
int offset;
switch(op_type){
case PARROT_ARG_I:
offset = ((char *)&interpreter->ctx.int_reg.registers[val])
- (char *)interpreter;
if (offset > 4095) {
internal_exception(JIT_ERROR,
"integer store register %d generates offset %d,
larger than 4095\n",
val, offset);
}
jit_info->native_ptr = emit_ldrstr_offset (jit_info->native_ptr,
cond_AL,
is_store,
is_pre,
0, 0,
hwreg,
INTERP_STRUCT_ADDR_REG,
offset);
break;
case PARROT_ARG_N:
default:
internal_exception(JIT_ERROR,
"Unsupported op parameter type %d in jit_int_store\n",
op_type);
}
}
static void emit_jump_to_op(Parrot_jit_info *jit_info, arm_cond_t cond,
int L, opcode_t disp) {
opcode_t opcode = jit_info->op_i + disp;
if(opcode <= jit_info->op_i) {
int offset = jit_info->op_map[opcode].offset -
(jit_info->native_ptr - jit_info->arena_start);
jit_info->native_ptr
= emit_branch(jit_info->native_ptr, cond, L, (offset >> 2) - 2);
return;
}
internal_exception(JIT_ERROR, "Can't go forward yet\n");
}
void Parrot_jit_dofixup(Parrot_jit_info *jit_info,
struct Parrot_Interp * interpreter)
{
/* Todo. */
}
/* My entry code is create a stack frame:
mov ip, sp
stmfd sp!, {r4, fp, ip, lr, pc}
sub fp, ip, #4
Then store the first parameter (pointer to the interpreter) in r4.
mov r4, r0
*/
void
Parrot_jit_begin(Parrot_jit_info *jit_info,
struct Parrot_Interp * interpreter)
{
jit_info->native_ptr = emit_mov (jit_info->native_ptr, REG12_ip, REG13_sp);
jit_info->native_ptr = emit_ldmstm (jit_info->native_ptr,
cond_AL, is_store, dir_FD,
is_writeback,
REG13_sp,
reg2mask(4) | reg2mask(REG11_fp)
| reg2mask(REG12_ip)
| reg2mask(REG14_lr)
| reg2mask(REG15_pc));
jit_info->native_ptr = emit_arith_immediate (jit_info->native_ptr, cond_AL,
SUB, 0, REG11_fp, REG12_ip,
4, 0);
jit_info->native_ptr = emit_mov (jit_info->native_ptr, 4, 0);
}
/* I'm going to load registers to call functions in general like this:
adr r14, .L1
ldmia r14!, {r0, r1, r2, pc} ; register list built by jit
.L1: r0 data
r1 data
r2 data
<where ever> ; address of function.
.L2: ; next instruction - return point from func.
# here I'm going to do
mov r1, r4 ; current interpreter is arg 1
adr r14, .L1
ldmia r14!, {r0, pc}
.L1: address of current opcode
<where ever> ; address of function for op
.L2: ; next instruction - return point from func.
*/
/*
XXX no.
need to adr beyond:
mov r1, r4 ; current interpreter is arg 1
adr r14, .L1
ldmda r14!, {r0, ip}
mov pc, ip
.L1 address of current opcode
dcd <where ever> ; address of function for op
.L2: ; next instruction - return point from func.
*/
void
Parrot_jit_normal_op(Parrot_jit_info *jit_info,
struct Parrot_Interp * interpreter)
{
jit_info->native_ptr = emit_mov (jit_info->native_ptr, r1, r4);
#ifndef ARM_K_BUG
jit_info->native_ptr = emit_mov (jit_info->native_ptr, REG14_lr, REG15_pc);
jit_info->native_ptr = emit_ldmstm (jit_info->native_ptr,
cond_AL, is_load, dir_IA,
is_writeback,
REG14_lr,
reg2mask(0) | reg2mask(REG15_pc));
#else
jit_info->native_ptr = emit_arith_immediate (jit_info->native_ptr, cond_AL,
ADD, 0, REG14_lr, REG15_pc,
4, 0);
jit_info->native_ptr = emit_ldmstm (jit_info->native_ptr,
cond_AL, is_load, dir_IA,
is_writeback,
REG14_lr,
reg2mask(0) | reg2mask(REG12_ip));
jit_info->native_ptr = emit_mov (jit_info->native_ptr, REG15_pc, REG12_ip);
#endif
emit_dcd (jit_info->native_ptr, (int) jit_info->cur_op);
emit_dcd (jit_info->native_ptr,
(int) interpreter->op_func_table[*(jit_info->cur_op)]);
}
/* We get back address of opcode in bytecode.
We want address of equivalent bit of jit code, which is stored as an
address at the same offset in a jit table. */
void Parrot_jit_cpcf_op(Parrot_jit_info *jit_info,
struct Parrot_Interp * interpreter)
{
Parrot_jit_normal_op(jit_info, interpreter);
/* This is effectively the pseudo-opcode ldr - ie load relative to PC.
So offset includes pipeline. */
jit_info->native_ptr = emit_ldrstr_offset (jit_info->native_ptr, cond_AL,
is_load, is_pre, 0, 0,
REG14_lr, REG15_pc, 0);
/* ldr pc, [r14, r0] */
/* lazy. this is offset type 0, 0x000 which is r0 with zero shift */
jit_info->native_ptr = emit_ldrstr (jit_info->native_ptr, cond_AL,
is_load, dir_Up, is_pre, 0, 0,
REG15_pc, REG14_lr, 2, 0);
/* and this "instruction" is never reached, so we can use it to store
the constant that we load into r14 */
emit_dcd (jit_info->native_ptr,
((long) jit_info->op_map) -
((long) interpreter->code->byte_code));
}
/*
* Local variables:
* c-indentation-style: bsd
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/
1.5 +2 -2 parrot/jit/sun4/jit_emit.h
Index: jit_emit.h
===================================================================
RCS file: /cvs/public/parrot/jit/sun4/jit_emit.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -w -r1.4 -r1.5
--- jit_emit.h 1 Aug 2002 19:58:41 -0000 1.4
+++ jit_emit.h 2 Aug 2002 03:25:37 -0000 1.5
@@ -3,7 +3,7 @@
**
** SPARC
**
-** $Id: jit_emit.h,v 1.4 2002/08/01 19:58:41 dan Exp $
+** $Id: jit_emit.h,v 1.5 2002/08/02 03:25:37 grunblatt Exp $
**/
/*