#include "config.h" #include #include #include #include #include #include #include /** * SECTION:orcpowerpc * @title: PowerPC * @short_description: code generation for PowerPC */ void orc_compiler_powerpc_init (OrcCompiler *compiler); void orc_compiler_powerpc_assemble (OrcCompiler *compiler); void orc_compiler_powerpc_register_rules (OrcTarget *target); const char * powerpc_get_regname(int i) { static const char *powerpc_regs[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", }; if (i>=ORC_GP_REG_BASE && icodeptr++ = (insn>>24); *compiler->codeptr++ = (insn>>16); *compiler->codeptr++ = (insn>>8); *compiler->codeptr++ = (insn>>0); } void powerpc_emit_add (OrcCompiler *compiler, int regd, int rega, int regb) { unsigned int insn; ORC_ASM_CODE(compiler," add %s, %s, %s\n", powerpc_get_regname(regd), powerpc_get_regname(rega), powerpc_get_regname(regb)); insn = 0x7c000214 | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= (powerpc_regnum (regb)<<11); powerpc_emit (compiler, insn); } void powerpc_emit_addi_rec (OrcCompiler *compiler, int regd, int rega, int imm) { unsigned int insn; ORC_ASM_CODE(compiler," addic. %s, %s, %d\n", powerpc_get_regname(regd), powerpc_get_regname(rega), imm); insn = 0x34000000 | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= imm&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_addi (OrcCompiler *compiler, int regd, int rega, int imm) { unsigned int insn; ORC_ASM_CODE(compiler," addi %s, %s, %d\n", powerpc_get_regname(regd), powerpc_get_regname(rega), imm); insn = (14<<26) | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= imm&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_lwz (OrcCompiler *compiler, int regd, int rega, int imm) { unsigned int insn; ORC_ASM_CODE(compiler," lwz %s, %d(%s)\n", powerpc_get_regname(regd), imm, powerpc_get_regname(rega)); insn = (32<<26) | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= imm&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_stw (OrcCompiler *compiler, int regs, int rega, int offset) { unsigned int insn; ORC_ASM_CODE(compiler," stw %s, %d(%s)\n", powerpc_get_regname(regs), offset, powerpc_get_regname(rega)); insn = 0x90000000 | (powerpc_regnum (regs)<<21) | (powerpc_regnum (rega)<<16); insn |= offset&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_stwu (OrcCompiler *compiler, int regs, int rega, int offset) { unsigned int insn; ORC_ASM_CODE(compiler," stwu %s, %d(%s)\n", powerpc_get_regname(regs), offset, powerpc_get_regname(rega)); insn = (37<<26) | (powerpc_regnum (regs)<<21) | (powerpc_regnum (rega)<<16); insn |= offset&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_ld (OrcCompiler *compiler, int regd, int rega, int imm) { unsigned int insn; ORC_ASM_CODE(compiler," ld %s, %d(%s)\n", powerpc_get_regname(regd), imm, powerpc_get_regname(rega)); insn = (58<<26) | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= imm&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_std (OrcCompiler *compiler, int regs, int rega, int offset) { unsigned int insn; ORC_ASM_CODE(compiler," std %s, %d(%s)\n", powerpc_get_regname(regs), offset, powerpc_get_regname(rega)); insn = (62<<26) | (powerpc_regnum (regs)<<21) | (powerpc_regnum (rega)<<16); insn |= offset&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_stdu (OrcCompiler *compiler, int regs, int rega, int offset) { unsigned int insn; ORC_ASM_CODE(compiler," stdu %s, %d(%s)\n", powerpc_get_regname(regs), offset, powerpc_get_regname(rega)); insn = (62<<26) | (powerpc_regnum (regs)<<21) | (powerpc_regnum (rega)<<16); insn |= (offset&0xffff) | 1; powerpc_emit (compiler, insn); } void powerpc_emit_srawi (OrcCompiler *compiler, int regd, int rega, int shift, int record) { unsigned int insn; ORC_ASM_CODE(compiler," srawi%s %s, %s, %d\n", (record)?".":"", powerpc_get_regname(regd), powerpc_get_regname(rega), shift); insn = (31<<26) | (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= (shift<<11) | (824<<1) | record; powerpc_emit (compiler, insn); } void powerpc_emit_655510 (OrcCompiler *compiler, int major, int d, int a, int b, int minor) { unsigned int insn; insn = (major<<26) | (d<<21) | (a<<16); insn |= (b<<11) | (minor<<0); powerpc_emit (compiler, insn); } void powerpc_emit_D (OrcCompiler *compiler, const char *name, unsigned int insn, int regd, int rega, int imm) { ORC_ASM_CODE(compiler," %s %s, %s, %d\n", name, powerpc_get_regname(regd), powerpc_get_regname(rega), imm); insn |= (powerpc_regnum (regd)<<21) | (powerpc_regnum (rega)<<16); insn |= imm&0xffff; powerpc_emit (compiler, insn); } void powerpc_emit_X (OrcCompiler *compiler, unsigned int insn, int d, int a, int b) { #if 0 unsigned int insn; insn = (major<<26) | (d<<21) | (a<<16); insn |= (b<<11) | (minor<<1) | (0<<0); powerpc_emit (compiler, insn); #endif insn |= ((d&0x1f)<<21); insn |= ((a&0x1f)<<16); insn |= ((b&0x1f)<<11); powerpc_emit (compiler, insn); } void powerpc_emit_VA (OrcCompiler *compiler, const char *name, unsigned int insn, int d, int a, int b, int c) { ORC_ASM_CODE(compiler," %s %s, %s, %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(b), powerpc_get_regname(c)); insn |= ((d&0x1f)<<21) | ((a&0x1f)<<16) | ((b&0x1f)<<11) | ((c&0x1f)<<6); powerpc_emit (compiler, insn); } void powerpc_emit_VA_acb (OrcCompiler *compiler, const char *name, unsigned int insn, int d, int a, int b, int c) { ORC_ASM_CODE(compiler," %s %s, %s, %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(c), powerpc_get_regname(b)); insn |= ((d&0x1f)<<21) | ((a&0x1f)<<16) | ((b&0x1f)<<11) | ((c&0x1f)<<6); powerpc_emit (compiler, insn); } void powerpc_emit_VXR (OrcCompiler *compiler, const char *name, unsigned int insn, int d, int a, int b, int record) { ORC_ASM_CODE(compiler," %s %s, %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(b)); insn |= ((d&0x1f)<<21) | ((a&0x1f)<<16) | ((b&0x1f)<<11); insn |= ((record&0x1)<<10); powerpc_emit (compiler, insn); } void powerpc_emit_VX (OrcCompiler *compiler, unsigned int insn, int d, int a, int b) { insn |= ((d&0x1f)<<21); insn |= ((a&0x1f)<<16); insn |= ((b&0x1f)<<11); powerpc_emit (compiler, insn); } void powerpc_emit_VX_2 (OrcCompiler *p, const char *name, unsigned int insn, int d, int a, int b) { ORC_ASM_CODE(p," %s %s, %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(b)); powerpc_emit_VX(p, insn, powerpc_regnum(d), powerpc_regnum(a), powerpc_regnum(b)); } void powerpc_emit_VX_b (OrcCompiler *p, const char *name, unsigned int insn, int b) { ORC_ASM_CODE(p," %s %s\n", name, powerpc_get_regname(b)); powerpc_emit_VX(p, insn, 0, 0, powerpc_regnum(b)); } void powerpc_emit_VX_db (OrcCompiler *p, const char *name, unsigned int insn, int d, int b) { ORC_ASM_CODE(p," %s %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(b)); powerpc_emit_VX(p, insn, powerpc_regnum(d), 0, powerpc_regnum(b)); } void powerpc_emit_VX_dbi (OrcCompiler *p, const char *name, unsigned int insn, int d, int b, int imm) { ORC_ASM_CODE(p," %s %s, %s, %d\n", name, powerpc_get_regname(d), powerpc_get_regname(b), imm); powerpc_emit_VX(p, insn, powerpc_regnum(d), imm, powerpc_regnum(b)); } void powerpc_emit_VX_3_reg (OrcCompiler *p, const char *name, unsigned int insn, int d, int a, int b, int c) { ORC_ASM_CODE(p," %s %s, %s, %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(b), powerpc_get_regname(c)); powerpc_emit_VX(p, insn, powerpc_regnum(d), powerpc_regnum(a), powerpc_regnum(b)); } void powerpc_emit_VX_3 (OrcCompiler *p, const char *name, unsigned int insn, int d, int a, int b, int c) { ORC_ASM_CODE(p," %s %s, %s, %s, %d\n", name, powerpc_get_regname(d), powerpc_get_regname(a), powerpc_get_regname(b), c); powerpc_emit_VX(p, insn, powerpc_regnum(d), powerpc_regnum(a), powerpc_regnum(b)); } void powerpc_emit_VX_4 (OrcCompiler *p, const char *name, unsigned int insn, int d, int a) { ORC_ASM_CODE(p," %s %s, %s\n", name, powerpc_get_regname(d), powerpc_get_regname(a)); powerpc_emit_VX(p, insn, powerpc_regnum(d), 0, powerpc_regnum(a)); } void powerpc_do_fixups (OrcCompiler *compiler) { int i; unsigned int insn; for(i=0;in_fixups;i++){ unsigned char *label = compiler->labels[compiler->fixups[i].label]; unsigned char *ptr = compiler->fixups[i].ptr; insn = *(unsigned int *)ptr; switch (compiler->fixups[i].type) { case 0: *(unsigned int *)ptr = (insn&0xffff0000) | ((insn + (label-ptr))&0xffff); break; case 1: *(unsigned int *)ptr = (insn&0xffff0000) | ((insn + (label-compiler->code))&0xffff); break; case 2: *(unsigned int *)ptr = (insn&0xfc000000) | ((insn + (label-ptr))&0x03ffffff); break; } } } void orc_powerpc_flush_cache (OrcCode *code) { #ifdef HAVE_POWERPC unsigned char *ptr; int cache_line_size = 32; int i; int size = code->code_size; ptr = code->code; #ifdef __powerpc64__ *(unsigned char **) ptr = (unsigned char *) code->exec + 24; #endif for (i=0;iexec; for (i=0;iconstants[i].value; switch (p->constants[i].type) { case ORC_CONST_ZERO: powerpc_emit_VX_2(p, "vxor", 0x100004c4, reg, reg, reg); return; case ORC_CONST_SPLAT_B: if (value < 16 && value >= -16) { ORC_ASM_CODE(p," vspltisb %s, %d\n", powerpc_get_regname(reg), value); powerpc_emit_VX(p, 0x1000030c, powerpc_regnum(reg), value & 0x1f, 0); return; } break; case ORC_CONST_SPLAT_W: if (value < 16 && value >= -16) { ORC_ASM_CODE(p," vspltish %s, %d\n", powerpc_get_regname(reg), value); powerpc_emit_VX(p, 0x1000034c, powerpc_regnum(reg), value & 0x1f, 0); return; } break; case ORC_CONST_SPLAT_L: if (value < 16 && value >= -16) { ORC_ASM_CODE(p," vspltisw %s, %d\n", powerpc_get_regname(reg), value); powerpc_emit_VX(p, 0x1000038c, powerpc_regnum(reg), value & 0x1f, 0); return; } break; default: break; } switch (p->constants[i].type) { case ORC_CONST_ZERO: for(j=0;j<4;j++){ p->constants[i].full_value[j] = 0; } break; case ORC_CONST_SPLAT_B: value &= 0xff; value |= (value<<8); value |= (value<<16); for(j=0;j<4;j++){ p->constants[i].full_value[j] = value; } break; case ORC_CONST_SPLAT_W: value &= 0xffff; value |= (value<<16); for(j=0;j<4;j++){ p->constants[i].full_value[j] = value; } break; case ORC_CONST_SPLAT_L: for(j=0;j<4;j++){ p->constants[i].full_value[j] = value; } break; default: break; } powerpc_load_long_constant (p, reg, p->constants[i].full_value[0], p->constants[i].full_value[1], p->constants[i].full_value[2], p->constants[i].full_value[3]); } void powerpc_load_long_constant (OrcCompiler *p, int reg, orc_uint32 a, orc_uint32 b, orc_uint32 c, orc_uint32 d) { int label_skip, label_data; int greg = p->gp_tmpreg; label_skip = orc_compiler_label_new (p); label_data = orc_compiler_label_new (p); powerpc_emit_b (p, label_skip); while ((p->codeptr - p->code) & 0xf) { ORC_ASM_CODE(p," .long 0x00000000\n"); powerpc_emit (p, 0x00000000); } powerpc_emit_label (p, label_data); ORC_ASM_CODE(p," .long 0x%08x\n", a); powerpc_emit (p, a); ORC_ASM_CODE(p," .long 0x%08x\n", b); powerpc_emit (p, b); ORC_ASM_CODE(p," .long 0x%08x\n", c); powerpc_emit (p, c); ORC_ASM_CODE(p," .long 0x%08x\n", d); powerpc_emit (p, d); powerpc_emit_label (p, label_skip); if (p->is_64bit) { powerpc_emit_ld (p, greg, POWERPC_R3, (int)ORC_STRUCT_OFFSET(OrcExecutor, arrays[ORC_VAR_A2])); powerpc_emit_ld (p, greg, greg, (int)ORC_STRUCT_OFFSET(OrcCode, exec)); } else { powerpc_emit_lwz (p, greg, POWERPC_R3, (int)ORC_STRUCT_OFFSET(OrcExecutor, arrays[ORC_VAR_A2])); powerpc_emit_lwz (p, greg, greg, (int)ORC_STRUCT_OFFSET(OrcCode, exec)); } powerpc_add_fixup (p, 1, p->codeptr, label_data); { unsigned int insn; ORC_ASM_CODE(p," addi %s, %s, %db - %s\n", powerpc_get_regname(greg), powerpc_get_regname(greg), label_data, p->program->name); insn = (14<<26) | (powerpc_regnum (greg)<<21) | (powerpc_regnum (greg)<<16); insn |= 0; powerpc_emit (p, insn); } ORC_ASM_CODE(p," lvx %s, 0, %s\n", powerpc_get_regname(reg), powerpc_get_regname(greg)); powerpc_emit_X (p, 0x7c0000ce, reg, 0, greg); } int powerpc_get_constant (OrcCompiler *p, int type, int value) { int reg = orc_compiler_get_temp_reg (p); int i; for(i=0;in_constants;i++){ if (p->constants[i].type == type && p->constants[i].value == value) { if (p->constants[i].alloc_reg != 0) { return p->constants[i].alloc_reg; } break; } } if (i == p->n_constants) { p->n_constants++; p->constants[i].type = type; p->constants[i].value = value; p->constants[i].alloc_reg = 0; } powerpc_load_constant (p, i, reg); return reg; } int powerpc_get_constant_full (OrcCompiler *p, int value0, int value1, int value2, int value3) { int reg = p->tmpreg; int i; for(i=0;in_constants;i++){ #if 0 if (p->constants[i].type == type && p->constants[i].value == value) { if (p->constants[i].alloc_reg != 0) { return p->constants[i].alloc_reg; } break; } #endif } if (i == p->n_constants) { p->n_constants++; p->constants[i].type = ORC_CONST_FULL; p->constants[i].full_value[0] = value0; p->constants[i].full_value[1] = value1; p->constants[i].full_value[2] = value2; p->constants[i].full_value[3] = value3; p->constants[i].alloc_reg = 0; } powerpc_load_constant (p, i, reg); return reg; } void powerpc_emit_ret (OrcCompiler *compiler) { ORC_ASM_CODE(compiler," ret\n"); //*compiler->codeptr++ = 0xc3; } void powerpc_add_fixup (OrcCompiler *compiler, int type, unsigned char *ptr, int label) { compiler->fixups[compiler->n_fixups].ptr = ptr; compiler->fixups[compiler->n_fixups].label = label; compiler->fixups[compiler->n_fixups].type = type; compiler->n_fixups++; if (compiler->n_fixups >= ORC_N_FIXUPS) { ORC_ERROR("too many fixups"); } } void powerpc_add_label (OrcCompiler *compiler, unsigned char *ptr, int label) { compiler->labels[label] = ptr; } void powerpc_emit_b (OrcCompiler *compiler, int label) { ORC_ASM_CODE(compiler," b %d%c\n", label, (compiler->labels[label]!=NULL) ? 'b' : 'f'); powerpc_add_fixup (compiler, 2, compiler->codeptr, label); powerpc_emit (compiler, 0x48000000); } void powerpc_emit_beq (OrcCompiler *compiler, int label) { ORC_ASM_CODE(compiler," ble- %d%c\n", label, (compiler->labels[label]!=NULL) ? 'b' : 'f'); powerpc_add_fixup (compiler, 0, compiler->codeptr, label); powerpc_emit (compiler, 0x40810000); } void powerpc_emit_bne (OrcCompiler *compiler, int label) { ORC_ASM_CODE(compiler," bdnz+ %d%c\n", label, (compiler->labels[label]!=NULL) ? 'b' : 'f'); powerpc_add_fixup (compiler, 0, compiler->codeptr, label); powerpc_emit (compiler, 0x42000000); } void powerpc_emit_label (OrcCompiler *compiler, int label) { ORC_ASM_CODE(compiler,"%d:\n", label); powerpc_add_label (compiler, compiler->codeptr, label); }