MWCC/compiler_and_linker/unsorted/InstrSelection.c

5349 lines
176 KiB
C

#include "compiler/InstrSelection.h"
#include "compiler/CError.h"
#include "compiler/CInt64.h"
#include "compiler/CMachine.h"
#include "compiler/CParser.h"
#include "compiler/CodeGen.h"
#include "compiler/CompilerTools.h"
#include "compiler/FunctionCalls.h"
#include "compiler/Intrinsics.h"
#include "compiler/Operands.h"
#include "compiler/PCode.h"
#include "compiler/PCodeInfo.h"
#include "compiler/PCodeUtilities.h"
#include "compiler/RegisterInfo.h"
#include "compiler/StructMoves.h"
#include "compiler/TOC.h"
#include "compiler/enode.h"
#include "compiler/objects.h"
#include "compiler/types.h"
PrecomputedOperand *precomputedoperands;
void (*cgdispatch[MAXEXPR + 1])(ENode *, short, short, Operand *);
// forward decls
static int ispowerof2(SInt32 val);
static void binary_immediate(Opcode opcode, ENode *left, SInt32 value, short outputReg, Operand *output);
static void shift_left_immediate(ENode *expr, short shift, short negate, short outputReg, Operand *output);
static void shift_right_immediate(ENode *expr, Type *type, short shift, short outputReg, Operand *output);
static void or_xor_immediate(Opcode opcode, ENode *expr, SInt32 value, short outputReg, Operand *output);
static void signed_divide_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output);
static void signed_mod_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output);
static void fp_binary_operator(Opcode opcode, ENode *left, ENode *right, short outputReg, Operand *output);
static void logical_expression_nobranch(ENode *cond, Boolean invert, Operand *output);
static void shift_and_mask(ENode *expr, short a, short b, short c, short outputReg, Operand *output);
static ENodeType invert_relop(ENodeType nt);
#define IS_INT_CONST(node) ( ENODE_IS((node), EINTCONST) && IS_TYPE_INT((node)->rtype) && (node)->rtype->size <= 4 )
#define IS_INT_CONST_ZERO(node) ( IS_INT_CONST(node) && (node)->data.intval.lo == 0 )
void init_cgdispatch(void) {
ENodeType t;
for (t = 0; t <= MAXEXPR; t++)
cgdispatch[t] = gen_UNEXPECTED;
cgdispatch[EPOSTINC] = gen_POSTINCDEC;
cgdispatch[EPOSTDEC] = gen_POSTINCDEC;
cgdispatch[EINDIRECT] = gen_INDIRECT;
cgdispatch[EMONMIN] = gen_MONMIN;
cgdispatch[EBINNOT] = gen_BINNOT;
cgdispatch[ELOGNOT] = gen_LOGICAL;
cgdispatch[EFORCELOAD] = gen_FORCELOAD;
cgdispatch[EMUL] = gen_MUL;
cgdispatch[EDIV] = gen_DIV;
cgdispatch[EMODULO] = gen_MODULO;
cgdispatch[EADD] = gen_ADD;
cgdispatch[ESUB] = gen_SUB;
cgdispatch[ESHL] = gen_SHL;
cgdispatch[ESHR] = gen_SHR;
cgdispatch[ELESS] = gen_COMPARE;
cgdispatch[EGREATER] = gen_COMPARE;
cgdispatch[ELESSEQU] = gen_COMPARE;
cgdispatch[EGREATEREQU] = gen_COMPARE;
cgdispatch[EEQU] = gen_COMPARE;
cgdispatch[ENOTEQU] = gen_COMPARE;
cgdispatch[EAND] = gen_AND;
cgdispatch[EXOR] = gen_XOR;
cgdispatch[EOR] = gen_OR;
cgdispatch[ELAND] = gen_LOGICAL;
cgdispatch[ELOR] = gen_LOGICAL;
cgdispatch[EASS] = gen_ASS;
cgdispatch[ECOMMA] = gen_COMMA;
cgdispatch[ETYPCON] = gen_TYPCON;
cgdispatch[EBITFIELD] = gen_BITFIELD;
cgdispatch[EINTCONST] = gen_INTCONST;
cgdispatch[EFLOATCONST] = gen_FLOATCONST;
cgdispatch[ESTRINGCONST] = gen_STRINGCONST;
cgdispatch[ECOND] = gen_COND;
cgdispatch[EFUNCCALL] = gen_FUNCCALL;
cgdispatch[EFUNCCALLP] = gen_FUNCCALL;
cgdispatch[EOBJREF] = gen_OBJREF;
cgdispatch[ENULLCHECK] = gen_NULLCHECK;
cgdispatch[EPRECOMP] = gen_PRECOMP;
cgdispatch[EDEFINE] = gen_DEFINE;
cgdispatch[EREUSE] = gen_REUSE;
cgdispatch[EVECTOR128CONST] = gen_VECTOR128CONST;
cgdispatch[ECONDASS] = gen_CONDASS;
}
void gen_DEFINE(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Operand *op;
if (!expr->data.diadic.right) {
op = lalloc(sizeof(Operand));
memclrw(op, sizeof(Operand));
expr->data.diadic.right = (ENode *) op;
GEN_NODE(expr->data.diadic.left, op);
}
op = (Operand *) expr->data.diadic.right;
*output = *op;
}
void gen_REUSE(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner = expr->data.monadic;
CError_ASSERT(250, ENODE_IS(inner, EDEFINE));
gen_DEFINE(inner, outputReg, outputRegHi, output);
}
void gen_POSTINCDEC(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
TypeBitfield *tbitfield;
ENode *inner;
Type *type;
Operand a;
Operand b;
Operand c;
Float fval;
int objReg;
int constReg;
int finalReg;
SInt32 incval;
inner = expr->data.monadic->data.monadic;
type = expr->rtype;
tbitfield = NULL;
memclrw(&a, sizeof(Operand));
memclrw(&b, sizeof(Operand));
memclrw(&c, sizeof(Operand));
if (TYPE_IS_8BYTES(type)) {
I8_gen_POSTINCDEC(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
if (ENODE_IS(inner, EOBJREF) && (objReg = OBJECT_REG(inner->data.objref))) {
output->optype = OpndType_FPR;
output->reg = (outputReg && outputReg != objReg) ? outputReg : ALLOC_FPR();
emitpcode(PC_FMR, output->reg, objReg);
fval = one_point_zero;
load_floating_constant(constReg = ALLOC_FPR(), type, &fval);
if (ENODE_IS(expr, EPOSTINC)) {
emitpcode((type->size == 4) ? PC_FADDS : PC_FADD, objReg, objReg, constReg);
} else {
emitpcode((type->size == 4) ? PC_FSUBS : PC_FSUB, objReg, objReg, constReg);
}
} else {
GEN_NODE(inner, &a);
indirect(&a, inner);
b = a;
ENSURE_FPR(&b, type, 0);
output->optype = OpndType_FPR;
output->reg = ALLOC_FPR();
emitpcode(PC_FMR, output->reg, b.reg);
fval = one_point_zero;
load_floating_constant(constReg = ALLOC_FPR(), type, &fval);
finalReg = ALLOC_FPR();
if (ENODE_IS(expr, EPOSTINC))
emitpcode((type->size == 4) ? PC_FADDS : PC_FADD, finalReg, b.reg, constReg);
else
emitpcode((type->size == 4) ? PC_FSUBS : PC_FSUB, finalReg, b.reg, constReg);
store_fp(finalReg, &a, type);
}
} else {
if (IS_TYPE_POINTER(type)) {
if (ENODE_IS(expr, EPOSTINC))
incval = TPTR_TARGET(type)->size;
else
incval = -TPTR_TARGET(type)->size;
} else {
if (ENODE_IS(expr, EPOSTINC))
incval = 1;
else
incval = -1;
}
if (ENODE_IS(inner, EOBJREF) && (objReg = OBJECT_REG(inner->data.objref))) {
output->optype = OpndType_GPR;
output->reg = (outputReg && outputReg != objReg) ? outputReg : ALLOC_GPR();
emitpcode(PC_MR, output->reg, objReg);
add_register_immediate(objReg, objReg, incval);
} else {
if (ENODE_IS(inner, EBITFIELD)) {
tbitfield = TYPE_BITFIELD(TPTR_TARGET(inner));
inner = inner->data.monadic;
}
GEN_NODE(inner, &a);
indirect(&a, inner);
b = a;
ENSURE_GPR(&b, type, 0);
if (tbitfield) {
c = b;
extract_bitfield(&c, tbitfield, 0, &b);
}
output->optype = OpndType_GPR;
output->reg = ALLOC_GPR();
emitpcode(PC_MR, output->reg, b.reg);
finalReg = ALLOC_GPR();
add_register_immediate(finalReg, b.reg, incval);
if (tbitfield) {
insert_bitfield(finalReg, &c, tbitfield);
finalReg = c.reg;
}
store(finalReg, &a, type);
}
}
}
void gen_INDIRECT(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *inner;
VarInfo *vi;
SInt32 postincvalue;
Operand op;
type = expr->rtype;
inner = expr->data.monadic;
if (TYPE_IS_8BYTES(type)) {
I8_gen_INDIRECT(expr, outputReg, outputRegHi, output);
return;
}
memclrw(&op, sizeof(Operand));
if (ENODE_IS(inner, EOBJREF) && OBJECT_REG(inner->data.objref)) {
vi = Registers_GetVarInfo(inner->data.objref);
switch (vi->rclass) {
case RegClass_GPR:
output->optype = OpndType_GPR;
break;
case RegClass_FPR:
output->optype = OpndType_FPR;
break;
case RegClass_VR:
output->optype = OpndType_VR;
break;
case RegClass_CRFIELD:
output->optype = OpndType_CRField;
break;
default:
CError_FATAL(456);
}
output->reg = vi->reg;
output->object = NULL;
return;
}
if (ENODE_IS(inner, EBITFIELD)) {
GEN_NODE(inner->data.monadic, &op);
indirect(&op, expr);
ENSURE_GPR(&op, type, 0);
extract_bitfield(&op, TYPE_BITFIELD(inner->rtype), outputReg, output);
return;
}
if (ispostincrementopportunity(inner, &op, &postincvalue) && (TYPE_FITS_IN_REGISTER(type) || IS_TYPE_FLOAT(type) || IS_TYPE_VECTOR(type))) {
indirect(&op, expr);
*output = op;
if (TYPE_FITS_IN_REGISTER(type)) {
ENSURE_GPR(output, type, outputReg);
} else if (IS_TYPE_FLOAT(type)) {
ENSURE_FPR(output, type, outputReg);
} else {
ENSURE_VR(output, type, outputReg);
}
add_register_immediate(op.reg, op.reg, postincvalue);
return;
}
GEN_NODE(inner, output);
indirect(output, expr);
}
void gen_MONMIN(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Type *type;
ENode *scan;
inner = expr->data.monadic;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_MONMIN(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
if (ENODE_IS(inner, EADD) && ENODE_IS(inner->data.diadic.left, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FNMADDS : PC_FNMADD,
inner->data.diadic.left->data.diadic.left,
inner->data.diadic.left->data.diadic.right,
inner->data.diadic.right,
outputReg,
output);
} else if (ENODE_IS(inner, EADD) && ENODE_IS(inner->data.diadic.right, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FNMADDS : PC_FNMADD,
inner->data.diadic.right->data.diadic.left,
inner->data.diadic.right->data.diadic.right,
inner->data.diadic.left,
outputReg,
output);
} else if (ENODE_IS(inner, ESUB) && ENODE_IS(inner->data.diadic.left, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FNMSUBS : PC_FNMSUB,
inner->data.diadic.left->data.diadic.left,
inner->data.diadic.left->data.diadic.right,
inner->data.diadic.right,
outputReg,
output);
} else {
fp_unary_operator(PC_FNEG, inner, outputReg, output);
}
return;
}
scan = inner;
while (ENODE_IS(scan, ETYPCON) && IS_TYPE_INT_OR_ENUM(type) && !is_unsigned(type))
scan = scan->data.monadic;
switch (scan->type) {
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
if (TYPE_FITS_IN_REGISTER(scan->data.diadic.left->rtype) && TYPE_FITS_IN_REGISTER(scan->data.diadic.right->rtype)) {
gen_negated_condition_gpr(scan, output, outputReg);
return;
}
}
unary_operator(PC_NEG, inner, outputReg, output);
}
void gen_BINNOT(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Type *type;
inner = expr->data.monadic;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_BINNOT(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(inner, EAND))
binary_operator(PC_NAND, inner->data.diadic.left, inner->data.diadic.right, outputReg, output);
else if (ENODE_IS(inner, EOR))
binary_operator(PC_NOR, inner->data.diadic.left, inner->data.diadic.right, outputReg, output);
else if (ENODE_IS(inner, EXOR))
binary_operator(PC_EQV, inner->data.diadic.left, inner->data.diadic.right, outputReg, output);
else
unary_operator(PC_NOT, inner, outputReg, output);
}
void gen_FORCELOAD(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
inner = expr->data.monadic;
GEN_NODE(inner, output);
if (IS_TYPE_FLOAT(inner->rtype)) {
ENSURE_FPR(output, inner->rtype, outputReg);
} else if (IS_TYPE_VECTOR(inner->rtype)) {
ENSURE_VR(output, inner->rtype, outputReg);
} else if (TYPE_FITS_IN_REGISTER(inner->rtype)) {
if (TYPE_IS_8BYTES(inner->rtype))
coerce_to_register_pair(output, inner->rtype, outputReg, outputRegHi);
else
ENSURE_GPR(output, inner->rtype, outputReg);
} else if (!IS_TYPE_VOID(inner->rtype)) {
CError_FATAL(681);
}
}
void gen_MUL(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
Type *type;
int tmp;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_MUL(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
fp_binary_operator((type->size == 4) ? PC_FMULS : PC_FMUL, left, right, outputReg, output);
return;
}
if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) {
shift_left_immediate(left, tmp, 0, outputReg, output);
} else if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(-right->data.intval.lo))) {
shift_left_immediate(left, tmp, 1, outputReg, output);
} else if (ENODE_IS(left, EINTCONST) && (tmp = ispowerof2(left->data.intval.lo))) {
shift_left_immediate(right, tmp, 0, outputReg, output);
} else if (ENODE_IS(left, EINTCONST) && (tmp = ispowerof2(-left->data.intval.lo))) {
shift_left_immediate(right, tmp, 1, outputReg, output);
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT(right->data.intval.lo)) {
binary_immediate(PC_MULLI, left, right->data.intval.lo, outputReg, output);
} else if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT(left->data.intval.lo)) {
binary_immediate(PC_MULLI, right, left->data.intval.lo, outputReg, output);
} else {
binary_operator(PC_MULLW, left, right, outputReg, output);
}
}
struct ms {
SInt32 m;
int s;
};
static void create_signed_magic(SInt32 val, struct ms *output) {
// PowerPC CWG page 57-58
int p;
UInt32 ad, anc, delta, q1, r1, q2, r2, t;
ad = abs(val);
t = 0x80000000U + ((UInt32) val >> 31);
anc = t - 1 - t % ad;
p = 31;
q1 = 0x80000000U / anc;
r1 = 0x80000000U - q1 * anc;
q2 = 0x80000000U / ad;
r2 = 0x80000000U - q2 * ad;
do {
p = p + 1;
q1 = 2 * q1;
r1 = 2 * r1;
if (r1 >= anc) {
q1 = q1 + 1;
r1 = r1 - anc;
}
q2 = 2 * q2;
r2 = 2 * r2;
if (r2 >= ad) {
q2 = q2 + 1;
r2 = r2 - ad;
}
delta = ad - r2;
} while (q1 < delta || (q1 == delta && r1 == 0));
// after loop
output->m = q2 + 1;
if (val < 0)
output->m = -output->m;
output->s = p - 32;
}
struct mu {
UInt32 m;
int a;
int s;
};
static void create_unsigned_magic(UInt32 val, struct mu *output) {
// PowerPC CWG page 58-59
int p;
UInt32 nc, delta, q1, r1, q2, r2;
output->a = 0;
nc = - 1 - (-val) % val;
p = 31;
q1 = 0x80000000U / nc;
r1 = 0x80000000U - q1 * nc;
q2 = 0x7FFFFFFFU / val;
r2 = 0x7FFFFFFFU - q2 * val;
do {
p = p + 1;
if (r1 >= nc - r1) {
q1 = 2 * q1 + 1;
r1 = 2 * r1 - nc;
} else {
q1 = 2 * q1;
r1 = 2 * r1;
}
if (r2 + 1 >= val - r2) {
if (q2 >= 0x7FFFFFFFU)
output->a = 1;
q2 = 2 * q2 + 1;
r2 = 2 * r2 + 1 - val;
} else {
if (q2 >= 0x80000000U)
output->a = 1;
q2 = 2 * q2;
r2 = 2 * r2 + 1;
}
delta = val - 1 - r2;
} while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0)));
output->m = q2 + 1;
output->s = p - 32;
}
void gen_DIV(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
Type *type;
int tmp;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_DIV_MOD(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
fp_binary_operator((type->size == 4) ? PC_FDIVS : PC_FDIV, left, right, outputReg, output);
return;
}
if (is_unsigned(type)) {
if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) {
shift_right_immediate(left, type, tmp, outputReg, output);
} else if (!copts.optimizesize && ENODE_IS(right, EINTCONST) && right->data.intval.lo != 1) {
SInt32 value;
int tmpreg1;
int tmpreg2;
int tmpreg3;
int tmpreg4;
int tmpreg5;
int tmpreg6;
int finalReg;
struct mu u_magicoutput;
Operand op1;
value = right->data.intval.lo;
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
finalReg = outputReg ? outputReg : ALLOC_GPR();
memclrw(&op1, sizeof(Operand));
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
tmpreg3 = op1.reg;
create_unsigned_magic(value, &u_magicoutput);
load_immediate(tmpreg2, u_magicoutput.m);
emitpcode(PC_MULHWU, tmpreg1, tmpreg2, tmpreg3);
if (u_magicoutput.a == 0) {
if (u_magicoutput.s)
emitpcode(PC_RLWINM, finalReg, tmpreg1, (32 - u_magicoutput.s) & 31, u_magicoutput.s, 31);
else
emitpcode(PC_MR, finalReg, tmpreg1);
} else if (u_magicoutput.a == 1) {
tmpreg4 = ALLOC_GPR();
if (copts.optimizationlevel > 1) {
tmpreg5 = ALLOC_GPR();
tmpreg6 = ALLOC_GPR();
} else {
tmpreg5 = tmpreg4;
tmpreg6 = tmpreg4;
}
emitpcode(PC_SUBF, tmpreg4, tmpreg1, tmpreg3);
emitpcode(PC_RLWINM, tmpreg5, tmpreg4, 31, 1, 31);
emitpcode(PC_ADD, tmpreg6, tmpreg5, tmpreg1);
emitpcode(PC_RLWINM, finalReg, tmpreg6, (32 - (u_magicoutput.s - 1)) & 31, u_magicoutput.s - 1, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
} else {
binary_operator(PC_DIVWU, left, right, outputReg, output);
}
} else {
SInt32 value;
if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) {
signed_divide_by_power_of_2(left, tmp, 0, outputReg, output);
} else if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(-right->data.intval.lo))) {
signed_divide_by_power_of_2(left, tmp, 1, outputReg, output);
} else if (!copts.optimizesize && ENODE_IS(right, EINTCONST) && (value = right->data.intval.lo) != 1u && value != -1) {
int tmpreg2;
int tmpreg3;
int tmpreg1;
int tmpreg4;
int finalReg;
struct ms s_magicoutput;
Operand op2;
value = right->data.intval.lo;
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
finalReg = outputReg ? outputReg : ALLOC_GPR();
memclrw(&op2, sizeof(Operand));
GEN_NODE(left, &op2);
ENSURE_GPR(&op2, left->rtype, 0);
tmpreg4 = op2.reg;
create_signed_magic(value, &s_magicoutput);
load_immediate(tmpreg2, s_magicoutput.m);
emitpcode(PC_MULHW, tmpreg1, tmpreg2, tmpreg4);
if (value > 0 && s_magicoutput.m < 0) {
int t = ALLOC_GPR();
emitpcode(PC_ADD, t, tmpreg1, tmpreg4);
tmpreg1 = t;
} else if (value < 0 && s_magicoutput.m > 0) {
int t = ALLOC_GPR();
emitpcode(PC_SUBF, t, tmpreg4, tmpreg1);
tmpreg1 = t;
}
if (s_magicoutput.s) {
int t = ALLOC_GPR();
emitpcode(PC_SRAWI, t, tmpreg1, s_magicoutput.s);
tmpreg1 = t;
}
emitpcode(PC_RLWINM, tmpreg3, tmpreg1, 1, 31, 31);
emitpcode(PC_ADD, finalReg, tmpreg1, tmpreg3);
output->optype = OpndType_GPR;
output->reg = finalReg;
} else {
binary_operator(PC_DIVW, left, right, outputReg, output);
}
}
}
void gen_MODULO(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
int tmp;
struct mu u_magicoutput;
struct ms s_magicoutput;
Operand op1;
Operand op2;
SInt32 value;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
if (TYPE_IS_8BYTES(expr->rtype)) {
I8_gen_DIV_MOD(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) {
if (is_unsigned(expr->rtype))
shift_and_mask(left, 0, 32 - tmp, 31, outputReg, output);
else
signed_mod_by_power_of_2(left, tmp, 0, outputReg, output);
} else if (!copts.optimizesize && ENODE_IS(right, EINTCONST) && (value = right->data.intval.lo) != 1u && value != -1) {
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
if (is_unsigned(expr->rtype)) {
int tmpreg1;
int tmpreg2;
int tmpreg3;
int tmpreg4;
int tmpreg5;
int tmpreg6;
int tmpreg7;
int tmpreg8;
int finalReg;
tmpreg1 = op1.reg;
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
finalReg = outputReg ? outputReg : ALLOC_GPR();
create_unsigned_magic(right->data.intval.lo, &u_magicoutput);
load_immediate(tmpreg3, u_magicoutput.m);
emitpcode(PC_MULHWU, tmpreg2, tmpreg3, tmpreg1);
if (u_magicoutput.a == 0 && u_magicoutput.s != 0)
emitpcode(PC_RLWINM, tmpreg2, tmpreg2, (32 - u_magicoutput.s) & 31, u_magicoutput.s, 31);
if (u_magicoutput.a == 1) {
tmpreg5 = ALLOC_GPR();
if (copts.optimizationlevel > 1) {
tmpreg6 = ALLOC_GPR();
tmpreg7 = ALLOC_GPR();
tmpreg8 = ALLOC_GPR();
} else {
tmpreg6 = tmpreg5;
tmpreg7 = tmpreg5;
tmpreg8 = tmpreg5;
}
emitpcode(PC_SUBF, tmpreg5, tmpreg2, tmpreg1);
emitpcode(PC_RLWINM, tmpreg6, tmpreg5, 31, 1, 31);
emitpcode(PC_ADD, tmpreg7, tmpreg6, tmpreg2);
emitpcode(PC_RLWINM, tmpreg8, tmpreg7, (32 - (u_magicoutput.s - 1)) & 31, u_magicoutput.s - 1, 31);
tmpreg2 = tmpreg8;
}
if (value > 0 && value < 0x7FFF) {
emitpcode(PC_MULLI, tmpreg4, tmpreg2, value);
} else {
GEN_NODE(right, &op2);
ENSURE_GPR(&op2, right->rtype, 0);
emitpcode(PC_MULLW, tmpreg4, tmpreg2, op2.reg);
}
emitpcode(PC_SUBF, finalReg, tmpreg4, tmpreg1);
output->optype = OpndType_GPR;
output->reg = finalReg;
} else {
int tmpreg1;
int tmpreg2;
int tmpreg3;
int tmpreg4;
int tmpreg5;
int tmpreg6;
int finalReg;
tmpreg1 = op1.reg;
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
tmpreg5 = ALLOC_GPR();
tmpreg6 = ALLOC_GPR();
finalReg = outputReg ? outputReg : ALLOC_GPR();
create_signed_magic(right->data.intval.lo, &s_magicoutput);
load_immediate(tmpreg3, s_magicoutput.m);
emitpcode(PC_MULHW, tmpreg2, tmpreg3, tmpreg1);
if (value > 0 && s_magicoutput.m < 0) {
int tmp = ALLOC_GPR();
emitpcode(PC_ADD, tmp, tmpreg2, tmpreg1);
tmpreg2 = tmp;
} else if (value < 0 && s_magicoutput.m > 0) {
int tmp = ALLOC_GPR();
emitpcode(PC_SUBF, tmp, tmpreg1, tmpreg2);
tmpreg2 = tmp;
}
if (s_magicoutput.s != 0) {
int tmp = ALLOC_GPR();
emitpcode(PC_SRAWI, tmp, tmpreg2, s_magicoutput.s);
tmpreg2 = tmp;
}
emitpcode(PC_RLWINM, tmpreg4, tmpreg2, 1, 31, 31);
emitpcode(PC_ADD, tmpreg5, tmpreg2, tmpreg4);
if (value < 0x7FFF && value > -0x4000) {
emitpcode(PC_MULLI, tmpreg6, tmpreg5, value);
} else {
GEN_NODE(right, &op2);
ENSURE_GPR(&op2, right->rtype, 0);
emitpcode(PC_MULLW, tmpreg6, tmpreg5, op2.reg);
}
emitpcode(PC_SUBF, finalReg, tmpreg6, tmpreg1);
output->optype = OpndType_GPR;
output->reg = finalReg;
}
} else {
int tmpreg1;
int tmpreg2;
int finalReg;
if (right->hascall) {
GEN_NODE(right, &op2);
ENSURE_GPR(&op2, right->rtype, 0);
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
} else {
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
GEN_NODE(right, &op2);
ENSURE_GPR(&op2, right->rtype, 0);
}
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(is_unsigned(expr->rtype) ? PC_DIVWU : PC_DIVW, tmpreg1, op1.reg, op2.reg);
emitpcode(PC_MULLW, tmpreg2, tmpreg1, op2.reg);
emitpcode(PC_SUBF, finalReg, tmpreg2, op1.reg);
output->optype = OpndType_GPR;
output->reg = finalReg;
}
}
void gen_ADD(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
Type *type;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (TYPE_IS_8BYTES(type)) {
I8_gen_ADD(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
if (ENODE_IS(left, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FMADDS : PC_FMADD,
left->data.diadic.left,
left->data.diadic.right,
right,
outputReg, output);
} else if (ENODE_IS(right, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FMADDS : PC_FMADD,
right->data.diadic.left,
right->data.diadic.right,
left,
outputReg, output);
} else {
fp_binary_operator(
(type->size == 4) ? PC_FADDS : PC_FADD,
left, right,
outputReg, output);
}
return;
}
if (right->hascall) {
GEN_NODE(right, &opright);
if (opright.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&opright, right->rtype, 0);
GEN_NODE(left, &opleft);
if (opleft.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&opleft, left->rtype, 0);
} else {
GEN_NODE(left, &opleft);
if (opleft.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&opleft, left->rtype, 0);
GEN_NODE(right, &opright);
if (opright.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&opright, right->rtype, 0);
}
if (IS_TYPE_POINTER(expr->rtype)) {
if (TYPE_IS_8BYTES(expr->data.diadic.left->rtype)) {
opleft.optype = OpndType_GPR;
opleft.regHi = 0;
}
if (TYPE_IS_8BYTES(expr->data.diadic.right->rtype)) {
opright.optype = OpndType_GPR;
opright.regHi = 0;
}
}
combine(&opleft, &opright, outputReg, output);
}
void gen_SUB(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
Type *type;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_SUB(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_FLOAT(type)) {
if (ENODE_IS(left, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FMSUBS : PC_FMSUB,
left->data.diadic.left,
left->data.diadic.right,
right,
outputReg, output);
} else if (ENODE_IS(right, EMUL) && copts.fp_contract) {
fp_multiply_add(
(type->size == 4) ? PC_FNMSUBS : PC_FNMSUB,
right->data.diadic.left,
right->data.diadic.right,
left,
outputReg, output);
} else {
fp_binary_operator(
(type->size == 4) ? PC_FSUBS : PC_FSUB,
left, right,
outputReg, output);
}
return;
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT(left->data.intval.lo))
binary_immediate(PC_SUBFIC, right, left->data.intval.lo, outputReg, output);
else
binary_operator(PC_SUBF, right, left, outputReg, output);
}
void gen_SHL(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_SHL_SHR(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(right, EINTCONST))
shift_left_immediate(left, right->data.intval.lo, 0, outputReg, output);
else
binary_operator(PC_SLW, left, right, outputReg, output);
}
void gen_SHR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_SHL_SHR(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(right, EINTCONST))
shift_right_immediate(left, type, right->data.intval.lo, outputReg, output);
else
binary_operator(is_unsigned(type) ? PC_SRW : PC_SRAW, left, right, outputReg, output);
}
void gen_AND(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
short first;
short last;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_AND(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(right, EINTCONST) && ismaskconstant(right->data.intval.lo, &first, &last)) {
if (ENODE_IS(left, ESHL) && ENODE_IS(left->data.diadic.right, EINTCONST) && (int)(left->data.diadic.right->data.intval.lo + last) < 32) {
shift_and_mask(
left->data.diadic.left,
left->data.diadic.right->data.intval.lo,
first, last,
outputReg, output);
} else if (ENODE_IS(left, ESHR) && ENODE_IS(left->data.diadic.right, EINTCONST) && (int)left->data.diadic.right->data.intval.lo <= first && last >= first) {
if (left->data.diadic.right->data.intval.lo == 0)
shift_and_mask(left->data.diadic.left, 0, first, last, outputReg, output);
else
shift_and_mask(left->data.diadic.left, 32 - left->data.diadic.right->data.intval.lo, first, last, outputReg, output);
} else {
shift_and_mask(left, 0, first, last, outputReg, output);
}
return;
}
if (ENODE_IS(right, EINTCONST) && FITS_IN_USHORT(right->data.intval.lo)) {
binary_immediate(PC_ANDI, left, right->data.intval.lo, outputReg, output);
return;
}
if (ENODE_IS(right, EINTCONST) && FITS_IN_HI_SHORT(right->data.intval.lo)) {
binary_immediate(PC_ANDIS, left, right->data.intval.lo >> 16, outputReg, output);
return;
}
if (ENODE_IS(left, EINTCONST) && ismaskconstant(left->data.intval.lo, &first, &last)) {
if (ENODE_IS(right, ESHL) && ENODE_IS(right->data.diadic.right, EINTCONST) && (int)(right->data.diadic.right->data.intval.lo + last) < 32) {
shift_and_mask(
right->data.diadic.left,
right->data.diadic.right->data.intval.lo,
first, last,
outputReg, output);
} else if (ENODE_IS(right, ESHR) && ENODE_IS(right->data.diadic.right, EINTCONST) && (int)right->data.diadic.right->data.intval.lo <= first) {
if (right->data.diadic.right->data.intval.lo == 0)
shift_and_mask(right->data.diadic.left, 0, first, last, outputReg, output);
else
shift_and_mask(right->data.diadic.left, 32 - right->data.diadic.right->data.intval.lo, first, last, outputReg, output);
} else {
shift_and_mask(right, 0, first, last, outputReg, output);
}
return;
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_USHORT(left->data.intval.lo)) {
binary_immediate(PC_ANDI, right, left->data.intval.lo, outputReg, output);
return;
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_HI_SHORT(left->data.intval.lo)) {
binary_immediate(PC_ANDIS, right, left->data.intval.lo >> 16, outputReg, output);
return;
}
if (ENODE_IS(right, EBINNOT))
binary_operator(PC_ANDC, left, right->data.monadic, outputReg, output);
else if (ENODE_IS(left, EBINNOT))
binary_operator(PC_ANDC, right, left->data.monadic, outputReg, output);
else
binary_operator(PC_AND, left, right, outputReg, output);
}
void gen_XOR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_XOR(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(left, EINTCONST))
or_xor_immediate(PC_XORI, right, left->data.intval.lo, outputReg, output);
else if (ENODE_IS(right, EINTCONST))
or_xor_immediate(PC_XORI, left, right->data.intval.lo, outputReg, output);
else if (ENODE_IS(right, EBINNOT))
binary_operator(PC_EQV, left, right->data.monadic, outputReg, output);
else if (ENODE_IS(left, EBINNOT))
binary_operator(PC_EQV, left->data.monadic, right, outputReg, output);
else
binary_operator(PC_XOR, left, right, outputReg, output);
}
void gen_OR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
type = expr->rtype;
if (TYPE_IS_8BYTES(type)) {
I8_gen_OR(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(left, EINTCONST))
or_xor_immediate(PC_ORI, right, left->data.intval.lo, outputReg, output);
else if (ENODE_IS(right, EINTCONST))
or_xor_immediate(PC_ORI, left, right->data.intval.lo, outputReg, output);
else if (ENODE_IS(right, EBINNOT))
binary_operator(PC_ORC, left, right->data.monadic, outputReg, output);
else if (ENODE_IS(left, EBINNOT))
binary_operator(PC_ORC, right, left->data.monadic, outputReg, output);
else
binary_operator(PC_OR, left, right, outputReg, output);
}
void gen_ASS(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
Operand op2;
VarInfo *vi;
SInt32 incval;
short align;
short align2;
type = expr->rtype;
if (ENODE_IS(expr, ECONDASS)) {
left = expr->data.cond.expr1;
if (ENODE_IS(left, EINDIRECT)) {
left = left->data.monadic;
} else {
CError_FATAL(1759);
}
right = expr->data.cond.expr2;
} else {
left = expr->data.diadic.left;
right = expr->data.diadic.right;
}
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
if (TYPE_IS_8BYTES(type)) {
I8_gen_ASS(expr, outputReg, outputRegHi, output);
return;
}
if (ENODE_IS(left, EOBJREF) && OBJECT_REG(left->data.objref)) {
vi = Registers_GetVarInfo(left->data.objref);
GEN_NODE_TO_REG(right, vi->reg, 0, &opright);
switch (vi->rclass) {
case RegClass_GPR:
ENSURE_GPR(&opright, type, vi->reg);
output->optype = OpndType_GPR;
break;
case RegClass_FPR:
ENSURE_FPR(&opright, type, vi->reg);
output->optype = OpndType_FPR;
break;
case RegClass_VR:
ENSURE_VR(&opright, type, vi->reg);
output->optype = OpndType_VR;
break;
default:
CError_FATAL(1810);
}
if (opright.reg != vi->reg) {
PCodeArg a, b;
a.kind = PCOp_REGISTER;
a.arg = vi->rclass;
a.data.reg.reg = vi->reg;
a.data.reg.effect = EffectWrite;
b.kind = PCOp_REGISTER;
b.arg = vi->rclass;
b.data.reg.reg = opright.reg;
b.data.reg.effect = EffectRead;
appendpcode(pclastblock, makecopyinstruction(&b, &a));
}
output->reg = vi->reg;
return;
}
if (IS_TYPE_FLOAT(type)) {
GEN_NODE_TO_FPR(right, &opright, right->rtype, 0);
if (ispostincrementopportunity(left, &opleft, &incval)) {
indirect(&opleft, expr);
store_fp(opright.reg, &opleft, type);
add_register_immediate(opleft.reg, opleft.reg, incval);
} else {
GEN_NODE(left, &opleft);
indirect(&opleft, expr);
store_fp(opright.reg, &opleft, type);
}
output->optype = OpndType_FPR;
output->reg = opright.reg;
return;
}
if (IS_TYPE_VECTOR(type)) {
GEN_NODE(right, &opright);
if (opright.optype == OpndType_Absolute)
ENSURE_VR(&opright, type, 0);
else
ENSURE_VR(&opright, right->rtype, 0);
if (ispostincrementopportunity(left, &opleft, &incval)) {
indirect(&opleft, expr);
store_v(opright.reg, &opleft, type);
add_register_immediate(opleft.reg, opleft.reg, incval);
} else {
GEN_NODE(left, &opleft);
indirect(&opleft, expr);
store_v(opright.reg, &opleft, type);
}
output->optype = OpndType_VR;
output->reg = opright.reg;
return;
}
if (TYPE_FITS_IN_REGISTER(type)) {
GEN_NODE_TO_GPR(right, &opright, right->rtype, 0);
if (ENODE_IS(left, EBITFIELD)) {
GEN_NODE(left->data.monadic, &opleft);
indirect(&opleft, expr);
op2 = opleft;
ENSURE_GPR(&op2, type, 0);
insert_bitfield(opright.reg, &op2, TYPE_BITFIELD(left->rtype));
store(op2.reg, &opleft, type);
if (!expr->ignored)
extract_bitfield(&op2, TYPE_BITFIELD(left->rtype), opright.reg, &opleft);
} else if (ispostincrementopportunity(left, &opleft, &incval)) {
indirect(&opleft, expr);
store(opright.reg, &opleft, type);
add_register_immediate(opleft.reg, opleft.reg, incval);
} else {
GEN_NODE(left, &opleft);
indirect(&opleft, expr);
store(opright.reg, &opleft, type);
}
output->optype = OpndType_GPR;
output->reg = opright.reg;
return;
}
GEN_NODE(right, &opright);
GEN_NODE(left, output);
indirect(output, expr);
if (output->object) {
if (output->object->datatype == DLOCAL && (output->object->u.var.info->flags & VarInfoFlag1))
align = CMach_ArgumentAlignment(type);
else
align = CMach_AllocationAlignment(type, output->object->qual);
} else {
align = CMach_AllocationAlignment(type, 0);
}
if (opright.object) {
if (opright.object->datatype == DLOCAL && (opright.object->u.var.info->flags & VarInfoFlag1))
align2 = CMach_ArgumentAlignment(type);
else
align2 = CMach_AllocationAlignment(type, opright.object->qual);
} else {
align2 = CMach_AllocationAlignment(type, 0);
}
if (align2 < align)
align = align2;
move_block(output, &opright, type->size, align);
}
ENode *evaluate_and_skip_comma(ENode *expr) {
Operand op;
ENode *inner;
memclrw(&op, sizeof(Operand));
while (ENODE_IS(expr, ECOMMA)) {
inner = expr->data.diadic.left;
GEN_NODE(inner, &op);
if (ENODE_IS(inner, EINDIRECT) && (op.flags & OpndFlags_Volatile)) {
if (TYPE_FITS_IN_REGISTER_2(inner->rtype)) {
ENSURE_GPR(&op, inner->rtype, 0);
} else if (IS_TYPE_FLOAT(inner->rtype)) {
ENSURE_FPR(&op, inner->rtype, 0);
}
}
expr = expr->data.diadic.right;
}
return expr;
}
void gen_COMMA(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
GEN_NODE(left, output);
if (ENODE_IS(left, EINDIRECT) && (output->flags & OpndFlags_Volatile)) {
if (TYPE_FITS_IN_REGISTER_2(left->rtype)) {
ENSURE_GPR(output, left->rtype, 0);
} else if (IS_TYPE_FLOAT(left->rtype)) {
ENSURE_FPR(output, left->rtype, 0);
}
}
GEN_NODE_TO_REG(right, outputReg, 0, output);
}
void gen_TYPCON(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Type *srctype;
Type *dsttype;
inner = expr->data.monadic;
srctype = inner->rtype;
dsttype = expr->rtype;
if (TYPE_IS_8BYTES(srctype) || TYPE_IS_8BYTES(dsttype)) {
I8_gen_TYPCON(expr, outputReg, outputRegHi, output);
return;
}
if (IS_TYPE_VOID(dsttype)) {
GEN_NODE(inner, output);
if (ENODE_IS(inner, EINDIRECT) && (output->flags & OpndFlags_Volatile)) {
if (TYPE_FITS_IN_REGISTER_2(srctype)) {
ENSURE_GPR(output, srctype, 0);
} else if (IS_TYPE_FLOAT(srctype)) {
ENSURE_FPR(output, srctype, 0);
}
}
} else if (IS_TYPE_INT_OR_ENUM(srctype)) {
if (IS_TYPE_FLOAT(dsttype)) {
GEN_NODE(inner, output);
if (srctype->size < 4)
extend32(output, srctype, 0);
ENSURE_GPR(output, srctype, 0);
if (is_unsigned(srctype))
convert_unsigned_to_floating(output, dsttype->size == 4, outputReg);
else
convert_integer_to_floating(output, dsttype->size == 4, outputReg);
} else if (IS_TYPE_VECTOR(dsttype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
ENSURE_VR(output, dsttype, outputReg);
} else if (
srctype->size < dsttype->size &&
!ENODE_IS_INDIRECT_TO(inner, EBITFIELD) &&
!ENODE_IS_ASSIGN_TO(inner, EBITFIELD) &&
!(ENODE_IS_RANGE(inner, EPOSTINC, EPREDEC) && ENODE_IS(inner->data.monadic->data.monadic, EBITFIELD))
) {
GEN_NODE(inner, output);
extend32(output, srctype, outputReg);
} else if (dsttype->size < srctype->size || dsttype->size < 4) {
GEN_NODE(inner, output);
ENSURE_GPR(output, srctype, 0);
extend32(output, dsttype, outputReg);
} else {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
}
} else if (IS_TYPE_POINTER(srctype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
if (dsttype->size < srctype->size)
ENSURE_GPR(output, srctype, outputReg);
} else if (IS_TYPE_FLOAT(srctype)) {
if (IS_TYPE_FLOAT(dsttype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
ENSURE_FPR(output, srctype, outputReg);
if (dsttype->size == 4 && srctype->size != 4) {
int tmp = outputReg ? outputReg : ALLOC_FPR();
emitpcode(PC_FRSP, tmp, output->reg);
output->optype = OpndType_FPR;
output->reg = tmp;
}
} else if (is_unsigned(dsttype) && dsttype->size == 4) {
GEN_NODE_TO_REG(inner, 1, 0, output);
ENSURE_FPR(output, srctype, 1);
convert_floating_to_unsigned(output, outputReg);
} else {
GEN_NODE_TO_REG(inner, 0, 0, output);
ENSURE_FPR(output, srctype, 0);
convert_floating_to_integer(output, outputReg);
}
} else if (IS_TYPE_VECTOR(srctype) && IS_TYPE_VECTOR(dsttype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
ENSURE_VR(output, srctype, outputReg);
} else if (srctype->size == dsttype->size) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
} else {
CError_FATAL(2224);
}
}
void gen_BITFIELD(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
CError_FATAL(2238);
}
void gen_INTCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
if (TYPE_IS_8BYTES(expr->rtype)) {
I8_gen_INTCONST(expr, outputReg, outputRegHi, output);
return;
}
output->optype = OpndType_Absolute;
output->immediate = CInt64_GetULong(&expr->data.intval);
}
void gen_FLOATCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
CError_FATAL(2294);
}
void gen_STRINGCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
CError_FATAL(2308);
}
static Boolean COND_is_ABS_MatchNodes(ENode *cond, ENode *expr1, ENode *expr2) {
if (cond->type != expr1->type || cond->type != expr2->type)
return 0;
if (!(TYPE_FITS_IN_REGISTER(cond->rtype) && TYPE_FITS_IN_REGISTER(expr1->rtype) && TYPE_FITS_IN_REGISTER(expr2->rtype)))
return 0;
if (cond->rtype->size != expr1->rtype->size || cond->rtype->size != expr2->rtype->size)
return 0;
switch (cond->type) {
case EOBJREF:
if (cond->data.objref != expr1->data.objref || cond->data.objref != expr2->data.objref)
return 0;
return 1;
case EINDIRECT:
case ETYPCON:
return COND_is_ABS_MatchNodes(cond->data.monadic, expr1->data.monadic, expr2->data.monadic);
default:
return 0;
}
}
static ENode *COND_is_ABS(ENode *cond, ENode *expr1, ENode *expr2) {
ENode *tmp;
int parity = 0;
while (ENODE_IS(cond, ELOGNOT)) {
parity = (parity + 1) & 1;
cond = cond->data.monadic;
}
if (parity) {
tmp = expr1;
expr1 = expr2;
expr2 = tmp;
}
switch (cond->type) {
case ELESS:
case ELESSEQU:
tmp = expr1;
expr1 = expr2;
expr2 = tmp;
break;
case EGREATER:
case EGREATEREQU:
break;
default:
return NULL;
}
if (IS_INT_CONST_ZERO(cond->data.diadic.right)) {
cond = cond->data.diadic.left;
} else if (IS_INT_CONST_ZERO(cond->data.diadic.left)) {
cond = cond->data.diadic.left;
tmp = expr1;
expr1 = expr2;
expr2 = tmp;
} else {
return NULL;
}
if (ENODE_IS(expr1, EADD) && ENODE_IS(expr2, ESUB)) {
if (COND_is_ABS_MatchNodes(cond, expr1->data.diadic.right, expr2->data.diadic.right))
return expr1;
else
return NULL;
}
if (!ENODE_IS(expr2, EMONMIN))
return NULL;
expr2 = expr2->data.monadic;
if (COND_is_ABS_MatchNodes(cond, expr1, expr2))
return expr1;
return NULL;
}
static int COND_has_const(ENode *expr1, ENode *expr2) {
SInt32 diff;
int result = 0;
if (IS_INT_CONST(expr1))
result += 1;
if (IS_INT_CONST(expr2))
result += 2;
if (result & 1) {
if (IS_INT_CONST_ZERO(expr1))
return 5;
}
if (result & 2) {
if (IS_INT_CONST_ZERO(expr2))
return 6;
}
if (result == 3) {
diff = expr1->data.intval.lo - expr2->data.intval.lo;
if (diff == 1 || diff == -1)
return 4;
}
return result;
}
static Boolean COND_is_COMPARE(ENode *cond, ENode *expr1, ENode *expr2, short outputReg, Operand *output) {
SInt32 left;
SInt32 right;
int parity;
int negate;
ENodeType nt;
while (ENODE_IS(expr1, ETYPCON) && TYPE_FITS_IN_REGISTER(expr1->rtype))
expr1 = expr1->data.monadic;
while (ENODE_IS(expr2, ETYPCON) && TYPE_FITS_IN_REGISTER(expr2->rtype))
expr2 = expr2->data.monadic;
if (!(ENODE_IS(expr1, EINTCONST) && TYPE_FITS_IN_REGISTER(expr1->rtype) && CInt64_IsInRange(expr1->data.intval, 4)))
return 0;
if (!(ENODE_IS(expr2, EINTCONST) && TYPE_FITS_IN_REGISTER(expr2->rtype) && CInt64_IsInRange(expr2->data.intval, 4)))
return 0;
left = CInt64_GetULong(&expr1->data.intval);
right = CInt64_GetULong(&expr2->data.intval);
parity = 0;
negate = 0;
switch (left) {
case 1:
if (right != 0)
return 0;
break;
case 0:
parity = 1;
if (right == -1)
negate = 1;
else if (right != 1)
return 0;
break;
case -1:
if (right != 0)
return 0;
negate = 1;
break;
default:
return 0;
}
while (ENODE_IS(cond, ELOGNOT)) {
parity = (parity + 1) & 1;
cond = cond->data.monadic;
}
if (parity) {
nt = invert_relop(cond->type);
if (nt == cond->type)
return 0;
cond->type = nt;
}
if (negate)
gen_negated_condition_gpr(cond, output, outputReg);
else
gen_condition_gpr(cond, output, outputReg);
return 1;
}
void gen_COND(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *cond;
ENode *expr1;
ENode *expr2;
Type *type;
PCodeLabel *label1;
PCodeLabel *label2;
PCodeLabel *label3;
Operand op1;
Operand op2;
int has_const;
int reg1;
int reg2;
int reg3;
short align;
short max_align;
expr1 = expr->data.cond.expr1;
expr2 = expr->data.cond.expr2;
type = expr->rtype;
label1 = makepclabel();
label2 = makepclabel();
label3 = makepclabel();
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
cond = evaluate_and_skip_comma(expr->data.cond.cond);
if (TOC_use_fsel(expr)) {
ENode *left;
ENode *right;
ENode *tmp;
ENodeType nt;
Boolean flag;
Operand op;
int fneg_reg;
int fneg_reg2;
int fneg_reg3;
int fsel_reg;
int final_reg;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
nt = cond->type;
flag = 0;
memclrw(&op, sizeof(Operand));
switch (nt) {
case EGREATEREQU:
case EEQU:
break;
case EGREATER:
tmp = left;
left = right;
right = tmp;
case ELESS:
case ENOTEQU:
tmp = expr1;
expr1 = expr2;
expr2 = tmp;
break;
case ELESSEQU:
tmp = left;
left = right;
right = tmp;
break;
default:
CError_FATAL(2780);
}
if (ENODE_IS(left, EFLOATCONST) && CMach_FloatIsZero(left->data.floatval)) {
GEN_NODE(right, &op);
ENSURE_FPR(&op, right->rtype, 0);
flag = 1;
} else if (ENODE_IS(right, EFLOATCONST) && CMach_FloatIsZero(right->data.floatval)) {
GEN_NODE(left, &op);
ENSURE_FPR(&op, left->rtype, 0);
} else {
fp_binary_operator((type->size == 4) ? PC_FSUBS : PC_FSUB, left, right, 0, &op);
}
switch (cond->type) {
case EEQU:
case ENOTEQU:
if (flag) {
GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0);
GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0);
fneg_reg = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg, op.reg);
fsel_reg = ALLOC_FPR();
emitpcode(PC_FSEL, fsel_reg, op.reg, op1.reg, op2.reg);
final_reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(PC_FSEL, final_reg, fneg_reg, fsel_reg, op2.reg);
} else {
GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0);
GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0);
fneg_reg2 = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg2, op.reg);
fsel_reg = ALLOC_FPR();
emitpcode(PC_FSEL, fsel_reg, op.reg, op1.reg, op2.reg);
final_reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(PC_FSEL, final_reg, fneg_reg2, fsel_reg, op2.reg);
}
break;
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0);
GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0);
fneg_reg3 = op.reg;
if (flag) {
fneg_reg3 = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg3, op.reg);
}
final_reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(PC_FSEL, final_reg, fneg_reg3, op1.reg, op2.reg);
break;
default:
CError_FATAL(2862);
}
output->optype = OpndType_FPR;
output->reg = final_reg;
return;
}
if (TOC_use_isel(expr, 1)) {
Operand isel_op1;
Operand isel_op2;
ENode *x;
ENode *y;
ENode *abs_expr;
memclrw(&isel_op1, sizeof(Operand));
memclrw(&isel_op2, sizeof(Operand));
if (COND_is_COMPARE(cond, expr1, expr2, outputReg, output))
return;
if ((abs_expr = COND_is_ABS(cond, expr1, expr2))) {
if (ENODE_IS(expr1, EADD) && ENODE_IS(expr2, ESUB)) {
x = expr1->data.diadic.left;
y = expr2->data.diadic.right;
if (y->hascall) {
GEN_NODE(y, &op2);
ENSURE_GPR(&op2, y->rtype, 0);
GEN_NODE(x, &op1);
if (op1.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&op1, x->rtype, 0);
} else {
GEN_NODE(x, &op1);
if (op1.optype >= OpndType_IndirectGPR_ImmOffset)
ENSURE_GPR(&op1, x->rtype, 0);
GEN_NODE(y, &op2);
ENSURE_GPR(&op2, y->rtype, 0);
}
reg1 = ALLOC_GPR();
emitpcode(PC_SRAWI, reg1, op2.reg, 31);
reg2 = ALLOC_GPR();
emitpcode(PC_XOR, reg2, reg1, op2.reg);
reg3 = ALLOC_GPR();
emitpcode(PC_SUBF, reg3, reg1, reg2);
op2.optype = OpndType_GPR;
op2.reg = reg3;
combine(&op1, &op2, outputReg, output);
} else {
GEN_NODE(abs_expr, output);
ENSURE_GPR(output, abs_expr->rtype, 0);
reg1 = ALLOC_GPR();
emitpcode(PC_SRAWI, reg1, output->reg, 31);
reg2 = ALLOC_GPR();
emitpcode(PC_XOR, reg2, reg1, output->reg);
reg3 = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBF, reg3, reg1, reg2);
output->optype = OpndType_GPR;
output->reg = reg3;
}
return;
}
if ((has_const = COND_has_const(expr1, expr2))) {
switch (COND_has_const(expr1, expr2)) {
case 0:
case 2:
break;
case 3:
case 4:
if (has_const == 4) {
if (expr1->data.intval.lo < expr2->data.intval.lo)
gen_negated_condition_gpr(cond, &isel_op1, 0);
else
gen_condition_gpr(cond, &isel_op1, 0);
GEN_NODE(expr1, &op1);
GEN_NODE(expr2, &op2);
reg1 = ALLOC_GPR();
ENSURE_GPR(&op2, expr2->rtype, reg1);
emitpcode(PC_ADD, reg1, isel_op1.reg, op2.reg);
if (outputReg) {
emitpcode(PC_MR, reg2 = outputReg, reg1);
reg1 = reg2;
}
output->optype = OpndType_GPR;
output->reg = reg1;
return;
}
break;
case 5:
case 6:
gen_negated_condition_gpr(cond, &isel_op1, 0);
ENSURE_GPR(&isel_op1, TYPE(&stunsignedint), 0);
GEN_NODE(expr1, &op1);
GEN_NODE(expr2, &op2);
reg1 = outputReg ? outputReg : ALLOC_GPR();
if (op1.optype == OpndType_Absolute && op1.immediate == 0) {
ENSURE_GPR(&op2, expr2->rtype, 0);
emitpcode(PC_ANDC, reg1, op2.reg, isel_op1.reg);
} else if (op2.optype == OpndType_Absolute && op2.immediate == 0) {
ENSURE_GPR(&op1, expr1->rtype, 0);
emitpcode(PC_AND, reg1, op1.reg, isel_op1.reg);
} else {
CError_FATAL(3119);
}
output->optype = OpndType_GPR;
output->reg = reg1;
return;
case 1:
reg2 = ALLOC_GPR();
reg1 = reg2;
logical_expression_nobranch(cond, 0, &isel_op2);
GEN_NODE_TO_REG(expr1, reg1, 0, &op1);
ENSURE_GPR(&op1, expr1->rtype, reg1);
if (op1.reg != reg1)
emitpcode(PC_MR, reg1, op1.reg);
branch_conditional(isel_op2.reg, isel_op2.regOffset, 1, label2);
branch_label(label1);
GEN_NODE_TO_REG(expr2, reg1, 0, &op2);
ENSURE_GPR(&op2, expr2->rtype, reg1);
if (op2.reg != reg1)
emitpcode(PC_MR, reg1, op2.reg);
branch_label(label2);
if (outputReg) {
emitpcode(PC_MR, reg2 = outputReg, reg1);
reg1 = reg2;
}
output->optype = OpndType_GPR;
output->reg = reg1;
return;
default:
CError_FATAL(3168);
}
}
reg1 = ALLOC_GPR();
logical_expression_nobranch(cond, 0, &isel_op2);
GEN_NODE_TO_REG(expr2, reg1, 0, &op2);
ENSURE_GPR(&op2, expr2->rtype, reg1);
if (op2.reg != reg1)
emitpcode(PC_MR, reg1, op2.reg);
branch_conditional(isel_op2.reg, isel_op2.regOffset, 0, label2);
branch_label(label1);
GEN_NODE_TO_REG(expr1, reg1, 0, &op1);
ENSURE_GPR(&op1, expr1->rtype, reg1);
if (op1.reg != reg1)
emitpcode(PC_MR, reg1, op1.reg);
branch_label(label2);
if (outputReg) {
emitpcode(PC_MR, reg2 = outputReg, reg1);
reg1 = reg2;
}
output->optype = OpndType_GPR;
output->reg = reg1;
return;
}
logical_expression(cond, label1, label2, label1);
branch_label(label1);
if (IS_TYPE_VOID(type) || expr->ignored) {
GEN_NODE(expr1, &op1);
branch_always(label3);
branch_label(label2);
GEN_NODE(expr2, &op2);
} else if (IS_TYPE_FLOAT(type)) {
if (expr1->hascall || expr2->hascall)
reg1 = ALLOC_FPR();
else
reg1 = outputReg ? outputReg : ALLOC_FPR();
GEN_NODE_TO_REG(expr1, reg1, 0, &op1);
ENSURE_FPR(&op1, expr1->rtype, reg1);
if (op1.reg != reg1)
emitpcode(PC_FMR, reg1, op1.reg);
branch_always(label3);
branch_label(label2);
GEN_NODE_TO_REG(expr2, reg1, 0, &op2);
ENSURE_FPR(&op2, expr2->rtype, reg1);
if (op2.reg != reg1)
emitpcode(PC_FMR, reg1, op2.reg);
output->optype = OpndType_FPR;
output->reg = reg1;
} else if (TYPE_IS_8BYTES(type)) {
if (expr1->hascall || expr2->hascall) {
reg1 = ALLOC_GPR();
reg3 = ALLOC_GPR();
reg2 = reg3;
} else {
reg1 = outputReg ? outputReg : ALLOC_GPR();
reg3 = outputRegHi ? outputRegHi : ALLOC_GPR();
reg2 = reg3;
}
GEN_NODE_TO_REG(expr1, reg1, reg2, &op1);
coerce_to_register_pair(&op1, expr1->rtype, reg1, reg2);
branch_always(label3);
branch_label(label2);
GEN_NODE_TO_REG(expr2, reg1, reg2, &op2);
coerce_to_register_pair(&op2, expr2->rtype, reg1, reg2);
output->optype = OpndType_GPRPair;
output->reg = reg1;
output->regHi = reg2;
} else if (TYPE_FITS_IN_REGISTER(type)) {
if (expr1->hascall || expr2->hascall)
reg1 = ALLOC_GPR();
else
reg1 = outputReg ? outputReg : ALLOC_GPR();
GEN_NODE_TO_REG(expr1, reg1, 0, &op1);
ENSURE_GPR(&op1, expr1->rtype, reg1);
if (op1.reg != reg1)
emitpcode(PC_MR, reg1, op1.reg);
branch_always(label3);
branch_label(label2);
GEN_NODE_TO_REG(expr2, reg1, 0, &op2);
ENSURE_GPR(&op2, expr2->rtype, reg1);
if (op2.reg != reg1)
emitpcode(PC_MR, reg1, op2.reg);
output->optype = OpndType_GPR;
output->reg = reg1;
} else if (IS_TYPE_VECTOR(type)) {
if (expr1->hascall || expr2->hascall)
reg1 = ALLOC_VR();
else
reg1 = outputReg ? outputReg : ALLOC_VR();
GEN_NODE_TO_REG(expr1, reg1, 0, &op1);
ENSURE_VR(&op1, expr1->rtype, reg1);
if (op1.reg != reg1)
emitpcode(PC_VMR, reg1, op1.reg);
branch_always(label3);
branch_label(label2);
GEN_NODE_TO_REG(expr2, reg1, 0, &op2);
ENSURE_VR(&op2, expr2->rtype, reg1);
if (op2.reg != reg1)
emitpcode(PC_VMR, reg1, op2.reg);
output->optype = OpndType_VR;
output->reg = reg1;
} else {
symbol_operand(output, maketemporary(type));
indirect(output, NULL);
coerce_to_addressable(output);
GEN_NODE(expr1, &op1);
if (op1.object) {
if (op1.object->datatype == DLOCAL && (op1.object->u.var.info->flags & VarInfoFlag1))
align = CMach_ArgumentAlignment(type);
else
align = CMach_AllocationAlignment(type, op1.object->qual);
} else {
align = CMach_AllocationAlignment(type, 0);
}
max_align = CMach_AllocationAlignment(type, 0);
if (align > max_align)
align = max_align;
move_block(output, &op1, type->size, align);
branch_always(label3);
branch_label(label2);
GEN_NODE(expr2, &op2);
if (op2.object) {
if (op2.object->datatype == DLOCAL && (op2.object->u.var.info->flags & VarInfoFlag1))
align = CMach_ArgumentAlignment(type);
else
align = CMach_AllocationAlignment(type, op2.object->qual);
} else {
align = CMach_AllocationAlignment(type, 0);
}
if (align > max_align)
align = max_align;
move_block(output, &op2, type->size, align);
}
branch_label(label3);
}
static Boolean CONDASS_is_ABS(ENode *cond, ENode *expr1, ENode *expr2) {
ENode *inner;
int parity = 0;
while (ENODE_IS(cond, ELOGNOT)) {
parity = (parity + 1) & 1;
cond = cond->data.monadic;
}
if (IS_INT_CONST_ZERO(cond->data.diadic.right)) {
inner = cond->data.diadic.left;
} else if (IS_INT_CONST_ZERO(cond->data.diadic.left)) {
inner = cond->data.diadic.left;
parity = (parity + 1) & 1;
} else {
return 0;
}
switch (cond->type) {
case EGREATER:
case EGREATEREQU:
if (!parity)
return 0;
break;
case ELESS:
case ELESSEQU:
if (parity)
return 0;
break;
default:
return 0;
}
if (!ENODE_IS(expr2, EMONMIN))
return 0;
expr2 = expr2->data.monadic;
if (ENODE_IS(inner, EASS)) {
inner = inner->data.diadic.left;
if (!ENODE_IS(expr2, EINDIRECT))
return 0;
expr2 = expr2->data.monadic;
if (!ENODE_IS(expr1, EINDIRECT))
return 0;
expr1 = expr1->data.monadic;
}
return COND_is_ABS_MatchNodes(inner, expr1, expr2);
}
static int CONDASS_is_OPASS_One(ENode *a, ENode *b, SInt32 *value, ENodeType *nodetype) {
Type *type;
type = a->rtype;
if (!ENODE_IS(a, EINDIRECT))
return 0;
a = a->data.monadic;
if (!ENODE_IS(a, EOBJREF))
return 0;
if (ENODE_IS(b, ETYPCON) && b->rtype == type)
b = b->data.monadic;
if (b->type != EOR && b->type != EADD && b->type != ESUB)
return 0;
*nodetype = b->type;
if (!IS_INT_CONST(b->data.diadic.right))
return 0;
*value = b->data.diadic.right->data.intval.lo;
if (*value != 1 && *value != -1)
return 0;
b = b->data.diadic.left;
if (ENODE_IS(b, ETYPCON) && TYPE_FITS_IN_REGISTER(b->rtype))
b = b->data.monadic;
if (!ENODE_IS(b, EINDIRECT))
return 0;
b = b->data.monadic;
if (!ENODE_IS(b, EOBJREF))
return 0;
if (a->data.objref == b->data.objref)
return 1;
return 0;
}
void gen_CONDASS(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *cond;
ENode *expr1;
ENode *expr2;
Type *type;
PCodeLabel *label1;
PCodeLabel *label2;
Operand op1;
Operand op2;
Operand op3;
int reg1;
int reg2;
expr1 = expr->data.cond.expr1;
expr2 = expr->data.cond.expr2;
type = expr->rtype;
label1 = makepclabel();
label2 = makepclabel();
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
memclrw(&op3, sizeof(Operand));
cond = evaluate_and_skip_comma(expr->data.cond.cond);
if (TOC_use_fsel(expr)) {
ENode *left;
ENode *right;
ENode *tmp;
ENodeType nt;
Boolean flag;
Boolean flag2;
Operand op;
int tmpreg;
int fneg_reg;
int fsel_reg;
int final_reg;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
nt = cond->type;
flag = 0;
memclrw(&op, sizeof(Operand));
CError_ASSERT(3704, ENODE_IS(expr1, EINDIRECT));
CError_ASSERT(3705, ENODE_IS(expr1->data.monadic, EOBJREF));
tmpreg = OBJECT_REG(expr1->data.monadic->data.objref);
final_reg = outputReg ? tmpreg : ALLOC_FPR();
switch (nt) {
case EGREATER:
tmp = left;
left = right;
right = tmp;
case ELESS:
case ENOTEQU:
tmp = expr1;
expr1 = expr2;
expr2 = tmp;
flag2 = 1;
break;
case ELESSEQU:
tmp = left;
left = right;
right = tmp;
flag2 = 0;
break;
case EGREATEREQU:
case EEQU:
flag2 = 0;
break;
default:
CError_FATAL(3744);
}
if (ENODE_IS(left, EFLOATCONST) && CMach_FloatIsZero(left->data.floatval)) {
GEN_NODE(right, &op);
ENSURE_FPR(&op, right->rtype, 0);
flag = 1;
} else if (ENODE_IS(right, EFLOATCONST) && CMach_FloatIsZero(right->data.floatval)) {
GEN_NODE(left, &op);
ENSURE_FPR(&op, left->rtype, 0);
} else {
fp_binary_operator((type->size == 4) ? PC_FSUBS : PC_FSUB, left, right, 0, &op);
}
switch (cond->type) {
case EEQU:
case ENOTEQU:
if (flag) {
GEN_NODE(expr1, &op1);
op3 = op1;
ENSURE_FPR(&op1, expr1->rtype, 0);
GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0);
fneg_reg = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg, op.reg);
fsel_reg = ALLOC_FPR();
emitpcode(PC_FSEL, fsel_reg, op.reg, op2.reg, op1.reg);
emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, fsel_reg);
} else {
GEN_NODE(expr1, &op1);
op3 = op1;
ENSURE_FPR(&op1, expr1->rtype, 0);
GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0);
fneg_reg = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg, op.reg);
fsel_reg = ALLOC_FPR();
emitpcode(PC_FSEL, fsel_reg, op.reg, op2.reg, op1.reg);
emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, fsel_reg);
}
break;
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
GEN_NODE(expr1, &op1);
GEN_NODE(expr2, &op2);
op3 = flag2 ? op2 : op1;
ENSURE_FPR(&op1, expr1->rtype, 0);
ENSURE_FPR(&op2, expr2->rtype, 0);
fneg_reg = op.reg;
if (flag) {
fneg_reg = ALLOC_FPR();
emitpcode(PC_FNEG, fneg_reg, op.reg);
}
emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, op1.reg);
break;
default:
CError_FATAL(2862);
}
if (op3.optype != OpndType_FPR)
store_fp(final_reg, &op3, type);
output->optype = OpndType_FPR;
output->reg = final_reg;
return;
}
if (TOC_use_isel(expr, 1)) {
Operand isel_op;
ENode *x;
ENode *y;
ENode *abs_expr;
memclrw(&isel_op, sizeof(Operand));
CError_ASSERT(3966, ENODE_IS(expr1, EINDIRECT));
CError_ASSERT(3968, ENODE_IS(expr1->data.monadic, EOBJREF));
if (CONDASS_is_ABS(cond, expr1, expr2)) {
if (ENODE_IS(cond->data.diadic.left, EASS))
GEN_NODE(cond->data.diadic.left, &isel_op);
else if (ENODE_IS(cond->data.diadic.right, EASS))
GEN_NODE(cond->data.diadic.right, &isel_op);
outputReg = OBJECT_REG(expr1->data.monadic->data.objref);
CError_ASSERT(3979, outputReg);
GEN_NODE(expr1, &op1);
op3 = op1;
CError_ASSERT(3986, op3.optype == OpndType_GPR && op3.reg == outputReg);
ENSURE_GPR(&op1, expr1->rtype, 0);
if (expr1->rtype->size < 4)
extend32(output, expr1->rtype, op3.reg);
reg1 = ALLOC_GPR();
reg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, reg1, op1.reg, 31);
emitpcode(PC_XOR, reg2, reg1, op1.reg);
emitpcode(PC_SUBF, outputReg, reg1, reg2);
output->optype = OpndType_GPR;
output->reg = op3.reg;
if (expr1->rtype->size < 4)
extend32(output, expr1->rtype, op3.reg);
return;
}
}
logical_expression(cond, label1, label2, label1);
branch_label(label1);
gen_ASS(expr, outputReg, outputRegHi, output);
branch_label(label2);
}
void gen_FUNCCALL(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
if (is_intrinsic_function_call(expr))
call_intrinsic_function(expr, outputReg, output);
else
call_function(expr, output);
}
void gen_OBJREF(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
symbol_operand(output, expr->data.objref);
}
void gen_UNEXPECTED(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
CError_FATAL(4160);
}
static int small(ENode *expr) {
Type *type;
type = expr->rtype;
if (!ENODE_IS(expr, ETYPCON))
return 0;
do {
expr = expr->data.monadic;
} while (ENODE_IS(expr, ETYPCON) && (type = expr->rtype)->size == 4);
return IS_TYPE_INT_OR_ENUM(type) && ((type->size < 2) || (type->size == 2 && !is_unsigned(type)));
}
void binary_operator(Opcode opcode, ENode *left, ENode *right, short outputReg, Operand *output) {
Operand opleft;
Operand opright;
int reg;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE_TO_GPR(right, &opright, right->rtype, 0);
GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0);
} else {
GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0);
GEN_NODE_TO_GPR(right, &opright, right->rtype, 0);
}
reg = outputReg ? outputReg : ALLOC_GPR();
if (opcode == PC_MULLW && small(left))
emitpcode(opcode, reg, opright.reg, opleft.reg);
else
emitpcode(opcode, reg, opleft.reg, opright.reg);
output->optype = OpndType_GPR;
output->reg = reg;
}
static void binary_immediate(Opcode opcode, ENode *left, SInt32 value, short outputReg, Operand *output) {
Operand opleft;
int reg;
memclrw(&opleft, sizeof(Operand));
GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
if (opcode == PC_MULLI && value == 0)
emitpcode(PC_LI, reg, 0);
else if (opcode == PC_MULLI && value == 1)
emitpcode(PC_MR, reg, opleft.reg);
else
emitpcode(opcode, reg, opleft.reg, value);
output->optype = OpndType_GPR;
output->reg = reg;
}
void unary_operator(Opcode opcode, ENode *expr, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(opcode, reg, op.reg);
output->optype = OpndType_GPR;
output->reg = reg;
}
static void or_xor_immediate(Opcode opcode, ENode *expr, SInt32 value, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
if (expr->rtype->size > 2 && value != (value & 0xFFFF)) {
if (value & 0xFFFF) {
emitpcode((opcode == PC_ORI) ? PC_ORIS : PC_XORIS, reg, op.reg, value >> 16);
emitpcode(opcode, reg, reg, value & 0xFFFF);
} else {
emitpcode((opcode == PC_ORI) ? PC_ORIS : PC_XORIS, reg, op.reg, value >> 16);
}
} else {
emitpcode(opcode, reg, op.reg, value & 0xFFFF);
}
output->optype = OpndType_GPR;
output->reg = reg;
}
static void shift_left_immediate(ENode *expr, short shift, short negate, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
if (negate)
reg = ALLOC_GPR();
else
reg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, reg, op.reg, shift & 31, 0, 31 - (shift & 31));
if (negate) {
int tmp = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_NEG, tmp, reg);
reg = tmp;
}
output->optype = OpndType_GPR;
output->reg = reg;
}
static void shift_right_immediate(ENode *expr, Type *type, short shift, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
if (is_unsigned(type))
emitpcode(PC_RLWINM, reg, op.reg, (32 - (shift & 31)) & 31, (shift & 31) + (32 - (type->size * 8)), 31);
else
emitpcode(PC_SRAWI, reg, op.reg, shift & 31);
output->optype = OpndType_GPR;
output->reg = reg;
}
static void signed_divide_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output) {
Operand op;
int reg;
int tmpreg1;
int tmpreg2;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
if (!copts.optimizesize && shift == 1) {
tmpreg1 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpreg1, op.reg, 1, 31, 31);
tmpreg2 = ALLOC_GPR();
emitpcode(PC_ADD, tmpreg2, tmpreg1, op.reg);
reg = (outputReg && !negate) ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, reg, tmpreg2, 1);
} else {
tmpreg1 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg1, op.reg, shift);
reg = (outputReg && !negate) ? outputReg : ALLOC_GPR();
emitpcode(PC_ADDZE, reg, tmpreg1);
}
if (negate) {
int prevreg = reg;
reg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_NEG, reg, prevreg);
}
output->optype = OpndType_GPR;
output->reg = reg;
}
static void signed_mod_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output) {
Operand op;
int reg;
int tmpreg1;
int tmpreg2;
int tmpreg3;
int tmpreg4;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
if (shift == 1) {
emitpcode(PC_RLWINM, tmpreg1, op.reg, 1, 31, 31);
emitpcode(PC_RLWINM, tmpreg2, op.reg, 0, 31, 31);
emitpcode(PC_XOR, tmpreg3, tmpreg2, tmpreg1);
emitpcode(PC_SUBF, reg, tmpreg1, tmpreg3);
} else {
tmpreg4 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpreg1, op.reg, 32 - shift, 0, 31 - (32 - shift));
emitpcode(PC_RLWINM, tmpreg2, op.reg, 1, 31, 31);
emitpcode(PC_SUBF, tmpreg3, tmpreg2, tmpreg1);
emitpcode(PC_RLWINM, tmpreg4, tmpreg3, shift, 0, 31);
emitpcode(PC_ADD, reg, tmpreg4, tmpreg2);
}
output->optype = OpndType_GPR;
output->reg = reg;
}
static void fp_binary_operator(Opcode opcode, ENode *left, ENode *right, short outputReg, Operand *output) {
Operand opleft;
Operand opright;
int reg;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE_TO_FPR(right, &opright, right->rtype, 0);
GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0);
} else {
GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0);
GEN_NODE_TO_FPR(right, &opright, right->rtype, 0);
}
reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(opcode, reg, opleft.reg, opright.reg);
output->optype = OpndType_FPR;
output->reg = reg;
}
void fp_unary_operator(Opcode opcode, ENode *expr, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE_TO_FPR(expr, &op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(opcode, reg, op.reg);
output->optype = OpndType_FPR;
output->reg = reg;
}
void fp_multiply_add(Opcode opcode, ENode *a, ENode *b, ENode *c, short outputReg, Operand *output) {
Operand opA;
Operand opB;
Operand opC;
int reg;
memclrw(&opA, sizeof(Operand));
memclrw(&opB, sizeof(Operand));
memclrw(&opC, sizeof(Operand));
if (c->hascall) {
GEN_NODE_TO_FPR(c, &opC, c->rtype, 0);
if (b->hascall) {
GEN_NODE_TO_FPR(b, &opB, b->rtype, 0);
GEN_NODE_TO_FPR(a, &opA, a->rtype, 0);
} else {
GEN_NODE_TO_FPR(a, &opA, a->rtype, 0);
GEN_NODE_TO_FPR(b, &opB, b->rtype, 0);
}
} else {
if (b->hascall) {
GEN_NODE_TO_FPR(b, &opB, b->rtype, 0);
GEN_NODE_TO_FPR(a, &opA, a->rtype, 0);
GEN_NODE_TO_FPR(c, &opC, c->rtype, 0);
} else {
GEN_NODE_TO_FPR(a, &opA, a->rtype, 0);
GEN_NODE_TO_FPR(b, &opB, b->rtype, 0);
GEN_NODE_TO_FPR(c, &opC, c->rtype, 0);
}
}
reg = outputReg ? outputReg : ALLOC_FPR();
emitpcode(opcode, reg, opA.reg, opB.reg, opC.reg);
output->optype = OpndType_FPR;
output->reg = reg;
}
void gen_COMPARE(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
expr = evaluate_and_skip_comma(expr);
if (TYPE_IS_8BYTES(expr->data.diadic.right->rtype) || TYPE_IS_8BYTES(expr->data.diadic.left->rtype))
I8_gen_condition(expr, output, 1);
else
gen_condition_gpr(expr, output, outputReg);
}
void gen_LOGICAL(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
ENodeType op;
expr = evaluate_and_skip_comma(expr);
inner = evaluate_and_skip_comma(expr->data.monadic);
expr->data.monadic = inner;
if (ENODE_IS(expr, ELOGNOT) && !ENODE_IS2(inner, ELAND, ELOR)) {
op = inner->type;
if (ENODE_IS(inner, ELOGNOT)) {
switch (inner->data.monadic->type) {
case ELOGNOT:
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
case ELAND:
case ELOR:
GEN_NODE(inner->data.monadic, output);
if (expr->data.monadic->rtype->size < 4)
extend32(output, expr->data.monadic->rtype, 0);
ENSURE_GPR(output, expr->data.monadic->rtype, 0);
return;
}
}
if (ENODE_IS(inner, ENOTEQU) && !TYPE_IS_8BYTES(inner->data.diadic.left->rtype) && ENODE_IS(inner->data.diadic.right, EINTCONST) && inner->data.diadic.right->data.intval.lo == 0) {
int tmpreg1;
int tmpreg2;
GEN_NODE(inner->data.diadic.left, output);
if (inner->data.diadic.left->rtype->size < 4)
extend32(output, inner->data.diadic.left->rtype, 0);
ENSURE_GPR(output, inner->data.diadic.left->rtype, 0);
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
CError_ASSERT(4853, output->optype == OpndType_GPR);
emitpcode(PC_CNTLZW, tmpreg2, output->reg);
emitpcode(PC_RLWINM, tmpreg1, tmpreg2, 27, 5, 31);
output->optype = OpndType_GPR;
output->reg = tmpreg1;
} else {
int tmpreg1;
int tmpreg2;
ENodeType inverted;
inverted = invert_relop(op);
if (op != inverted && !IS_TYPE_FLOAT(inner->data.diadic.left->rtype)) {
inner->type = inverted;
gen_COMPARE(inner, 0, 0, output);
inner->type = inverted;
return;
}
GEN_NODE(inner, output);
if (inner->rtype->size < 4)
extend32(output, inner->rtype, 0);
ENSURE_GPR(output, inner->rtype, 0);
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
CError_ASSERT(4883, output->optype == OpndType_GPR);
emitpcode(PC_CNTLZW, tmpreg2, output->reg);
emitpcode(PC_RLWINM, tmpreg1, tmpreg2, 27, 5, 31);
output->optype = OpndType_GPR;
output->reg = tmpreg1;
}
} else {
PCodeLabel *label1;
PCodeLabel *label2;
int tmpreg;
label1 = makepclabel();
label2 = makepclabel();
tmpreg = ALLOC_GPR();
emitpcode(PC_LI, tmpreg, 0);
logical_expression(expr, label1, label2, label1);
branch_label(label1);
emitpcode(PC_LI, tmpreg, 1);
branch_label(label2);
output->optype = OpndType_GPR;
output->reg = tmpreg;
}
}
void gen_NULLCHECK(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
PrecomputedOperand *precomp;
int flag;
int reg;
PCodeLabel *label;
left = expr->data.nullcheck.nullcheckexpr;
right = expr->data.nullcheck.condexpr;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
flag = !IS_TYPE_VOID(expr->rtype) && !expr->ignored;
GEN_NODE(left, &opleft);
if (left->rtype->size < 4)
extend32(&opleft, left->rtype, 0);
ENSURE_GPR(&opleft, left->rtype, 0);
precomp = lalloc(sizeof(PrecomputedOperand));
precomp->precompid = expr->data.nullcheck.precompid;
precomp->operand = opleft;
precomp->next = precomputedoperands;
precomputedoperands = precomp;
emitpcode(PC_CMPI, 0, opleft.reg, 0);
if (flag) {
emitpcode(PC_MR, reg = ALLOC_GPR(), opleft.reg);
}
label = makepclabel();
branch_conditional(0, EEQU, 1, label);
GEN_NODE(right, &opright);
precomputedoperands = precomputedoperands->next;
if (flag) {
ENSURE_GPR(&opright, right->rtype, reg);
if (opright.reg != reg)
emitpcode(PC_MR, reg, opright.reg);
output->optype = OpndType_GPR;
output->reg = reg;
}
branch_label(label);
}
void gen_PRECOMP(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
PrecomputedOperand *precomp;
for (precomp = precomputedoperands; precomp; precomp = precomp->next) {
if (precomp->precompid == expr->data.precompid)
break;
}
*output = precomp->operand;
}
void logical_expression(ENode *cond, PCodeLabel *if_true, PCodeLabel *if_false, PCodeLabel *end) {
PCodeLabel *label;
Operand op;
memclrw(&op, sizeof(Operand));
cond = evaluate_and_skip_comma(cond);
switch (cond->type) {
case ELAND:
label = makepclabel();
logical_expression(cond->data.diadic.left, label, if_false, label);
branch_label(label);
logical_expression(cond->data.diadic.right, if_true, if_false, end);
break;
case ELOR:
label = makepclabel();
logical_expression(cond->data.diadic.left, if_true, label, label);
branch_label(label);
logical_expression(cond->data.diadic.right, if_true, if_false, end);
break;
case ELOGNOT:
logical_expression(cond->data.monadic, if_false, if_true, end);
break;
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
if (TYPE_IS_8BYTES(cond->data.diadic.right->rtype) || TYPE_IS_8BYTES(cond->data.diadic.left->rtype))
I8_gen_condition(cond, &op, 0);
else
gen_condition(cond, &op);
if (end == if_true)
branch_conditional(op.reg, op.regOffset, 0, if_false);
else
branch_conditional(op.reg, op.regOffset, 1, if_true);
break;
default:
CError_FATAL(5160);
}
}
static void logical_expression_nobranch(ENode *cond, Boolean invert, Operand *output) {
cond = evaluate_and_skip_comma(cond);
switch (cond->type) {
case ELOGNOT:
logical_expression_nobranch(cond->data.monadic, 1, output);
break;
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
if (invert) {
ENodeType nt = invert_relop(cond->type);
CError_ASSERT(5190, nt != cond->type);
cond->type = nt;
}
if (TYPE_IS_8BYTES(cond->data.diadic.right->rtype) || TYPE_IS_8BYTES(cond->data.diadic.left->rtype))
I8_gen_condition(cond, output, 0);
else
gen_condition(cond, output);
break;
default:
CError_FATAL(5206);
}
}
static ENodeType invert_relop(ENodeType nt) {
switch (nt) {
case ELESS: return EGREATEREQU;
case EGREATER: return ELESSEQU;
case ELESSEQU: return EGREATER;
case EGREATEREQU: return ELESS;
case EEQU: return ENOTEQU;
case ENOTEQU: return EEQU;
default: return nt;
}
}
static int reverse_relop(int nt) {
switch (nt) {
case ELESS: return EGREATER;
case EGREATER: return ELESS;
case ELESSEQU: return EGREATEREQU;
case EGREATEREQU: return ELESSEQU;
default: return nt;
}
}
void gen_condition(ENode *cond, Operand *output) {
ENode *left;
ENode *right;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
if (IS_TYPE_FLOAT(left->rtype)) {
compare_floating(cond->type, left, right, output);
return;
}
if (ENODE_IS(right, EINTCONST)) {
if (is_unsigned(left->rtype)) {
UInt32 val = right->data.intval.lo;
if (FITS_IN_USHORT(val)) {
compare_immediate(cond->type, left, val, output);
return;
} else if (ENODE_IS2(cond, EEQU, ENOTEQU)) {
compare_immediate_long(cond->type, left, val, output);
return;
}
} else {
UInt32 val = right->data.intval.lo;
if (FITS_IN_SHORT(val)) {
compare_immediate(cond->type, left, val, output);
return;
} else if (ENODE_IS2(cond, EEQU, ENOTEQU)) {
compare_immediate_long(cond->type, left, val, output);
return;
}
}
} else if (ENODE_IS(left, EINTCONST)) {
if (is_unsigned(right->rtype)) {
UInt32 val = left->data.intval.lo;
if (FITS_IN_USHORT(val)) {
compare_immediate(reverse_relop(cond->type), right, val, output);
return;
} else if (ENODE_IS2(cond, EEQU, ENOTEQU)) {
compare_immediate_long(reverse_relop(cond->type), right, val, output);
return;
}
} else {
UInt32 val = left->data.intval.lo;
if (FITS_IN_SHORT(val)) {
compare_immediate(reverse_relop(cond->type), right, val, output);
return;
} else if (ENODE_IS2(cond, EEQU, ENOTEQU)) {
compare_immediate_long(reverse_relop(cond->type), right, val, output);
return;
}
}
}
compare_integer(cond->type, left, right, output);
}
void gen_condition_gpr(ENode *cond, Operand *output, short outputReg) {
ENode *left;
ENode *right;
Operand condOp;
int finalReg;
int tmpReg;
int tmpReg2;
int tmpReg3;
int tmpReg4;
int a;
int b;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
memclrw(&condOp, sizeof(Operand));
if (!IS_TYPE_FLOAT(left->rtype)) {
Operand op1;
Operand op2;
Operand opTmp;
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
memclrw(&opTmp, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &op2);
if (!IS_INT_CONST_ZERO(right)) {
if (right->rtype->size < 4)
extend32(&op2, right->rtype, 0);
ENSURE_GPR(&op2, right->rtype, 0);
}
GEN_NODE(left, &op1);
if (left->rtype->size < 4)
extend32(&op1, left->rtype, 0);
ENSURE_GPR(&op1, left->rtype, 0);
} else {
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
if (left->rtype->size < 4)
extend32(&op1, left->rtype, 0);
GEN_NODE(right, &op2);
if (!IS_INT_CONST_ZERO(right)) {
if (right->rtype->size < 4)
extend32(&op2, right->rtype, 0);
ENSURE_GPR(&op2, right->rtype, 0);
}
}
switch (cond->type) {
case EEQU:
if (
copts.peephole &&
IS_INT_CONST(right) &&
pclastblock->pcodeCount > 0 &&
pclastblock->lastPCode->op == PC_RLWINM &&
pclastblock->lastPCode->args[0].data.reg.reg == op1.reg
)
{
PCode *pc = pclastblock->lastPCode;
SInt32 a = pc->args[2].data.imm.value;
SInt32 b = pc->args[3].data.imm.value;
SInt32 value = right->data.intval.lo;
if (b == pc->args[4].data.imm.value) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
if (value != (value & 1)) {
emitpcode(PC_LI, finalReg, 0);
} else if (value == 0) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM, tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
emitpcode(PC_XORI, finalReg, tmpReg, 1);
} else if (value == 1) {
emitpcode(
PC_RLWINM, finalReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
} else {
CError_FATAL(5434);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
}
if (IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_CNTLZW, tmpReg, op1.reg, 0);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg, 27, 5, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op1.reg, op2.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_CNTLZW, tmpReg2, tmpReg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg2, 27, 5, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
case ENOTEQU:
if (
copts.peephole &&
IS_INT_CONST(right) &&
pclastblock->pcodeCount > 0 &&
pclastblock->lastPCode->op == PC_RLWINM &&
pclastblock->lastPCode->args[0].data.reg.reg == op1.reg
)
{
PCode *pc = pclastblock->lastPCode;
SInt32 a = pc->args[2].data.imm.value;
SInt32 b = pc->args[3].data.imm.value;
SInt32 value = right->data.intval.lo;
if (b == pc->args[4].data.imm.value) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
if (value != (value & 1)) {
emitpcode(PC_LI, finalReg, 1);
} else if (value == 0) {
emitpcode(
PC_RLWINM, finalReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
} else if (value == 1) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM, tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
emitpcode(PC_XORI, finalReg, tmpReg, 1);
} else {
CError_FATAL(5503);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
}
if (IS_INT_CONST_ZERO(right)) {
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_ADDIC, tmpReg, op1.reg, -1);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg, op1.reg);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_NEG, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_OR, tmpReg2, tmpReg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg2, 1, 31, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op1.reg, op2.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ADDIC, tmpReg2, tmpReg, -1);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg2, tmpReg);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op1.reg, op2.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg2, op2.reg, op1.reg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_OR, tmpReg3, tmpReg, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg3, 1, 31, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
case EGREATEREQU:
if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg, op1.reg, 1, 31, 31);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_XORI, finalReg, tmpReg, 1);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
opTmp = op2;
op2 = op1;
op1 = opTmp;
case ELESSEQU:
if (is_unsigned(left->rtype)) {
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_LI, tmpReg, -1);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg2, op1.reg, op2.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFZE, finalReg, tmpReg);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op1.reg, op2.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ORC, tmpReg2, op2.reg, op1.reg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg3, tmpReg, 31, 1, 31);
tmpReg4 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg4, tmpReg3, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg4, 1, 31, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_LI, tmpReg, 1);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_CNTLZW, tmpReg2, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWNM, finalReg, tmpReg, tmpReg2, 31, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpReg2, op2.reg, 31);
tmpReg = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg, op1.reg, 1, 31, 31);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg3, op1.reg, op2.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_ADDE, finalReg, tmpReg2, tmpReg);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
case EGREATER:
if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_NEG, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ANDC, tmpReg2, tmpReg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg2, 1, 31, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
opTmp = op2;
op2 = op1;
op1 = opTmp;
case ELESS:
if (is_unsigned(left->rtype)) {
if (left->rtype->size <= 2) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op2.reg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg, 1, 31, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
} else {
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBFE, tmpReg2, tmpReg, tmpReg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_NEG, finalReg, tmpReg2);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_XOR, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_CNTLZW, tmpReg2, tmpReg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_SLW, tmpReg3, op2.reg, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg3, 1, 31, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
return;
}
if (IS_INT_CONST_ZERO(right)) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, op1.reg, 1, 31, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
tmpReg = ALLOC_GPR();
emitpcode(PC_XOR, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpReg2, tmpReg, 1);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_AND, tmpReg3, tmpReg, op2.reg);
tmpReg4 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg4, tmpReg3, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, finalReg, tmpReg4, 1, 31, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
default:
CError_FATAL(5777);
}
}
gen_condition(cond, &condOp);
emitpcode(PC_MFCR, tmpReg = used_virtual_registers[RegClass_GPR]++);
a = 0;
b = condOp.reg * 4;
switch (condOp.regOffset) {
case ENOTEQU:
a = 1;
case EEQU:
b += 2;
break;
case EGREATEREQU:
a = 1;
break;
case ELESSEQU:
a = 1;
case EGREATER:
b += 1;
break;
}
finalReg = outputReg ? outputReg : ALLOC_GPR();
if (a) {
emitpcode(PC_RLWINM, tmpReg, tmpReg, b + 1, 31, 31);
emitpcode(PC_XORI, finalReg, tmpReg, 1);
} else {
emitpcode(PC_RLWINM, finalReg, tmpReg, b + 1, 31, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
}
void gen_negated_condition_gpr(ENode *cond, Operand *output, short outputReg) {
ENode *left;
ENode *right;
Operand op1;
Operand op2;
Operand opTmp;
int finalReg;
int tmpReg;
int tmpReg2;
int tmpReg3;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
CError_ASSERT(5843, TYPE_FITS_IN_REGISTER(left->rtype) && TYPE_FITS_IN_REGISTER(right->rtype));
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
memclrw(&opTmp, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &op2);
if (!IS_INT_CONST_ZERO(right)) {
if (right->rtype->size < 4)
extend32(&op2, right->rtype, 0);
ENSURE_GPR(&op2, right->rtype, 0);
}
GEN_NODE(left, &op1);
if (left->rtype->size < 4)
extend32(&op1, left->rtype, 0);
ENSURE_GPR(&op1, left->rtype, 0);
} else {
GEN_NODE(left, &op1);
ENSURE_GPR(&op1, left->rtype, 0);
if (left->rtype->size < 4)
extend32(&op1, left->rtype, 0);
GEN_NODE(right, &op2);
if (!IS_INT_CONST_ZERO(right)) {
if (right->rtype->size < 4)
extend32(&op2, right->rtype, 0);
ENSURE_GPR(&op2, right->rtype, 0);
}
}
switch (cond->type) {
case EEQU:
if (
copts.peephole &&
IS_INT_CONST(right) &&
pclastblock->pcodeCount > 0 &&
pclastblock->lastPCode->op == PC_RLWINM &&
pclastblock->lastPCode->args[0].data.reg.reg == op1.reg
)
{
PCode *pc = pclastblock->lastPCode;
SInt32 a = pc->args[2].data.imm.value;
SInt32 b = pc->args[3].data.imm.value;
SInt32 value = right->data.intval.lo;
if (b == pc->args[4].data.imm.value) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
if (value != (value & 1)) {
emitpcode(PC_LI, finalReg, 0);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (value == 0) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM, tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
emitpcode(PC_ADDI, finalReg, tmpReg, 0, -1);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (value == 1) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM,
tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31,
31,
31);
emitpcode(PC_NEG, finalReg, tmpReg);
output->optype = OpndType_GPR;
output->reg = tmpReg; // bug???
return;
}
CError_FATAL(5923);
}
}
if (IS_INT_CONST_ZERO(right)) {
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_ADDIC, tmpReg, op1.reg, -1);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg, tmpReg);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_CNTLZW, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg2, tmpReg, 27, 31, 31);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_NEG, finalReg, tmpReg2);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ADDIC, tmpReg2, tmpReg, -1);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg2, tmpReg2);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg2, op1.reg, op2.reg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_NOR, tmpReg3, tmpReg, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, tmpReg3, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
case ENOTEQU:
if (
copts.peephole &&
IS_INT_CONST(right) &&
pclastblock->pcodeCount > 0 &&
pclastblock->lastPCode->op == PC_RLWINM &&
pclastblock->lastPCode->args[0].data.reg.reg == op1.reg
)
{
PCode *pc = pclastblock->lastPCode;
SInt32 a = pc->args[2].data.imm.value;
SInt32 b = pc->args[3].data.imm.value;
SInt32 value = right->data.intval.lo;
if (b == pc->args[4].data.imm.value) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
if (value != (value & 1)) {
emitpcode(PC_LI, finalReg, -1);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (value == 0) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM, tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
emitpcode(PC_NEG, finalReg, tmpReg);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (value == 1) {
tmpReg = ALLOC_GPR();
emitpcode(
PC_RLWINM, tmpReg,
pc->args[1].data.reg.reg,
(a + b + 1) & 31, 31, 31);
emitpcode(PC_ADDI, finalReg, tmpReg, 0, -1);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
CError_FATAL(6031);
}
}
if (IS_INT_CONST_ZERO(right)) {
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBFIC, tmpReg, op1.reg, 0);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg, tmpReg);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_NEG, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_OR, tmpReg2, tmpReg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, tmpReg2, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (copts.optimizesize) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBFIC, tmpReg2, tmpReg, 0);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg2, tmpReg2);
} else {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg2, op1.reg, op2.reg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_OR, tmpReg3, tmpReg, tmpReg2);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, tmpReg3, 31);
}
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
case EGREATEREQU:
if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg, op1.reg, 1, 31, 31);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_ADDI, finalReg, tmpReg, 0, -1);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
opTmp = op2;
op2 = op1;
op1 = opTmp;
case ELESSEQU:
if (is_unsigned(left->rtype)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg, op1.reg, op2.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ADDZE, tmpReg2, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBF, finalReg, tmpReg2, op1.reg);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_NEG, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ORC, tmpReg2, op1.reg, tmpReg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, tmpReg2, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
tmpReg = ALLOC_GPR();
emitpcode(PC_XORIS, tmpReg, op1.reg, 0x8000);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_SUBF, tmpReg2, op1.reg, op2.reg);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_ADDC, tmpReg3, tmpReg2, tmpReg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg3, tmpReg3);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
case EGREATER:
if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_NEG, tmpReg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_ANDC, tmpReg2, tmpReg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, tmpReg2, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
opTmp = op2;
op2 = op1;
op1 = opTmp;
case ELESS:
if (is_unsigned(left->rtype)) {
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg, op2.reg, op1.reg);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg, tmpReg);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
if (IS_INT_CONST_ZERO(right)) {
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SRAWI, finalReg, op1.reg, 31);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
}
tmpReg = ALLOC_GPR();
emitpcode(PC_SUBFC, tmpReg, op2.reg, op1.reg);
tmpReg2 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg2, op2.reg, 1, 31, 31);
tmpReg3 = ALLOC_GPR();
emitpcode(PC_RLWINM, tmpReg3, op1.reg, 1, 31, 31);
finalReg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_SUBFE, finalReg, tmpReg3, tmpReg2);
output->optype = OpndType_GPR;
output->reg = finalReg;
return;
default:
CError_FATAL(6240);
}
}
void compare_floating(short nt, ENode *left, ENode *right, Operand *output) {
Operand opleft;
Operand opright;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE_TO_FPR(right, &opright, right->rtype, 0);
GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0);
} else {
GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0);
GEN_NODE_TO_FPR(right, &opright, right->rtype, 0);
}
emitpcode((nt == EEQU || nt == ENOTEQU) ? PC_FCMPU : PC_FCMPO, 0, opleft.reg, opright.reg);
if (nt == ELESSEQU) {
emitpcode(PC_CROR, 0, 2, 0, 0, 0, 2);
nt = EEQU;
} else if (nt == EGREATEREQU) {
emitpcode(PC_CROR, 0, 2, 0, 1, 0, 2);
nt = EEQU;
}
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = nt;
}
void compare_integer(short nt, ENode *left, ENode *right, Operand *output) {
Operand opleft;
Operand opright;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
if (right->rtype->size < 4)
extend32(&opright, right->rtype, 0);
ENSURE_GPR(&opright, right->rtype, 0);
GEN_NODE(left, &opleft);
if (left->rtype->size < 4)
extend32(&opleft, left->rtype, 0);
ENSURE_GPR(&opleft, left->rtype, 0);
} else {
GEN_NODE(left, &opleft);
ENSURE_GPR(&opleft, left->rtype, 0);
if (left->rtype->size < 4)
extend32(&opleft, left->rtype, 0);
GEN_NODE(right, &opright);
if (right->rtype->size < 4)
extend32(&opright, right->rtype, 0);
ENSURE_GPR(&opright, right->rtype, 0);
}
emitpcode(is_unsigned(left->rtype) ? PC_CMPL : PC_CMP, 0, opleft.reg, opright.reg);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = nt;
}
void compare_immediate(short nt, ENode *left, SInt32 value, Operand *output) {
int postIncFlag;
short postIncReg;
Operand op;
SInt32 postIncValue;
memclrw(&op, sizeof(Operand));
postIncFlag = ispostincrementopportunity(left, &op, &postIncValue);
if (!postIncFlag) {
GEN_NODE(left, &op);
if (op.optype != OpndType_CRField) {
if (left->rtype->size < 4)
extend32(&op, left->rtype, 0);
else
ENSURE_GPR(&op, left->rtype, 0);
}
} else {
postIncReg = op.reg;
if (left->rtype->size < 4)
extend32(&op, left->rtype, 0);
ENSURE_GPR(&op, left->rtype, 0);
}
if (op.optype == OpndType_CRField) {
if (
(nt == EEQU && value == 1) ||
(nt == ENOTEQU && value == 0) ||
(nt == EGREATER && value == 0) ||
(nt == EGREATEREQU && value == 1)
)
{
*output = op;
return;
}
if (
(nt == EEQU && value == 0) ||
(nt == ENOTEQU && value == 1) ||
(nt == ELESS && value == 1) ||
(nt == ELESSEQU && value == 0)
)
{
*output = op;
switch (op.regOffset) {
case EEQU:
output->regOffset = ENOTEQU;
return;
case ENOTEQU:
output->regOffset = EEQU;
return;
case ELESS:
output->regOffset = EGREATEREQU;
return;
case EGREATER:
output->regOffset = ELESSEQU;
return;
case ELESSEQU:
output->regOffset = EGREATER;
return;
case EGREATEREQU:
output->regOffset = ELESS;
return;
}
}
ENSURE_GPR(&op, left->rtype, 0);
}
if (
copts.peephole &&
value == 0 &&
pclastblock->pcodeCount > 0 &&
pclastblock->lastPCode->op != PC_RLWINM &&
(PCODE_FLAG_SET_F(pclastblock->lastPCode) & (fIsMove | fSideEffects | fCanSetRecordBit | fOpTypeGPR)) == (fCanSetRecordBit | fOpTypeGPR) &&
pclastblock->lastPCode->args[0].data.reg.reg == op.reg &&
(!is_unsigned(left->rtype) || nt == EEQU || nt == ENOTEQU)
)
{
pcsetrecordbit(pclastblock->lastPCode);
} else {
emitpcode(is_unsigned(left->rtype) ? PC_CMPLI : PC_CMPI, 0, op.reg, value);
}
if (postIncFlag)
add_register_immediate(postIncReg, postIncReg, postIncValue);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = nt;
}
void compare_immediate_long(short nt, ENode *left, SInt32 value, Operand *output) {
int postIncFlag;
short postIncReg;
int outputReg;
Operand op;
SInt32 postIncValue;
memclrw(&op, sizeof(Operand));
postIncFlag = ispostincrementopportunity(left, &op, &postIncValue);
if (!postIncFlag) {
GEN_NODE(left, &op);
} else {
postIncReg = op.reg;
}
if (left->rtype->size < 4)
extend32(&op, left->rtype, 0);
ENSURE_GPR(&op, left->rtype, 0);
outputReg = ALLOC_GPR();
emitpcode(PC_ADDIS, outputReg, op.reg, 0, (SInt16) (~(value >> 16) + 1));
emitpcode(PC_CMPLI, 0, outputReg, value & 0xFFFF);
if (postIncFlag)
add_register_immediate(postIncReg, postIncReg, postIncValue);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = nt;
}
static int ismask(SInt32 value, short *first, short *last) {
int start, end, bit;
start = end = -1;
for (bit = 31; bit >= 0; bit--) {
if (value & 1) {
if (start != -1)
return 0;
if (end == -1)
end = bit;
} else {
if (end != -1 && start == -1)
start = bit + 1;
}
value >>= 1;
}
if (end == -1)
return 0;
if (start == -1)
start = 0;
*first = start;
*last = end;
return 1;
}
int ismaskconstant(SInt32 value, short *first, short *last) {
short my_first;
short my_last;
if (ismask(value, first, last))
return 1;
if (value && ismask(~value, &my_first, &my_last)) {
*first = my_last + 1;
*last = my_first - 1;
return 1;
} else {
return 0;
}
}
static void shift_and_mask(ENode *expr, short a, short b, short c, short outputReg, Operand *output) {
Operand op;
int reg;
memclrw(&op, sizeof(Operand));
GEN_NODE(expr, &op);
ENSURE_GPR(&op, expr->rtype, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_RLWINM, reg, op.reg, a, b, c);
output->optype = OpndType_GPR;
output->reg = reg;
}
int ispostincrementopportunity(ENode *expr, Operand *op, SInt32 *value) {
Type *type;
int reg;
type = expr->rtype;
if (!ENODE_IS2(expr, EPOSTINC, EPOSTDEC))
return 0;
if (!ENODE_IS(expr->data.monadic, EINDIRECT))
return 0;
if (!ENODE_IS(expr->data.monadic->data.monadic, EOBJREF))
return 0;
reg = OBJECT_REG(expr->data.monadic->data.monadic->data.objref);
if (!reg)
return 0;
if (IS_TYPE_POINTER(type)) {
if (ENODE_IS(expr, EPOSTINC))
*value = TPTR_TARGET(type)->size;
else
*value = -TPTR_TARGET(type)->size;
} else {
if (ENODE_IS(expr, EPOSTINC))
*value = 1;
else
*value = -1;
}
op->optype = OpndType_GPR;
op->reg = reg;
return 1;
}
void add_register_immediate(short regA, short regB, SInt32 value) {
if (!FITS_IN_SHORT(value)) {
emitpcode(PC_ADDIS, regA, regB, 0, HIGH_PART(value));
if (LOW_PART(value))
emitpcode(PC_ADDI, regA, regA, 0, LOW_PART(value));
} else {
emitpcode(PC_ADDI, regA, regB, 0, value);
}
}
static int ispowerof2(SInt32 val) {
int bit = getbit(val);
return (bit > 0 && bit < 31) ? bit : 0;
}
void I8_gen_ADD(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
int is_uns;
ENode *left;
ENode *right;
short reg;
short regHi;
short tmpreg1;
short tmpreg2;
SInt32 skipleft;
SInt32 skipright;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
} else {
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
}
reg = ALLOC_GPR();
regHi = ALLOC_GPR();
is_uns = is_unsigned(expr->rtype) != 0;
skipleft = GetSizeSkip(left);
skipright = GetSizeSkip(right);
if (skipleft < skipright) {
Operand tmpop;
SInt32 tmp;
expr->data.diadic.left = right;
expr->data.diadic.right = left;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
tmpop = opright;
opright = opleft;
opleft = tmpop;
tmp = skipleft;
skipleft = skipright;
skipright = tmp;
}
switch (skipleft + skipright) {
case 1 + 1:
case 1 + 2:
case 2 + 2:
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_ADDIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo)) {
emitpcode(PC_ADDIC, reg, opleft.reg, LOW_PART(right->data.intval.lo));
} else {
emitpcode(PC_ADDC, reg, opleft.reg, opright.reg);
}
if (is_uns)
emitpcode(PC_LI, regHi, 0);
else
emitpcode(PC_SRAWI, regHi, reg, 31);
break;
case 1 + 4:
case 2 + 4:
case 4 + 4:
if (!is_uns) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg2, opleft.reg, 31);
emitpcode(PC_SRAWI, tmpreg1, opright.reg, 31);
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_ADDIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo)) {
emitpcode(PC_ADDIC, reg, opleft.reg, LOW_PART(right->data.intval.lo));
} else {
emitpcode(PC_ADDC, reg, opleft.reg, opright.reg);
}
if (is_uns) {
tmpreg1 = ALLOC_GPR();
emitpcode(PC_LI, tmpreg1, 0);
emitpcode(PC_ADDZE, regHi, tmpreg1);
} else {
emitpcode(PC_ADDE, regHi, tmpreg1, tmpreg2);
}
break;
case 1 + 8:
case 2 + 8:
case 4 + 8:
CError_ASSERT(6933, skipleft == 8);
if (!is_uns) {
tmpreg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg2, opright.reg, 31);
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo) && left->data.intval.hi == 0) {
emitpcode(PC_ADDIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo) && right->data.intval.hi == 0) {
emitpcode(PC_ADDIC, reg, opleft.reg, LOW_PART(right->data.intval.lo));
} else {
emitpcode(PC_ADDC, reg, opleft.reg, opright.reg);
}
if (is_uns)
emitpcode(PC_ADDZE, regHi, opleft.regHi);
else
emitpcode(PC_ADDE, regHi, opleft.regHi, tmpreg2);
break;
case 8 + 8:
emitpcode(PC_ADDC, reg, opleft.reg, opright.reg);
emitpcode(PC_ADDE, regHi, opleft.regHi, opright.regHi);
break;
default:
CError_FATAL(6979);
}
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_INTCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
short reg;
short regHi;
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
load_immediate(reg, expr->data.intval.lo);
load_immediate(regHi, expr->data.intval.hi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_SUB(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
int is_uns;
ENode *left;
ENode *right;
short reg;
short regHi;
short tmpreg1;
short tmpreg2;
short tmpreg3;
SInt32 skipleft;
SInt32 skipright;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
} else {
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
}
reg = ALLOC_GPR();
regHi = ALLOC_GPR();
is_uns = is_unsigned(expr->rtype) != 0;
skipleft = GetSizeSkip(left);
skipright = GetSizeSkip(right);
switch (skipleft + skipright) {
case 1 + 1:
case 1 + 2:
case 2 + 2:
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_SUBFIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else {
emitpcode(PC_SUBFC, reg, opright.reg, opleft.reg);
}
if (is_uns)
emitpcode(PC_LI, regHi, 0);
else
emitpcode(PC_SRAWI, regHi, reg, 31);
break;
case 1 + 4:
case 2 + 4:
case 4 + 4:
if (!is_uns) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg2, opleft.reg, 31);
emitpcode(PC_SRAWI, tmpreg1, opright.reg, 31);
}
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_SUBFIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else {
emitpcode(PC_SUBFC, reg, opright.reg, opleft.reg);
}
if (is_uns) {
tmpreg1 = ALLOC_GPR();
emitpcode(PC_LI, tmpreg1, 0);
emitpcode(PC_SUBFZE, regHi, tmpreg1);
} else {
emitpcode(PC_SUBFE, regHi, tmpreg1, tmpreg2);
}
break;
case 1 + 8:
case 2 + 8:
case 4 + 8:
if (skipleft < skipright) {
emitpcode(PC_SUBFC, reg, opright.reg, opleft.reg);
emitpcode(PC_SUBFE, regHi, opright.regHi, opleft.regHi);
} else {
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo) && left->data.intval.hi == 0) {
emitpcode(PC_SUBFIC, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else {
emitpcode(PC_SUBFC, reg, opright.reg, opleft.reg);
}
tmpreg1 = ALLOC_GPR();
emitpcode(PC_LI, tmpreg1, 0);
emitpcode(PC_SUBFE, regHi, tmpreg1, opleft.regHi);
}
break;
case 8 + 8:
emitpcode(PC_SUBFC, reg, opright.reg, opleft.reg);
emitpcode(PC_SUBFE, regHi, opright.regHi, opleft.regHi);
break;
default:
CError_FATAL(7211);
}
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_XOR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
short reg;
short regHi;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
} else {
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
}
CError_ASSERT(7254, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPRPair);
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
emitpcode(PC_XOR, reg, opleft.reg, opright.reg);
emitpcode(PC_XOR, regHi, opleft.regHi, opright.regHi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_OR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
short reg;
short regHi;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
} else {
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
}
CError_ASSERT(7304, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPRPair);
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
emitpcode(PC_OR, reg, opleft.reg, opright.reg);
emitpcode(PC_OR, regHi, opleft.regHi, opright.regHi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_AND(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *left;
ENode *right;
short reg;
short regHi;
Operand opleft;
Operand opright;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
} else {
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
}
CError_ASSERT(7354, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPRPair);
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
emitpcode(PC_AND, reg, opleft.reg, opright.reg);
emitpcode(PC_AND, regHi, opleft.regHi, opright.regHi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
int I8_getbit(UInt64 val) {
switch (val) {
case 0: return -1;
case 1ULL << 0: return 0;
case 1ULL << 1: return 1;
case 1ULL << 2: return 2;
case 1ULL << 3: return 3;
case 1ULL << 4: return 4;
case 1ULL << 5: return 5;
case 1ULL << 6: return 6;
case 1ULL << 7: return 7;
case 1ULL << 8: return 8;
case 1ULL << 9: return 9;
case 1ULL << 10: return 10;
case 1ULL << 11: return 11;
case 1ULL << 12: return 12;
case 1ULL << 13: return 13;
case 1ULL << 14: return 14;
case 1ULL << 15: return 15;
case 1ULL << 16: return 16;
case 1ULL << 17: return 17;
case 1ULL << 18: return 18;
case 1ULL << 19: return 19;
case 1ULL << 20: return 20;
case 1ULL << 21: return 21;
case 1ULL << 22: return 22;
case 1ULL << 23: return 23;
case 1ULL << 24: return 24;
case 1ULL << 25: return 25;
case 1ULL << 26: return 26;
case 1ULL << 27: return 27;
case 1ULL << 28: return 28;
case 1ULL << 29: return 29;
case 1ULL << 30: return 30;
case 1ULL << 31: return 31;
case 1ULL << 32: return 32;
case 1ULL << 33: return 33;
case 1ULL << 34: return 34;
case 1ULL << 35: return 35;
case 1ULL << 36: return 36;
case 1ULL << 37: return 37;
case 1ULL << 38: return 38;
case 1ULL << 39: return 39;
case 1ULL << 40: return 40;
case 1ULL << 41: return 41;
case 1ULL << 42: return 42;
case 1ULL << 43: return 43;
case 1ULL << 44: return 44;
case 1ULL << 45: return 45;
case 1ULL << 46: return 46;
case 1ULL << 47: return 47;
case 1ULL << 48: return 48;
case 1ULL << 49: return 49;
case 1ULL << 50: return 50;
case 1ULL << 51: return 51;
case 1ULL << 52: return 52;
case 1ULL << 53: return 53;
case 1ULL << 54: return 54;
case 1ULL << 55: return 55;
case 1ULL << 56: return 56;
case 1ULL << 57: return 57;
case 1ULL << 58: return 58;
case 1ULL << 59: return 59;
case 1ULL << 60: return 60;
case 1ULL << 61: return 61;
case 1ULL << 62: return 62;
case 1ULL << 63: return 63;
default: return -2;
}
}
int I8_log2n(UInt64 val) {
int bit = I8_getbit(val);
return (bit > 0 && bit < 63) ? bit : 0;
}
void I8_ShiftLeftImmediate(Operand opnd, SInt32 value, int is_unsigned, SInt32 size, short reg, short regHi) {
if (opnd.reg == reg || opnd.regHi == regHi || opnd.reg == regHi || opnd.regHi == reg)
CError_FATAL(7703);
if (value < 32) {
emitpcode(PC_RLWINM, reg, opnd.reg, value, 0, 31 - value);
if (size > 4) {
emitpcode(PC_RLWINM, regHi, opnd.regHi, value, 0, 31 - value);
emitpcode(PC_RLWIMI, regHi, opnd.reg, value, 32 - value, 31);
} else {
emitpcode(PC_RLWINM, regHi, opnd.reg, value, 32 - value, 31);
}
} else if (value <= 63) {
if (value == 32)
emitpcode(PC_MR, regHi, opnd.reg);
else
emitpcode(PC_RLWINM, regHi, opnd.reg, value - 32, 0, 63 - value);
emitpcode(PC_LI, reg, 0);
} else {
CError_FATAL(7732);
}
}
void I8_ShiftRightImmediate(Operand opnd, SInt32 value, int is_unsigned, short reg, short regHi, int unk) {
short tmpreg1;
short tmpreg2;
short tmpreg3;
short tmpreg4;
if (opnd.reg == reg || opnd.regHi == regHi || opnd.reg == regHi || opnd.regHi == reg)
CError_FATAL(7756);
if (value < 32) {
emitpcode(PC_RLWINM, reg, opnd.reg, 32 - value, 0, 31);
emitpcode(PC_RLWIMI, reg, opnd.regHi, 32 - value, 0, value - 1);
if (is_unsigned) {
emitpcode(PC_RLWINM, regHi, opnd.regHi, 32 - value, value, 31);
} else if (unk) {
tmpreg1 = ALLOC_GPR();
emitpcode(PC_MR, tmpreg1, opnd.regHi);
emitpcode(PC_RLWIMI, tmpreg1, opnd.reg, 0, 31 - (value - 1), 31);
emitpcode(PC_SRAWI, regHi, tmpreg1, value);
} else {
emitpcode(PC_SRAWI, regHi, opnd.regHi, value);
}
} else if (value == 32) {
if (is_unsigned) {
emitpcode(PC_MR, reg, opnd.regHi);
emitpcode(PC_LI, regHi, 0);
} else if (unk) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
emitpcode(PC_NEG, tmpreg1, opnd.reg);
emitpcode(PC_OR, tmpreg2, tmpreg1, opnd.reg);
emitpcode(PC_RLWINM, tmpreg3, tmpreg2, 1, 31, 31);
emitpcode(PC_RLWIMI, tmpreg3, opnd.regHi, 0, 0, 0);
emitpcode(PC_MR, reg, opnd.regHi);
emitpcode(PC_SRAWI, regHi, value, 31);
} else {
emitpcode(PC_MR, reg, opnd.regHi);
emitpcode(PC_SRAWI, regHi, opnd.regHi, 31);
}
} else if (value <= 63) {
if (is_unsigned) {
emitpcode(PC_RLWINM, reg, opnd.regHi, 64 - value, value - 32, 31);
emitpcode(PC_LI, regHi, 0);
} else if (unk) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
emitpcode(PC_NEG, tmpreg1, opnd.reg);
emitpcode(PC_OR, tmpreg2, tmpreg1, opnd.reg);
emitpcode(PC_RLWINM, tmpreg3, tmpreg2, 1, 31, 31);
emitpcode(PC_OR, tmpreg4, opnd.regHi, tmpreg3);
emitpcode(PC_SRAWI, regHi, opnd.regHi, 31);
emitpcode(PC_SRAWI, reg, tmpreg4, value - 32);
} else {
emitpcode(PC_SRAWI, reg, opnd.regHi, value - 32);
emitpcode(PC_SRAWI, regHi, opnd.regHi, 31);
}
} else {
CError_FATAL(7866);
}
}
void I8_gen_MUL(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
int is_uns;
ENode *left;
ENode *right;
short reg;
short regHi;
short tmpreg1;
short tmpreg2;
short tmpreg3;
short tmpreg4;
SInt32 skipleft;
SInt32 skipright;
Operand opleft;
Operand opright;
SInt64 leftval;
SInt64 rightval;
int shift;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
skipleft = GetSizeSkip(left);
skipright = GetSizeSkip(right);
if (ENODE_IS(right, EINTCONST) && ENODE_IS(left, EINTCONST))
CError_FATAL(7900);
if (ENODE_IS(left, EINTCONST))
leftval = left->data.intval.lo + (((SInt64) ((skipleft < 8) ? 0 : left->data.intval.hi)) << 32);
if (ENODE_IS(right, EINTCONST))
rightval = right->data.intval.lo + (((SInt64) ((skipright < 8) ? 0 : right->data.intval.hi)) << 32);
if (right->hascall) {
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
if (!(ENODE_IS(left, EINTCONST) && I8_log2n(leftval) > 0)) {
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
}
} else {
if (!(ENODE_IS(left, EINTCONST) && I8_log2n(leftval) > 0)) {
GEN_NODE(left, &opleft);
if (TYPE_IS_8BYTES(left->rtype))
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
else
ENSURE_GPR(&opleft, left->rtype, 0);
}
if (!(ENODE_IS(right, EINTCONST) && I8_log2n(rightval) > 0)) {
GEN_NODE(right, &opright);
if (TYPE_IS_8BYTES(right->rtype))
coerce_to_register_pair(&opright, right->rtype, 0, 0);
else
ENSURE_GPR(&opright, right->rtype, 0);
}
}
is_uns = is_unsigned(expr->rtype) != 0;
if (skipleft < skipright) {
Operand tmpop;
SInt64 tmp64;
SInt32 tmp;
expr->data.diadic.left = right;
expr->data.diadic.right = left;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
tmpop = opright;
opright = opleft;
opleft = tmpop;
tmp64 = leftval;
leftval = rightval;
rightval = tmp64;
tmp = skipleft;
skipleft = skipright;
skipright = tmp;
}
reg = ALLOC_GPR();
regHi = ALLOC_GPR();
if (ENODE_IS(left, EINTCONST) && (shift = I8_log2n(leftval)) > 0) {
I8_ShiftLeftImmediate(opright, shift, is_uns, skipright, reg, regHi);
} else if (ENODE_IS(right, EINTCONST) && (shift = I8_log2n(rightval)) > 0) {
I8_ShiftLeftImmediate(opleft, shift, is_uns, skipleft, reg, regHi);
} else {
switch (skipleft + skipright) {
case 1 + 1:
case 1 + 2:
case 2 + 2:
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_MULLI, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo)) {
emitpcode(PC_MULLI, reg, opleft.reg, LOW_PART(right->data.intval.lo));
} else {
emitpcode(PC_MULLW, reg, opleft.reg, opright.reg);
}
if (is_uns)
emitpcode(PC_LI, regHi, 0);
else
emitpcode(PC_SRAWI, regHi, reg, 31);
break;
case 1 + 4:
case 2 + 4:
case 4 + 4:
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo)) {
emitpcode(PC_MULLI, reg, opright.reg, LOW_PART(left->data.intval.lo));
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo)) {
emitpcode(PC_MULLI, reg, opleft.reg, LOW_PART(right->data.intval.lo));
} else {
emitpcode(PC_MULLW, reg, opleft.reg, opright.reg);
}
if (is_uns)
emitpcode(PC_MULHWU, regHi, opleft.reg, opright.reg);
else
emitpcode(PC_MULHW, regHi, opleft.reg, opright.reg);
break;
case 1 + 8:
case 2 + 8:
case 4 + 8:
CError_ASSERT(8097, skipleft == 8);
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo) && left->data.intval.hi == 0) {
emitpcode(PC_MULLI, reg, opright.reg, LOW_PART(left->data.intval.lo));
if (is_uns)
emitpcode(PC_MULHWU, regHi, opright.reg, opleft.reg);
else
emitpcode(PC_MULHW, regHi, opright.reg, opleft.reg);
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo) && right->data.intval.hi == 0) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
if (is_uns) {
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLI, tmpreg1, opleft.regHi, LOW_PART(right->data.intval.lo));
emitpcode(PC_MULLI, reg, opleft.reg, LOW_PART(right->data.intval.lo));
emitpcode(PC_ADD, regHi, tmpreg2, tmpreg1);
} else {
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg4, opright.reg, 31);
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLI, tmpreg1, opleft.regHi, LOW_PART(right->data.intval.lo));
emitpcode(PC_MULLI, reg, opleft.reg, LOW_PART(right->data.intval.lo));
emitpcode(PC_MULLW, tmpreg3, opleft.reg, tmpreg4);
emitpcode(PC_ADD, regHi, tmpreg2, tmpreg1);
emitpcode(PC_ADD, regHi, regHi, tmpreg3);
}
} else {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
if (is_uns) {
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLW, tmpreg1, opleft.regHi, opright.reg);
emitpcode(PC_MULLW, reg, opleft.reg, opright.reg);
emitpcode(PC_ADD, regHi, tmpreg2, tmpreg1);
} else {
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
emitpcode(PC_SRAWI, tmpreg4, opright.reg, 31);
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLW, tmpreg1, opleft.regHi, opright.reg);
emitpcode(PC_MULLW, reg, opleft.reg, opright.reg);
emitpcode(PC_MULLW, tmpreg3, opleft.reg, tmpreg4);
emitpcode(PC_ADD, regHi, tmpreg2, tmpreg1);
emitpcode(PC_ADD, regHi, regHi, tmpreg3);
}
}
break;
case 8 + 8:
if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT2(left->data.intval.lo) && left->data.intval.hi == 0) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
emitpcode(PC_MULHWU, tmpreg1, opright.reg, opleft.reg);
emitpcode(PC_MULLW, tmpreg2, opright.reg, opleft.regHi);
emitpcode(PC_MULLI, reg, opright.reg, LOW_PART(left->data.intval.lo));
emitpcode(PC_ADD, regHi, tmpreg1, tmpreg2);
} else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT2(right->data.intval.lo) && right->data.intval.hi == 0) {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLW, tmpreg1, opleft.regHi, opright.reg);
emitpcode(PC_MULLI, reg, opleft.reg, LOW_PART(right->data.intval.lo));
emitpcode(PC_ADD, regHi, tmpreg2, tmpreg1);
} else {
tmpreg1 = ALLOC_GPR();
tmpreg2 = ALLOC_GPR();
tmpreg3 = ALLOC_GPR();
tmpreg4 = ALLOC_GPR();
emitpcode(PC_MULHWU, tmpreg2, opleft.reg, opright.reg);
emitpcode(PC_MULLW, tmpreg1, opleft.regHi, opright.reg);
emitpcode(PC_ADD, tmpreg3, tmpreg2, tmpreg1);
emitpcode(PC_MULLW, tmpreg4, opleft.reg, opright.regHi);
emitpcode(PC_MULLW, reg, opleft.reg, opright.reg);
emitpcode(PC_ADD, regHi, tmpreg3, tmpreg4);
}
break;
default:
CError_FATAL(8218);
}
}
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_BINNOT(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Operand op;
int reg;
int regHi;
inner = expr->data.monadic;
memclrw(&op, sizeof(Operand));
GEN_NODE(inner, &op);
coerce_to_register_pair(&op, inner->rtype, 0, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
emitpcode(PC_NOR, reg, op.reg, op.reg);
emitpcode(PC_NOR, regHi, op.regHi, op.regHi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_MONMIN(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Operand op;
int reg;
int regHi;
inner = expr->data.monadic;
memclrw(&op, sizeof(Operand));
GEN_NODE(inner, &op);
coerce_to_register_pair(&op, inner->rtype, 0, 0);
reg = outputReg ? outputReg : ALLOC_GPR();
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
emitpcode(PC_SUBFIC, reg, op.reg, 0);
emitpcode(PC_SUBFZE, regHi, op.regHi);
output->optype = OpndType_GPRPair;
output->reg = reg;
output->regHi = regHi;
}
void I8_gen_ASS(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *type;
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
VarInfo *vi;
type = expr->rtype;
if (ENODE_IS(expr, ECONDASS)) {
left = expr->data.cond.expr1;
if (ENODE_IS(left, EINDIRECT))
left = left->data.monadic;
else
CError_FATAL(8328);
right = expr->data.cond.expr2;
} else {
left = expr->data.diadic.left;
right = expr->data.diadic.right;
}
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
if (ENODE_IS(left, EOBJREF) && OBJECT_REG(left->data.objref)) {
vi = Registers_GetVarInfo(left->data.objref);
GEN_NODE_TO_REG(right, vi->reg, vi->regHi, &opright);
if (vi->rclass != RegClass_GPR) {
CError_FATAL(8348);
} else {
coerce_to_register_pair(&opright, type, vi->reg, vi->regHi);
*output = opright;
}
return;
}
if (TYPE_FITS_IN_REGISTER(type)) {
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
if (ENODE_IS(left, EBITFIELD)) {
CError_FATAL(8376);
} else {
GEN_NODE(left, &opleft);
indirect(&opleft, left);
store_pair(opright.reg, opright.regHi, &opleft, type);
}
output->optype = OpndType_GPRPair;
output->reg = opright.reg;
output->regHi = opright.regHi;
}
}
void I8_gen_POSTINCDEC(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
Type *type;
int flag;
int reg;
int regHi;
Operand op1;
Operand op2;
Operand op3;
inner = expr->data.monadic->data.monadic;
type = expr->rtype;
flag = 0;
memclrw(&op1, sizeof(Operand));
memclrw(&op2, sizeof(Operand));
if (ENODE_IS(inner, EOBJREF) && (reg = OBJECT_REG(inner->data.objref))) {
regHi = Registers_GetVarInfo(inner->data.objref)->regHi;
output->optype = OpndType_GPRPair;
output->reg = (outputReg && outputReg != reg && outputReg != regHi) ? outputReg : ALLOC_GPR();
output->regHi = (outputRegHi && outputRegHi != regHi && outputRegHi != reg) ? outputRegHi : ALLOC_GPR();
emitpcode(PC_MR, output->reg, reg);
emitpcode(PC_MR, output->regHi, regHi);
if (ENODE_IS(expr, EPOSTINC)) {
emitpcode(PC_ADDIC, reg, reg, 1);
emitpcode(PC_ADDME, regHi, regHi);
} else {
emitpcode(PC_ADDIC, reg, reg, -1);
emitpcode(PC_ADDZE, regHi, regHi);
}
return;
}
CError_ASSERT(8446, !ENODE_IS(inner, EBITFIELD));
GEN_NODE(inner, &op1);
indirect(&op1, inner);
op2 = op1;
coerce_to_register_pair(&op2, type, 0, 0);
output->optype = OpndType_GPRPair;
output->reg = ALLOC_GPR();
output->regHi = ALLOC_GPR();
emitpcode(PC_MR, output->reg, op2.reg);
emitpcode(PC_MR, output->regHi, op2.regHi);
reg = ALLOC_GPR();
regHi = ALLOC_GPR();
if (ENODE_IS(expr, EPOSTINC)) {
emitpcode(PC_ADDIC, reg, op2.reg, 1);
emitpcode(PC_ADDZE, regHi, op2.regHi);
} else {
emitpcode(PC_ADDIC, reg, op2.reg, -1);
emitpcode(PC_ADDME, regHi, op2.regHi);
}
store_pair(reg, regHi, &op1, type);
}
void I8_gen_INDIRECT(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
ENode *inner;
VarInfo *vi;
inner = expr->data.monadic;
if (ENODE_IS(inner, EOBJREF) && OBJECT_REG(inner->data.objref)) {
vi = Registers_GetVarInfo(inner->data.objref);
switch (vi->rclass) {
case RegClass_GPR:
output->optype = OpndType_GPRPair;
break;
case RegClass_FPR:
output->optype = OpndType_FPR;
break;
default:
CError_FATAL(8511);
}
output->reg = vi->reg;
output->regHi = vi->regHi;
output->object = NULL;
return;
}
if (ENODE_IS(inner, EBITFIELD)) {
CError_FATAL(8529);
return;
}
GEN_NODE(inner, output);
indirect(output, inner);
}
void I8_gen_condition(ENode *cond, Operand *output, int write_to_gpr) {
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
Operand tmpop;
int reg1;
int reg2;
int reg3;
int reg4;
int reg5;
left = cond->data.diadic.left;
right = cond->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
memclrw(&tmpop, sizeof(Operand));
if (right->hascall) {
GEN_NODE(right, &opright);
if (right->rtype->size < 4)
extend32(&opright, right->rtype, 0);
ENSURE_GPR(&opright, right->rtype, 0);
if (right->rtype->size < 8) {
short tmp = ALLOC_GPR();
if (is_unsigned(right->rtype))
load_immediate(tmp, 0);
else
emitpcode(PC_SRAWI, tmp, opright.reg, 31);
opright.optype = OpndType_GPRPair;
opright.regHi = tmp;
}
GEN_NODE(left, &opleft);
if (left->rtype->size < 4)
extend32(&opleft, left->rtype, 0);
ENSURE_GPR(&opleft, left->rtype, 0);
if (left->rtype->size < 8) {
short tmp = ALLOC_GPR();
// this looks like a bug??? surely this should check left->rtype
if (is_unsigned(right->rtype))
load_immediate(tmp, 0);
else
emitpcode(PC_SRAWI, tmp, opleft.reg, 31);
opleft.optype = OpndType_GPRPair;
opleft.regHi = tmp;
}
} else {
GEN_NODE(left, &opleft);
ENSURE_GPR(&opleft, left->rtype, 0);
if (left->rtype->size < 4)
extend32(&opleft, left->rtype, 0);
if (left->rtype->size < 8) {
short tmp = ALLOC_GPR();
if (is_unsigned(right->rtype))
load_immediate(tmp, 0);
else
emitpcode(PC_SRAWI, tmp, opleft.reg, 31);
opleft.optype = OpndType_GPRPair;
opleft.regHi = tmp;
}
GEN_NODE(right, &opright);
if (right->rtype->size < 4)
extend32(&opright, right->rtype, 0);
ENSURE_GPR(&opright, right->rtype, 0);
if (right->rtype->size < 8) {
short tmp = ALLOC_GPR();
if (is_unsigned(right->rtype))
load_immediate(tmp, 0);
else
emitpcode(PC_SRAWI, tmp, opright.reg, 31);
opright.optype = OpndType_GPRPair;
opright.regHi = tmp;
}
}
CError_ASSERT(8704, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPRPair);
switch (cond->type) {
case EEQU:
case ENOTEQU:
reg1 = ALLOC_GPR();
reg2 = ALLOC_GPR();
emitpcode(PC_XOR, reg1, opleft.reg, opright.reg);
emitpcode(PC_XOR, reg2, opleft.regHi, opright.regHi);
emitpcode(PC_OR, reg2, reg1, reg2);
if (write_to_gpr) {
if (ENODE_IS(cond, EEQU)) {
emitpcode(PC_CNTLZW, reg2, reg2);
emitpcode(PC_RLWINM, reg2, reg2, 27, 5, 31);
} else {
emitpcode(PC_ADDIC, reg1, reg2, -1);
emitpcode(PC_SUBFE, reg2, reg1, reg2);
}
output->optype = OpndType_GPR;
output->reg = reg2;
output->regHi = 0;
} else {
emitpcode(PC_CMPI, 0, reg2, 0);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = cond->type;
}
break;
case EGREATER:
tmpop = opleft;
opleft = opright;
opright = tmpop;
case ELESS:
reg1 = ALLOC_GPR();
reg2 = ALLOC_GPR();
reg3 = ALLOC_GPR();
if (left->rtype != TYPE(&stunsignedlonglong) && right->rtype != TYPE(&stunsignedlonglong)) {
emitpcode(PC_XORIS, reg1, opleft.regHi, 0x8000);
emitpcode(PC_XORIS, reg2, opright.regHi, 0x8000);
reg4 = reg1;
reg5 = reg2;
} else {
reg4 = opleft.regHi;
reg5 = opright.regHi;
}
emitpcode(PC_SUBFC, reg3, opright.reg, opleft.reg);
emitpcode(PC_SUBFE, reg2, reg5, reg4);
emitpcode(PC_SUBFE, reg2, reg1, reg1);
emitpcode(PC_NEG, reg2, reg2);
if (write_to_gpr) {
output->optype = OpndType_GPR;
output->reg = reg2;
output->regHi = 0;
} else {
emitpcode(PC_CMPI, 0, reg2, 0);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = ENOTEQU;
}
break;
case ELESSEQU:
tmpop = opleft;
opleft = opright;
opright = tmpop;
case EGREATEREQU:
reg1 = ALLOC_GPR();
reg2 = ALLOC_GPR();
reg3 = ALLOC_GPR();
if (left->rtype != TYPE(&stunsignedlonglong) && right->rtype != TYPE(&stunsignedlonglong)) {
emitpcode(PC_XORIS, reg1, opleft.regHi, 0x8000);
emitpcode(PC_XORIS, reg2, opright.regHi, 0x8000);
reg4 = reg1;
reg5 = reg2;
} else {
reg4 = opleft.regHi;
reg5 = opright.regHi;
}
emitpcode(PC_SUBFC, reg3, opright.reg, opleft.reg);
emitpcode(PC_SUBFE, reg2, reg5, reg4);
emitpcode(PC_SUBFE, reg2, reg1, reg1);
emitpcode(PC_NEG, reg2, reg2);
if (write_to_gpr) {
emitpcode(PC_SUBFIC, reg2, reg2, 1);
output->optype = OpndType_GPR;
output->reg = reg2;
output->regHi = 0;
} else {
emitpcode(PC_CMPI, 0, reg2, 0);
output->optype = OpndType_CRField;
output->reg = 0;
output->regOffset = EEQU;
}
break;
default:
CError_FATAL(8814);
}
}
void I8_gen_SHL_SHR(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
static UInt32 used_regs[RegClassMax] = {0, 0, 0, 0, (1 << 3) | (1 << 4) | (1 << 5)};
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
int is_uns;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
output->optype = OpndType_GPRPair;
output->reg = ALLOC_GPR();
output->regHi = ALLOC_GPR();
is_uns = is_unsigned(expr->rtype) != 0;
if (ENODE_IS(right, EINTCONST)) {
if (ENODE_IS(expr, ESHL)) {
I8_ShiftLeftImmediate(opleft, right->data.intval.lo, is_uns, expr->rtype->size, output->reg, output->regHi);
} else {
I8_ShiftRightImmediate(opleft, right->data.intval.lo, is_uns, output->reg, output->regHi, 0);
}
return;
}
GEN_NODE(right, &opright);
ENSURE_GPR(&opright, right->rtype, 0);
if (opright.optype == OpndType_GPRPair) {
opright.regHi = 0;
opright.optype = OpndType_GPR;
}
CError_ASSERT(8890, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPR);
if (opleft.regHi != high_reg)
emitpcode(PC_MR, high_reg, opleft.regHi);
if (opleft.reg != low_reg)
emitpcode(PC_MR, low_reg, opleft.reg);
if (opright.reg != 5)
emitpcode(PC_MR, 5, opright.reg);
if (ENODE_IS(expr, ESHR)) {
if (is_unsigned(left->rtype))
branch_subroutine(rt_shr2u, 0, used_regs);
else
branch_subroutine(rt_shr2i, 0, used_regs);
} else if (ENODE_IS(expr, ESHL)) {
branch_subroutine(rt_shl2i, 0, used_regs);
} else {
CError_FATAL(8909);
}
emitpcode(PC_MR, output->reg, low_reg);
emitpcode(PC_MR, output->regHi, high_reg);
}
void I8_gen_DIV_MOD(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
static UInt32 used_regs[RegClassMax] = {0, 0, 0, 0, (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)};
int is_uns;
ENode *left;
ENode *right;
Operand opleft;
Operand opright;
SInt64 constval;
int shift;
left = expr->data.diadic.left;
right = expr->data.diadic.right;
memclrw(&opleft, sizeof(Operand));
memclrw(&opright, sizeof(Operand));
GEN_NODE(left, &opleft);
coerce_to_register_pair(&opleft, left->rtype, 0, 0);
output->optype = OpndType_GPRPair;
output->reg = ALLOC_GPR();
output->regHi = ALLOC_GPR();
is_uns = is_unsigned(expr->rtype) != 0;
if (ENODE_IS(right, EINTCONST))
constval = (((SInt64) right->data.intval.hi) << 32) + right->data.intval.lo;
if (ENODE_IS(right, EINTCONST) && ((shift = I8_log2n(constval)) > 0)) {
CError_ASSERT(8976, opleft.optype == OpndType_GPRPair);
if (ENODE_IS(expr, EDIV)) {
I8_ShiftRightImmediate(opleft, shift, is_uns, output->reg, output->regHi, 1);
if (!is_uns) {
emitpcode(PC_ADDZE, output->reg, output->reg);
emitpcode(PC_ADDZE, output->regHi, output->regHi);
}
} else {
if (is_uns) {
if (shift < 32) {
emitpcode(PC_LI, output->regHi, 0);
emitpcode(PC_RLWINM, output->reg, opleft.reg, 0, 32 - shift, 31);
} else if (shift == 32) {
emitpcode(PC_LI, output->regHi, 0);
emitpcode(PC_MR, output->reg, opleft.reg);
} else if (shift <= 63) {
emitpcode(PC_RLWINM, output->regHi, opleft.regHi, 0, 32 - (shift - 32), 31);
emitpcode(PC_MR, output->reg, opleft.reg);
} else {
CError_FATAL(9018);
}
} else {
short tmpreg1 = ALLOC_GPR();
short tmpreg2 = ALLOC_GPR();
I8_ShiftRightImmediate(opleft, shift, is_uns, output->reg, output->regHi, 1);
emitpcode(PC_ADDZE, output->reg, output->reg);
emitpcode(PC_ADDZE, output->regHi, output->regHi);
I8_ShiftLeftImmediate(*output, shift, is_uns, expr->rtype->size, tmpreg1, tmpreg2);
emitpcode(PC_SUBFC, output->reg, tmpreg1, opleft.reg);
emitpcode(PC_SUBFE, output->regHi, tmpreg2, opleft.regHi);
}
}
return;
}
GEN_NODE(right, &opright);
coerce_to_register_pair(&opright, right->rtype, 0, 0);
CError_ASSERT(9048, opleft.optype == OpndType_GPRPair && opright.optype == OpndType_GPRPair);
if (opleft.regHi != high_reg)
emitpcode(PC_MR, high_reg, opleft.regHi);
if (opleft.reg != low_reg)
emitpcode(PC_MR, low_reg, opleft.reg);
if (opright.regHi != high_reg2)
emitpcode(PC_MR, high_reg2, opright.regHi);
if (opright.reg != low_reg2)
emitpcode(PC_MR, low_reg2, opright.reg);
if (ENODE_IS(expr, EDIV)) {
if (is_unsigned(left->rtype) || is_unsigned(right->rtype))
branch_subroutine(rt_div2u, 0, used_regs);
else
branch_subroutine(rt_div2i, 0, used_regs);
} else if (ENODE_IS(expr, EMODULO)) {
if (is_unsigned(left->rtype) || is_unsigned(right->rtype))
branch_subroutine(rt_mod2u, 0, used_regs);
else
branch_subroutine(rt_mod2i, 0, used_regs);
} else {
CError_FATAL(9074);
}
emitpcode(PC_MR, output->reg, low_reg);
emitpcode(PC_MR, output->regHi, high_reg);
}
void I8_gen_TYPCON(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
Type *dsttype;
ENode *inner;
Type *srctype;
short regHi;
short reg;
static UInt32 used_regs[RegClassMax] = {0, 0, 0, 0, (1 << 3) | (1 << 4)};
static UInt32 used_regs_f1[RegClassMax] = {0, 0, 0, (1 << 1), 0};
inner = expr->data.monadic;
srctype = inner->rtype;
dsttype = expr->rtype;
if (IS_TYPE_VOID(dsttype)) {
GEN_NODE(inner, output);
if (ENODE_IS(inner, EINDIRECT) && (output->flags & OpndFlags_Volatile))
coerce_to_register_pair(output, inner->rtype, 0, 0);
output->optype = OpndType_Absolute;
output->immediate = 0;
return;
}
if (IS_TYPE_INT_OR_ENUM(srctype)) {
if (IS_TYPE_FLOAT(dsttype)) {
GEN_NODE(inner, output);
coerce_to_register_pair(output, srctype, 0, 0);
if (output->regHi != high_reg)
emitpcode(PC_MR, high_reg, output->regHi);
if (output->reg != low_reg)
emitpcode(PC_MR, low_reg, output->reg);
if (is_unsigned(srctype)) {
branch_subroutine(
(dsttype->size == 4) ? rt_cvt_ull_flt : rt_cvt_ull_dbl,
0,
used_regs);
} else {
branch_subroutine(
(dsttype->size == 4) ? rt_cvt_sll_flt : rt_cvt_sll_dbl,
0,
used_regs);
}
output->optype = OpndType_FPR;
output->reg = ALLOC_FPR();
emitpcode(PC_FMR, output->reg, 1);
return;
}
if (srctype->size < dsttype->size) {
CError_ASSERT(9171, TYPE_IS_8BYTES(dsttype));
GEN_NODE(inner, output);
if (srctype->size < 4 &&
!ENODE_IS_INDIRECT_TO(inner, EBITFIELD) &&
!((ENODE_IS_ASSIGN(inner) || ENODE_IS_RANGE(inner, EPOSTINC, EPREDEC)) && ENODE_IS(inner->data.monadic->data.monadic, EBITFIELD))
) {
extend32(output, srctype, outputReg);
}
extend64(output, srctype, outputReg, outputRegHi);
} else {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
if (dsttype->size < srctype->size) {
coerce_to_register_pair(output, srctype, outputReg, outputRegHi);
output->optype = OpndType_GPR;
output->regHi = 0;
}
}
return;
}
if (IS_TYPE_POINTER(srctype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
CError_ASSERT(9200, TYPE_IS_8BYTES(expr->rtype));
GEN_NODE_TO_REG(inner, outputReg, 0, output);
regHi = outputRegHi ? outputRegHi : ALLOC_GPR();
if (regHi == output->reg) {
reg = outputReg ? outputReg : ALLOC_GPR();
emitpcode(PC_MR, reg, output->reg);
output->reg = reg;
}
if (is_unsigned(inner->rtype))
load_immediate(regHi, 0);
else
emitpcode(PC_SRAWI, regHi, output->reg, 31);
output->optype = OpndType_GPRPair;
output->regHi = regHi;
return;
}
if (IS_TYPE_FLOAT(srctype)) {
if (IS_TYPE_FLOAT(dsttype)) {
CError_FATAL(9222);
return;
}
GEN_NODE(inner, output);
ENSURE_FPR(output, srctype, 0);
if (output->reg != 1)
emitpcode(PC_FMR, 1, output->reg);
branch_subroutine(rt_cvt_dbl_usll, 0, used_regs_f1);
output->optype = OpndType_GPRPair;
output->reg = ALLOC_GPR();
output->regHi = ALLOC_GPR();
emitpcode(PC_MR, output->reg, low_reg);
emitpcode(PC_MR, output->regHi, high_reg);
return;
}
if (IS_TYPE_STRUCT(srctype)) {
GEN_NODE_TO_REG(inner, outputReg, 0, output);
if (TYPE_IS_8BYTES(expr->rtype) && dsttype->size == srctype->size) {
coerce_to_register_pair(output, srctype, outputReg, outputRegHi);
} else {
CError_FATAL(9256);
}
return;
}
CError_FATAL(9261);
}
void gen_VECTOR128CONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) {
int gpr;
int vr;
COVCResult result;
vr = outputReg ? outputReg : ALLOC_VR();
if (!canoptimizevectorconst(&expr->data.vector128val, expr->rtype, &result))
CError_FATAL(9282);
if (result.op1 != -1) {
emitpcode(result.op1, vr, result.arg);
output->optype = OpndType_VR;
output->reg = vr;
return;
}
if (result.op2 != -1) {
gpr = ALLOC_GPR();
emitpcode(PC_LI, gpr, result.arg);
emitpcode(result.op2, vr, 0, gpr);
output->optype = OpndType_VR;
output->reg = vr;
return;
}
CError_FATAL(9298);
}