summaryrefslogtreecommitdiff
path: root/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c
diff options
context:
space:
mode:
Diffstat (limited to 'compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c')
-rw-r--r--compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c1613
1 files changed, 1613 insertions, 0 deletions
diff --git a/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c b/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c
new file mode 100644
index 0000000..368f8c5
--- /dev/null
+++ b/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/PCodeAssembly.c
@@ -0,0 +1,1613 @@
+#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(PPCErrorStr109);
+ else if (offset < -0x8000)
+ PPCError_Error(PPCErrorStr109);
+ } 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;
+ SectionHandle section;
+ EntryPoint *ep;
+ WeirdOperand wop;
+
+ codesize = pccomputeoffsets();
+ if (codesize <= 0)
+ PPCError_Error(PPCErrorStr190, 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, codesize + tbsize);
+ 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;
+}