#include "compiler/PCodeAssembly.h" #include "compiler/CError.h" #include "compiler/CFunc.h" #include "compiler/CMangler.h" #include "compiler/CParser.h" #include "compiler/CodeGen.h" #include "compiler/ObjGenMachO.h" #include "compiler/PCode.h" #include "compiler/PCodeInfo.h" #include "compiler/PCodeListing.h" #include "compiler/PPCError.h" #include "compiler/RegisterInfo.h" #include "compiler/StackFrame.h" #include "compiler/TOC.h" #include "compiler/objects.h" static UInt32 codebase; static SInt32 pcode_update_mem_labeldiff_imm(PCode *instr, const PCodeArg *op, WeirdOperand *wop) { SInt32 offset; Object *object; UInt8 arg; if (op->kind == PCOp_MEMORY) { object = op->data.mem.obj; offset = op->data.mem.offset; switch (object->datatype) { case DLOCAL: switch ((UInt8) op->arg) { case RefType_1: offset += local_offset_16(object); break; case RefType_D: offset = local_offset_lo(object, offset); break; case RefType_C: offset = local_offset_ha(object, offset); break; default: CError_FATAL(83); } break; case DDATA: case DFUNC: case DVFUNC: case DNONLAZYPTR: switch ((UInt8) op->arg) { case RefType_6: wop->type = MW_RELOC_5_LO16; break; case RefType_2: wop->type = MW_RELOC_3; break; case RefType_3: wop->type = MW_RELOC_4; break; case RefType_8: wop->type = MW_RELOC_7_HA16; break; case RefType_7: wop->type = MW_RELOC_6_HI16; break; default: CError_FATAL(135); } wop->x2 = object; CError_ASSERT(144, offset == 0); break; default: CError_FATAL(164); } } else if (op->kind == PCOp_LABELDIFF) { arg = op->arg; offset = op->data.labeldiff.labelA->block->codeOffset - op->data.labeldiff.labelB->block->codeOffset; offset += op->data.labeldiff.offset; if (arg == 1) offset = -offset; if (offset > 0x7FFF) PPCError_Error(109); else if (offset < -0x8000) PPCError_Error(109); } else if (op->kind == PCOp_IMMEDIATE) { offset = op->data.imm.value; } else { CError_FATAL(193); } return offset; } UInt32 assemblepcode(PCode *instr, UInt32 offset, WeirdOperand *wop) { UInt32 bits; bits = opcodeinfo[instr->op].insn; wop->type = -1; wop->x6 = 0; switch (instr->op) { case PC_BL: { int flag = PCODE_FLAG_SET_T(instr) & fAbsolute; if (instr->args[0].kind == PCOp_MEMORY) { bits |= instr->args[0].data.mem.offset & 0x3FFFFFC; wop->type = MW_RELOC_2_BR24; wop->x2 = instr->args[0].data.mem.obj; if (flag == 0) wop->type = MW_RELOC_2_BR24; else CError_FATAL(246); } else if (instr->args[0].kind == PCOp_IMMEDIATE) { bits |= instr->args[0].data.imm.value & 0x3FFFFFC; if (flag) bits |= 2; } else { bits |= (instr->args[0].data.label.label->block->codeOffset - offset) & 0x3FFFFFC; if (flag) CError_FATAL(261); } break; } case PC_B: { int flag = PCODE_FLAG_SET_T(instr) & fAbsolute; if (instr->args[0].kind == PCOp_MEMORY) { bits |= instr->args[0].data.mem.offset & 0x3FFFFFC; wop->x2 = instr->args[0].data.mem.obj; if (flag == 0) wop->type = MW_RELOC_2_BR24; else CError_FATAL(288); } else if (instr->args[0].kind == PCOp_IMMEDIATE) { bits |= instr->args[0].data.imm.value & 0x3FFFFFC; if (flag) bits |= 2; } else { bits |= (instr->args[0].data.label.label->block->codeOffset - offset) & 0x3FFFFFC; if (flag) CError_FATAL(302); } if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; break; } case PC_BDNZ: case PC_BDZ: { int flag = PCODE_FLAG_SET_T(instr) & fAbsolute; if (instr->args[0].kind == PCOp_MEMORY) { bits |= instr->args[0].data.mem.offset & 0xFFFC; wop->x2 = instr->args[0].data.mem.obj; if (flag == 0) wop->type = MW_RELOC_8; else CError_FATAL(333); } else { SInt32 value; if (instr->args[0].kind == PCOp_IMMEDIATE) value = instr->args[0].data.imm.value; else value = instr->args[0].data.label.label->block->codeOffset - offset; bits |= value & 0xFFFF; if (value < 0) { if (PCODE_FLAG_SET_T(instr) & fBranchNotTaken) bits |= 0x200000u; } else { if (PCODE_FLAG_SET_T(instr) & fBranchTaken) bits |= 0x200000u; } } if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; break; } case PC_BC: { int flag = PCODE_FLAG_SET_T(instr) & fAbsolute; bits |= (instr->args[0].data.imm.value & 31) << 21; bits |= ((instr->args[1].data.reg.reg * 4 + instr->args[2].data.imm.value) & 31) << 16; if (instr->args[3].kind == PCOp_MEMORY) { bits |= instr->args[3].data.mem.offset & 0xFFFC; wop->x2 = instr->args[3].data.mem.obj; if (flag == 0) wop->type = MW_RELOC_8; else CError_FATAL(387); } else { SInt32 value; if (instr->args[3].kind == PCOp_IMMEDIATE) value = instr->args[3].data.imm.value; else value = instr->args[3].data.label.label->block->codeOffset - offset; bits |= value & 0xFFFF; if (value < 0) { if (PCODE_FLAG_SET_T(instr) & fBranchNotTaken) bits |= 0x200000u; } else { if (PCODE_FLAG_SET_T(instr) & fBranchTaken) bits |= 0x200000u; } } if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; break; } case PC_BT: case PC_BF: case PC_BDNZT: case PC_BDNZF: case PC_BDZT: case PC_BDZF: { int flag = PCODE_FLAG_SET_T(instr) & fAbsolute; bits |= ((instr->args[0].data.reg.reg * 4 + instr->args[1].data.imm.value) & 31) << 16; if (instr->args[2].kind == PCOp_MEMORY) { bits |= instr->args[2].data.mem.offset & 0xFFFC; wop->x2 = instr->args[2].data.mem.obj; if (flag == 0) wop->type = MW_RELOC_8; else CError_FATAL(446); } else { SInt32 value; if (instr->args[2].kind == PCOp_IMMEDIATE) { value = instr->args[2].data.imm.value; if (flag) bits |= 2; } else { value = instr->args[2].data.label.label->block->codeOffset - offset; CError_ASSERT(458, !flag); } bits |= value & 0xFFFF; if (value < 0) { if (PCODE_FLAG_SET_T(instr) & fBranchNotTaken) bits |= 0x200000u; } else { if (PCODE_FLAG_SET_T(instr) & fBranchTaken) bits |= 0x200000u; } } if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; break; } case PC_BTLR: case PC_BTCTR: case PC_BFLR: case PC_BFCTR: bits |= ((instr->args[0].data.reg.reg * 4 + instr->args[1].data.imm.value) & 31) << 16; if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; if (PCODE_FLAG_SET_T(instr) & fBranchTaken) bits |= 0x200000u; break; case PC_BCLR: case PC_BCCTR: bits |= instr->args[0].data.imm.value << 21; bits |= ((instr->args[1].data.reg.reg * 4 + instr->args[2].data.imm.value) & 31) << 16; case PC_BLR: case PC_BCTR: case PC_BCTRL: case PC_BLRL: if (PCODE_FLAG_SET_T(instr) & fLink) bits |= 1; if (PCODE_FLAG_SET_T(instr) & fBranchTaken) bits |= 0x200000u; break; case PC_CRAND: case PC_CRANDC: case PC_CREQV: case PC_CRNAND: case PC_CRNOR: case PC_CROR: case PC_CRORC: case PC_CRXOR: bits |= ((instr->args[0].data.reg.reg * 4 + instr->args[1].data.imm.value) & 31) << 21; bits |= ((instr->args[2].data.reg.reg * 4 + instr->args[3].data.imm.value) & 31) << 16; bits |= ((instr->args[4].data.reg.reg * 4 + instr->args[5].data.imm.value) & 31) << 11; break; case PC_MCRF: bits |= instr->args[0].data.reg.reg << 23; bits |= instr->args[1].data.reg.reg << 18; break; case PC_LBZ: case PC_LBZU: case PC_LHZ: case PC_LHZU: case PC_LHA: case PC_LHAU: case PC_LWZ: case PC_LWZU: case PC_LMW: case PC_STB: case PC_STBU: case PC_STH: case PC_STHU: case PC_STW: case PC_STWU: case PC_STMW: case PC_LFS: case PC_LFSU: case PC_LFD: case PC_LFDU: case PC_STFS: case PC_STFSU: case PC_STFD: case PC_STFDU: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= pcode_update_mem_labeldiff_imm(instr, &instr->args[2], wop) & 0xFFFF; break; case PC_LBZX: case PC_LBZUX: case PC_LHZX: case PC_LHZUX: case PC_LHAX: case PC_LHAUX: case PC_LHBRX: case PC_LWZX: case PC_LWZUX: case PC_LWBRX: case PC_STBX: case PC_STBUX: case PC_STHX: case PC_STHUX: case PC_STHBRX: case PC_STWX: case PC_STWUX: case PC_STWBRX: case PC_LFSX: case PC_LFSUX: case PC_LFDX: case PC_LFDUX: case PC_STFSX: case PC_STFSUX: case PC_STFDX: case PC_STFDUX: case PC_LWARX: case PC_LSWX: case PC_STFIWX: case PC_STSWX: case PC_STWCX: case PC_ECIWX: case PC_ECOWX: case PC_DCREAD: case PC_TLBSX: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_DCBF: case PC_DCBST: case PC_DCBT: case PC_DCBTST: case PC_DCBZ: case PC_DCBI: case PC_ICBI: case PC_DCCCI: case PC_ICBT: case PC_ICCCI: case PC_ICREAD: case PC_DCBA: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 11; break; case PC_ADD: case PC_ADDC: case PC_ADDE: case PC_DIVW: case PC_DIVWU: case PC_MULHW: case PC_MULHWU: case PC_MULLW: case PC_SUBF: case PC_SUBFC: case PC_SUBFE: bits |= instr->args[2].data.reg.reg << 11; case PC_ADDME: case PC_ADDZE: case PC_NEG: case PC_SUBFME: case PC_SUBFZE: case PC_MFROM: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; if (PCODE_FLAG_SET_F(instr) & fOverflow) bits |= 0x400; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_ADDI: case PC_ADDIC: case PC_ADDICR: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= pcode_update_mem_labeldiff_imm(instr, &instr->args[2], wop) & 0xFFFF; break; case PC_ADDIS: bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[0].data.reg.reg << 21; bits |= pcode_update_mem_labeldiff_imm(instr, &instr->args[2], wop) & 0xFFFF; break; case PC_MULLI: case PC_SUBFIC: bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[2].data.imm.value & 0xFFFF; break; case PC_LI: case PC_LIS: bits |= instr->args[0].data.reg.reg << 21; bits |= pcode_update_mem_labeldiff_imm(instr, &instr->args[1], wop) & 0xFFFF; break; case PC_ANDI: case PC_ANDIS: case PC_ORI: case PC_ORIS: case PC_XORI: case PC_XORIS: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= pcode_update_mem_labeldiff_imm(instr, &instr->args[2], wop) & 0xFFFF; break; case PC_AND: case PC_OR: case PC_XOR: case PC_NAND: case PC_NOR: case PC_EQV: case PC_ANDC: case PC_ORC: case PC_SLW: case PC_SRW: case PC_SRAW: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_EXTSH: case PC_EXTSB: case PC_CNTLZW: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_MR: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_NOT: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_SRAWI: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= (instr->args[2].data.imm.value & 31) << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_RLWINM: case PC_RLWIMI: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= (instr->args[2].data.imm.value & 31) << 11; bits |= (instr->args[3].data.imm.value & 31) << 6; bits |= (instr->args[4].data.imm.value & 31) << 1; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_RLWNM: bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[2].data.reg.reg << 11; bits |= (instr->args[3].data.imm.value & 31) << 6; bits |= (instr->args[4].data.imm.value & 31) << 1; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_CMP: case PC_CMPL: bits |= instr->args[0].data.reg.reg << 23; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; break; case PC_CMPI: case PC_CMPLI: bits |= instr->args[0].data.reg.reg << 23; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.imm.value & 0xFFFF; break; case PC_MTXER: case PC_MTCTR: case PC_MTLR: case PC_MTMSR: case PC_MFMSR: case PC_MFXER: case PC_MFCTR: case PC_MFLR: case PC_MFCR: bits |= instr->args[0].data.reg.reg << 21; break; case PC_MFFS: bits |= instr->args[0].data.reg.reg << 21; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_MTCRF: bits |= instr->args[0].data.imm.value << 12; bits |= instr->args[1].data.reg.reg << 21; break; case PC_MTFSF: bits |= (instr->args[0].data.imm.value & 0xFF) << 17; bits |= instr->args[1].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_FMR: case PC_FABS: case PC_FNEG: case PC_FNABS: case PC_FRES: case PC_FRSQRTE: case PC_FRSP: case PC_FCTIW: case PC_FCTIWZ: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_FADD: case PC_FADDS: case PC_FSUB: case PC_FSUBS: case PC_FDIV: case PC_FDIVS: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_FMADD: case PC_FMADDS: case PC_FMSUB: case PC_FMSUBS: case PC_FNMADD: case PC_FNMADDS: case PC_FNMSUB: case PC_FNMSUBS: case PC_FSEL: bits |= instr->args[3].data.reg.reg << 11; case PC_FMUL: case PC_FMULS: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 6; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_FCMPU: case PC_FCMPO: bits |= instr->args[0].data.reg.reg << 23; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; break; case PC_MTSPR: if (instr->args[0].kind == PCOp_REGISTER) { CError_ASSERT(1027, instr->args[0].arg == RegClass_SPR); CError_ASSERT(1028, instr->args[0].data.reg.reg < 4); bits |= ((spr_to_sysreg[instr->args[0].data.reg.reg] & 0x1F) << 16) + ((spr_to_sysreg[instr->args[0].data.reg.reg] & 0x3E0) << 6); } else if (instr->args[0].kind == PCOp_SYSREG && instr->args[0].arg == 0) { bits |= ((instr->args[0].data.reg.reg & 0x1F) << 16) + ((instr->args[0].data.reg.reg & 0x3E0) << 6); } else { CError_FATAL(1033); } bits |= instr->args[1].data.reg.reg << 21; break; case PC_MTDCR: if (instr->args[0].kind == PCOp_IMMEDIATE) { bits |= ((instr->args[0].data.imm.value & 0x1F) << 16) + ((instr->args[0].data.imm.value & 0x3E0) << 6); } else { CError_FATAL(1042); } bits |= instr->args[1].data.reg.reg << 21; break; case PC_MFSPR: bits |= instr->args[0].data.reg.reg << 21; if (instr->args[1].kind == PCOp_REGISTER && instr->args[1].arg == RegClass_SPR) { CError_ASSERT(1055, instr->args[1].data.reg.reg < 4); bits |= ((spr_to_sysreg[instr->args[1].data.reg.reg] & 0x1F) << 16) + ((spr_to_sysreg[instr->args[1].data.reg.reg] & 0x3E0) << 6); } else if (instr->args[1].kind == PCOp_SYSREG && instr->args[1].arg == 0) { bits |= ((instr->args[1].data.reg.reg & 0x1F) << 16) + ((instr->args[1].data.reg.reg & 0x3E0) << 6); } else { CError_FATAL(1060); } break; case PC_MFDCR: bits |= instr->args[0].data.reg.reg << 21; if (instr->args[1].kind == PCOp_IMMEDIATE) { bits |= ((instr->args[1].data.imm.value & 0x1F) << 16) + ((instr->args[1].data.imm.value & 0x3E0) << 6); } else { CError_FATAL(1069); } break; case PC_LSWI: case PC_STSWI: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= (instr->args[2].data.imm.value & 31) << 11; break; case PC_MCRFS: bits |= (instr->args[1].data.imm.value & 7) << 18; case PC_MCRXR: bits |= instr->args[0].data.reg.reg << 23; break; case PC_MFTB: bits |= instr->args[0].data.reg.reg << 21; if (instr->args[1].kind == PCOp_SYSREG && instr->args[1].arg == 0) { if (instr->args[1].data.reg.reg == 284) bits |= 0xC4000u; else if (instr->args[1].data.reg.reg == 285) bits |= 0xD4000u; else CError_FATAL(1100); } else { CError_FATAL(1103); } break; case PC_MTSR: bits |= instr->args[1].data.reg.reg << 21; bits |= (instr->args[0].data.imm.value & 15) << 16; break; case PC_MFSR: bits |= instr->args[0].data.reg.reg << 21; bits |= (instr->args[1].data.imm.value & 15) << 16; break; case PC_MFSRIN: case PC_MTSRIN: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; break; case PC_MTFSB0: case PC_MTFSB1: bits |= (instr->args[0].data.imm.value & 31) << 21; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_MTFSFI: bits |= instr->args[0].data.reg.reg << 23; bits |= (instr->args[1].data.imm.value & 15) << 12; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_FSQRT: case PC_FSQRTS: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_TLBIE: case PC_TLBLD: case PC_TLBLI: bits |= instr->args[0].data.reg.reg << 11; break; case PC_TW: bits |= (instr->args[0].data.imm.value & 31) << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; break; case PC_TWI: bits |= (instr->args[0].data.imm.value & 31) << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.imm.value & 0xFFFF; break; case PC_OPWORD: CError_ASSERT(1176, instr->args[0].kind != PCOp_MEMORY); bits = pcode_update_mem_labeldiff_imm(instr, &instr->args[0], wop); break; case PC_MASKG: case PC_MASKIR: bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_LSCBX: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_DIV: case PC_DIVS: case PC_DOZ: case PC_MUL: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fOverflow) bits |= 0x400; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_NABS: case PC_ABS: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; if (PCODE_FLAG_SET_F(instr) & fOverflow) bits |= 0x400; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_CLCS: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_DOZI: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.imm.value & 0xFFFF; break; case PC_RLMI: bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; bits |= (instr->args[3].data.imm.value & 31) << 6; bits |= (instr->args[4].data.imm.value & 31) << 1; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_SLE: case PC_SLEQ: case PC_SLLQ: case PC_SLQ: case PC_SRAQ: case PC_SRE: case PC_SREA: case PC_SREQ: case PC_SRLQ: case PC_SRQ: case PC_RRIB: bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_SLIQ: case PC_SLLIQ: case PC_SRAIQ: case PC_SRIQ: case PC_SRLIQ: bits |= instr->args[1].data.reg.reg << 21; bits |= instr->args[0].data.reg.reg << 16; bits |= (instr->args[2].data.imm.value & 31) << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 1; break; case PC_TLBRE: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= (instr->args[2].data.imm.value & 1) << 11; break; case PC_TLBWE: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= (instr->args[2].data.imm.value & 1) << 11; break; case PC_WRTEE: bits |= instr->args[0].data.reg.reg << 21; break; case PC_WRTEEI: bits |= instr->args[0].data.imm.value << 15; break; case PC_DSTT: case PC_DSTSTT: bits |= 0x2000000u; bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 11; bits |= (instr->args[2].data.imm.value & 3) << 21; break; case PC_DST: case PC_DSTST: bits |= (instr->args[3].data.imm.value & 1) << 25; bits |= instr->args[0].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 11; bits |= (instr->args[2].data.imm.value & 3) << 21; break; case PC_DSSALL: bits |= 0x2000000u; break; case PC_DSS: bits |= (instr->args[1].data.imm.value & 1) << 25; bits |= (instr->args[0].data.imm.value & 3) << 21; break; case PC_LVEBX: case PC_LVEHX: case PC_LVEWX: case PC_LVSL: case PC_LVSR: case PC_LVX: case PC_LVXL: case PC_STVEBX: case PC_STVEHX: case PC_STVEWX: case PC_STVX: case PC_STVXL: case PC_VADDCUW: case PC_VADDFP: case PC_VADDSBS: case PC_VADDSHS: case PC_VADDSWS: case PC_VADDUBM: case PC_VADDUBS: case PC_VADDUHM: case PC_VADDUHS: case PC_VADDUWM: case PC_VADDUWS: case PC_VAND: case PC_VANDC: case PC_VAVGSB: case PC_VAVGSH: case PC_VAVGSW: case PC_VAVGUB: case PC_VAVGUH: case PC_VAVGUW: case PC_VMAXFP: case PC_VMAXSB: case PC_VMAXSH: case PC_VMAXSW: case PC_VMAXUB: case PC_VMAXUH: case PC_VMAXUW: case PC_VMINFP: case PC_VMINSB: case PC_VMINSH: case PC_VMINSW: case PC_VMINUB: case PC_VMINUH: case PC_VMINUW: case PC_VMRGHB: case PC_VMRGHH: case PC_VMRGHW: case PC_VMRGLB: case PC_VMRGLH: case PC_VMRGLW: case PC_VMULESB: case PC_VMULESH: case PC_VMULEUB: case PC_VMULEUH: case PC_VMULOSB: case PC_VMULOSH: case PC_VMULOUB: case PC_VMULOUH: case PC_VNOR: case PC_VOR: case PC_VPKPX: case PC_VPKSHSS: case PC_VPKSHUS: case PC_VPKSWSS: case PC_VPKSWUS: case PC_VPKUHUM: case PC_VPKUHUS: case PC_VPKUWUM: case PC_VPKUWUS: case PC_VRLB: case PC_VRLH: case PC_VRLW: case PC_VSL: case PC_VSLB: case PC_VSLH: case PC_VSLO: case PC_VSLW: case PC_VSR: case PC_VSRAB: case PC_VSRAH: case PC_VSRAW: case PC_VSRB: case PC_VSRH: case PC_VSRO: case PC_VSRW: case PC_VSUBCUW: case PC_VSUBFP: case PC_VSUBSBS: case PC_VSUBSHS: case PC_VSUBSWS: case PC_VSUBUBM: case PC_VSUBUBS: case PC_VSUBUHM: case PC_VSUBUHS: case PC_VSUBUWM: case PC_VSUBUWS: case PC_VSUMSWS: case PC_VSUM2SWS: case PC_VSUM4SBS: case PC_VSUM4SHS: case PC_VSUM4UBS: case PC_VXOR: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; break; case PC_VCFSX: case PC_VCFUX: case PC_VCTSXS: case PC_VCTUXS: case PC_VSPLTB: case PC_VSPLTH: case PC_VSPLTW: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; bits |= (instr->args[2].data.imm.value & 31) << 16; break; case PC_VEXPTEFP: case PC_VLOGEFP: case PC_VREFP: case PC_VRFIM: case PC_VRFIN: case PC_VRFIP: case PC_VRFIZ: case PC_VRSQRTEFP: case PC_VUPKHPX: case PC_VUPKHSB: case PC_VUPKHSH: case PC_VUPKLPX: case PC_VUPKLSB: case PC_VUPKLSH: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 11; break; case PC_VCMPBFP: case PC_VCMPEQFP: case PC_VCMPEQUB: case PC_VCMPEQUH: case PC_VCMPEQUW: case PC_VCMPGEFP: case PC_VCMPGTFP: case PC_VCMPGTSB: case PC_VCMPGTSH: case PC_VCMPGTSW: case PC_VCMPGTUB: case PC_VCMPGTUH: case PC_VCMPGTUW: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; if (PCODE_FLAG_SET_F(instr) & fRecordBit) bits |= 0x400; break; case PC_VSPLTISB: case PC_VSPLTISH: case PC_VSPLTISW: bits |= instr->args[0].data.reg.reg << 21; bits |= (instr->args[1].data.imm.value & 31) << 16; break; case PC_VMHADDSHS: case PC_VMHRADDSHS: case PC_VMLADDUHM: case PC_VMSUMMBM: case PC_VMSUMSHM: case PC_VMSUMSHS: case PC_VMSUMUBM: case PC_VMSUMUHM: case PC_VMSUMUHS: case PC_VPERM: case PC_VSEL: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; bits |= instr->args[3].data.reg.reg << 6; break; case PC_VMADDFP: case PC_VNMSUBFP: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 6; bits |= instr->args[3].data.reg.reg << 11; break; case PC_VSLDOI: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[2].data.reg.reg << 11; bits |= (instr->args[3].data.imm.value & 15) << 6; break; case PC_VMR: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 11; break; case PC_VMRP: bits |= instr->args[0].data.reg.reg << 21; bits |= instr->args[1].data.reg.reg << 16; bits |= instr->args[1].data.reg.reg << 11; break; case PC_MFVSCR: bits |= instr->args[0].data.reg.reg << 21; break; case PC_MTVSCR: bits |= instr->args[0].data.reg.reg << 11; break; case PC_EIEIO: case PC_ISYNC: case PC_SYNC: case PC_RFI: case PC_NOP: case PC_SC: case PC_TLBIA: case PC_TLBSYNC: case PC_TRAP: case PC_DSA: case PC_ESA: case PC_RFCI: break; default: CError_FATAL(2203); } return CTool_EndianConvertWord32(bits); } static PCode *targetinstruction(PCodeLabel *label) { PCodeBlock *block = label->block; while (block->pcodeCount == 0) block = block->nextBlock; return block->firstPCode; } static void invertybit(PCode *instr, SInt32 value) { if (instr->op == PC_BC) { if (instr->args[0].data.imm.value & 1) { if (value < 0) instr->flags |= fBranchNotTaken; else instr->flags |= fBranchTaken; } } else if (instr->op == PC_BCCTR || instr->op == PC_BCLR) { if (instr->args[0].data.imm.value & 1) instr->flags |= fBranchTaken; } if (PCODE_FLAG_SET_T(instr) & fBranchTaken) instr->flags = (instr->flags & ~fBranchTaken) | fBranchNotTaken; else if (PCODE_FLAG_SET_T(instr) & fBranchNotTaken) instr->flags = (instr->flags & ~fBranchNotTaken) | fBranchTaken; else if (value < 0) instr->flags = (instr->flags & ~fBranchTaken) | fBranchNotTaken; else instr->flags = (instr->flags & ~fBranchNotTaken) | fBranchTaken; } static void insertlongbranches(SInt32 mask) { PCodeBlock *block; PCodeLabel *label; SInt32 i; i = 0; for (block = pcbasicblocks; block; block = block->nextBlock) { block->codeOffset = i; if (block->pcodeCount) { i += block->pcodeCount * 4; if (block->pcodeCount && (block->lastPCode->flags & fIsBranch)) i += 4; } } for (block = pcbasicblocks; block; block = block->nextBlock) { if (block->pcodeCount && (block->lastPCode->flags & fIsBranch)) { switch (block->lastPCode->op) { case PC_BT: case PC_BF: i = block->codeOffset + ((block->pcodeCount - 1) * 4); if (block->lastPCode->args[2].kind == PCOp_LABEL) { label = block->lastPCode->args[2].data.label.label; i = label->block->codeOffset - i; if (i != ((SInt16) (i & mask))) { block->lastPCode->op = (block->lastPCode->op == PC_BT) ? PC_BF : PC_BT; block->lastPCode->args[2].data.label.label = block->nextBlock->labels; invertybit(block->lastPCode, i); appendpcode(block, makepcode(PC_B, label)); } } break; case PC_BC: i = block->codeOffset + ((block->pcodeCount - 1) * 4); if (block->lastPCode->args[3].kind == PCOp_LABEL) { label = block->lastPCode->args[3].data.label.label; i = label->block->codeOffset - i; invertybit(block->lastPCode, i); if (i != ((SInt16) (i & mask))) { switch (block->lastPCode->args[0].data.imm.value & 30) { case 0: case 2: case 8: case 10: block->lastPCode->args[0].data.imm.value ^= 11; block->lastPCode->args[3].data.label.label = block->nextBlock->labels; break; case 16: case 18: block->lastPCode->args[0].data.imm.value ^= 3; block->lastPCode->args[3].data.label.label = block->nextBlock->labels; break; case 4: case 12: block->lastPCode->args[0].data.imm.value ^= 9; block->lastPCode->args[3].data.label.label = block->nextBlock->labels; break; case 20: deletepcode(block->lastPCode); break; default: CError_FATAL(2368); } appendpcode(block, makepcode(PC_B, label)); } } break; case PC_BDNZ: case PC_BDZ: i = block->codeOffset + ((block->pcodeCount - 1) * 4); if (block->lastPCode->args[0].kind == PCOp_LABEL) { label = block->lastPCode->args[0].data.label.label; i = label->block->codeOffset - i; if (i != ((SInt16) (i & mask))) { switch (block->lastPCode->op) { case PC_BDZ: block->lastPCode->op = PC_BDNZ; break; case PC_BDNZ: block->lastPCode->op = PC_BDZ; break; default: CError_FATAL(2389); } block->lastPCode->args[0].data.label.label = block->nextBlock->labels; invertybit(block->lastPCode, i); appendpcode(block, makepcode(PC_B, label)); } } break; case PC_BDNZT: case PC_BDNZF: case PC_BDZT: case PC_BDZF: i = block->codeOffset + ((block->pcodeCount - 1) * 4); if (block->lastPCode->args[2].kind == PCOp_LABEL) { label = block->lastPCode->args[2].data.label.label; i = label->block->codeOffset - i; if (i != ((SInt16) (i & mask))) { switch (block->lastPCode->op) { case PC_BDNZT: block->lastPCode->op = PC_BDZF; break; case PC_BDNZF: block->lastPCode->op = PC_BDZT; break; case PC_BDZT: block->lastPCode->op = PC_BDNZF; break; case PC_BDZF: block->lastPCode->op = PC_BDNZT; break; default: CError_FATAL(2420); } block->lastPCode->args[2].data.label.label = block->nextBlock->labels; invertybit(block->lastPCode, i); appendpcode(block, makepcode(PC_B, label)); } } break; } } } } SInt32 optimizefinalbranches(SInt32 codesize) { PCodeBlock *block; PCode *instr; SInt32 offset; int changed; int deleted; PCodeLabel *label; PCode *target; do { changed = deleted = 0; for (block = pcbasicblocks; block; block = block->nextBlock) { if (block->pcodeCount == 0) continue; instr = block->lastPCode; if (!(instr->flags & fIsBranch)) continue; offset = block->codeOffset + (block->pcodeCount - 1) * 4; if (instr->op == PC_B && instr->args[0].kind == PCOp_LABEL) { label = instr->args[0].data.label.label; target = targetinstruction(label); if (label->block->codeOffset == (offset + 4)) { deletepcode(instr); changed = deleted = 1; } else if (target->op == PC_B) { if (target->args[0].kind == PCOp_LABEL && target->args[0].data.label.label != instr->args[0].data.label.label) { instr->args[0].data.label.label = target->args[0].data.label.label; changed = 1; } } else if (target->op == PC_BLR) { instr->op = PC_BLR; changed = 1; } continue; } if ((instr->op == PC_BT || instr->op == PC_BF) && instr->args[2].kind == PCOp_LABEL) { PCodeBlock *block2 = instr->block; label = instr->args[2].data.label.label; target = targetinstruction(label); if (label->block->codeOffset == (offset + 4)) { deletepcode(instr); changed = deleted = 1; } else if (target->op == PC_B) { if (target->args[0].kind == PCOp_LABEL && target->args[0].data.label.label != instr->args[2].data.label.label) { instr->args[2].data.label.label = target->args[0].data.label.label; changed = 1; } } else if (copts.opt_bcc_lr_ctr) { if (target->op == PC_BLR) { if (instr->op == PC_BT) instr->op = PC_BTLR; else instr->op = PC_BFLR; instr->argCount = 2; changed = 1; } else if (target->op == PC_BCTR) { if (instr->op == PC_BT) instr->op = PC_BTCTR; else instr->op = PC_BFCTR; instr->argCount = 2; changed = 1; } else if ( block2->nextBlock && block2->nextBlock->firstPCode && block2->nextBlock->firstPCode->op == PC_BLR && label->block->codeOffset == (offset + 8) ) { if ( block2->nextBlock->predecessors && block2->nextBlock->predecessors->block == block2 && !block2->nextBlock->predecessors->nextLink ) { if (instr->op == PC_BT) instr->op = PC_BFLR; else instr->op = PC_BTLR; change_num_operands(instr, 2); deletepcode(block2->nextBlock->firstPCode); changed = deleted = 1; } } else if ( block2->nextBlock && block2->nextBlock->firstPCode && block2->nextBlock->firstPCode->op == PC_BCTR && label->block->codeOffset == (offset + 8) ) { if ( block2->nextBlock->predecessors && block2->nextBlock->predecessors->block == block2 && !block2->nextBlock->predecessors->nextLink ) { if (instr->op == PC_BT) instr->op = PC_BFCTR; else instr->op = PC_BTCTR; change_num_operands(instr, 2); deletepcode(block2->nextBlock->firstPCode); changed = deleted = 1; } } } continue; } if ( instr->op == PC_BC && instr->args[3].kind == PCOp_LABEL && !(PCODE_FLAG_SET_T(instr) & (fSideEffects | fLink)) ) { PCodeBlock *block2 = instr->block; label = instr->args[3].data.label.label; target = targetinstruction(label); if (label->block->codeOffset == (offset + 4)) { deletepcode(instr); changed = deleted = 1; } else if (target->op == PC_B) { if (target->args[0].kind == PCOp_LABEL && target->args[0].data.label.label != instr->args[3].data.label.label) { instr->args[3].data.label.label = target->args[0].data.label.label; changed = 1; } } else if (copts.opt_bcc_lr_ctr) { if (target->op == PC_BLR) { instr->op = PC_BCLR; instr->argCount = 3; changed = 1; } else if (target->op == PC_BCTR) { instr->op = PC_BCCTR; instr->argCount = 3; changed = 1; } else if ( block2->nextBlock && block2->nextBlock->firstPCode && block2->nextBlock->firstPCode->op == PC_BLR && label->block->codeOffset == (offset + 8) ) { SInt32 val = instr->args[0].data.imm.value & 30; if ( block2->nextBlock->predecessors && block2->nextBlock->predecessors->block == block2 && !block2->nextBlock->predecessors->nextLink ) { if ((val & 30) == 4) instr->args[0].data.imm.value = val | 12; else if ((val & 30) == 12) instr->args[0].data.imm.value = val & 23; instr->op = PC_BCLR; instr->argCount = 3; deletepcode(block2->nextBlock->firstPCode); changed = deleted = 1; } } else if ( block2->nextBlock && block2->nextBlock->firstPCode && block2->nextBlock->firstPCode->op == PC_BCTR && label->block->codeOffset == (offset + 8) ) { SInt32 val = instr->args[0].data.imm.value & 30; if ( block2->nextBlock->predecessors && block2->nextBlock->predecessors->block == block2 && !block2->nextBlock->predecessors->nextLink ) { if ((val & 30) == 4) instr->args[0].data.imm.value = val | 12; else if ((val & 30) == 12) instr->args[0].data.imm.value = val & 23; instr->op = PC_BCCTR; instr->argCount = 3; deletepcode(block2->nextBlock->firstPCode); changed = deleted = 1; } } } } } if (deleted) codesize = pccomputeoffsets(); } while (changed); return codesize; } static SInt32 insert_align_nops(Object *func, SInt32 codesize) { PCodeBlock *block; int changed; PCodeBlock *prev; do { changed = 0; for (block = pcbasicblocks; block; block = block->nextBlock) { if ( (block->flags & fPCBlockFlag6000) == fPCBlockFlag2000 && (block->codeOffset & 7) && block->pcodeCount < 8 && (prev = block->prevBlock) && !(prev->flags & fPCBlockFlag2000) ) { if (prev->lastPCode && prev->lastPCode->op == PC_NOP && !(prev->lastPCode->flags & fSideEffects)) { deletepcode(prev->lastPCode); } else { PCode *nop = makepcode(PC_NOP); nop->flags &= ~fSideEffects; appendpcode(prev, nop); } codesize = pccomputeoffsets(); changed = 1; } } if (changed) { pclistblocks(CMangler_GetLinkName(func)->name, "AFTER INSERT ALIGN NOPs"); if (codesize > 32766) { insertlongbranches(32766); codesize = pccomputeoffsets(); } } } while (changed); return codesize; } SInt32 assemblefunction(Object *func, EntryPoint *entrypoints) { void *tbdata; GList *gl; PCodeBlock *block; PCode *instr; SInt32 offset2; SInt32 codesize; SInt32 tbsize; SInt32 offset; MachOSection *section; EntryPoint *ep; WeirdOperand wop; codesize = pccomputeoffsets(); if (codesize <= 0) PPCError_Error(190, func->name->name); if (copts.peephole || copts.optimizationlevel >= 3) codesize = optimizefinalbranches(codesize); if (codesize > 32766) { insertlongbranches(32766); codesize = pccomputeoffsets(); } if (copts.function_align > 4) codesize = insert_align_nops(func, codesize); tbsize = 0; if (copts.traceback) tbdata = generate_traceback(codesize, CMangler_GetLinkName(func)->name, &tbsize, func); if (func->section == SECT_DEFAULT) func->section = SECT_TEXT; offset = tbsize; section = ObjGen_DeclareCode(func); gl = ObjGen_GetSectionGList(section); codebase = gl->size; AppendGListNoData(gl, codesize + tbsize); if (copts.filesyminfo) { ObjGen_SymFunc(func); ObjGen_Line(functionbodyoffset, 0); ObjGen_DeclareSymInfo(); } if (uses_globals && pic_base_reg) ObjGen_DeclarePICBase(func, pic_base_pcodelabel->block->codeOffset); if (entrypoints) { for (ep = entrypoints; ep; ep = ep->next) { ObjGen_DeclareEntry(ep->object, ep->block->codeOffset); } } for (block = pcbasicblocks; block; block = block->nextBlock) { for (offset2 = block->codeOffset, instr = block->firstPCode; instr; instr = instr->nextPCode, offset2 += 4) { if (copts.filesyminfo && instr->sourceoffset != -1) ObjGen_Line(instr->sourceoffset, offset2); *((UInt32 *) (*gl->data + codebase + offset2)) = assemblepcode(instr, offset2, &wop); if (wop.type != -1) ObjGen_RelocateObj(section, offset2, wop.x2, wop.type); } } if (copts.filesyminfo) ObjGenMach_SymFuncEnd(func, codesize); if (copts.traceback) memcpy(*gl->data + codebase + codesize, tbdata, offset); if (copts.traceback) return codesize + tbsize; else return codesize; }