e576bb2002-10-11Martin Nilsson /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information.
47451f2002-11-07Henrik Grubbström (Grubba) || $Id: sparc.c,v 1.23 2002/11/07 17:27:25 grubba Exp $
e576bb2002-10-11Martin Nilsson */
1b10db2002-10-08Martin Nilsson 
dd6bca2001-07-20Henrik Grubbström (Grubba) /* * Machine code generator for sparc. * * Henrik Grubbström 20010720 */
fc31002001-07-24Henrik Grubbström (Grubba) #include "operators.h"
4f43322002-11-05Henrik Grubbström (Grubba) /* * Register definitions */ #define SPARC_REG_G0 0 #define SPARC_REG_G1 1 #define SPARC_REG_G2 2 #define SPARC_REG_G3 3 #define SPARC_REG_G4 4 #define SPARC_REG_G5 5 #define SPARC_REG_G6 6 #define SPARC_REG_G7 7 #define SPARC_REG_O0 8 #define SPARC_REG_O1 9 #define SPARC_REG_O2 10 #define SPARC_REG_O3 11 #define SPARC_REG_O4 12 #define SPARC_REG_O5 13 #define SPARC_REG_O6 14 /* SP */ #define SPARC_REG_O7 15 #define SPARC_REG_L0 16 #define SPARC_REG_L1 17 #define SPARC_REG_L2 18 #define SPARC_REG_L3 19 #define SPARC_REG_L4 20 #define SPARC_REG_L5 21 #define SPARC_REG_L6 22 #define SPARC_REG_L7 23 #define SPARC_REG_I0 24 #define SPARC_REG_I1 25 #define SPARC_REG_I2 26 #define SPARC_REG_I3 27 #define SPARC_REG_I4 28 #define SPARC_REG_I5 29 #define SPARC_REG_I6 30 /* FP */ #define SPARC_REG_I7 31 /* PC */ /*
0dc1b72002-11-06Henrik Grubbström (Grubba)  * ALU operations.
4f43322002-11-05Henrik Grubbström (Grubba)  */ #define SPARC_OP3_AND 0x01 #define SPARC_OP3_ANDcc 0x11 #define SPARC_OP3_ANDN 0x05 #define SPARC_OP3_ANDNcc 0x15 #define SPARC_OP3_OR 0x02 #define SPARC_OP3_ORcc 0x12 #define SPARC_OP3_ORN 0x06 #define SPARC_OP3_ORNcc 0x16 #define SPARC_OP3_XOR 0x03 #define SPARC_OP3_XORcc 0x13 #define SPARC_OP3_XNOR 0x07 #define SPARC_OP3_XNORcc 0x17
0dc1b72002-11-06Henrik Grubbström (Grubba) #define SPARC_OP3_ADD 0x00 #define SPARC_OP3_ADDcc 0x10 #define SPARC_OP3_ADDC 0x08 #define SPARC_OP3_ADDCcc 0x18
f5e8622002-11-07Henrik Grubbström (Grubba) #define SPARC_OP3_SUB 0x04 #define SPARC_OP3_SUBcc 0x14 #define SPARC_OP3_SUBC 0x0c #define SPARC_OP3_SUBCcc 0x1c
327cdd2002-11-07Henrik Grubbström (Grubba) #define SPARC_OP3_SLL 0x25 #define SPARC_OP3_SRL 0x26 #define SPARC_OP3_SRA 0x27 #define SPARC_OP3_RD 0x28
f5e8622002-11-07Henrik Grubbström (Grubba) #define SPARC_OP3_SAVE 0x3c
4f43322002-11-05Henrik Grubbström (Grubba) 
327cdd2002-11-07Henrik Grubbström (Grubba) #define SPARC_RD_REG_CCR 0x02 #define SPARC_RD_REG_PC 0x05
0dc1b72002-11-06Henrik Grubbström (Grubba) #define SPARC_ALU_OP(OP3, D, S1, S2, I) \
4f43322002-11-05Henrik Grubbström (Grubba)  add_to_program(0x80000000|((D)<<25)|((OP3)<<19)|((S1)<<14)|((I)<<13)| \
f5e8622002-11-07Henrik Grubbström (Grubba)  ((S2)&0x1fff))
4f43322002-11-05Henrik Grubbström (Grubba) 
0dc1b72002-11-06Henrik Grubbström (Grubba) #define SPARC_OR(D,S1,S2,I) SPARC_ALU_OP(SPARC_OP3_OR, D, S1, S2, I)
4f43322002-11-05Henrik Grubbström (Grubba) 
0dc1b72002-11-06Henrik Grubbström (Grubba) #define SPARC_SRA(D,S1,S2,I) SPARC_ALU_OP(SPARC_OP3_SRA, D, S1, S2, I) #define SPARC_ADD(D,S1,S2,I) SPARC_ALU_OP(SPARC_OP3_ADD, D, S1, S2, I)
4f43322002-11-05Henrik Grubbström (Grubba) 
327cdd2002-11-07Henrik Grubbström (Grubba) #define SPARC_RD(D, RDREG) SPARC_ALU_OP(SPARC_OP3_RD, D, RDREG, 0, 0)
4f43322002-11-05Henrik Grubbström (Grubba) #define SPARC_SETHI(D, VAL) \ add_to_program(0x01000000|((D)<<25)|(((VAL)>>10)&0x3fffff)) #define SET_REG(REG, X) do { \ INT32 val_ = X; \ INT32 reg_ = REG; \ if ((-4096 <= val_) && (val_ <= 4095)) { \ /* or %g0, val_, reg */ \
40f4622002-11-05Henrik Grubbström (Grubba)  SPARC_OR(reg_, SPARC_REG_G0, val_, 1); \
4f43322002-11-05Henrik Grubbström (Grubba)  } else { \ /* sethi %hi(val_), reg */ \ SPARC_SETHI(reg_, val_); \ if (val_ & 0x3ff) { \ /* or reg, %lo(val_), reg */ \
8d0cf12002-11-05Henrik Grubbström (Grubba)  SPARC_OR(reg_, reg_, val_ & 0x3ff, 1); \
4f43322002-11-05Henrik Grubbström (Grubba)  } \ if (val_ < 0) { \ /* Sign extend. */ \ /* sra reg, %g0, reg */ \ SPARC_SRA(reg_, reg_, SPARC_REG_G0, 0); \ } \ } \ } while(0)
f1d1eb2001-07-20Henrik Grubbström (Grubba) #define ADD_CALL(X, DELAY_OK) do { \
dd6bca2001-07-20Henrik Grubbström (Grubba)  INT32 delta_; \ struct program *p_ = Pike_compiler->new_program; \ INT32 off_ = p_->num_program; \
f1d1eb2001-07-20Henrik Grubbström (Grubba)  /* noop */ \ INT32 delay_ = 0x01000000; \ \ if (DELAY_OK) { \ /* Move the previous opcode to the delay-slot. */ \ delay_ = p_->program[--off_]; \ } else { \ add_to_program(0); /* Placeholder... */ \ } \
dd6bca2001-07-20Henrik Grubbström (Grubba)  /* call X */ \ delta_ = ((PIKE_OPCODE_T *)(X)) - (p_->program + off_); \ p_->program[off_] = 0x40000000 | (delta_ & 0x3fffffff); \ add_to_relocations(off_); \
f1d1eb2001-07-20Henrik Grubbström (Grubba)  add_to_program(delay_); \
327cdd2002-11-07Henrik Grubbström (Grubba)  sparc_last_pc = off_; /* Value in %o7. */ \ sparc_codegen_state |= SPARC_CODEGEN_PC_IS_SET; \
0dc1b72002-11-06Henrik Grubbström (Grubba)  } while(0) /* * Register conventions: * * I6 Frame pointer * I7 Return address * * L0 Pike_fp * L1 Pike_fp->pc * * O6 Stack Pointer * O7 Program Counter * */ #define SPARC_REG_PIKE_FP SPARC_REG_L0 #define SPARC_REG_SP SPARC_REG_O6 #define SPARC_REG_PC SPARC_REG_O7 /* * Code generator state. */ unsigned INT32 sparc_codegen_state = 0; int sparc_last_pc = 0; #define LOAD_PIKE_FP() do { \
ebba5c2002-11-07Henrik Grubbström (Grubba)  if (!(sparc_codegen_state & SPARC_CODEGEN_FP_IS_SET)) { \
0dc1b72002-11-06Henrik Grubbström (Grubba)  SET_REG(SPARC_REG_PIKE_FP, \ ((INT32)(&Pike_interpreter.frame_pointer))); \ /* lduw [ %i0 ], %i0 */ \ add_to_program(0xc0000000|(SPARC_REG_PIKE_FP<<25)| \ (SPARC_REG_PIKE_FP<<14)); \ sparc_codegen_state |= SPARC_CODEGEN_FP_IS_SET; \ } \
dd6bca2001-07-20Henrik Grubbström (Grubba)  } while(0)
ebba5c2002-11-07Henrik Grubbström (Grubba) #define UNLOAD_PIKE_FP() (sparc_codegen_state &= ~SPARC_CODEGEN_FP_IS_SET)
4f43322002-11-05Henrik Grubbström (Grubba) /* * Allocate a stack frame. * * Note that the prologue size must be constant. */ void sparc_ins_entry(void) { /* save %sp, -112, %sp */
0dc1b72002-11-06Henrik Grubbström (Grubba)  add_to_program(0x81e02000|(SPARC_REG_SP<<25)| (SPARC_REG_SP<<14)|((-112)&0x1fff));
b3618b2002-11-06Henrik Grubbström (Grubba)  FLUSH_CODE_GENERATOR_STATE();
4f43322002-11-05Henrik Grubbström (Grubba) }
8d0cf12002-11-05Henrik Grubbström (Grubba) /* Update Pike_fp->pc */
4f43322002-11-05Henrik Grubbström (Grubba) void sparc_update_pc(void) {
327cdd2002-11-07Henrik Grubbström (Grubba)  /* rd %pc, %i0 */ SPARC_RD(SPARC_REG_I0, SPARC_RD_REG_PC);
0dc1b72002-11-06Henrik Grubbström (Grubba)  LOAD_PIKE_FP();
327cdd2002-11-07Henrik Grubbström (Grubba)  /* stw %pc, [ %pike_fp + pc ] */ add_to_program(0xc0202000|(SPARC_REG_I0<<25)|(SPARC_REG_PIKE_FP<<14)|
4f43322002-11-05Henrik Grubbström (Grubba)  OFFSETOF(pike_frame, pc)); }
f1d1eb2001-07-20Henrik Grubbström (Grubba) static void low_ins_f_byte(unsigned int b, int delay_ok)
dd6bca2001-07-20Henrik Grubbström (Grubba) {
11ed4b2001-07-24Henrik Grubbström (Grubba)  void *addr;
fc31002001-07-24Henrik Grubbström (Grubba) 
dd6bca2001-07-20Henrik Grubbström (Grubba) #ifdef PIKE_DEBUG if(store_linenumbers && b<F_MAX_OPCODE) ADD_COMPILED(b); #endif /* PIKE_DEBUG */ b-=F_OFFSET; #ifdef PIKE_DEBUG if(b>255) Pike_error("Instruction too big %d\n",b); #endif
11ed4b2001-07-24Henrik Grubbström (Grubba)  addr = instrs[b].address;
9a928d2002-05-10Martin Stjernholm #ifdef PIKE_DEBUG if (d_flag < 3) #endif
fc31002001-07-24Henrik Grubbström (Grubba)  /* This is not very pretty */ switch(b) { case F_MAKE_ITERATOR - F_OFFSET: { extern void f_Iterator(INT32); SET_REG(SPARC_REG_O0, 1); delay_ok = 1; addr = (void *)f_Iterator; } break; case F_ADD - F_OFFSET: SET_REG(SPARC_REG_O0, 2); delay_ok = 1; addr = (void *)f_add; break; }
9a928d2002-05-10Martin Stjernholm 
f5e8622002-11-07Henrik Grubbström (Grubba)  { static int last_prog_id=-1; static size_t last_num_linenumbers=(size_t)~0; if(last_prog_id != Pike_compiler->new_program->id || last_num_linenumbers != Pike_compiler->new_program->num_linenumbers) { last_prog_id=Pike_compiler->new_program->id; last_num_linenumbers = Pike_compiler->new_program->num_linenumbers; LOAD_PIKE_FP();
327cdd2002-11-07Henrik Grubbström (Grubba)  /* Note: We fill the delay slot with the following opcode.
f5e8622002-11-07Henrik Grubbström (Grubba)  * This works since the new %o7 is available immediately. * (Sparc Architecture Manual V9 p149.) */
327cdd2002-11-07Henrik Grubbström (Grubba)  /* stw %o7, [ %pike_fp, %offsetof(pike_frame, pc) ] */ add_to_program(0xc0202000|(SPARC_REG_O7<<25)|(SPARC_REG_PIKE_FP<<14)|
f5e8622002-11-07Henrik Grubbström (Grubba)  OFFSETOF(pike_frame, pc));
327cdd2002-11-07Henrik Grubbström (Grubba) 
f5e8622002-11-07Henrik Grubbström (Grubba)  delay_ok = 1; } }
11ed4b2001-07-24Henrik Grubbström (Grubba)  ADD_CALL(addr, delay_ok);
ebba5c2002-11-07Henrik Grubbström (Grubba)  /* This is probably only needed for some instructions, but... */ UNLOAD_PIKE_FP();
f1d1eb2001-07-20Henrik Grubbström (Grubba) } void ins_f_byte(unsigned int opcode) { low_ins_f_byte(opcode, 0);
dd6bca2001-07-20Henrik Grubbström (Grubba) } void ins_f_byte_with_arg(unsigned int a,unsigned INT32 b) {
7df3482001-07-21Henrik Grubbström (Grubba)  SET_REG(SPARC_REG_O0, b);
f1d1eb2001-07-20Henrik Grubbström (Grubba)  low_ins_f_byte(a, 1);
dd6bca2001-07-20Henrik Grubbström (Grubba)  return; } void ins_f_byte_with_2_args(unsigned int a, unsigned INT32 c, unsigned INT32 b) {
7df3482001-07-21Henrik Grubbström (Grubba)  SET_REG(SPARC_REG_O0, c); SET_REG(SPARC_REG_O1, b);
f1d1eb2001-07-20Henrik Grubbström (Grubba)  low_ins_f_byte(a, 1);
dd6bca2001-07-20Henrik Grubbström (Grubba)  return; }
697e0a2001-07-20Henrik Grubbström (Grubba)  #define addstr(s, l) low_my_binary_strcat((s), (l), buf) #define adddata2(s,l) addstr((char *)(s),(l) * sizeof((s)[0])); void sparc_encode_program(struct program *p, struct dynamic_buffer_s *buf) { size_t prev = 0, rel; /* De-relocate the program... */ for (rel = 0; rel < p->num_relocations; rel++) { size_t off = p->relocations[rel]; INT32 opcode; #ifdef PIKE_DEBUG if (off < prev) {
5aad932002-08-15Marcus Comstedt  Pike_fatal("Relocations in bad order!\n");
697e0a2001-07-20Henrik Grubbström (Grubba)  } #endif /* PIKE_DEBUG */ adddata2(p->program + prev, off - prev); #ifdef PIKE_DEBUG if ((p->program[off] & 0xc0000000) != 0x40000000) {
5aad932002-08-15Marcus Comstedt  Pike_fatal("Bad relocation!\n");
697e0a2001-07-20Henrik Grubbström (Grubba)  } #endif /* PIKE_DEBUG */ /* Relocate to being relative to NULL */ opcode = 0x40000000 | ((p->program[off] + (((INT32)(p->program)>>2))) & 0x3fffffff); adddata2(&opcode, 1); prev = off+1; } adddata2(p->program + prev, p->num_program - prev); } void sparc_decode_program(struct program *p) { /* Relocate the program... */ PIKE_OPCODE_T *prog = p->program; INT32 delta = ((INT32)p->program)>>2; size_t rel = p->num_relocations; while (rel--) { #ifdef PIKE_DEBUG if ((prog[p->relocations[rel]] & 0xc0000000) != 0x40000000) { Pike_error("Bad relocation: %d, off:%d, opcode: 0x%08x\n", rel, p->relocations[rel], prog[p->relocations[rel]]); } #endif /* PIKE_DEBUG */ prog[p->relocations[rel]] = 0x40000000 | (((prog[p->relocations[rel]] & 0x3fffffff) - delta) & 0x3fffffff); } }
9d2e2d2001-07-21Henrik Grubbström (Grubba)  /* * Inline machine-code... */ const unsigned INT32 sparc_flush_instruction_cache[] = { /* * On entry: * %o0 : void *ptr * %o1 : size_t nbytes * * cmp %o1, #1 * .l0: * flush %o0+%o1 * bge,a .l0 * subcc %o1, 8, %o1 * retl * or %g0,%g0,%o0 */ /* 1000 0000 1010 0000 0010 0000 0000 0000 */ 0x80a02000|(SPARC_REG_O1<<14)|1, /* 1000 0001 1101 1000 0000 0000 0000 0000 */ 0x81d80000|(SPARC_REG_O0<<14)|(SPARC_REG_O1), /* 0011 0110 1000 0000 0000 0000 0000 0000 */ 0x36800000|((-1)&0x3fffff), /* 1000 0000 1010 0000 0010 0000 0000 0000 */ 0x80a02000|(SPARC_REG_O1<<25)|(SPARC_REG_O1<<14)|8, /* 1000 0001 1100 0000 0010 0000 0000 0000 */ 0x81c02000|(SPARC_REG_O7<<14)|8, /* 1000 0000 0001 0000 0000 0000 0000 0000 */ 0x80100000|(SPARC_REG_O0<<25), };
f5e8622002-11-07Henrik Grubbström (Grubba) 
47451f2002-11-07Henrik Grubbström (Grubba) static void sparc_disass_rd_reg(int reg_no) { switch(reg_no & 0x1f) { case SPARC_RD_REG_CCR: fprintf(stderr, "%%ccr"); break; case SPARC_RD_REG_PC: fprintf(stderr, "%%pc"); break; default: fprintf(stderr, "%%sr(%d)", reg_no & 0x1f); break; } }
f5e8622002-11-07Henrik Grubbström (Grubba) static void sparc_disass_reg(int reg_no) { fprintf(stderr, "%%%c%1x", "goli"[(reg_no>>3)&3], reg_no & 7); } void sparc_disassemble_code(void *addr, size_t bytes) { unsigned INT32 *code = addr; size_t len = (bytes+3)>>2; while(len--) { unsigned INT32 opcode = *code; fprintf(stderr, "%p %08x ", code, opcode); switch(opcode & 0xc0000000) { case 0x00000000: { /* Sethi etc. */ int op2 = (opcode >> 22) & 0x7; switch(op2) { case 4: fprintf(stderr, "sethi %%hi(0x%08x), ", opcode << 10); break; } sparc_disass_reg(opcode>>25); fprintf(stderr, "\n"); } break; case 0x40000000: /* Call */ fprintf(stderr, "call 0x%p\n", ((char *)code) + (opcode << 2)); break; case 0x80000000: { /* ALU operation. */ int op3 = (opcode >> 19) & 0x3f; char buf[16]; char *mnemonic = NULL; if (!(op3 & 0x20)) { switch(op3 & 0xf) { case SPARC_OP3_ADD: mnemonic = "add"; break; /* 0 */ case SPARC_OP3_AND: mnemonic = "and"; break; /* 1 */ case SPARC_OP3_OR: mnemonic = "or"; break; /* 2 */ case SPARC_OP3_XOR: mnemonic = "xor"; break; /* 3 */ case SPARC_OP3_SUB: mnemonic = "sub"; break; /* 4 */ case SPARC_OP3_ANDN: mnemonic = "andn"; break; /* 5 */ case SPARC_OP3_ORN: mnemonic = "orn"; break; /* 6 */ case SPARC_OP3_XNOR: mnemonic = "xnor"; break; /* 7 */ case SPARC_OP3_ADDC: mnemonic = "addc"; break; /* 8 */ case SPARC_OP3_SUBC: mnemonic = "subc"; break; /* c */ default: sprintf(buf, "op3(0x%02x)", op3 & 0xf); mnemonic = buf; break; } if (op3 & 0x10) { fprintf(stderr, "%scc ", mnemonic); } else { fprintf(stderr, "%s ", mnemonic); } } else { switch(op3) { case SPARC_OP3_SLL: mnemonic = "sll"; break; case SPARC_OP3_SRL: mnemonic = "srl"; break; case SPARC_OP3_SRA: mnemonic = "sra"; break;
47451f2002-11-07Henrik Grubbström (Grubba)  case SPARC_OP3_RD: mnemonic = "rd"; break;
f5e8622002-11-07Henrik Grubbström (Grubba)  case SPARC_OP3_SAVE: mnemonic = "save"; break; default: sprintf(buf, "op3(0x%02x)", op3); mnemonic = buf; break; } fprintf(stderr, "%s ", mnemonic); }
47451f2002-11-07Henrik Grubbström (Grubba)  if (op3 == SPARC_OP3_RD) { sparc_disass_rd_reg(opcode>>14); fprintf(stderr, ", ");
f5e8622002-11-07Henrik Grubbström (Grubba)  } else {
47451f2002-11-07Henrik Grubbström (Grubba)  sparc_disass_reg(opcode>>14);
f5e8622002-11-07Henrik Grubbström (Grubba)  fprintf(stderr, ", ");
47451f2002-11-07Henrik Grubbström (Grubba)  if (opcode & 0x00002000) { fprintf(stderr, "0x%04x, ", opcode & 0x1fff); } else { sparc_disass_reg(opcode); fprintf(stderr, ", "); }
f5e8622002-11-07Henrik Grubbström (Grubba)  } sparc_disass_reg(opcode >> 25); fprintf(stderr, "\n"); } break; case 0xc0000000: { /* Memory operations. */ int op3 = (opcode >> 19) & 0x3f; char buf[16]; char *mnemonic = NULL; switch(op3) { case 0x00: mnemonic="lduw"; break; case 0x01: mnemonic="ldub"; break; case 0x02: mnemonic="lduh"; break; case 0x03: mnemonic="ldd"; break; case 0x04: mnemonic="stw"; break; case 0x05: mnemonic="stb"; break; case 0x06: mnemonic="sth"; break; case 0x07: mnemonic="std"; break; case 0x08: mnemonic="ldsw"; break; case 0x09: mnemonic="ldsb"; break; case 0x0a: mnemonic="ldsh"; break; case 0x0b: mnemonic="ldx"; break; case 0x0e: mnemonic="stx"; break; default: sprintf(buf, "op3(0x%02x)", op3); mnemonic = buf; break; } if (op3 & 0x04) { /* Store */ fprintf(stderr, "%s ", mnemonic); sparc_disass_reg(opcode >> 25); fprintf(stderr, ", ["); sparc_disass_reg(opcode >> 14); if (opcode & 0x00002000) { fprintf(stderr, ", 0x%04x", opcode & 0x1fff); } else { fprintf(stderr, ", "); sparc_disass_reg(opcode); } fprintf(stderr, "]\n"); } else { /* Load */ fprintf(stderr, "%s [", mnemonic); sparc_disass_reg(opcode >> 14); if (opcode & 0x00002000) { fprintf(stderr, ", 0x%04x", opcode & 0x1fff); } else { fprintf(stderr, ", "); sparc_disass_reg(opcode); } fprintf(stderr, "], "); sparc_disass_reg(opcode >> 25); fprintf(stderr, "\n"); } } break; } code++; } }