#include "compiler/Operands.h" #include "compiler/CError.h" #include "compiler/CMachine.h" #include "compiler/CParser.h" #include "compiler/CodeGen.h" #include "compiler/CompilerTools.h" #include "compiler/PCode.h" #include "compiler/PCodeInfo.h" #include "compiler/PCodeUtilities.h" #include "compiler/RegisterInfo.h" #include "compiler/Registers.h" #include "compiler/StackFrame.h" #include "compiler/TOC.h" #include "compiler/enode.h" #include "compiler/objects.h" unsigned long long uns_to_float_cc = 0x4330000000000000; unsigned long long int_to_float_cc = 0x4330000080000000; Float one_point_zero = {1.0}; void load_immediate(short reg, SInt32 value) { short tmpreg = reg; short tmpreg2; if (!FITS_IN_SHORT(value)) { if (copts.optimizationlevel > 1 && value) tmpreg = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_LIS, tmpreg2 = tmpreg, 0, (short) HIGH_PART(value)); if (LOW_PART(value)) emitpcode(PC_ADDI, reg, tmpreg2, 0, LOW_PART(value)); } else { emitpcode(PC_LI, reg, value); } } static void set_op_flags(Operand *op, ENode *expr) { CError_ASSERT(118, op); if (expr) { if (expr->type == EINTCONST) { op->flags = 0; if (expr->flags & ENODE_FLAG_VOLATILE) op->flags |= OpndFlags_Volatile; if (expr->flags & ENODE_FLAG_CONST) op->flags |= OpndFlags_Const; } else { op->flags = CParserIsVolatileExpr(expr) ? OpndFlags_Volatile : 0; op->flags |= CParserIsConstExpr(expr) ? OpndFlags_Const : 0; } } else { op->flags = 0; } } void symbol_operand(Operand *op, Object *obj) { memclrw(op, sizeof(Operand)); op->optype = OpndType_Symbol; op->object = obj; } void indirect(Operand *op, ENode *expr) { switch (op->optype) { case OpndType_GPRPair: CError_FATAL(163); case OpndType_CRField: case OpndType_IndirectGPR_ImmOffset: case OpndType_IndirectGPR_Indexed: case OpndType_IndirectSymbol: if (op->optype) Coerce_to_register(op, TYPE(&void_ptr), 0); case OpndType_GPR: op->immOffset = 0; op->object = NULL; case OpndType_GPR_ImmOffset: op->optype = OpndType_IndirectGPR_ImmOffset; set_op_flags(op, expr); break; case OpndType_GPR_Indexed: op->optype = OpndType_IndirectGPR_Indexed; set_op_flags(op, expr); break; case OpndType_Absolute: if (FITS_IN_SHORT(op->immediate)) { op->reg = 0; op->immOffset = op->immediate; } else { emitpcode(PC_LIS, op->reg = used_virtual_registers[RegClass_GPR]++, 0, (short) HIGH_PART(op->immediate)); op->immOffset = LOW_PART(op->immediate); } op->object = NULL; op->optype = OpndType_IndirectGPR_ImmOffset; set_op_flags(op, expr); break; case OpndType_Symbol: op->optype = OpndType_IndirectSymbol; set_op_flags(op, expr); break; default: CError_FATAL(215); } } #define COMBO_OP(a, b) (b + (a * 11)) void combine(Operand *opA, Operand *opB, short output_reg, Operand *opOut) { Operand *tmp_op; int tmp; if (opA->optype == OpndType_Symbol || opA->optype == OpndType_IndirectSymbol) coerce_to_addressable(opA); if (opB->optype == OpndType_Symbol || opB->optype == OpndType_IndirectSymbol) coerce_to_addressable(opB); switch (COMBO_OP(opA->optype, opB->optype)) { case COMBO_OP(OpndType_GPR, OpndType_GPR): opOut->optype = OpndType_GPR_Indexed; opOut->reg = opA->reg; opOut->regOffset = opB->reg; break; case COMBO_OP(OpndType_GPR_ImmOffset, OpndType_GPR_ImmOffset): if (FITS_IN_SHORT(opA->immOffset + opB->immOffset) && (!opA->object || !opB->object)) { opB->immOffset += opA->immOffset; if (!opB->object) opB->object = opA->object; } else { tmp = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; add_immediate(tmp, opA->reg, opA->object, opA->immOffset); opA->reg = tmp; } case COMBO_OP(OpndType_GPR, OpndType_GPR_ImmOffset): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_GPR_ImmOffset, OpndType_GPR): if (opA->reg == _FP_ || opA->reg == _CALLER_SP_) { opOut->optype = OpndType_GPR_Indexed; opOut->reg = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; opOut->regOffset = opB->reg; add_immediate(opOut->reg, opA->reg, opA->object, LOW_PART(opA->immOffset)); } else if (opB->reg == _FP_ || opB->reg == _CALLER_SP_) { opOut->optype = OpndType_GPR_Indexed; opOut->reg = (output_reg && (output_reg != opA->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; opOut->regOffset = opA->reg; add_immediate(opOut->reg, opB->reg, opA->object, LOW_PART(opA->immOffset)); } else if (opA->object) { opOut->optype = OpndType_GPR_Indexed; opOut->reg = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; opOut->regOffset = opB->reg; add_immediate(opOut->reg, opA->reg, opA->object, LOW_PART(opA->immOffset)); } else { opOut->optype = OpndType_GPR_ImmOffset; opOut->reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; opOut->immOffset = opA->immOffset; opOut->object = opA->object; emitpcode(PC_ADD, opOut->reg, opA->reg, opB->reg); } break; case COMBO_OP(OpndType_GPR, OpndType_GPR_Indexed): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_GPR_Indexed, OpndType_GPR): opOut->optype = OpndType_GPR_Indexed; opOut->reg = opA->reg; opOut->regOffset = (output_reg && (output_reg != opA->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, opOut->regOffset, opA->regOffset, opB->reg); break; case COMBO_OP(OpndType_GPR_ImmOffset, OpndType_GPR_Indexed): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_GPR_Indexed, OpndType_GPR_ImmOffset): if (opB->object) { opOut->optype = OpndType_GPR_Indexed; opOut->reg = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; opOut->regOffset = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, opOut->reg, opA->reg, opA->regOffset); add_immediate(opOut->regOffset, opB->reg, opB->object, opB->immOffset); } else { opOut->optype = OpndType_GPR_ImmOffset; opOut->immOffset = opB->immOffset; opOut->object = opB->object; opOut->reg = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, opOut->reg, opA->reg, opA->regOffset); emitpcode(PC_ADD, opOut->reg, opOut->reg, opB->reg); } break; case COMBO_OP(OpndType_GPR_Indexed, OpndType_GPR_Indexed): opOut->optype = OpndType_GPR_Indexed; opOut->reg = opA->reg; opOut->regOffset = (output_reg && (output_reg != opA->regOffset)) ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, opOut->regOffset, opB->reg, opB->regOffset); emitpcode(PC_ADD, opOut->regOffset, opOut->regOffset, opA->regOffset); break; case COMBO_OP(OpndType_GPR_ImmOffset, OpndType_Absolute): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_Absolute, OpndType_GPR_ImmOffset): if (!opB->object) { opOut->optype = OpndType_GPR_ImmOffset; opOut->reg = opB->reg; opOut->immOffset = opB->immOffset; opOut->object = opB->object; if (FITS_IN_SHORT(opOut->immOffset + opA->immediate)) { opOut->immOffset += opA->immediate; } else { opOut->reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; if (!HIGH_PART(opA->immediate)) { emitpcode(PC_ADDI, opOut->reg, opB->reg, 0, LOW_PART(opA->immediate)); } else { emitpcode(PC_ADDIS, opOut->reg, opB->reg, 0, (short) HIGH_PART(opA->immediate)); if (FITS_IN_SHORT(opOut->immOffset + LOW_PART(opA->immediate))) { opOut->immOffset += LOW_PART(opA->immediate); } else { emitpcode(PC_ADDI, opOut->reg, opOut->reg, 0, LOW_PART(opA->immediate)); } } } break; } else if (opB->object->datatype == DLOCAL && can_add_displ_to_local(opB->object, opB->immOffset + opA->immediate)) { opOut->optype = OpndType_GPR_ImmOffset; opOut->object = opB->object; opOut->reg = opB->reg; opOut->immOffset = LOW_PART(opB->immOffset + opA->immediate); break; } else { opOut->reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; add_immediate(opOut->reg, opB->reg, opB->object, opB->immOffset); opB->optype = OpndType_GPR; opB->reg = opOut->reg; tmp_op = opA; opA = opB; opB = tmp_op; } case COMBO_OP(OpndType_GPR, OpndType_Absolute): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_Absolute, OpndType_GPR): opOut->optype = (opA->immediate != 0) ? OpndType_GPR_ImmOffset : OpndType_GPR; opOut->immOffset = LOW_PART(opA->immediate); opOut->object = NULL; if (FITS_IN_SHORT(opA->immediate)) { opOut->reg = opB->reg; } else { opOut->reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADDIS, opOut->reg, opB->reg, 0, (short) HIGH_PART(opA->immediate)); } break; case COMBO_OP(OpndType_GPR_Indexed, OpndType_Absolute): tmp_op = opA; opA = opB; opB = tmp_op; case COMBO_OP(OpndType_Absolute, OpndType_GPR_Indexed): opOut->optype = OpndType_GPR_Indexed; opOut->reg = opB->reg; opOut->regOffset = (output_reg && (output_reg != opB->reg)) ? output_reg : used_virtual_registers[RegClass_GPR]++; if (!HIGH_PART(opA->immediate)) { emitpcode(PC_ADDI, opOut->regOffset, opB->regOffset, 0, LOW_PART(opA->immediate)); } else { emitpcode(PC_ADDIS, opOut->regOffset, opB->regOffset, 0, (short) HIGH_PART(opA->immediate)); if (LOW_PART(opA->immediate)) emitpcode(PC_ADDI, opOut->regOffset, opOut->regOffset, 0, LOW_PART(opA->immediate)); } break; case COMBO_OP(OpndType_Absolute, OpndType_Absolute): opOut->optype = OpndType_Absolute; opOut->immediate = opA->immediate + opB->immediate; break; default: CError_FATAL(415); } } void coerce_to_addressable(Operand *op) { UInt32 offset; short reg; short flag; short tmp; Object *obj; flag = 0; obj = op->object; tmp = 0; switch (op->optype) { case OpndType_GPR: case OpndType_GPR_ImmOffset: case OpndType_GPR_Indexed: case OpndType_GPRPair: case OpndType_Absolute: case OpndType_VR: case OpndType_CRField: case OpndType_IndirectGPR_ImmOffset: case OpndType_IndirectGPR_Indexed: break; case OpndType_IndirectSymbol: flag = 1; case OpndType_Symbol: if (obj->datatype == DLOCAL) { if (!local_is_16bit_offset(obj)) { reg = used_virtual_registers[RegClass_GPR]++; op_absolute_ha(reg, local_base_register(obj), obj, 0, 1); op->optype = OpndType_GPR_ImmOffset; op->reg = reg; op->object = obj; } else { op->optype = OpndType_GPR_ImmOffset; op->reg = local_base_register(obj); op->object = obj; } } else if (obj->datatype == DABSOLUTE) { offset = obj->u.address; if (FITS_IN_SHORT(offset)) { op->reg = 0; op->immOffset = obj->u.address; } else { emitpcode(PC_LIS, op->reg = used_virtual_registers[RegClass_GPR]++, 0, (short) HIGH_PART(offset)); op->immOffset = LOW_PART(obj->u.address); } op->object = obj; op->optype = OpndType_GPR_ImmOffset; } else { if (copts.codegen_pic) tmp = pic_base_reg; reg = used_virtual_registers[RegClass_GPR]++; op_absolute_ha(reg, tmp, obj, 0, 1); op->optype = OpndType_GPR_ImmOffset; op->reg = reg; } if (flag) { if (op->optype == OpndType_GPR_ImmOffset) { op->optype = OpndType_IndirectGPR_ImmOffset; } else { CError_FATAL(563); } } break; default: CError_FATAL(581); } } void Coerce_to_register(Operand *op, Type *type, short output_reg) { SInt32 offset; Opcode opcode; short reg; short tmp; short cond_neg; short cond; short bit_offset; short bit_size; if (TYPE_IS_8BYTES(type)) { coerce_to_register_pair(op, type, output_reg, 0); return; } coerce_to_addressable(op); switch (op->optype) { case OpndType_GPRPair: return; case OpndType_GPR: return; case OpndType_GPR_ImmOffset: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; add_immediate(reg, op->reg, op->object, op->immOffset); break; case OpndType_GPR_Indexed: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, reg, op->reg, op->regOffset); break; case OpndType_Absolute: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; offset = op->immediate; if (FITS_IN_SHORT(offset)) { emitpcode(PC_LI, reg, offset); } else { tmp = reg; if (copts.optimizationlevel > 1 && offset) tmp = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_LIS, tmp, 0, (short) HIGH_PART(offset)); if (LOW_PART(offset)) emitpcode(PC_ADDI, reg, tmp, 0, LOW_PART(offset)); } break; case OpndType_IndirectGPR_ImmOffset: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; opcode = PC_LWZ; if (IS_TYPE_INT(type) || IS_TYPE_ENUM(type)) { switch (type->size) { case 1: opcode = PC_LBZ; break; case 2: if (is_unsigned(type)) opcode = PC_LHZ; else opcode = PC_LHA; break; } } else { CError_ASSERT(680, IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)); } load_store_register(opcode, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; opcode = PC_LWZX; if (IS_TYPE_INT(type) || IS_TYPE_ENUM(type)) { switch (type->size) { case 1: opcode = PC_LBZX; break; case 2: if (is_unsigned(type)) opcode = PC_LHZX; else opcode = PC_LHAX; break; } } else { CError_ASSERT(724, IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)); } emitpcode(opcode, reg, op->reg, op->regOffset); setpcodeflags(op->flags); break; case OpndType_CRField: cond_neg = 0; cond = 0; reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_MFCR, tmp = reg); switch (op->regOffset) { case ENOTEQU: cond_neg = 1; case EEQU: cond = 2; break; case EGREATEREQU: cond_neg = 1; case ELESS: cond = 0; break; case ELESSEQU: cond_neg = 1; case EGREATER: cond = 1; break; default: CError_FATAL(758); } bit_offset = cond + (op->reg << 2); bit_size = 1; emitpcode(PC_RLWINM, tmp, tmp, (bit_size + bit_offset) & 31, 32 - bit_size, 31); if (cond_neg) emitpcode(PC_XORI, tmp, tmp, 1); break; default: CError_FATAL(769); } op->optype = OpndType_GPR; op->reg = reg; } void coerce_to_register_pair(Operand *op, Type *type, short output_reg, short output_regHi) { SInt32 offset; short reg; short regHi; short tmp1; short tmp2; regHi = -1; CError_ASSERT(794, TYPE_IS_8BYTES(type) || (IS_TYPE_STRUCT(type) && type->size == 8)); coerce_to_addressable(op); switch (op->optype) { case OpndType_GPRPair: if (output_reg && !output_regHi) output_regHi = used_virtual_registers[RegClass_GPR]++; if (output_regHi && !output_reg) output_reg = used_virtual_registers[RegClass_GPR]++; if (op->reg != output_reg || op->regHi != output_regHi) { tmp1 = output_reg ? output_reg : op->reg; tmp2 = output_regHi ? output_regHi : op->regHi; if (tmp1 != op->reg) { if (tmp1 == op->regHi) { CError_ASSERT(818, tmp1 != tmp2); emitpcode(PC_MR, tmp2, op->regHi); emitpcode(PC_MR, tmp1, op->reg); } else { emitpcode(PC_MR, tmp1, op->reg); if (op->regHi != tmp2) emitpcode(PC_MR, tmp2, op->regHi); } } else if (tmp2 != op->regHi) { if (tmp2 == op->reg) { CError_ASSERT(832, tmp1 != tmp2); emitpcode(PC_MR, tmp1, op->reg); emitpcode(PC_MR, tmp2, op->regHi); } else { emitpcode(PC_MR, tmp2, op->regHi); if (op->reg != tmp1) emitpcode(PC_MR, tmp1, op->reg); } } } reg = op->reg; regHi = op->regHi; break; case OpndType_GPR: CError_FATAL(849); break; case OpndType_GPR_ImmOffset: CError_FATAL(852); break; case OpndType_GPR_Indexed: CError_FATAL(855); break; case OpndType_Absolute: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; offset = op->immediate; if (FITS_IN_SHORT(offset)) { emitpcode(PC_LI, reg, offset); } else { tmp1 = reg; if (copts.optimizationlevel > 1 && offset) tmp1 = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_LIS, tmp1, 0, (short) HIGH_PART(offset)); if (LOW_PART(offset)) emitpcode(PC_ADDI, reg, tmp1, 0, LOW_PART(offset)); } regHi = output_regHi ? output_regHi : used_virtual_registers[RegClass_GPR]++; if (is_unsigned(type) || offset >= 0) load_immediate(regHi, 0); else load_immediate(regHi, -1); break; case OpndType_IndirectGPR_ImmOffset: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; regHi = output_regHi ? output_regHi : used_virtual_registers[RegClass_GPR]++; if (op->reg == regHi) { if (op->reg == reg) { CError_FATAL(887); } else { load_store_register(PC_LWZ, reg, op->reg, op->object, op->immOffset + low_offset); setpcodeflags(op->flags); load_store_register(PC_LWZ, regHi, op->reg, op->object, op->immOffset + high_offset); setpcodeflags(op->flags); } } else { load_store_register(PC_LWZ, regHi, op->reg, op->object, op->immOffset + high_offset); setpcodeflags(op->flags); load_store_register(PC_LWZ, reg, op->reg, op->object, op->immOffset + low_offset); setpcodeflags(op->flags); } break; case OpndType_IndirectGPR_Indexed: reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; regHi = output_regHi ? output_regHi : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, reg, op->reg, op->regOffset); load_store_register(PC_LWZ, regHi, reg, NULL, high_offset); setpcodeflags(op->flags); load_store_register(PC_LWZ, reg, reg, NULL, low_offset); setpcodeflags(op->flags); break; default: CError_FATAL(912); } if (regHi == -1) { CError_FATAL(916); } else { op->optype = OpndType_GPRPair; op->reg = reg; op->regHi = regHi; } } void Coerce_to_fp_register(Operand *op, Type *type, short output_reg) { short reg; coerce_to_addressable(op); switch (op->optype) { case OpndType_FPR: reg = op->reg; break; case OpndType_IndirectGPR_ImmOffset: reg = output_reg ? output_reg : used_virtual_registers[RegClass_FPR]++; load_store_register((type->size == 4) ? PC_LFS : PC_LFD, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: reg = output_reg ? output_reg : used_virtual_registers[RegClass_FPR]++; emitpcode((type->size == 4) ? PC_LFSX : PC_LFDX, reg, op->reg, op->regOffset, 0, 0x390); setpcodeflags(op->flags); break; default: CError_FATAL(986); } op->optype = OpndType_FPR; op->reg = reg; } void Coerce_to_v_register(Operand *op, Type *type, short output_reg) { short reg; coerce_to_addressable(op); switch (op->optype) { case OpndType_VR: reg = op->reg; break; case OpndType_IndirectGPR_ImmOffset: reg = output_reg ? output_reg : used_virtual_registers[RegClass_VR]++; load_store_register(PC_LVX, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: reg = output_reg ? output_reg : used_virtual_registers[RegClass_VR]++; emitpcode(PC_LVX, reg, op->reg, op->regOffset); setpcodeflags(op->flags); break; case OpndType_Absolute: reg = output_reg ? output_reg : used_virtual_registers[RegClass_VR]++; switch (TYPE_STRUCT(type)->stype) { case STRUCT_VECTOR_UCHAR: case STRUCT_VECTOR_SCHAR: case STRUCT_VECTOR_BCHAR: emitpcode(PC_VSPLTISB, reg, op->immediate); break; case STRUCT_VECTOR_USHORT: case STRUCT_VECTOR_SSHORT: case STRUCT_VECTOR_BSHORT: case STRUCT_VECTOR_PIXEL: emitpcode(PC_VSPLTISH, reg, op->immediate); break; case STRUCT_VECTOR_UINT: case STRUCT_VECTOR_SINT: case STRUCT_VECTOR_BINT: case STRUCT_VECTOR_FLOAT: emitpcode(PC_VSPLTISW, reg, op->immediate); break; default: CError_FATAL(1049); } op->optype = OpndType_VR; op->reg = reg; setpcodeflags(op->flags); break; default: CError_FATAL(1059); } op->optype = OpndType_VR; op->reg = reg; } void store(short reg, Operand *op, Type *type) { Opcode opcode; coerce_to_addressable(op); switch (op->optype) { case OpndType_IndirectGPR_ImmOffset: opcode = PC_STW; if (IS_TYPE_INT(type) || IS_TYPE_ENUM(type)) { switch (type->size) { case 1: opcode = PC_STB; break; case 2: opcode = PC_STH; break; } } else { CError_ASSERT(1171, IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)); } load_store_register(opcode, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: opcode = PC_STWX; if (IS_TYPE_INT(type) || IS_TYPE_ENUM(type)) { switch (type->size) { case 1: opcode = PC_STBX; break; case 2: opcode = PC_STHX; break; } } else { CError_ASSERT(1188, IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)); } emitpcode(opcode, reg, op->reg, op->regOffset); setpcodeflags(op->flags); break; default: CError_FATAL(1193); } } void store_pair(short reg, short regHi, Operand *op, Type *type) { short tmp; CError_ASSERT(1208, TYPE_IS_8BYTES(type)); coerce_to_addressable(op); switch (op->optype) { case OpndType_IndirectGPR_ImmOffset: load_store_register(PC_STW, reg, op->reg, op->object, op->immOffset + low_offset); setpcodeflags(op->flags); load_store_register(PC_STW, regHi, op->reg, op->object, op->immOffset + high_offset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: tmp = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_ADD, tmp, op->reg, op->regOffset); load_store_register(PC_STW, reg, tmp, NULL, low_offset); setpcodeflags(op->flags); load_store_register(PC_STW, regHi, tmp, NULL, high_offset); setpcodeflags(op->flags); break; default: CError_FATAL(1228); } } void store_fp(short reg, Operand *op, Type *type) { coerce_to_addressable(op); switch (op->optype) { case OpndType_IndirectGPR_ImmOffset: load_store_register((type->size == 4) ? PC_STFS : PC_STFD, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: emitpcode((type->size == 4) ? PC_STFSX : PC_STFDX, reg, op->reg, op->regOffset); setpcodeflags(op->flags); break; default: CError_FATAL(1259); } } void store_v(short reg, Operand *op, Type *tstruct) { coerce_to_addressable(op); switch (op->optype) { case OpndType_IndirectGPR_ImmOffset: load_store_register(PC_STVX, reg, op->reg, op->object, op->immOffset); setpcodeflags(op->flags); break; case OpndType_IndirectGPR_Indexed: emitpcode(PC_STVX, reg, op->reg, op->regOffset); setpcodeflags(op->flags); break; default: CError_FATAL(1283); } } static Boolean last_matches_rlwinm_or_exts(Operand *op, short opcode, short b, short c) { PCode *pc; if (pclastblock->pcodeCount <= 0) return 0; pc = pclastblock->lastPCode; if (pc->args[0].kind != PCOp_REGISTER || pc->args[0].arg != RegClass_GPR || pc->args[0].data.reg.reg != op->reg) return 0; if (pc->op != opcode && (opcode != PC_EXTSH || pc->op != PC_EXTSB)) return 0; if (opcode == PC_RLWINM) { if (pc->args[2].data.imm.value != 0 || pc->args[3].data.imm.value != b || pc->args[4].data.imm.value != c) return 0; } return 1; } void extend32(Operand *op, Type *type, short output_reg) { int r28; int reg; r28 = op->optype >= OpndType_IndirectGPR_ImmOffset; if (op->optype != OpndType_GPR) Coerce_to_register(op, type, output_reg); switch (type->size) { case 1: if (is_unsigned(type)) { if (r28) return; if (last_matches_rlwinm_or_exts(op, PC_RLWINM, 24, 31)) return; reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_RLWINM, reg, op->reg, 0, 24, 31); } else { reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; if (last_matches_rlwinm_or_exts(op, PC_EXTSB, 0, 0)) return; emitpcode(PC_EXTSB, reg, op->reg); } break; case 2: if (r28) return; if (is_unsigned(type)) { if (last_matches_rlwinm_or_exts(op, PC_RLWINM, 16, 31)) return; reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_RLWINM, reg, op->reg, 0, 16, 31); } else { if (last_matches_rlwinm_or_exts(op, PC_EXTSH, 0, 0)) return; reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_EXTSH, reg, op->reg); } break; default: CError_FATAL(1389); } op->optype = OpndType_GPR; op->reg = reg; } void extend64(Operand *op, Type *type, short output_reg, short output_regHi) { short tmp; short regHi; if (op->optype != OpndType_GPR) Coerce_to_register(op, type, output_reg); regHi = output_regHi ? output_regHi : used_virtual_registers[RegClass_GPR]++; if (regHi == op->reg) { tmp = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; emitpcode(PC_MR, tmp, op->reg); op->reg = tmp; } if (is_unsigned(type)) load_immediate(regHi, 0); else emitpcode(PC_SRAWI, regHi, op->reg, 31); op->optype = OpndType_GPRPair; op->regHi = regHi; } void load_floating_constant(short reg, Type *type, Float *data) { Object *object; Object *indObject; Operand op1; Operand op2; Operand op3; SInt32 offset = 0; memclrw(&op1, sizeof(Operand)); object = CreateFloatConst(type, data, &offset); indObject = createIndirect(object, 0, 1); if (indObject) { symbol_operand(&op1, indObject); indirect(&op1, NULL); } else { symbol_operand(&op1, object); } if (offset) { op2 = op1; memclrw(&op3, sizeof(Operand)); op3.optype = OpndType_Absolute; op3.immediate = offset; if (op2.optype != OpndType_GPR) Coerce_to_register(&op2, TYPE(&void_ptr), 0); combine(&op2, &op3, 0, &op1); } indirect(&op1, NULL); if (op1.optype != OpndType_FPR) Coerce_to_fp_register(&op1, type, reg); } void convert_integer_to_floating(Operand *op, Boolean is_single, short output_reg) { Operand temp_op; Float d; int const_reg; int tmp_reg; int work_reg; int result_reg; Opcode opcode; symbol_operand(&temp_op, maketemporary(TYPE(&stdouble))); coerce_to_addressable(&temp_op); d.value = *((double *) &int_to_float_cc); const_reg = used_virtual_registers[RegClass_FPR]++; load_floating_constant(const_reg, TYPE(&stdouble), &d); tmp_reg = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_XORIS, tmp_reg, op->reg, 0x8000); load_store_register(PC_STW, tmp_reg, temp_op.reg, temp_op.object, low_offset); emitpcode(PC_LIS, tmp_reg = used_virtual_registers[RegClass_GPR]++, 0, 0x4330); load_store_register(PC_STW, tmp_reg, temp_op.reg, temp_op.object, high_offset); load_store_register(PC_LFD, work_reg = used_virtual_registers[RegClass_FPR]++, temp_op.reg, temp_op.object, 0); result_reg = output_reg ? output_reg : used_virtual_registers[RegClass_FPR]++; if (is_single != 0) opcode = PC_FSUBS; else opcode = PC_FSUB; emitpcode(opcode, result_reg, work_reg, const_reg); op->optype = OpndType_FPR; op->reg = result_reg; } void convert_unsigned_to_floating(Operand *op, Boolean is_single, short output_reg) { Operand temp_op; Float d; int const_reg; int tmp_reg; int work_reg; int result_reg; Opcode opcode; symbol_operand(&temp_op, maketemporary(TYPE(&stdouble))); coerce_to_addressable(&temp_op); d.value = *((double *) &uns_to_float_cc); const_reg = used_virtual_registers[RegClass_FPR]++; load_floating_constant(const_reg, TYPE(&stdouble), &d); load_store_register(PC_STW, op->reg, temp_op.reg, temp_op.object, low_offset); emitpcode(PC_LIS, tmp_reg = used_virtual_registers[RegClass_GPR]++, 0, 0x4330); load_store_register(PC_STW, tmp_reg, temp_op.reg, temp_op.object, high_offset); load_store_register(PC_LFD, work_reg = used_virtual_registers[RegClass_FPR]++, temp_op.reg, temp_op.object, 0); result_reg = output_reg ? output_reg : used_virtual_registers[RegClass_FPR]++; if (is_single != 0) opcode = PC_FSUBS; else opcode = PC_FSUB; emitpcode(opcode, result_reg, work_reg, const_reg); op->optype = OpndType_FPR; op->reg = result_reg; } void convert_floating_to_integer(Operand *op, short output_reg) { Operand temp_op; int tmp_reg; int result_reg; symbol_operand(&temp_op, maketemporary(TYPE(&stdouble))); coerce_to_addressable(&temp_op); tmp_reg = used_virtual_registers[RegClass_FPR]++; emitpcode(PC_FCTIWZ, tmp_reg, op->reg); load_store_register(PC_STFD, tmp_reg, temp_op.reg, temp_op.object, 0); result_reg = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; load_store_register(PC_LWZ, result_reg, temp_op.reg, temp_op.object, low_offset); op->optype = OpndType_GPR; op->reg = result_reg; } void convert_floating_to_unsigned(Operand *op, short output_reg) { static UInt32 used_regs[RegClassMax] = {0, 0, 0, 2, 0}; if (op->reg != 1) emitpcode(PC_FMR, 1, op->reg); branch_subroutine(rt_cvt_fp2unsigned, 0, used_regs); op->optype = OpndType_GPR; op->reg = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_MR, op->reg, 3); } void extract_bitfield(Operand *input_op, TypeBitfield *tbitfield, short output_reg, Operand *output_op) { int r27; int offset; int tmp_reg; int output; offset = tbitfield->unkB; output = output_reg ? output_reg : used_virtual_registers[RegClass_GPR]++; r27 = tbitfield->unkA + (32 - (tbitfield->bitfieldtype->size * 8)); if (is_unsigned(tbitfield->bitfieldtype)) { emitpcode(PC_RLWINM, output, input_op->reg, (r27 + offset) & 31, 32 - offset, 31); } else if (r27 == 0) { emitpcode(PC_SRAWI, output, input_op->reg, 32 - offset); } else { tmp_reg = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_RLWINM, tmp_reg, input_op->reg, r27 & 31, 0, offset); emitpcode(PC_SRAWI, output, tmp_reg, 32 - offset); } output_op->optype = OpndType_GPR; output_op->reg = output; } void insert_bitfield(short reg, Operand *op, TypeBitfield *tbitfield) { int offset = tbitfield->unkB; int r7 = tbitfield->unkA + (32 - (tbitfield->bitfieldtype->size * 8)); emitpcode(PC_RLWIMI, op->reg, reg, 32 - (r7 + offset), r7, r7 + offset - 1); } void load_address(short dest_reg, Operand *op) { coerce_to_addressable(op); if (op->optype == OpndType_IndirectGPR_ImmOffset) { if (!op->immOffset && !op->object) { if (op->reg != dest_reg) emitpcode(PC_MR, dest_reg, op->reg); } else { add_immediate(dest_reg, op->reg, op->object, (SInt16) op->immOffset); } } else if (op->optype == OpndType_IndirectGPR_Indexed) { emitpcode(PC_ADD, dest_reg, op->reg, op->regOffset); } else { CError_FATAL(1849); } }