MWCC/compiler_and_linker/unsorted/Operands.c

1041 lines
38 KiB
C

#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 (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 (opA->immediate != 0)
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 (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 (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_TYPE_4:
case STRUCT_TYPE_5:
case STRUCT_TYPE_6:
emitpcode(PC_VSPLTISB, reg, op->immediate);
break;
case STRUCT_TYPE_7:
case STRUCT_TYPE_8:
case STRUCT_TYPE_9:
case STRUCT_TYPE_E:
emitpcode(PC_VSPLTISH, reg, op->immediate);
break;
case STRUCT_TYPE_A:
case STRUCT_TYPE_B:
case STRUCT_TYPE_C:
case STRUCT_TYPE_D:
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);
}
}