#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.value); 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.value); finalReg = ALLOC_FPR(); if (ENODE_IS(expr, EPOSTINC)) emitpcode((type->size == 4) ? PC_FADDS : PC_FADD, finalReg, b.reg, constReg); else emitpcode((type->size == 4) ? PC_FSUBS : PC_FSUB, finalReg, b.reg, constReg); store_fp(finalReg, &a, type); } } else { if (IS_TYPE_POINTER(type)) { if (ENODE_IS(expr, EPOSTINC)) incval = TPTR_TARGET(type)->size; else incval = -TPTR_TARGET(type)->size; } else { if (ENODE_IS(expr, EPOSTINC)) incval = 1; else incval = -1; } if (ENODE_IS(inner, EOBJREF) && (objReg = OBJECT_REG(inner->data.objref))) { output->optype = OpndType_GPR; output->reg = (outputReg && outputReg != objReg) ? outputReg : ALLOC_GPR(); emitpcode(PC_MR, output->reg, objReg); add_register_immediate(objReg, objReg, incval); } else { if (ENODE_IS(inner, EBITFIELD)) { tbitfield = TYPE_BITFIELD(TPTR_TARGET(inner)); inner = inner->data.monadic; } GEN_NODE(inner, &a); indirect(&a, inner); b = a; ENSURE_GPR(&b, type, 0); if (tbitfield) { c = b; extract_bitfield(&c, tbitfield, 0, &b); } output->optype = OpndType_GPR; output->reg = ALLOC_GPR(); emitpcode(PC_MR, output->reg, b.reg); finalReg = ALLOC_GPR(); add_register_immediate(finalReg, b.reg, incval); if (tbitfield) { insert_bitfield(finalReg, &c, tbitfield); finalReg = c.reg; } store(finalReg, &a, type); } } } void gen_INDIRECT(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *inner; VarInfo *vi; SInt32 postincvalue; Operand op; type = expr->rtype; inner = expr->data.monadic; if (TYPE_IS_8BYTES(type)) { I8_gen_INDIRECT(expr, outputReg, outputRegHi, output); return; } memclrw(&op, sizeof(Operand)); if (ENODE_IS(inner, EOBJREF) && OBJECT_REG(inner->data.objref)) { vi = Registers_GetVarInfo(inner->data.objref); switch (vi->rclass) { case RegClass_GPR: output->optype = OpndType_GPR; break; case RegClass_FPR: output->optype = OpndType_FPR; break; case RegClass_VR: output->optype = OpndType_VR; break; case RegClass_CRFIELD: output->optype = OpndType_CRField; break; default: CError_FATAL(456); } output->reg = vi->reg; output->object = NULL; return; } if (ENODE_IS(inner, EBITFIELD)) { GEN_NODE(inner->data.monadic, &op); indirect(&op, expr); ENSURE_GPR(&op, type, 0); extract_bitfield(&op, TYPE_BITFIELD(inner->rtype), outputReg, output); return; } if (ispostincrementopportunity(inner, &op, &postincvalue) && (TYPE_FITS_IN_REGISTER(type) || IS_TYPE_FLOAT(type) || IS_TYPE_VECTOR(type))) { indirect(&op, expr); *output = op; if (TYPE_FITS_IN_REGISTER(type)) { ENSURE_GPR(output, type, outputReg); } else if (IS_TYPE_FLOAT(type)) { ENSURE_FPR(output, type, outputReg); } else { ENSURE_VR(output, type, outputReg); } add_register_immediate(op.reg, op.reg, postincvalue); return; } GEN_NODE(inner, output); indirect(output, expr); } void gen_MONMIN(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *inner; Type *type; ENode *scan; inner = expr->data.monadic; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_MONMIN(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_FLOAT(type)) { if (ENODE_IS(inner, EADD) && ENODE_IS(inner->data.diadic.left, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FNMADDS : PC_FNMADD, inner->data.diadic.left->data.diadic.left, inner->data.diadic.left->data.diadic.right, inner->data.diadic.right, outputReg, output); } else if (ENODE_IS(inner, EADD) && ENODE_IS(inner->data.diadic.right, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FNMADDS : PC_FNMADD, inner->data.diadic.right->data.diadic.left, inner->data.diadic.right->data.diadic.right, inner->data.diadic.left, outputReg, output); } else if (ENODE_IS(inner, ESUB) && ENODE_IS(inner->data.diadic.left, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FNMSUBS : PC_FNMSUB, inner->data.diadic.left->data.diadic.left, inner->data.diadic.left->data.diadic.right, inner->data.diadic.right, outputReg, output); } else { fp_unary_operator(PC_FNEG, inner, outputReg, output); } return; } scan = inner; while (ENODE_IS(scan, ETYPCON) && IS_TYPE_INT_OR_ENUM(type) && !is_unsigned(type)) scan = scan->data.monadic; switch (scan->type) { case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: case EEQU: case ENOTEQU: if (TYPE_FITS_IN_REGISTER(scan->data.diadic.left->rtype) && TYPE_FITS_IN_REGISTER(scan->data.diadic.right->rtype)) { gen_negated_condition_gpr(scan, output, outputReg); return; } } unary_operator(PC_NEG, inner, outputReg, output); } void gen_BINNOT(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *inner; Type *type; inner = expr->data.monadic; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_BINNOT(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(inner, EAND)) binary_operator(PC_NAND, inner->data.diadic.left, inner->data.diadic.right, outputReg, output); else if (ENODE_IS(inner, EOR)) binary_operator(PC_NOR, inner->data.diadic.left, inner->data.diadic.right, outputReg, output); else if (ENODE_IS(inner, EXOR)) binary_operator(PC_EQV, inner->data.diadic.left, inner->data.diadic.right, outputReg, output); else unary_operator(PC_NOT, inner, outputReg, output); } void gen_FORCELOAD(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *inner; inner = expr->data.monadic; GEN_NODE(inner, output); if (IS_TYPE_FLOAT(inner->rtype)) { ENSURE_FPR(output, inner->rtype, outputReg); } else if (IS_TYPE_VECTOR(inner->rtype)) { ENSURE_VR(output, inner->rtype, outputReg); } else if (TYPE_FITS_IN_REGISTER(inner->rtype)) { if (TYPE_IS_8BYTES(inner->rtype)) coerce_to_register_pair(output, inner->rtype, outputReg, outputRegHi); else ENSURE_GPR(output, inner->rtype, outputReg); } else if (!IS_TYPE_VOID(inner->rtype)) { CError_FATAL(681); } } void gen_MUL(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; Type *type; int tmp; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_MUL(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_FLOAT(type)) { fp_binary_operator((type->size == 4) ? PC_FMULS : PC_FMUL, left, right, outputReg, output); return; } if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) { shift_left_immediate(left, tmp, 0, outputReg, output); } else if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(-right->data.intval.lo))) { shift_left_immediate(left, tmp, 1, outputReg, output); } else if (ENODE_IS(left, EINTCONST) && (tmp = ispowerof2(left->data.intval.lo))) { shift_left_immediate(right, tmp, 0, outputReg, output); } else if (ENODE_IS(left, EINTCONST) && (tmp = ispowerof2(-left->data.intval.lo))) { shift_left_immediate(right, tmp, 1, outputReg, output); } else if (ENODE_IS(right, EINTCONST) && FITS_IN_SHORT(right->data.intval.lo)) { binary_immediate(PC_MULLI, left, right->data.intval.lo, outputReg, output); } else if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT(left->data.intval.lo)) { binary_immediate(PC_MULLI, right, left->data.intval.lo, outputReg, output); } else { binary_operator(PC_MULLW, left, right, outputReg, output); } } struct ms { SInt32 m; int s; }; static void create_signed_magic(SInt32 val, struct ms *output) { // PowerPC CWG page 57-58 int p; UInt32 ad, anc, delta, q1, r1, q2, r2, t; ad = abs(val); t = 0x80000000U + ((UInt32) val >> 31); anc = t - 1 - t % ad; p = 31; q1 = 0x80000000U / anc; r1 = 0x80000000U - q1 * anc; q2 = 0x80000000U / ad; r2 = 0x80000000U - q2 * ad; do { p = p + 1; q1 = 2 * q1; r1 = 2 * r1; if (r1 >= anc) { q1 = q1 + 1; r1 = r1 - anc; } q2 = 2 * q2; r2 = 2 * r2; if (r2 >= ad) { q2 = q2 + 1; r2 = r2 - ad; } delta = ad - r2; } while (q1 < delta || (q1 == delta && r1 == 0)); // after loop output->m = q2 + 1; if (val < 0) output->m = -output->m; output->s = p - 32; } struct mu { UInt32 m; int a; int s; }; static void create_unsigned_magic(UInt32 val, struct mu *output) { // PowerPC CWG page 58-59 int p; UInt32 nc, delta, q1, r1, q2, r2; output->a = 0; nc = - 1 - (-val) % val; p = 31; q1 = 0x80000000U / nc; r1 = 0x80000000U - q1 * nc; q2 = 0x7FFFFFFFU / val; r2 = 0x7FFFFFFFU - q2 * val; do { p = p + 1; if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } if (r2 + 1 >= val - r2) { if (q2 >= 0x7FFFFFFFU) output->a = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - val; } else { if (q2 >= 0x80000000U) output->a = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } delta = val - 1 - r2; } while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0))); output->m = q2 + 1; output->s = p - 32; } void gen_DIV(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; Type *type; int tmp; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_DIV_MOD(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_FLOAT(type)) { fp_binary_operator((type->size == 4) ? PC_FDIVS : PC_FDIV, left, right, outputReg, output); return; } if (is_unsigned(type)) { if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) { shift_right_immediate(left, type, tmp, outputReg, output); } else if (!copts.optimize_for_size && ENODE_IS(right, EINTCONST) && right->data.intval.lo != 1) { SInt32 value; int tmpreg1; int tmpreg2; int tmpreg3; int tmpreg4; int tmpreg5; int tmpreg6; int finalReg; struct mu u_magicoutput; Operand op1; value = right->data.intval.lo; tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); finalReg = outputReg ? outputReg : ALLOC_GPR(); memclrw(&op1, sizeof(Operand)); GEN_NODE(left, &op1); ENSURE_GPR(&op1, left->rtype, 0); tmpreg3 = op1.reg; create_unsigned_magic(value, &u_magicoutput); load_immediate(tmpreg2, u_magicoutput.m); emitpcode(PC_MULHWU, tmpreg1, tmpreg2, tmpreg3); if (u_magicoutput.a == 0) { if (u_magicoutput.s) emitpcode(PC_RLWINM, finalReg, tmpreg1, (32 - u_magicoutput.s) & 31, u_magicoutput.s, 31); else emitpcode(PC_MR, finalReg, tmpreg1); } else if (u_magicoutput.a == 1) { tmpreg4 = ALLOC_GPR(); if (copts.optimizationlevel > 1) { tmpreg5 = ALLOC_GPR(); tmpreg6 = ALLOC_GPR(); } else { tmpreg5 = tmpreg4; tmpreg6 = tmpreg4; } emitpcode(PC_SUBF, tmpreg4, tmpreg1, tmpreg3); emitpcode(PC_RLWINM, tmpreg5, tmpreg4, 31, 1, 31); emitpcode(PC_ADD, tmpreg6, tmpreg5, tmpreg1); emitpcode(PC_RLWINM, finalReg, tmpreg6, (32 - (u_magicoutput.s - 1)) & 31, u_magicoutput.s - 1, 31); } output->optype = OpndType_GPR; output->reg = finalReg; } else { binary_operator(PC_DIVWU, left, right, outputReg, output); } } else { SInt32 value; if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) { signed_divide_by_power_of_2(left, tmp, 0, outputReg, output); } else if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(-right->data.intval.lo))) { signed_divide_by_power_of_2(left, tmp, 1, outputReg, output); } else if (!copts.optimize_for_size && ENODE_IS(right, EINTCONST) && (value = right->data.intval.lo) != 1u && value != -1) { int tmpreg2; int tmpreg3; int tmpreg1; int tmpreg4; int finalReg; struct ms s_magicoutput; Operand op2; value = right->data.intval.lo; tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); tmpreg3 = ALLOC_GPR(); finalReg = outputReg ? outputReg : ALLOC_GPR(); memclrw(&op2, sizeof(Operand)); GEN_NODE(left, &op2); ENSURE_GPR(&op2, left->rtype, 0); tmpreg4 = op2.reg; create_signed_magic(value, &s_magicoutput); load_immediate(tmpreg2, s_magicoutput.m); emitpcode(PC_MULHW, tmpreg1, tmpreg2, tmpreg4); if (value > 0 && s_magicoutput.m < 0) { int t = ALLOC_GPR(); emitpcode(PC_ADD, t, tmpreg1, tmpreg4); tmpreg1 = t; } else if (value < 0 && s_magicoutput.m > 0) { int t = ALLOC_GPR(); emitpcode(PC_SUBF, t, tmpreg4, tmpreg1); tmpreg1 = t; } if (s_magicoutput.s) { int t = ALLOC_GPR(); emitpcode(PC_SRAWI, t, tmpreg1, s_magicoutput.s); tmpreg1 = t; } emitpcode(PC_RLWINM, tmpreg3, tmpreg1, 1, 31, 31); emitpcode(PC_ADD, finalReg, tmpreg1, tmpreg3); output->optype = OpndType_GPR; output->reg = finalReg; } else { binary_operator(PC_DIVW, left, right, outputReg, output); } } } void gen_MODULO(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; int tmp; struct mu u_magicoutput; struct ms s_magicoutput; Operand op1; Operand op2; SInt32 value; left = expr->data.diadic.left; right = expr->data.diadic.right; memclrw(&op1, sizeof(Operand)); memclrw(&op2, sizeof(Operand)); if (TYPE_IS_8BYTES(expr->rtype)) { I8_gen_DIV_MOD(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(right, EINTCONST) && (tmp = ispowerof2(right->data.intval.lo))) { if (is_unsigned(expr->rtype)) shift_and_mask(left, 0, 32 - tmp, 31, outputReg, output); else signed_mod_by_power_of_2(left, tmp, 0, outputReg, output); } else if (!copts.optimize_for_size && ENODE_IS(right, EINTCONST) && (value = right->data.intval.lo) != 1u && value != -1) { GEN_NODE(left, &op1); ENSURE_GPR(&op1, left->rtype, 0); if (is_unsigned(expr->rtype)) { int tmpreg1; int tmpreg2; int tmpreg3; int tmpreg4; int tmpreg5; int tmpreg6; int tmpreg7; int tmpreg8; int finalReg; tmpreg1 = op1.reg; tmpreg2 = ALLOC_GPR(); tmpreg3 = ALLOC_GPR(); tmpreg4 = ALLOC_GPR(); finalReg = outputReg ? outputReg : ALLOC_GPR(); create_unsigned_magic(right->data.intval.lo, &u_magicoutput); load_immediate(tmpreg3, u_magicoutput.m); emitpcode(PC_MULHWU, tmpreg2, tmpreg3, tmpreg1); if (u_magicoutput.a == 0 && u_magicoutput.s != 0) emitpcode(PC_RLWINM, tmpreg2, tmpreg2, (32 - u_magicoutput.s) & 31, u_magicoutput.s, 31); if (u_magicoutput.a == 1) { tmpreg5 = ALLOC_GPR(); if (copts.optimizationlevel > 1) { tmpreg6 = ALLOC_GPR(); tmpreg7 = ALLOC_GPR(); tmpreg8 = ALLOC_GPR(); } else { tmpreg6 = tmpreg5; tmpreg7 = tmpreg5; tmpreg8 = tmpreg5; } emitpcode(PC_SUBF, tmpreg5, tmpreg2, tmpreg1); emitpcode(PC_RLWINM, tmpreg6, tmpreg5, 31, 1, 31); emitpcode(PC_ADD, tmpreg7, tmpreg6, tmpreg2); emitpcode(PC_RLWINM, tmpreg8, tmpreg7, (32 - (u_magicoutput.s - 1)) & 31, u_magicoutput.s - 1, 31); tmpreg2 = tmpreg8; } if (value > 0 && value < 0x7FFF) { emitpcode(PC_MULLI, tmpreg4, tmpreg2, value); } else { GEN_NODE(right, &op2); ENSURE_GPR(&op2, right->rtype, 0); emitpcode(PC_MULLW, tmpreg4, tmpreg2, op2.reg); } emitpcode(PC_SUBF, finalReg, tmpreg4, tmpreg1); output->optype = OpndType_GPR; output->reg = finalReg; } else { int tmpreg1; int tmpreg2; int tmpreg3; int tmpreg4; int tmpreg5; int tmpreg6; int finalReg; tmpreg1 = op1.reg; tmpreg2 = ALLOC_GPR(); tmpreg3 = ALLOC_GPR(); tmpreg4 = ALLOC_GPR(); tmpreg5 = ALLOC_GPR(); tmpreg6 = ALLOC_GPR(); finalReg = outputReg ? outputReg : ALLOC_GPR(); create_signed_magic(right->data.intval.lo, &s_magicoutput); load_immediate(tmpreg3, s_magicoutput.m); emitpcode(PC_MULHW, tmpreg2, tmpreg3, tmpreg1); if (value > 0 && s_magicoutput.m < 0) { int tmp = ALLOC_GPR(); emitpcode(PC_ADD, tmp, tmpreg2, tmpreg1); tmpreg2 = tmp; } else if (value < 0 && s_magicoutput.m > 0) { int tmp = ALLOC_GPR(); emitpcode(PC_SUBF, tmp, tmpreg1, tmpreg2); tmpreg2 = tmp; } if (s_magicoutput.s != 0) { int tmp = ALLOC_GPR(); emitpcode(PC_SRAWI, tmp, tmpreg2, s_magicoutput.s); tmpreg2 = tmp; } emitpcode(PC_RLWINM, tmpreg4, tmpreg2, 1, 31, 31); emitpcode(PC_ADD, tmpreg5, tmpreg2, tmpreg4); if (value < 0x7FFF && value > -0x4000) { emitpcode(PC_MULLI, tmpreg6, tmpreg5, value); } else { GEN_NODE(right, &op2); ENSURE_GPR(&op2, right->rtype, 0); emitpcode(PC_MULLW, tmpreg6, tmpreg5, op2.reg); } emitpcode(PC_SUBF, finalReg, tmpreg6, tmpreg1); output->optype = OpndType_GPR; output->reg = finalReg; } } else { int tmpreg1; int tmpreg2; int finalReg; if (right->hascall) { GEN_NODE(right, &op2); ENSURE_GPR(&op2, right->rtype, 0); GEN_NODE(left, &op1); ENSURE_GPR(&op1, left->rtype, 0); } else { GEN_NODE(left, &op1); ENSURE_GPR(&op1, left->rtype, 0); GEN_NODE(right, &op2); ENSURE_GPR(&op2, right->rtype, 0); } tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); finalReg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(is_unsigned(expr->rtype) ? PC_DIVWU : PC_DIVW, tmpreg1, op1.reg, op2.reg); emitpcode(PC_MULLW, tmpreg2, tmpreg1, op2.reg); emitpcode(PC_SUBF, finalReg, tmpreg2, op1.reg); output->optype = OpndType_GPR; output->reg = finalReg; } } void gen_ADD(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; Type *type; Operand opleft; Operand opright; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; memclrw(&opleft, sizeof(Operand)); memclrw(&opright, sizeof(Operand)); if (TYPE_IS_8BYTES(type)) { I8_gen_ADD(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_FLOAT(type)) { if (ENODE_IS(left, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FMADDS : PC_FMADD, left->data.diadic.left, left->data.diadic.right, right, outputReg, output); } else if (ENODE_IS(right, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FMADDS : PC_FMADD, right->data.diadic.left, right->data.diadic.right, left, outputReg, output); } else { fp_binary_operator( (type->size == 4) ? PC_FADDS : PC_FADD, left, right, outputReg, output); } return; } if (right->hascall) { GEN_NODE(right, &opright); if (opright.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&opright, right->rtype, 0); GEN_NODE(left, &opleft); if (opleft.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&opleft, left->rtype, 0); } else { GEN_NODE(left, &opleft); if (opleft.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&opleft, left->rtype, 0); GEN_NODE(right, &opright); if (opright.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&opright, right->rtype, 0); } if (IS_TYPE_POINTER(expr->rtype)) { if (TYPE_IS_8BYTES(expr->data.diadic.left->rtype)) { opleft.optype = OpndType_GPR; opleft.regHi = 0; } if (TYPE_IS_8BYTES(expr->data.diadic.right->rtype)) { opright.optype = OpndType_GPR; opright.regHi = 0; } } combine(&opleft, &opright, outputReg, output); } void gen_SUB(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; Type *type; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_SUB(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_FLOAT(type)) { if (ENODE_IS(left, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FMSUBS : PC_FMSUB, left->data.diadic.left, left->data.diadic.right, right, outputReg, output); } else if (ENODE_IS(right, EMUL) && copts.fp_contract) { fp_multiply_add( (type->size == 4) ? PC_FNMSUBS : PC_FNMSUB, right->data.diadic.left, right->data.diadic.right, left, outputReg, output); } else { fp_binary_operator( (type->size == 4) ? PC_FSUBS : PC_FSUB, left, right, outputReg, output); } return; } if (ENODE_IS(left, EINTCONST) && FITS_IN_SHORT(left->data.intval.lo)) binary_immediate(PC_SUBFIC, right, left->data.intval.lo, outputReg, output); else binary_operator(PC_SUBF, right, left, outputReg, output); } void gen_SHL(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_SHL_SHR(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(right, EINTCONST)) shift_left_immediate(left, right->data.intval.lo, 0, outputReg, output); else binary_operator(PC_SLW, left, right, outputReg, output); } void gen_SHR(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_SHL_SHR(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(right, EINTCONST)) shift_right_immediate(left, type, right->data.intval.lo, outputReg, output); else binary_operator(is_unsigned(type) ? PC_SRW : PC_SRAW, left, right, outputReg, output); } void gen_AND(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; short first; short last; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_AND(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(right, EINTCONST) && ismaskconstant(right->data.intval.lo, &first, &last)) { if (ENODE_IS(left, ESHL) && ENODE_IS(left->data.diadic.right, EINTCONST) && (int)(left->data.diadic.right->data.intval.lo + last) < 32) { shift_and_mask( left->data.diadic.left, left->data.diadic.right->data.intval.lo, first, last, outputReg, output); } else if (ENODE_IS(left, ESHR) && ENODE_IS(left->data.diadic.right, EINTCONST) && (int)left->data.diadic.right->data.intval.lo <= first && last >= first) { if (left->data.diadic.right->data.intval.lo == 0) shift_and_mask(left->data.diadic.left, 0, first, last, outputReg, output); else shift_and_mask(left->data.diadic.left, 32 - left->data.diadic.right->data.intval.lo, first, last, outputReg, output); } else { shift_and_mask(left, 0, first, last, outputReg, output); } return; } if (ENODE_IS(right, EINTCONST) && FITS_IN_USHORT(right->data.intval.lo)) { binary_immediate(PC_ANDI, left, right->data.intval.lo, outputReg, output); return; } if (ENODE_IS(right, EINTCONST) && FITS_IN_HI_SHORT(right->data.intval.lo)) { binary_immediate(PC_ANDIS, left, right->data.intval.lo >> 16, outputReg, output); return; } if (ENODE_IS(left, EINTCONST) && ismaskconstant(left->data.intval.lo, &first, &last)) { if (ENODE_IS(right, ESHL) && ENODE_IS(right->data.diadic.right, EINTCONST) && (int)(right->data.diadic.right->data.intval.lo + last) < 32) { shift_and_mask( right->data.diadic.left, right->data.diadic.right->data.intval.lo, first, last, outputReg, output); } else if (ENODE_IS(right, ESHR) && ENODE_IS(right->data.diadic.right, EINTCONST) && (int)right->data.diadic.right->data.intval.lo <= first) { if (right->data.diadic.right->data.intval.lo == 0) shift_and_mask(right->data.diadic.left, 0, first, last, outputReg, output); else shift_and_mask(right->data.diadic.left, 32 - right->data.diadic.right->data.intval.lo, first, last, outputReg, output); } else { shift_and_mask(right, 0, first, last, outputReg, output); } return; } if (ENODE_IS(left, EINTCONST) && FITS_IN_USHORT(left->data.intval.lo)) { binary_immediate(PC_ANDI, right, left->data.intval.lo, outputReg, output); return; } if (ENODE_IS(left, EINTCONST) && FITS_IN_HI_SHORT(left->data.intval.lo)) { binary_immediate(PC_ANDIS, right, left->data.intval.lo >> 16, outputReg, output); return; } if (ENODE_IS(right, EBINNOT)) binary_operator(PC_ANDC, left, right->data.monadic, outputReg, output); else if (ENODE_IS(left, EBINNOT)) binary_operator(PC_ANDC, right, left->data.monadic, outputReg, output); else binary_operator(PC_AND, left, right, outputReg, output); } void gen_XOR(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_XOR(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(left, EINTCONST)) or_xor_immediate(PC_XORI, right, left->data.intval.lo, outputReg, output); else if (ENODE_IS(right, EINTCONST)) or_xor_immediate(PC_XORI, left, right->data.intval.lo, outputReg, output); else if (ENODE_IS(right, EBINNOT)) binary_operator(PC_EQV, left, right->data.monadic, outputReg, output); else if (ENODE_IS(left, EBINNOT)) binary_operator(PC_EQV, left->data.monadic, right, outputReg, output); else binary_operator(PC_XOR, left, right, outputReg, output); } void gen_OR(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; left = expr->data.diadic.left; right = expr->data.diadic.right; type = expr->rtype; if (TYPE_IS_8BYTES(type)) { I8_gen_OR(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(left, EINTCONST)) or_xor_immediate(PC_ORI, right, left->data.intval.lo, outputReg, output); else if (ENODE_IS(right, EINTCONST)) or_xor_immediate(PC_ORI, left, right->data.intval.lo, outputReg, output); else if (ENODE_IS(right, EBINNOT)) binary_operator(PC_ORC, left, right->data.monadic, outputReg, output); else if (ENODE_IS(left, EBINNOT)) binary_operator(PC_ORC, right, left->data.monadic, outputReg, output); else binary_operator(PC_OR, left, right, outputReg, output); } void gen_ASS(ENode *expr, short outputReg, short outputRegHi, Operand *output) { Type *type; ENode *left; ENode *right; Operand opleft; Operand opright; Operand op2; VarInfo *vi; SInt32 incval; short align; short align2; type = expr->rtype; if (ENODE_IS(expr, ECONDASS)) { left = expr->data.cond.expr1; if (ENODE_IS(left, EINDIRECT)) { left = left->data.monadic; } else { CError_FATAL(1759); } right = expr->data.cond.expr2; } else { left = expr->data.diadic.left; right = expr->data.diadic.right; } memclrw(&opleft, sizeof(Operand)); memclrw(&opright, sizeof(Operand)); memclrw(&op2, sizeof(Operand)); if (TYPE_IS_8BYTES(type)) { I8_gen_ASS(expr, outputReg, outputRegHi, output); return; } if (ENODE_IS(left, EOBJREF) && OBJECT_REG(left->data.objref)) { vi = Registers_GetVarInfo(left->data.objref); GEN_NODE_TO_REG(right, vi->reg, 0, &opright); switch (vi->rclass) { case RegClass_GPR: ENSURE_GPR(&opright, type, vi->reg); output->optype = OpndType_GPR; break; case RegClass_FPR: ENSURE_FPR(&opright, type, vi->reg); output->optype = OpndType_FPR; break; case RegClass_VR: ENSURE_VR(&opright, type, vi->reg); output->optype = OpndType_VR; break; default: CError_FATAL(1810); } if (opright.reg != vi->reg) { PCodeArg a, b; a.kind = PCOp_REGISTER; a.arg = vi->rclass; a.data.reg.reg = vi->reg; a.data.reg.effect = EffectWrite; b.kind = PCOp_REGISTER; b.arg = vi->rclass; b.data.reg.reg = opright.reg; b.data.reg.effect = EffectRead; appendpcode(pclastblock, makecopyinstruction(&b, &a)); } output->reg = vi->reg; return; } if (IS_TYPE_FLOAT(type)) { GEN_NODE_TO_FPR(right, &opright, right->rtype, 0); if (ispostincrementopportunity(left, &opleft, &incval)) { indirect(&opleft, expr); store_fp(opright.reg, &opleft, type); add_register_immediate(opleft.reg, opleft.reg, incval); } else { GEN_NODE(left, &opleft); indirect(&opleft, expr); store_fp(opright.reg, &opleft, type); } output->optype = OpndType_FPR; output->reg = opright.reg; return; } if (IS_TYPE_VECTOR(type)) { GEN_NODE(right, &opright); if (opright.optype == OpndType_Absolute) ENSURE_VR(&opright, type, 0); else ENSURE_VR(&opright, right->rtype, 0); if (ispostincrementopportunity(left, &opleft, &incval)) { indirect(&opleft, expr); store_v(opright.reg, &opleft, type); add_register_immediate(opleft.reg, opleft.reg, incval); } else { GEN_NODE(left, &opleft); indirect(&opleft, expr); store_v(opright.reg, &opleft, type); } output->optype = OpndType_VR; output->reg = opright.reg; return; } if (TYPE_FITS_IN_REGISTER(type)) { GEN_NODE_TO_GPR(right, &opright, right->rtype, 0); if (ENODE_IS(left, EBITFIELD)) { GEN_NODE(left->data.monadic, &opleft); indirect(&opleft, expr); op2 = opleft; ENSURE_GPR(&op2, type, 0); insert_bitfield(opright.reg, &op2, TYPE_BITFIELD(left->rtype)); store(op2.reg, &opleft, type); if (!expr->ignored) extract_bitfield(&op2, TYPE_BITFIELD(left->rtype), opright.reg, &opleft); } else if (ispostincrementopportunity(left, &opleft, &incval)) { indirect(&opleft, expr); store(opright.reg, &opleft, type); add_register_immediate(opleft.reg, opleft.reg, incval); } else { GEN_NODE(left, &opleft); indirect(&opleft, expr); store(opright.reg, &opleft, type); } output->optype = OpndType_GPR; output->reg = opright.reg; return; } GEN_NODE(right, &opright); GEN_NODE(left, output); indirect(output, expr); if (output->object) { if (output->object->datatype == DLOCAL && (output->object->u.var.info->flags & VarInfoFlag1)) align = CMach_ArgumentAlignment(type); else align = CMach_AllocationAlignment(type, output->object->qual); } else { align = CMach_AllocationAlignment(type, 0); } if (opright.object) { if (opright.object->datatype == DLOCAL && (opright.object->u.var.info->flags & VarInfoFlag1)) align2 = CMach_ArgumentAlignment(type); else align2 = CMach_AllocationAlignment(type, opright.object->qual); } else { align2 = CMach_AllocationAlignment(type, 0); } if (align2 < align) align = align2; move_block(output, &opright, type->size, align); } ENode *evaluate_and_skip_comma(ENode *expr) { Operand op; ENode *inner; memclrw(&op, sizeof(Operand)); while (ENODE_IS(expr, ECOMMA)) { inner = expr->data.diadic.left; GEN_NODE(inner, &op); if (ENODE_IS(inner, EINDIRECT) && (op.flags & OpndFlags_Volatile)) { if (TYPE_FITS_IN_REGISTER_2(inner->rtype)) { ENSURE_GPR(&op, inner->rtype, 0); } else if (IS_TYPE_FLOAT(inner->rtype)) { ENSURE_FPR(&op, inner->rtype, 0); } } expr = expr->data.diadic.right; } return expr; } void gen_COMMA(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; left = expr->data.diadic.left; right = expr->data.diadic.right; GEN_NODE(left, output); if (ENODE_IS(left, EINDIRECT) && (output->flags & OpndFlags_Volatile)) { if (TYPE_FITS_IN_REGISTER_2(left->rtype)) { ENSURE_GPR(output, left->rtype, 0); } else if (IS_TYPE_FLOAT(left->rtype)) { ENSURE_FPR(output, left->rtype, 0); } } GEN_NODE_TO_REG(right, outputReg, 0, output); } void gen_TYPCON(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *inner; Type *srctype; Type *dsttype; inner = expr->data.monadic; srctype = inner->rtype; dsttype = expr->rtype; if (TYPE_IS_8BYTES(srctype) || TYPE_IS_8BYTES(dsttype)) { I8_gen_TYPCON(expr, outputReg, outputRegHi, output); return; } if (IS_TYPE_VOID(dsttype)) { GEN_NODE(inner, output); if (ENODE_IS(inner, EINDIRECT) && (output->flags & OpndFlags_Volatile)) { if (TYPE_FITS_IN_REGISTER_2(srctype)) { ENSURE_GPR(output, srctype, 0); } else if (IS_TYPE_FLOAT(srctype)) { ENSURE_FPR(output, srctype, 0); } } } else if (IS_TYPE_INT_OR_ENUM(srctype)) { if (IS_TYPE_FLOAT(dsttype)) { GEN_NODE(inner, output); if (srctype->size < 4) extend32(output, srctype, 0); ENSURE_GPR(output, srctype, 0); if (is_unsigned(srctype)) convert_unsigned_to_floating(output, dsttype->size == 4, outputReg); else convert_integer_to_floating(output, dsttype->size == 4, outputReg); } else if (IS_TYPE_VECTOR(dsttype)) { GEN_NODE_TO_REG(inner, outputReg, 0, output); ENSURE_VR(output, dsttype, outputReg); } else if ( srctype->size < dsttype->size && !ENODE_IS_INDIRECT_TO(inner, EBITFIELD) && !ENODE_IS_ASSIGN_TO(inner, EBITFIELD) && !(ENODE_IS_RANGE(inner, EPOSTINC, EPREDEC) && ENODE_IS(inner->data.monadic->data.monadic, EBITFIELD)) ) { GEN_NODE(inner, output); extend32(output, srctype, outputReg); } else if (dsttype->size < srctype->size || dsttype->size < 4) { GEN_NODE(inner, output); ENSURE_GPR(output, srctype, 0); extend32(output, dsttype, outputReg); } else { GEN_NODE_TO_REG(inner, outputReg, 0, output); } } else if (IS_TYPE_POINTER(srctype)) { GEN_NODE_TO_REG(inner, outputReg, 0, output); if (dsttype->size < srctype->size) ENSURE_GPR(output, srctype, outputReg); } else if (IS_TYPE_FLOAT(srctype)) { if (IS_TYPE_FLOAT(dsttype)) { GEN_NODE_TO_REG(inner, outputReg, 0, output); ENSURE_FPR(output, srctype, outputReg); if (dsttype->size == 4 && srctype->size != 4) { int tmp = outputReg ? outputReg : ALLOC_FPR(); emitpcode(PC_FRSP, tmp, output->reg); output->optype = OpndType_FPR; output->reg = tmp; } } else if (is_unsigned(dsttype) && dsttype->size == 4) { GEN_NODE_TO_REG(inner, 1, 0, output); ENSURE_FPR(output, srctype, 1); convert_floating_to_unsigned(output, outputReg); } else { GEN_NODE_TO_REG(inner, 0, 0, output); ENSURE_FPR(output, srctype, 0); convert_floating_to_integer(output, outputReg); } } else if (IS_TYPE_VECTOR(srctype) && IS_TYPE_VECTOR(dsttype)) { GEN_NODE_TO_REG(inner, outputReg, 0, output); ENSURE_VR(output, srctype, outputReg); } else if (srctype->size == dsttype->size) { GEN_NODE_TO_REG(inner, outputReg, 0, output); } else { CError_FATAL(2224); } } void gen_BITFIELD(ENode *expr, short outputReg, short outputRegHi, Operand *output) { CError_FATAL(2238); } void gen_INTCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) { if (TYPE_IS_8BYTES(expr->rtype)) { I8_gen_INTCONST(expr, outputReg, outputRegHi, output); return; } output->optype = OpndType_Absolute; output->immediate = CInt64_GetULong(&expr->data.intval); } void gen_FLOATCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) { CError_FATAL(2294); } void gen_STRINGCONST(ENode *expr, short outputReg, short outputRegHi, Operand *output) { CError_FATAL(2308); } static Boolean COND_is_ABS_MatchNodes(ENode *cond, ENode *expr1, ENode *expr2) { if (cond->type != expr1->type || cond->type != expr2->type) return 0; if (!(TYPE_FITS_IN_REGISTER(cond->rtype) && TYPE_FITS_IN_REGISTER(expr1->rtype) && TYPE_FITS_IN_REGISTER(expr2->rtype))) return 0; if (cond->rtype->size != expr1->rtype->size || cond->rtype->size != expr2->rtype->size) return 0; switch (cond->type) { case EOBJREF: if (cond->data.objref != expr1->data.objref || cond->data.objref != expr2->data.objref) return 0; return 1; case EINDIRECT: case ETYPCON: return COND_is_ABS_MatchNodes(cond->data.monadic, expr1->data.monadic, expr2->data.monadic); default: return 0; } } static ENode *COND_is_ABS(ENode *cond, ENode *expr1, ENode *expr2) { ENode *tmp; int parity = 0; while (ENODE_IS(cond, ELOGNOT)) { parity = (parity + 1) & 1; cond = cond->data.monadic; } if (parity) { tmp = expr1; expr1 = expr2; expr2 = tmp; } switch (cond->type) { case ELESS: case ELESSEQU: tmp = expr1; expr1 = expr2; expr2 = tmp; break; case EGREATER: case EGREATEREQU: break; default: return NULL; } if (IS_INT_CONST_ZERO(cond->data.diadic.right)) { cond = cond->data.diadic.left; } else if (IS_INT_CONST_ZERO(cond->data.diadic.left)) { cond = cond->data.diadic.left; tmp = expr1; expr1 = expr2; expr2 = tmp; } else { return NULL; } if (ENODE_IS(expr1, EADD) && ENODE_IS(expr2, ESUB)) { if (COND_is_ABS_MatchNodes(cond, expr1->data.diadic.right, expr2->data.diadic.right)) return expr1; else return NULL; } if (!ENODE_IS(expr2, EMONMIN)) return NULL; expr2 = expr2->data.monadic; if (COND_is_ABS_MatchNodes(cond, expr1, expr2)) return expr1; return NULL; } static int COND_has_const(ENode *expr1, ENode *expr2) { SInt32 diff; int result = 0; if (IS_INT_CONST(expr1)) result += 1; if (IS_INT_CONST(expr2)) result += 2; if (result & 1) { if (IS_INT_CONST_ZERO(expr1)) return 5; } if (result & 2) { if (IS_INT_CONST_ZERO(expr2)) return 6; } if (result == 3) { diff = expr1->data.intval.lo - expr2->data.intval.lo; if (diff == 1 || diff == -1) return 4; } return result; } static Boolean COND_is_COMPARE(ENode *cond, ENode *expr1, ENode *expr2, short outputReg, Operand *output) { SInt32 left; SInt32 right; int parity; int negate; ENodeType nt; while (ENODE_IS(expr1, ETYPCON) && TYPE_FITS_IN_REGISTER(expr1->rtype)) expr1 = expr1->data.monadic; while (ENODE_IS(expr2, ETYPCON) && TYPE_FITS_IN_REGISTER(expr2->rtype)) expr2 = expr2->data.monadic; if (!(ENODE_IS(expr1, EINTCONST) && TYPE_FITS_IN_REGISTER(expr1->rtype) && CInt64_IsInRange(expr1->data.intval, 4))) return 0; if (!(ENODE_IS(expr2, EINTCONST) && TYPE_FITS_IN_REGISTER(expr2->rtype) && CInt64_IsInRange(expr2->data.intval, 4))) return 0; left = CInt64_GetULong(&expr1->data.intval); right = CInt64_GetULong(&expr2->data.intval); parity = 0; negate = 0; switch (left) { case 1: if (right != 0) return 0; break; case 0: parity = 1; if (right == -1) negate = 1; else if (right != 1) return 0; break; case -1: if (right != 0) return 0; negate = 1; break; default: return 0; } while (ENODE_IS(cond, ELOGNOT)) { parity = (parity + 1) & 1; cond = cond->data.monadic; } if (parity) { nt = invert_relop(cond->type); if (nt == cond->type) return 0; cond->type = nt; } if (negate) gen_negated_condition_gpr(cond, output, outputReg); else gen_condition_gpr(cond, output, outputReg); return 1; } void gen_COND(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *cond; ENode *expr1; ENode *expr2; Type *type; PCodeLabel *label1; PCodeLabel *label2; PCodeLabel *label3; Operand op1; Operand op2; int has_const; int reg1; int reg2; int reg3; short align; short max_align; expr1 = expr->data.cond.expr1; expr2 = expr->data.cond.expr2; type = expr->rtype; label1 = makepclabel(); label2 = makepclabel(); label3 = makepclabel(); memclrw(&op1, sizeof(Operand)); memclrw(&op2, sizeof(Operand)); cond = evaluate_and_skip_comma(expr->data.cond.cond); if (TOC_use_fsel(expr)) { ENode *left; ENode *right; ENode *tmp; ENodeType nt; Boolean flag; Operand op; int fneg_reg; int fneg_reg2; int fneg_reg3; int fsel_reg; int final_reg; left = cond->data.diadic.left; right = cond->data.diadic.right; nt = cond->type; flag = 0; memclrw(&op, sizeof(Operand)); switch (nt) { case EGREATEREQU: case EEQU: break; case EGREATER: tmp = left; left = right; right = tmp; case ELESS: case ENOTEQU: tmp = expr1; expr1 = expr2; expr2 = tmp; break; case ELESSEQU: tmp = left; left = right; right = tmp; break; default: CError_FATAL(2780); } if (ENODE_IS(left, EFLOATCONST) && CMach_FloatIsZero(left->data.floatval)) { GEN_NODE(right, &op); ENSURE_FPR(&op, right->rtype, 0); flag = 1; } else if (ENODE_IS(right, EFLOATCONST) && CMach_FloatIsZero(right->data.floatval)) { GEN_NODE(left, &op); ENSURE_FPR(&op, left->rtype, 0); } else { fp_binary_operator((type->size == 4) ? PC_FSUBS : PC_FSUB, left, right, 0, &op); } switch (cond->type) { case EEQU: case ENOTEQU: if (flag) { GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0); GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0); fneg_reg = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg, op.reg); fsel_reg = ALLOC_FPR(); emitpcode(PC_FSEL, fsel_reg, op.reg, op1.reg, op2.reg); final_reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(PC_FSEL, final_reg, fneg_reg, fsel_reg, op2.reg); } else { GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0); GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0); fneg_reg2 = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg2, op.reg); fsel_reg = ALLOC_FPR(); emitpcode(PC_FSEL, fsel_reg, op.reg, op1.reg, op2.reg); final_reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(PC_FSEL, final_reg, fneg_reg2, fsel_reg, op2.reg); } break; case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: GEN_NODE_TO_FPR(expr1, &op1, expr1->rtype, 0); GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0); fneg_reg3 = op.reg; if (flag) { fneg_reg3 = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg3, op.reg); } final_reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(PC_FSEL, final_reg, fneg_reg3, op1.reg, op2.reg); break; default: CError_FATAL(2862); } output->optype = OpndType_FPR; output->reg = final_reg; return; } if (TOC_use_isel(expr, 1)) { Operand isel_op1; Operand isel_op2; ENode *x; ENode *y; ENode *abs_expr; memclrw(&isel_op1, sizeof(Operand)); memclrw(&isel_op2, sizeof(Operand)); if (COND_is_COMPARE(cond, expr1, expr2, outputReg, output)) return; if ((abs_expr = COND_is_ABS(cond, expr1, expr2))) { if (ENODE_IS(expr1, EADD) && ENODE_IS(expr2, ESUB)) { x = expr1->data.diadic.left; y = expr2->data.diadic.right; if (y->hascall) { GEN_NODE(y, &op2); ENSURE_GPR(&op2, y->rtype, 0); GEN_NODE(x, &op1); if (op1.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&op1, x->rtype, 0); } else { GEN_NODE(x, &op1); if (op1.optype >= OpndType_IndirectGPR_ImmOffset) ENSURE_GPR(&op1, x->rtype, 0); GEN_NODE(y, &op2); ENSURE_GPR(&op2, y->rtype, 0); } reg1 = ALLOC_GPR(); emitpcode(PC_SRAWI, reg1, op2.reg, 31); reg2 = ALLOC_GPR(); emitpcode(PC_XOR, reg2, reg1, op2.reg); reg3 = ALLOC_GPR(); emitpcode(PC_SUBF, reg3, reg1, reg2); op2.optype = OpndType_GPR; op2.reg = reg3; combine(&op1, &op2, outputReg, output); } else { GEN_NODE(abs_expr, output); ENSURE_GPR(output, abs_expr->rtype, 0); reg1 = ALLOC_GPR(); emitpcode(PC_SRAWI, reg1, output->reg, 31); reg2 = ALLOC_GPR(); emitpcode(PC_XOR, reg2, reg1, output->reg); reg3 = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_SUBF, reg3, reg1, reg2); op2.optype = OpndType_GPR; op2.reg = reg3; } return; } if ((has_const = COND_has_const(expr1, expr2))) { switch (COND_has_const(expr1, expr2)) { case 0: case 2: break; case 3: case 4: if (has_const == 4) { if (expr1->data.intval.lo < expr2->data.intval.lo) gen_negated_condition_gpr(cond, &isel_op1, 0); else gen_condition_gpr(cond, &isel_op1, 0); GEN_NODE(expr1, &op1); GEN_NODE(expr2, &op2); reg1 = ALLOC_GPR(); ENSURE_GPR(&op2, expr2->rtype, reg1); emitpcode(PC_ADD, reg1, isel_op1.reg, op2.reg); if (outputReg) { emitpcode(PC_MR, reg2 = outputReg, reg1); reg1 = reg2; } output->optype = OpndType_GPR; output->reg = reg1; return; } break; case 5: case 6: gen_negated_condition_gpr(cond, &isel_op1, 0); ENSURE_GPR(&isel_op1, TYPE(&stunsignedint), 0); GEN_NODE(expr1, &op1); GEN_NODE(expr2, &op2); reg1 = outputReg ? outputReg : ALLOC_GPR(); if (op1.optype == OpndType_Absolute && op1.immediate == 0) { ENSURE_GPR(&op2, expr2->rtype, 0); emitpcode(PC_ANDC, reg1, op2.reg, isel_op1.reg); } else if (op2.optype == OpndType_Absolute && op2.immediate == 0) { ENSURE_GPR(&op1, expr1->rtype, 0); emitpcode(PC_AND, reg1, op1.reg, isel_op1.reg); } else { CError_FATAL(3119); } output->optype = OpndType_GPR; output->reg = reg1; return; case 1: reg2 = ALLOC_GPR(); reg1 = reg2; logical_expression_nobranch(cond, 0, &isel_op2); GEN_NODE_TO_REG(expr1, reg1, 0, &op1); ENSURE_GPR(&op1, expr1->rtype, reg1); if (op1.reg != reg1) emitpcode(PC_MR, reg1, op1.reg); branch_conditional(isel_op2.reg, isel_op2.regOffset, 1, label2); branch_label(label1); GEN_NODE_TO_REG(expr2, reg1, 0, &op2); ENSURE_GPR(&op2, expr2->rtype, reg1); if (op2.reg != reg1) emitpcode(PC_MR, reg1, op2.reg); branch_label(label2); if (outputReg) { emitpcode(PC_MR, reg2 = outputReg, reg1); reg1 = reg2; } output->optype = OpndType_GPR; output->reg = reg1; return; default: CError_FATAL(3168); } } reg1 = ALLOC_GPR(); logical_expression_nobranch(cond, 0, &isel_op2); GEN_NODE_TO_REG(expr2, reg1, 0, &op2); ENSURE_GPR(&op2, expr2->rtype, reg1); if (op2.reg != reg1) emitpcode(PC_MR, reg1, op2.reg); branch_conditional(isel_op2.reg, isel_op2.regOffset, 0, label2); branch_label(label1); GEN_NODE_TO_REG(expr1, reg1, 0, &op1); ENSURE_GPR(&op1, expr1->rtype, reg1); if (op1.reg != reg1) emitpcode(PC_MR, reg1, op1.reg); branch_label(label2); if (outputReg) { emitpcode(PC_MR, reg2 = outputReg, reg1); reg1 = reg2; } output->optype = OpndType_GPR; output->reg = reg1; return; } logical_expression(cond, label1, label2, label1); branch_label(label1); if (IS_TYPE_VOID(type) || expr->ignored) { GEN_NODE(expr1, &op1); branch_always(label3); branch_label(label2); GEN_NODE(expr2, &op2); } else if (IS_TYPE_FLOAT(type)) { if (expr1->hascall || expr2->hascall) reg1 = ALLOC_FPR(); else reg1 = outputReg ? outputReg : ALLOC_FPR(); GEN_NODE_TO_REG(expr1, reg1, 0, &op1); ENSURE_FPR(&op1, expr1->rtype, reg1); if (op1.reg != reg1) emitpcode(PC_FMR, reg1, op1.reg); branch_always(label3); branch_label(label2); GEN_NODE_TO_REG(expr2, reg1, 0, &op2); ENSURE_FPR(&op2, expr2->rtype, reg1); if (op2.reg != reg1) emitpcode(PC_FMR, reg1, op2.reg); output->optype = OpndType_FPR; output->reg = reg1; } else if (TYPE_IS_8BYTES(type)) { if (expr1->hascall || expr2->hascall) { reg1 = ALLOC_GPR(); reg3 = ALLOC_GPR(); reg2 = reg3; } else { reg1 = outputReg ? outputReg : ALLOC_GPR(); reg3 = outputRegHi ? outputRegHi : ALLOC_GPR(); reg2 = reg3; } GEN_NODE_TO_REG(expr1, reg1, reg2, &op1); coerce_to_register_pair(&op1, expr1->rtype, reg1, reg2); branch_always(label3); branch_label(label2); GEN_NODE_TO_REG(expr2, reg1, reg2, &op2); coerce_to_register_pair(&op2, expr2->rtype, reg1, reg2); output->optype = OpndType_GPRPair; output->reg = reg1; output->regHi = reg2; } else if (TYPE_FITS_IN_REGISTER(type)) { if (expr1->hascall || expr2->hascall) reg1 = ALLOC_GPR(); else reg1 = outputReg ? outputReg : ALLOC_GPR(); GEN_NODE_TO_REG(expr1, reg1, 0, &op1); ENSURE_GPR(&op1, expr1->rtype, reg1); if (op1.reg != reg1) emitpcode(PC_MR, reg1, op1.reg); branch_always(label3); branch_label(label2); GEN_NODE_TO_REG(expr2, reg1, 0, &op2); ENSURE_GPR(&op2, expr2->rtype, reg1); if (op2.reg != reg1) emitpcode(PC_MR, reg1, op2.reg); output->optype = OpndType_GPR; output->reg = reg1; } else if (IS_TYPE_VECTOR(type)) { if (expr1->hascall || expr2->hascall) reg1 = ALLOC_VR(); else reg1 = outputReg ? outputReg : ALLOC_VR(); GEN_NODE_TO_REG(expr1, reg1, 0, &op1); ENSURE_VR(&op1, expr1->rtype, reg1); if (op1.reg != reg1) emitpcode(PC_VMR, reg1, op1.reg); branch_always(label3); branch_label(label2); GEN_NODE_TO_REG(expr2, reg1, 0, &op2); ENSURE_VR(&op2, expr2->rtype, reg1); if (op2.reg != reg1) emitpcode(PC_VMR, reg1, op2.reg); output->optype = OpndType_VR; output->reg = reg1; } else { symbol_operand(output, maketemporary(type)); indirect(output, NULL); coerce_to_addressable(output); GEN_NODE(expr1, &op1); if (op1.object) { if (op1.object->datatype == DLOCAL && (op1.object->u.var.info->flags & VarInfoFlag1)) align = CMach_ArgumentAlignment(type); else align = CMach_AllocationAlignment(type, op1.object->qual); } else { align = CMach_AllocationAlignment(type, 0); } max_align = CMach_AllocationAlignment(type, 0); if (align > max_align) align = max_align; move_block(output, &op1, type->size, align); branch_always(label3); branch_label(label2); GEN_NODE(expr2, &op2); if (op2.object) { if (op2.object->datatype == DLOCAL && (op2.object->u.var.info->flags & VarInfoFlag1)) align = CMach_ArgumentAlignment(type); else align = CMach_AllocationAlignment(type, op2.object->qual); } else { align = CMach_AllocationAlignment(type, 0); } if (align > max_align) align = max_align; move_block(output, &op2, type->size, align); } branch_label(label3); } static Boolean CONDASS_is_ABS(ENode *cond, ENode *expr1, ENode *expr2) { ENode *inner; int parity = 0; while (ENODE_IS(cond, ELOGNOT)) { parity = (parity + 1) & 1; cond = cond->data.monadic; } if (IS_INT_CONST_ZERO(cond->data.diadic.right)) { inner = cond->data.diadic.left; } else if (IS_INT_CONST_ZERO(cond->data.diadic.left)) { inner = cond->data.diadic.left; parity = (parity + 1) & 1; } else { return 0; } switch (cond->type) { case EGREATER: case EGREATEREQU: if (!parity) return 0; break; case ELESS: case ELESSEQU: if (parity) return 0; break; default: return 0; } if (!ENODE_IS(expr2, EMONMIN)) return 0; expr2 = expr2->data.monadic; if (ENODE_IS(inner, EASS)) { inner = inner->data.diadic.left; if (!ENODE_IS(expr2, EINDIRECT)) return 0; expr2 = expr2->data.monadic; if (!ENODE_IS(expr1, EINDIRECT)) return 0; expr1 = expr1->data.monadic; } return COND_is_ABS_MatchNodes(inner, expr1, expr2); } static int CONDASS_is_OPASS_One(ENode *a, ENode *b, SInt32 *value, ENodeType *nodetype) { Type *type; type = a->rtype; if (!ENODE_IS(a, EINDIRECT)) return 0; a = a->data.monadic; if (!ENODE_IS(a, EOBJREF)) return 0; if (ENODE_IS(b, ETYPCON) && b->rtype == type) b = b->data.monadic; if (b->type != EOR && b->type != EADD && b->type != ESUB) return 0; *nodetype = b->type; if (!IS_INT_CONST(b->data.diadic.right)) return 0; *value = b->data.diadic.right->data.intval.lo; if (*value != 1 && *value != -1) return 0; b = b->data.diadic.left; if (ENODE_IS(b, ETYPCON) && TYPE_FITS_IN_REGISTER(b->rtype)) b = b->data.monadic; if (!ENODE_IS(b, EINDIRECT)) return 0; b = b->data.monadic; if (!ENODE_IS(b, EOBJREF)) return 0; if (a->data.objref == b->data.objref) return 1; return 0; } void gen_CONDASS(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *cond; ENode *expr1; ENode *expr2; Type *type; PCodeLabel *label1; PCodeLabel *label2; Operand op1; Operand op2; Operand op3; int reg1; int reg2; expr1 = expr->data.cond.expr1; expr2 = expr->data.cond.expr2; type = expr->rtype; label1 = makepclabel(); label2 = makepclabel(); memclrw(&op1, sizeof(Operand)); memclrw(&op2, sizeof(Operand)); memclrw(&op3, sizeof(Operand)); cond = evaluate_and_skip_comma(expr->data.cond.cond); if (TOC_use_fsel(expr)) { ENode *left; ENode *right; ENode *tmp; ENodeType nt; Boolean flag; Boolean flag2; Operand op; int tmpreg; int fneg_reg; int fsel_reg; int final_reg; left = cond->data.diadic.left; right = cond->data.diadic.right; nt = cond->type; flag = 0; memclrw(&op, sizeof(Operand)); CError_ASSERT(3704, ENODE_IS(expr1, EINDIRECT)); CError_ASSERT(3705, ENODE_IS(expr1->data.monadic, EOBJREF)); tmpreg = OBJECT_REG(expr1->data.monadic->data.objref); final_reg = outputReg ? tmpreg : ALLOC_FPR(); switch (nt) { case EGREATER: tmp = left; left = right; right = tmp; case ELESS: case ENOTEQU: tmp = expr1; expr1 = expr2; expr2 = tmp; flag2 = 1; break; case ELESSEQU: tmp = left; left = right; right = tmp; flag2 = 0; break; case EGREATEREQU: case EEQU: flag2 = 0; break; default: CError_FATAL(3744); } if (ENODE_IS(left, EFLOATCONST) && CMach_FloatIsZero(left->data.floatval)) { GEN_NODE(right, &op); ENSURE_FPR(&op, right->rtype, 0); flag = 1; } else if (ENODE_IS(right, EFLOATCONST) && CMach_FloatIsZero(right->data.floatval)) { GEN_NODE(left, &op); ENSURE_FPR(&op, left->rtype, 0); } else { fp_binary_operator((type->size == 4) ? PC_FSUBS : PC_FSUB, left, right, 0, &op); } switch (cond->type) { case EEQU: case ENOTEQU: if (flag) { GEN_NODE(expr1, &op1); op3 = op1; ENSURE_FPR(&op1, expr1->rtype, 0); GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0); fneg_reg = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg, op.reg); fsel_reg = ALLOC_FPR(); emitpcode(PC_FSEL, fsel_reg, op.reg, op2.reg, op1.reg); emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, fsel_reg); } else { GEN_NODE(expr1, &op1); op3 = op1; ENSURE_FPR(&op1, expr1->rtype, 0); GEN_NODE_TO_FPR(expr2, &op2, expr2->rtype, 0); fneg_reg = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg, op.reg); fsel_reg = ALLOC_FPR(); emitpcode(PC_FSEL, fsel_reg, op.reg, op2.reg, op1.reg); emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, fsel_reg); } break; case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: GEN_NODE(expr1, &op1); GEN_NODE(expr2, &op2); op3 = flag2 ? op2 : op1; ENSURE_FPR(&op1, expr1->rtype, 0); ENSURE_FPR(&op2, expr2->rtype, 0); fneg_reg = op.reg; if (flag) { fneg_reg = ALLOC_FPR(); emitpcode(PC_FNEG, fneg_reg, op.reg); } emitpcode(PC_FSEL, final_reg, fneg_reg, op2.reg, op1.reg); break; default: CError_FATAL(2862); } if (op3.optype != OpndType_FPR) store_fp(final_reg, &op3, type); output->optype = OpndType_FPR; output->reg = final_reg; return; } if (TOC_use_isel(expr, 1)) { Operand isel_op; ENode *x; ENode *y; ENode *abs_expr; memclrw(&isel_op, sizeof(Operand)); CError_ASSERT(3966, ENODE_IS(expr1, EINDIRECT)); CError_ASSERT(3968, ENODE_IS(expr1->data.monadic, EOBJREF)); if (CONDASS_is_ABS(cond, expr1, expr2)) { if (ENODE_IS(cond->data.diadic.left, EASS)) GEN_NODE(cond->data.diadic.left, &isel_op); else if (ENODE_IS(cond->data.diadic.right, EASS)) GEN_NODE(cond->data.diadic.right, &isel_op); outputReg = OBJECT_REG(expr1->data.monadic->data.objref); CError_ASSERT(3979, outputReg); GEN_NODE(expr1, &op1); op3 = op1; CError_ASSERT(3986, op3.optype == OpndType_GPR && op3.reg == outputReg); ENSURE_GPR(&op1, expr1->rtype, 0); if (expr1->rtype->size < 4) extend32(output, expr1->rtype, op3.reg); reg1 = ALLOC_GPR(); reg2 = ALLOC_GPR(); emitpcode(PC_SRAWI, reg1, op1.reg, 31); emitpcode(PC_XOR, reg2, reg1, op1.reg); emitpcode(PC_SUBF, outputReg, reg1, reg2); output->optype = OpndType_GPR; output->reg = op3.reg; if (expr1->rtype->size < 4) extend32(output, expr1->rtype, op3.reg); return; } } logical_expression(cond, label1, label2, label1); branch_label(label1); gen_ASS(expr, outputReg, outputRegHi, output); branch_label(label2); } void gen_FUNCCALL(ENode *expr, short outputReg, short outputRegHi, Operand *output) { if (is_intrinsic_function_call(expr)) call_intrinsic_function(expr, outputReg, output); else call_function(expr, output); } void gen_OBJREF(ENode *expr, short outputReg, short outputRegHi, Operand *output) { symbol_operand(output, expr->data.objref); } void gen_UNEXPECTED(ENode *expr, short outputReg, short outputRegHi, Operand *output) { CError_FATAL(4160); } static int small(ENode *expr) { Type *type; type = expr->rtype; if (!ENODE_IS(expr, ETYPCON)) return 0; do { expr = expr->data.monadic; } while (ENODE_IS(expr, ETYPCON) && (type = expr->rtype)->size == 4); return IS_TYPE_INT_OR_ENUM(type) && ((type->size < 2) || (type->size == 2 && !is_unsigned(type))); } void binary_operator(Opcode opcode, ENode *left, ENode *right, short outputReg, Operand *output) { Operand opleft; Operand opright; int reg; memclrw(&opleft, sizeof(Operand)); memclrw(&opright, sizeof(Operand)); if (right->hascall) { GEN_NODE_TO_GPR(right, &opright, right->rtype, 0); GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0); } else { GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0); GEN_NODE_TO_GPR(right, &opright, right->rtype, 0); } reg = outputReg ? outputReg : ALLOC_GPR(); if (opcode == PC_MULLW && small(left)) emitpcode(opcode, reg, opright.reg, opleft.reg); else emitpcode(opcode, reg, opleft.reg, opright.reg); output->optype = OpndType_GPR; output->reg = reg; } static void binary_immediate(Opcode opcode, ENode *left, SInt32 value, short outputReg, Operand *output) { Operand opleft; int reg; memclrw(&opleft, sizeof(Operand)); GEN_NODE_TO_GPR(left, &opleft, left->rtype, 0); reg = outputReg ? outputReg : ALLOC_GPR(); if (opcode == PC_MULLI && value == 0) emitpcode(PC_LI, reg, 0); else if (opcode == PC_MULLI && value == 1) emitpcode(PC_MR, reg, opleft.reg); else emitpcode(opcode, reg, opleft.reg, value); output->optype = OpndType_GPR; output->reg = reg; } void unary_operator(Opcode opcode, ENode *expr, short outputReg, Operand *output) { Operand op; int reg; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(opcode, reg, op.reg); output->optype = OpndType_GPR; output->reg = reg; } static void or_xor_immediate(Opcode opcode, ENode *expr, SInt32 value, short outputReg, Operand *output) { Operand op; int reg; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); reg = outputReg ? outputReg : ALLOC_GPR(); if (expr->rtype->size > 2 && value != (value & 0xFFFF)) { if (value & 0xFFFF) { emitpcode((opcode == PC_ORI) ? PC_ORIS : PC_XORIS, reg, op.reg, value >> 16); emitpcode(opcode, reg, reg, value & 0xFFFF); } else { emitpcode((opcode == PC_ORI) ? PC_ORIS : PC_XORIS, reg, op.reg, value >> 16); } } else { emitpcode(opcode, reg, op.reg, value & 0xFFFF); } output->optype = OpndType_GPR; output->reg = reg; } static void shift_left_immediate(ENode *expr, short shift, short negate, short outputReg, Operand *output) { Operand op; int reg; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); if (negate) reg = ALLOC_GPR(); else reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, op.reg, shift & 31, 0, 31 - (shift & 31)); if (negate) { int tmp = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_NEG, tmp, reg); reg = tmp; } output->optype = OpndType_GPR; output->reg = reg; } static void shift_right_immediate(ENode *expr, Type *type, short shift, short outputReg, Operand *output) { Operand op; int reg; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); reg = outputReg ? outputReg : ALLOC_GPR(); if (is_unsigned(type)) emitpcode(PC_RLWINM, reg, op.reg, (32 - (shift & 31)) & 31, (shift & 31) + (32 - (type->size * 8)), 31); else emitpcode(PC_SRAWI, reg, op.reg, shift & 31); output->optype = OpndType_GPR; output->reg = reg; } static void signed_divide_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output) { Operand op; int reg; int tmpreg1; int tmpreg2; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); if (!copts.optimize_for_size && shift == 1) { tmpreg1 = ALLOC_GPR(); emitpcode(PC_RLWINM, tmpreg1, op.reg, 1, 31, 31); tmpreg2 = ALLOC_GPR(); emitpcode(PC_ADD, tmpreg2, tmpreg1, op.reg); reg = (outputReg && !negate) ? outputReg : ALLOC_GPR(); emitpcode(PC_SRAWI, reg, tmpreg2, 1); } else { tmpreg1 = ALLOC_GPR(); emitpcode(PC_SRAWI, tmpreg1, op.reg, shift); reg = (outputReg && !negate) ? outputReg : ALLOC_GPR(); emitpcode(PC_ADDZE, reg, tmpreg1); } if (negate) { int prevreg = reg; reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_NEG, reg, prevreg); } output->optype = OpndType_GPR; output->reg = reg; } static void signed_mod_by_power_of_2(ENode *expr, int shift, int negate, short outputReg, Operand *output) { Operand op; int reg; int tmpreg1; int tmpreg2; int tmpreg3; int tmpreg4; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_GPR(expr, &op, expr->rtype, 0); reg = outputReg ? outputReg : ALLOC_GPR(); tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); tmpreg3 = ALLOC_GPR(); if (shift == 1) { emitpcode(PC_RLWINM, tmpreg1, op.reg, 1, 31, 31); emitpcode(PC_RLWINM, tmpreg2, op.reg, 0, 31, 31); emitpcode(PC_XOR, tmpreg3, tmpreg2, tmpreg1); emitpcode(PC_SUBF, reg, tmpreg1, tmpreg3); } else { tmpreg4 = ALLOC_GPR(); emitpcode(PC_RLWINM, tmpreg1, op.reg, 32 - shift, 0, 31 - (32 - shift)); emitpcode(PC_RLWINM, tmpreg2, op.reg, 1, 31, 31); emitpcode(PC_SUBF, tmpreg3, tmpreg2, tmpreg1); emitpcode(PC_RLWINM, tmpreg4, tmpreg3, shift, 0, 31); emitpcode(PC_ADD, reg, tmpreg4, tmpreg2); } output->optype = OpndType_GPR; output->reg = reg; } static void fp_binary_operator(Opcode opcode, ENode *left, ENode *right, short outputReg, Operand *output) { Operand opleft; Operand opright; int reg; memclrw(&opleft, sizeof(Operand)); memclrw(&opright, sizeof(Operand)); if (right->hascall) { GEN_NODE_TO_FPR(right, &opright, right->rtype, 0); GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0); } else { GEN_NODE_TO_FPR(left, &opleft, left->rtype, 0); GEN_NODE_TO_FPR(right, &opright, right->rtype, 0); } reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(opcode, reg, opleft.reg, opright.reg); output->optype = OpndType_FPR; output->reg = reg; } void fp_unary_operator(Opcode opcode, ENode *expr, short outputReg, Operand *output) { Operand op; int reg; memclrw(&op, sizeof(Operand)); GEN_NODE_TO_FPR(expr, &op, expr->rtype, 0); reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(opcode, reg, op.reg); output->optype = OpndType_FPR; output->reg = reg; } void fp_multiply_add(Opcode opcode, ENode *a, ENode *b, ENode *c, short outputReg, Operand *output) { Operand opA; Operand opB; Operand opC; int reg; memclrw(&opA, sizeof(Operand)); memclrw(&opB, sizeof(Operand)); memclrw(&opC, sizeof(Operand)); if (c->hascall) { GEN_NODE_TO_FPR(c, &opC, c->rtype, 0); if (b->hascall) { GEN_NODE_TO_FPR(b, &opB, b->rtype, 0); GEN_NODE_TO_FPR(a, &opA, a->rtype, 0); } else { GEN_NODE_TO_FPR(a, &opA, a->rtype, 0); GEN_NODE_TO_FPR(b, &opB, b->rtype, 0); } } else { if (b->hascall) { GEN_NODE_TO_FPR(b, &opB, b->rtype, 0); GEN_NODE_TO_FPR(a, &opA, a->rtype, 0); GEN_NODE_TO_FPR(c, &opC, c->rtype, 0); } else { GEN_NODE_TO_FPR(a, &opA, a->rtype, 0); GEN_NODE_TO_FPR(b, &opB, b->rtype, 0); GEN_NODE_TO_FPR(c, &opC, c->rtype, 0); } } reg = outputReg ? outputReg : ALLOC_FPR(); emitpcode(opcode, reg, opA.reg, opB.reg, opC.reg); output->optype = OpndType_FPR; output->reg = reg; } void gen_COMPARE(ENode *expr, short outputReg, short outputRegHi, Operand *output) { expr = evaluate_and_skip_comma(expr); if (TYPE_IS_8BYTES(expr->data.diadic.right->rtype) || TYPE_IS_8BYTES(expr->data.diadic.left->rtype)) I8_gen_condition(expr, output, 1); else gen_condition_gpr(expr, output, outputReg); } void gen_LOGICAL(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *inner; ENodeType op; expr = evaluate_and_skip_comma(expr); inner = evaluate_and_skip_comma(expr->data.monadic); expr->data.monadic = inner; if (ENODE_IS(expr, ELOGNOT) && !ENODE_IS2(inner, ELAND, ELOR)) { op = inner->type; if (ENODE_IS(inner, ELOGNOT)) { switch (inner->data.monadic->type) { case ELOGNOT: case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: case EEQU: case ENOTEQU: case ELAND: case ELOR: GEN_NODE(inner->data.monadic, output); if (expr->data.monadic->rtype->size < 4) extend32(output, expr->data.monadic->rtype, 0); ENSURE_GPR(output, expr->data.monadic->rtype, 0); return; } } if (ENODE_IS(inner, ENOTEQU) && !TYPE_IS_8BYTES(inner->data.diadic.left->rtype) && ENODE_IS(inner->data.diadic.right, EINTCONST) && inner->data.diadic.right->data.intval.lo == 0) { int tmpreg1; int tmpreg2; GEN_NODE(inner->data.diadic.left, output); if (inner->data.diadic.left->rtype->size < 4) extend32(output, inner->data.diadic.left->rtype, 0); ENSURE_GPR(output, inner->data.diadic.left->rtype, 0); tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); CError_ASSERT(4853, output->optype == OpndType_GPR); emitpcode(PC_CNTLZW, tmpreg2, output->reg); emitpcode(PC_RLWINM, tmpreg1, tmpreg2, 27, 5, 31); output->optype = OpndType_GPR; output->reg = tmpreg1; } else { int tmpreg1; int tmpreg2; ENodeType inverted; inverted = invert_relop(op); if (op != inverted && !IS_TYPE_FLOAT(inner->data.diadic.left->rtype)) { inner->type = inverted; gen_COMPARE(inner, 0, 0, output); inner->type = inverted; return; } GEN_NODE(inner, output); if (inner->rtype->size < 4) extend32(output, inner->rtype, 0); ENSURE_GPR(output, inner->rtype, 0); tmpreg1 = ALLOC_GPR(); tmpreg2 = ALLOC_GPR(); CError_ASSERT(4883, output->optype == OpndType_GPR); emitpcode(PC_CNTLZW, tmpreg2, output->reg); emitpcode(PC_RLWINM, tmpreg1, tmpreg2, 27, 5, 31); output->optype = OpndType_GPR; output->reg = tmpreg1; } } else { PCodeLabel *label1; PCodeLabel *label2; int tmpreg; label1 = makepclabel(); label2 = makepclabel(); tmpreg = ALLOC_GPR(); emitpcode(PC_LI, tmpreg, 0); logical_expression(expr, label1, label2, label1); branch_label(label1); emitpcode(PC_LI, tmpreg, 1); branch_label(label2); output->optype = OpndType_GPR; output->reg = tmpreg; } } void gen_NULLCHECK(ENode *expr, short outputReg, short outputRegHi, Operand *output) { ENode *left; ENode *right; Operand opleft; Operand opright; PrecomputedOperand *precomp; int flag; int reg; PCodeLabel *label; left = expr->data.nullcheck.nullcheckexpr; right = expr->data.nullcheck.condexpr; memclrw(&opleft, sizeof(Operand)); memclrw(&opright, sizeof(Operand)); flag = !IS_TYPE_VOID(expr->rtype) && !expr->ignored; GEN_NODE(left, &opleft); if (left->rtype->size < 4) extend32(&opleft, left->rtype, 0); ENSURE_GPR(&opleft, left->rtype, 0); precomp = lalloc(sizeof(PrecomputedOperand)); precomp->precompid = expr->data.nullcheck.precompid; precomp->operand = opleft; precomp->next = precomputedoperands; precomputedoperands = precomp; emitpcode(PC_CMPI, 0, opleft.reg, 0); if (flag) { emitpcode(PC_MR, reg = ALLOC_GPR(), opleft.reg); } label = makepclabel(); branch_conditional(0, EEQU, 1, label); GEN_NODE(right, &opright); precomputedoperands = precomputedoperands->next; if (flag) { ENSURE_GPR(&opright, right->rtype, reg); if (opright.reg != reg) emitpcode(PC_MR, reg, opright.reg); output->optype = OpndType_GPR; output->reg = reg; } branch_label(label); } void gen_PRECOMP(ENode *expr, short outputReg, short outputRegHi, Operand *output) { PrecomputedOperand *precomp; for (precomp = precomputedoperands; precomp; precomp = precomp->next) { if (precomp->precompid == expr->data.precompid) break; } *output = precomp->operand; } void logical_expression(ENode *cond, PCodeLabel *if_true, PCodeLabel *if_false, PCodeLabel *end) { PCodeLabel *label; Operand op; memclrw(&op, sizeof(Operand)); cond = evaluate_and_skip_comma(cond); switch (cond->type) { case ELAND: label = makepclabel(); logical_expression(cond->data.diadic.left, label, if_false, label); branch_label(label); logical_expression(cond->data.diadic.right, if_true, if_false, end); break; case ELOR: label = makepclabel(); logical_expression(cond->data.diadic.left, if_true, label, label); branch_label(label); logical_expression(cond->data.diadic.right, if_true, if_false, end); break; case ELOGNOT: logical_expression(cond->data.monadic, if_false, if_true, end); break; case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: case EEQU: case ENOTEQU: if (TYPE_IS_8BYTES(cond->data.diadic.right->rtype) || TYPE_IS_8BYTES(cond->data.diadic.left->rtype)) I8_gen_condition(cond, &op, 0); else gen_condition(cond, &op); if (end == if_true) branch_conditional(op.reg, op.regOffset, 0, if_false); else branch_conditional(op.reg, op.regOffset, 1, if_true); break; default: CError_FATAL(5160); } } static void logical_expression_nobranch(ENode *cond, Boolean invert, Operand *output) { cond = evaluate_and_skip_comma(cond); switch (cond->type) { case ELOGNOT: logical_expression_nobranch(cond->data.monadic, 1, output); break; case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: case EEQU: case ENOTEQU: if (invert) { ENodeType nt = invert_relop(cond->type); CError_ASSERT(5190, nt != cond->type); cond->type = nt; } if (TYPE_IS_8BYTES(cond->data.diadic.right->rtype) || TYPE_IS_8BYTES(cond->data.diadic.left->rtype)) I8_gen_condition(cond, output, 0); else gen_condition(cond, output); break; default: CError_FATAL(5206); } } static ENodeType invert_relop(ENodeType nt) { switch (nt) { case ELESS: return EGREATEREQU; case EGREATER: return ELESSEQU; case ELESSEQU: return EGREATER; case EGREATEREQU: return ELESS; case EEQU: return ENOTEQU; case ENOTEQU: return EEQU; default: return nt; } } static int reverse_relop(int nt) { switch (nt) { case ELESS: return EGREATER; case EGREATER: return ELESS; case ELESSEQU: return EGREATEREQU; case EGREATEREQU: return ELESSEQU; default: return nt; } } void gen_condition(ENode *cond, Operand *output) { ENode *left; ENode *right; left = cond->data.diadic.left; right = cond->data.diadic.right; if (IS_TYPE_FLOAT(left->rtype)) { compare_floating(cond->type, left, right, output); return; } if (ENODE_IS(right, EINTCONST)) { if (is_unsigned(left->rtype)) { UInt32 val = right->data.intval.lo; if (FITS_IN_USHORT(val)) { compare_immediate(cond->type, left, val, output); return; } else if (ENODE_IS2(cond, EEQU, ENOTEQU)) { compare_immediate_long(cond->type, left, val, output); return; } } else { UInt32 val = right->data.intval.lo; if (FITS_IN_SHORT(val)) { compare_immediate(cond->type, left, val, output); return; } else if (ENODE_IS2(cond, EEQU, ENOTEQU)) { compare_immediate_long(cond->type, left, val, output); return; } } } else if (ENODE_IS(left, EINTCONST)) { if (is_unsigned(right->rtype)) { UInt32 val = left->data.intval.lo; if (FITS_IN_USHORT(val)) { compare_immediate(reverse_relop(cond->type), right, val, output); return; } else if (ENODE_IS2(cond, EEQU, ENOTEQU)) { compare_immediate_long(reverse_relop(cond->type), right, val, output); return; } } else { UInt32 val = left->data.intval.lo; if (FITS_IN_SHORT(val)) { compare_immediate(reverse_relop(cond->type), right, val, output); return; } else if (ENODE_IS2(cond, EEQU, ENOTEQU)) { compare_immediate_long(reverse_relop(cond->type), right, val, output); return; } } } compare_integer(cond->type, left, right, output); } void gen_condition_gpr(ENode *cond, Operand *output, short outputReg) { ENode *left; ENode *right; Operand op1; int tmpReg; int reg; int a; int b; left = cond->data.diadic.left; right = cond->data.diadic.right; memclrw(&op1, sizeof(Operand)); if (!IS_TYPE_FLOAT(left->rtype)) { Operand op2; Operand op3; Operand op4; memclrw(&op2, sizeof(Operand)); memclrw(&op3, sizeof(Operand)); memclrw(&op4, sizeof(Operand)); if (right->hascall) { GEN_NODE(right, &op3); if (!IS_INT_CONST_ZERO(right)) { if (right->rtype->size < 4) extend32(&op3, right->rtype, 0); ENSURE_GPR(&op3, right->rtype, 0); } GEN_NODE(left, &op2); if (left->rtype->size < 4) extend32(&op2, left->rtype, 0); ENSURE_GPR(&op2, left->rtype, 0); } else { GEN_NODE(left, &op2); ENSURE_GPR(&op2, left->rtype, 0); if (left->rtype->size < 4) extend32(&op2, left->rtype, 0); GEN_NODE(right, &op3); if (!IS_INT_CONST_ZERO(right)) { if (right->rtype->size < 4) extend32(&op3, right->rtype, 0); ENSURE_GPR(&op3, right->rtype, 0); } } switch (cond->type) { case EEQU: if ( copts.peephole && IS_INT_CONST(right) && pclastblock->pcodeCount > 0) { PCode *pc = pclastblock->lastPCode; if (pc->op == PC_RLWINM && pc->args[0].data.reg.reg == op2.reg) { SInt32 r6 = pc->args[2].data.imm.value; SInt32 r7 = pc->args[3].data.imm.value; SInt32 r3 = right->data.intval.lo; if (r7 == pc->args[4].data.imm.value) { reg = outputReg ? outputReg : ALLOC_GPR(); if (r3 != (r3 & 1)) { emitpcode(PC_LI, reg, 0); } else if (r3 == 0) { int tmpreg = ALLOC_GPR(); emitpcode( PC_RLWINM, tmpreg, pc->args[1].data.reg.reg, (r6 + r7 + 1) & 31, 31, 31); emitpcode(PC_XORI, reg, tmpreg, 1); } else if (r3 == 1) { emitpcode( PC_RLWINM, reg, pc->args[1].data.reg.reg, (r6 + r7 + 1) & 31, 31, 31); } else { CError_FATAL(5434); } output->optype = OpndType_GPR; output->reg = reg; return; } } } if (IS_INT_CONST_ZERO(right)) { //int tmpReg = ALLOC_GPR(); // affected int tmpReg; int t = used_virtual_registers[RegClass_GPR]; used_virtual_registers[RegClass_GPR]++; emitpcode(PC_CNTLZW, t, op2.reg, 0); tmpReg = t; reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg, 27, 5, 31); output->optype = OpndType_GPR; output->reg = reg; return; } else { int tmpReg, tmpReg2; tmpReg = ALLOC_GPR(); // affected emitpcode(PC_SUBF, tmpReg, op2.reg, op3.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_CNTLZW, tmpReg2, tmpReg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg2, 27, 5, 31); output->optype = OpndType_GPR; output->reg = reg; return; } case ENOTEQU: if ( copts.peephole && IS_INT_CONST(right) && pclastblock->pcodeCount > 0) { PCode *pc = pclastblock->lastPCode; if (pc->op == PC_RLWINM && pc->args[0].data.reg.reg == op2.reg) { SInt32 r6 = pc->args[2].data.imm.value; SInt32 r7 = pc->args[3].data.imm.value; SInt32 r3 = right->data.intval.lo; if (r7 == pc->args[4].data.imm.value) { reg = outputReg ? outputReg : ALLOC_GPR(); if (r3 != (r3 & 1)) { emitpcode(PC_LI, reg, 1); } else if (r3 == 0) { emitpcode( PC_RLWINM, reg, pc->args[1].data.reg.reg, (r6 + r7 + 1) & 31, 31, 31); } else if (r3 == 1) { int tmpreg = ALLOC_GPR(); // affected emitpcode( PC_RLWINM, tmpreg, pc->args[1].data.reg.reg, (r6 + r7 + 1) & 31, 31, 31); emitpcode(PC_XORI, reg, tmpreg, 1); } else { CError_FATAL(5503); } output->optype = OpndType_GPR; output->reg = reg; return; } } } if (IS_INT_CONST_ZERO(right)) { if (copts.optimize_for_size) { int tmpReg; tmpReg = ALLOC_GPR(); // affected emitpcode(PC_ADDIC, tmpReg, op2.reg, -1); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_SUBFE, reg, tmpReg, op2.reg); } else { int tmpReg, tmpReg2; // tmpReg, tmpReg2 swap tmpReg = ALLOC_GPR(); emitpcode(PC_NEG, tmpReg, op2.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_OR, tmpReg2, tmpReg, op2.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg2, 1, 31, 31); } output->optype = OpndType_GPR; output->reg = reg; return; } else { if (copts.optimize_for_size) { int tmpReg, tmpReg2; // tmpReg, tmpReg2 swap tmpReg = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg, op2.reg, op3.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_ADDIC, tmpReg2, tmpReg, -1); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_SUBFE, reg, tmpReg2, tmpReg); } else { int tmpReg, tmpReg2, tmpReg3; // tmpReg, tmpReg2 swap tmpReg = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg, op2.reg, op3.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg2, op3.reg, op2.reg); tmpReg3 = ALLOC_GPR(); emitpcode(PC_OR, tmpReg3, tmpReg, tmpReg2); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg3, 1, 31, 31); } output->optype = OpndType_GPR; output->reg = reg; return; } case EGREATEREQU: if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) { int tmpReg; tmpReg = ALLOC_GPR(); emitpcode(PC_RLWINM, tmpReg, op2.reg, 1, 31, 31); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_XORI, reg, tmpReg, 1); output->optype = OpndType_GPR; output->reg = reg; return; } op4 = op3; op3 = op2; op2 = op4; case ELESSEQU: if (is_unsigned(left->rtype)) { if (copts.optimize_for_size) { int tmpReg, tmpReg2; tmpReg = ALLOC_GPR(); emitpcode(PC_LI, tmpReg, -1); tmpReg2 = ALLOC_GPR(); emitpcode(PC_SUBFC, tmpReg2, op2.reg, op3.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_SUBFZE, reg, tmpReg); } else { int tmpReg, tmpReg2, tmpReg3, tmpReg4; tmpReg = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg, op2.reg, op3.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_ORC, tmpReg2, op3.reg, op2.reg); tmpReg3 = ALLOC_GPR(); emitpcode(PC_RLWINM, tmpReg3, tmpReg, 31, 1, 31); tmpReg4 = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg4, tmpReg3, tmpReg2); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg4, 1, 31, 31); } output->optype = OpndType_GPR; output->reg = reg; return; } if (IS_INT_CONST_ZERO(right)) { int tmpReg, tmpReg2; tmpReg = ALLOC_GPR(); emitpcode(PC_LI, tmpReg, 1); tmpReg2 = ALLOC_GPR(); emitpcode(PC_CNTLZW, tmpReg2, op2.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWNM, reg, tmpReg, tmpReg2, 31, 31); output->optype = OpndType_GPR; output->reg = reg; return; } else { int tmpReg, tmpReg2, tmpReg3; tmpReg2 = ALLOC_GPR(); emitpcode(PC_SRAWI, tmpReg2, op3.reg, 31); tmpReg = ALLOC_GPR(); emitpcode(PC_RLWINM, tmpReg, op2.reg, 1, 31, 31); tmpReg3 = ALLOC_GPR(); emitpcode(PC_SUBFC, tmpReg3, op2.reg, op3.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_ADDE, reg, tmpReg2, tmpReg); output->optype = OpndType_GPR; output->reg = reg; return; } case EGREATER: if (!is_unsigned(left->rtype) && IS_INT_CONST_ZERO(right)) { int tmpReg, tmpReg2; tmpReg = ALLOC_GPR(); emitpcode(PC_NEG, tmpReg, op2.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_ANDC, tmpReg2, tmpReg, op2.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg2, 1, 31, 31); output->optype = OpndType_GPR; output->reg = reg; return; } op4 = op3; op3 = op2; op2 = op4; case ELESS: if (is_unsigned(left->rtype)) { if (left->rtype->size <= 2) { int tmpReg; tmpReg = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg, op3.reg, op2.reg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg, 1, 31, 31); output->optype = OpndType_GPR; output->reg = reg; } else { if (copts.optimize_for_size) { int tmpReg, tmpReg2; tmpReg = ALLOC_GPR(); emitpcode(PC_SUBFC, tmpReg, op3.reg, op2.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_SUBFE, tmpReg2, tmpReg, tmpReg); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_NEG, reg, tmpReg2); } else { int tmpReg, tmpReg2, tmpReg3; tmpReg = ALLOC_GPR(); emitpcode(PC_XOR, tmpReg, op3.reg, op2.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_CNTLZW, tmpReg2, tmpReg); tmpReg3 = ALLOC_GPR(); emitpcode(PC_SLW, tmpReg3, op3.reg, tmpReg2); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg3, 1, 31, 31); } output->optype = OpndType_GPR; output->reg = reg; return; } return; } else { if (IS_INT_CONST_ZERO(right)) { reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, op2.reg, 1, 31, 31); output->optype = OpndType_GPR; output->reg = reg; return; } else { int tmpReg, tmpReg2, tmpReg3, tmpReg4; tmpReg = ALLOC_GPR(); emitpcode(PC_XOR, tmpReg, op3.reg, op2.reg); tmpReg2 = ALLOC_GPR(); emitpcode(PC_SRAWI, tmpReg2, tmpReg, 1); tmpReg3 = ALLOC_GPR(); emitpcode(PC_AND, tmpReg3, tmpReg, op3.reg); tmpReg4 = ALLOC_GPR(); emitpcode(PC_SUBF, tmpReg4, tmpReg3, tmpReg2); reg = outputReg ? outputReg : ALLOC_GPR(); emitpcode(PC_RLWINM, reg, tmpReg4, 1, 31, 31); output->optype = OpndType_GPR; output->reg = reg; return; } } default: CError_FATAL(5777); } } gen_condition(cond, &op1); emitpcode(PC_MFCR, tmpReg = used_virtual_registers[RegClass_GPR]++); a = 0; b = op1.reg * 4; switch (op1.regOffset) { case ENOTEQU: a = 1; case EEQU: b += 2; break; case EGREATEREQU: a = 1; break; case ELESSEQU: a = 1; case EGREATER: b += 1; break; } reg = outputReg ? outputReg : ALLOC_GPR(); if (a) { emitpcode(PC_RLWINM, tmpReg, tmpReg, b + 1, 31, 31); emitpcode(PC_XORI, reg, tmpReg, 1); } else { emitpcode(PC_RLWINM, reg, tmpReg, b + 1, 31, 31); } output->optype = OpndType_GPR; output->reg = reg; } void gen_negated_condition_gpr(ENode *cond, Operand *output, short outputReg) { } 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) { } void compare_immediate_long(short nt, ENode *left, SInt32 value, Operand *output) { } 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) { } void I8_gen_OR(ENode *expr, short outputReg, short outputRegHi, Operand *output) { } void I8_gen_AND(ENode *expr, short outputReg, short outputRegHi, Operand *output) { } 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(8238); 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); }