mirror of https://git.wuffs.org/MWCC
1019 lines
38 KiB
C
1019 lines
38 KiB
C
#include "compiler/Operands.h"
|
|
#include "compiler/CError.h"
|
|
#include "compiler/CMachine.h"
|
|
#include "compiler/CParser.h"
|
|
#include "compiler/PCode.h"
|
|
#include "compiler/PCodeInfo.h"
|
|
#include "compiler/PCodeUtilities.h"
|
|
#include "compiler/Registers.h"
|
|
#include "compiler/StackFrame.h"
|
|
#include "compiler/enode.h"
|
|
#include "compiler/objects.h"
|
|
#include "compiler/CodeGen.h"
|
|
#include "compiler/RegisterInfo.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;
|
|
short 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, TypeIntegral *tint, 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((tint->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((tint->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, TypeStruct *tstruct, 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 (tstruct->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) {
|
|
short 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 *tint) {
|
|
coerce_to_addressable(op);
|
|
switch (op->optype) {
|
|
case OpndType_IndirectGPR_ImmOffset:
|
|
load_store_register((tint->size == 4) ? PC_STFS : PC_STFD, reg, op->reg, op->object, op->immOffset);
|
|
setpcodeflags(op->flags);
|
|
break;
|
|
case OpndType_IndirectGPR_Indexed:
|
|
emitpcode((tint->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, double *data) {
|
|
// do me AFTER
|
|
}
|
|
|
|
void convert_integer_to_floating(Operand *op, Boolean is_single, short output_reg) {
|
|
// issue with matching the PC_FSUB/PC_FSUBS ternary
|
|
Operand temp_op;
|
|
double d;
|
|
int const_reg;
|
|
int tmp_reg;
|
|
int work_reg;
|
|
int result_reg;
|
|
short opcode;
|
|
|
|
symbol_operand(&temp_op, maketemporary((Type *) &stdouble));
|
|
coerce_to_addressable(&temp_op);
|
|
d = *((double *) &int_to_float_cc);
|
|
|
|
const_reg = used_virtual_registers[RegClass_FPR]++;
|
|
load_floating_constant(const_reg, &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]++;
|
|
//opcode = PC_FSUB;
|
|
//if (is_single)
|
|
// opcode = PC_FSUBS;
|
|
//opcode = (is_single != 0) ? PC_FSUBS : PC_FSUB;
|
|
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) {
|
|
// issue with matching the PC_FSUB/PC_FSUBS ternary
|
|
Operand temp_op;
|
|
double d;
|
|
int const_reg;
|
|
int tmp_reg;
|
|
int work_reg;
|
|
int result_reg;
|
|
short opcode;
|
|
|
|
symbol_operand(&temp_op, maketemporary((Type *) &stdouble));
|
|
coerce_to_addressable(&temp_op);
|
|
d = *((double *) &uns_to_float_cc);
|
|
|
|
const_reg = used_virtual_registers[RegClass_FPR]++;
|
|
load_floating_constant(const_reg, &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]++;
|
|
//opcode = PC_FSUB;
|
|
//if (is_single)
|
|
// opcode = PC_FSUBS;
|
|
//opcode = (is_single != 0) ? PC_FSUBS : PC_FSUB;
|
|
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, op->immOffset);
|
|
}
|
|
} else if (op->optype == OpndType_IndirectGPR_Indexed) {
|
|
emitpcode(PC_ADD, dest_reg, op->reg, op->regOffset);
|
|
} else {
|
|
CError_FATAL(1849);
|
|
}
|
|
}
|