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