mirror of https://git.wuffs.org/MWCC
5349 lines
176 KiB
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.optimize_for_size && 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.optimize_for_size && 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.optimize_for_size && 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);
|
|
op2.optype = OpndType_GPR;
|
|
op2.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.optimize_for_size && 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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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.optimize_for_size) {
|
|
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);
|
|
}
|