#include "compiler/FunctionCalls.h" #include "compiler/CError.h" #include "compiler/CFunc.h" #include "compiler/CMachine.h" #include "compiler/CParser.h" #include "compiler/CodeGen.h" #include "compiler/CompilerTools.h" #include "compiler/InstrSelection.h" #include "compiler/Operands.h" #include "compiler/PCode.h" #include "compiler/PCodeUtilities.h" #include "compiler/Registers.h" #include "compiler/StackFrame.h" #include "compiler/StructMoves.h" #include "compiler/types.h" enum { AIF_PassInGPR = 1, AIF_PassInFPR = 2, AIF_PassOnStack = 4, AIF_ExtendTo32Bits = 8, AIF_ForceDoublePrecision = 0x10, AIF_PassInVR = 0x20, AIF_PassMask = AIF_PassInGPR | AIF_PassInFPR | AIF_PassOnStack | AIF_PassInVR }; #ifdef __MWERKS__ #pragma options align=mac68k #endif typedef struct ArgInfo { struct ArgInfo *next; ENode *expr; Operand opnd; SInt32 offset; short gpr; short gprHi; short fpr; short vr; short evaluated; short flags; } ArgInfo; #ifdef __MWERKS__ #pragma options align=reset #endif // forward decls static void branch_subroutine_indirect_ctr(Operand *addrOpnd, UInt32 *used_regs); static ArgInfo *make_arginfo(ENode *expr) { ArgInfo *info = lalloc(sizeof(ArgInfo)); memclrw(info, sizeof(ArgInfo)); info->next = NULL; info->expr = expr; info->offset = -1; info->gpr = -1; info->gprHi = -1; info->fpr = -1; info->vr = -1; info->evaluated = 0; info->flags = 0; return info; } static ArgInfo *analyze_arguments(ENode *funcref, ENodeList *arg_expr, FuncArg *arg, UInt32 *used_regs, Boolean *resultHasFloats, char has_varargs) { ArgInfo *infos; ArgInfo *info; SInt32 displ; SInt32 arg_size; int gpr_counter; int fpr_counter; int vr_counter; Type *type; RegClass rclass; Boolean spilledVectorFlag; infos = NULL; displ = 0; gpr_counter = 3; fpr_counter = 1; vr_counter = 2; for (rclass = 0; rclass < RegClassMax; rclass++) used_regs[rclass] = 0; *resultHasFloats = 0; while (arg_expr) { if (arg_expr->node == funcref) { arg_expr = arg_expr->next; arg = arg->next; continue; } type = arg_expr->node->rtype; if (infos) { info->next = make_arginfo(arg_expr->node); info = info->next; } else { infos = info = make_arginfo(arg_expr->node); } arg_size = 0; if (IS_TYPE_VECTOR(type)) { if (arg == &elipsis) { spilledVectorFlag = 1; info->flags |= AIF_PassOnStack; } else { spilledVectorFlag = 0; if (vr_counter <= 13) { info->flags |= AIF_PassInVR; info->vr = vr_counter; used_regs[RegClass_VR] |= 1 << vr_counter; } else { spilledVectorFlag = 1; info->flags |= AIF_PassOnStack; } } if (has_varargs) { if (gpr_counter < 10) { gpr_counter = ((gpr_counter - 2) & ~3) + 5; if (arg == &elipsis && gpr_counter < 10) { info->flags |= AIF_PassInGPR; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= (15 << gpr_counter) & 0x7E0; } gpr_counter += 4; } spilledVectorFlag = 1; } if (spilledVectorFlag) arg_size = 16; vr_counter++; } else if (IS_TYPE_FLOAT(type)) { *resultHasFloats = 1; if (!arg || arg == &oldstyle) { if (fpr_counter <= 13) { info->flags |= AIF_PassInFPR; info->fpr = fpr_counter; used_regs[RegClass_FPR] |= 1 << fpr_counter; } else { info->flags |= AIF_PassOnStack | AIF_ForceDoublePrecision; } arg_size = 8; fpr_counter++; gpr_counter += 2; } else if (arg == &elipsis) { if (gpr_counter < 10) { info->flags |= AIF_PassInGPR; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= 3 << gpr_counter; } else if (gpr_counter == 10) { info->flags |= AIF_PassInGPR | AIF_PassOnStack | AIF_ForceDoublePrecision; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= 3 << gpr_counter; } else { info->flags |= AIF_PassOnStack | AIF_ForceDoublePrecision; } arg_size = 8; fpr_counter++; gpr_counter += 2; } else { if (fpr_counter <= 13) { info->flags |= AIF_PassInFPR; info->fpr = fpr_counter; used_regs[RegClass_FPR] |= 1 << fpr_counter; } else { info->flags |= AIF_PassOnStack; } if (type->size == 4) { arg_size = 4; gpr_counter++; } else { arg_size = 8; gpr_counter += 2; } fpr_counter++; } } else if (TYPE_IS_8BYTES(type)) { if (gpr_counter <= 10) { info->flags |= AIF_PassInGPR; if (copts.littleendian) { info->gpr = gpr_counter; info->gprHi = gpr_counter + 1; } else { info->gpr = gpr_counter + 1; info->gprHi = gpr_counter; } used_regs[RegClass_GPR] |= 1 << gpr_counter; if ((gpr_counter + 1) <= 10) used_regs[RegClass_GPR] |= 1 << (gpr_counter + 1); } else { info->flags |= AIF_PassOnStack; } arg_size = 8; gpr_counter += 2; } else if (TYPE_FITS_IN_REGISTER(type)) { if ((!arg || arg == &elipsis || arg == &oldstyle) && type->size < 4) info->flags |= AIF_ExtendTo32Bits; if (gpr_counter <= 10) { info->flags |= AIF_PassInGPR; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= 1 << gpr_counter; } else { info->flags |= AIF_PassOnStack; } arg_size = 4; gpr_counter++; } else if (IS_TYPE_ARRAY(type) || IS_TYPE_NONVECTOR_STRUCT(type) || IS_TYPE_CLASS(type) || IS_TYPE_12BYTES_MEMBERPOINTER(type)) { SInt32 gprs_needed = (type->size >> 2) + ((type->size & 3) != 0); if (gpr_counter <= 10) { if ((gpr_counter + gprs_needed - 1) <= 10) { info->flags |= AIF_PassInGPR; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= ((1 << gprs_needed) - 1) << gpr_counter; } else { info->flags |= AIF_PassInGPR | AIF_PassOnStack; info->gpr = gpr_counter; used_regs[RegClass_GPR] |= ((1 << (11 - gpr_counter)) - 1) << gpr_counter; } } else { info->flags |= AIF_PassOnStack; } gpr_counter += gprs_needed; arg_size = type->size; } else { CError_FATAL(421); } displ = set_out_param_displ(displ, type, info->flags & AIF_PassOnStack, &info->offset, arg_size); arg_expr = arg_expr->next; if (arg && arg != &elipsis && arg != &oldstyle) arg = arg->next; } update_out_param_size(displ); return infos; } static void pass_in_memory(ArgInfo *info) { Type *type; Operand opnd; type = info->expr->rtype; memclrw(&opnd, sizeof(Operand)); if (TYPE_FITS_IN_REGISTER(type)) { if (TYPE_IS_8BYTES(type)) { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); coerce_to_register_pair(&info->opnd, type, 0, 0); load_store_register( PC_STW, info->opnd.reg, 1, NULL, low_offset + out_param_displ_to_offset(info->offset)); load_store_register( PC_STW, info->opnd.regHi, 1, NULL, high_offset + out_param_displ_to_offset(info->offset)); } else { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); if (info->flags & AIF_ExtendTo32Bits) extend32(&info->opnd, type, 0); ENSURE_GPR(&info->opnd, type, 0); load_store_register( PC_STW, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); } } else if (IS_TYPE_FLOAT(type)) { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); ENSURE_FPR(&info->opnd, type, 0); if (type->size == 4 && !(info->flags & AIF_ForceDoublePrecision)) { load_store_register( PC_STFS, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); } else { load_store_register( PC_STFD, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); } } else if (IS_TYPE_VECTOR(type)) { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); ENSURE_VR(&info->opnd, type, 0); load_store_register( PC_STVX, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); } else { opnd.optype = OpndType_IndirectGPR_ImmOffset; opnd.reg = 1; opnd.object = NULL; opnd.immOffset = out_param_displ_to_offset(info->offset); if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); move_block(&opnd, &info->opnd, type->size, CMach_ArgumentAlignment(type)); } } static void pass_in_register(ArgInfo *info) { Type *type; type = info->expr->rtype; if ((info->flags & AIF_PassMask) == AIF_PassInFPR) { if (!info->evaluated) GEN_NODE_TO_REG(info->expr, info->fpr, 0, &info->opnd); ENSURE_FPR(&info->opnd, type, info->fpr); if (info->opnd.reg != info->fpr) emitpcode(PC_FMR, info->fpr, info->opnd.reg); } else if ((info->flags & AIF_PassMask) == AIF_PassInVR) { if (!info->evaluated) GEN_NODE_TO_REG(info->expr, info->vr, 0, &info->opnd); ENSURE_VR(&info->opnd, type, info->vr); if (info->opnd.reg != info->vr) emitpcode(PC_VMR, info->vr, info->opnd.reg); } else if (TYPE_FITS_IN_REGISTER(type)) { if (TYPE_IS_8BYTES(type)) { if (!info->evaluated) GEN_NODE_TO_REG(info->expr, info->gpr, info->gprHi, &info->opnd); coerce_to_register_pair(&info->opnd, type, info->gpr, info->gprHi); if (copts.littleendian) { if (info->gprHi > 10) { load_store_register( PC_STW, info->opnd.regHi, 1, NULL, high_offset + out_param_displ_to_offset(info->offset)); } } else { if (info->gpr > 10) { load_store_register( PC_STW, info->opnd.reg, 1, NULL, low_offset + out_param_displ_to_offset(info->offset)); } } } else { if (!info->evaluated) GEN_NODE_TO_REG(info->expr, info->gpr, 0, &info->opnd); if (info->flags & AIF_ExtendTo32Bits) extend32(&info->opnd, type, info->gpr); ENSURE_GPR(&info->opnd, type, info->gpr); if (info->opnd.reg != info->gpr) emitpcode(PC_MR, info->gpr, info->opnd.reg); } } else if (IS_TYPE_FLOAT(type)) { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); if (type->size != 4 && info->opnd.optype == OpndType_IndirectGPR_ImmOffset) { load_store_register( PC_LWZ, info->gpr, info->opnd.reg, info->opnd.object, info->opnd.immOffset); load_store_register( PC_LWZ, info->gpr + 1, info->opnd.reg, info->opnd.object, info->opnd.immOffset + 4); } else { ENSURE_FPR(&info->opnd, type, 0); load_store_register( PC_STFD, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); load_store_register( PC_LWZ, info->gpr, 1, NULL, out_param_displ_to_offset(info->offset)); load_store_register( PC_LWZ, info->gpr + 1, 1, NULL, out_param_displ_to_offset(info->offset) + 4); } } else if (IS_TYPE_VECTOR(type)) { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); if (info->opnd.optype == OpndType_IndirectGPR_ImmOffset) { load_store_register( PC_LWZ, info->gpr, info->opnd.reg, info->opnd.object, info->opnd.immOffset); load_store_register( PC_LWZ, info->gpr + 1, info->opnd.reg, info->opnd.object, info->opnd.immOffset + 4); if ((info->gpr + 2) < 10) { load_store_register( PC_LWZ, info->gpr + 2, info->opnd.reg, info->opnd.object, info->opnd.immOffset + 8); load_store_register( PC_LWZ, info->gpr + 3, info->opnd.reg, info->opnd.object, info->opnd.immOffset + 12); } } else { ENSURE_VR(&info->opnd, type, 0); load_store_register( PC_STVX, info->opnd.reg, 1, NULL, out_param_displ_to_offset(info->offset)); load_store_register( PC_LWZ, info->gpr, 1, NULL, out_param_displ_to_offset(info->offset)); load_store_register( PC_LWZ, info->gpr + 1, 1, NULL, out_param_displ_to_offset(info->offset) + 4); if ((info->gpr + 2) < 10) { load_store_register( PC_LWZ, info->gpr + 2, 1, NULL, out_param_displ_to_offset(info->offset) + 8); load_store_register( PC_LWZ, info->gpr + 3, 1, NULL, out_param_displ_to_offset(info->offset) + 12); } } } else { if (!info->evaluated) GEN_NODE(info->expr, &info->opnd); if (type->size <= 4) { if (info->opnd.optype == OpndType_IndirectSymbol) coerce_to_addressable(&info->opnd); if (info->opnd.optype == OpndType_IndirectGPR_ImmOffset) { load_store_register( PC_LWZ, info->gpr, info->opnd.reg, info->opnd.object, info->opnd.immOffset); } else if (info->opnd.optype == OpndType_IndirectGPR_Indexed) { emitpcode( PC_LWZX, info->gpr, info->opnd.reg, info->opnd.regOffset); } } else { SInt32 gprs_needed = (type->size >> 2) + ((type->size & 3) != 0); SInt32 i; make_addressable(&info->opnd, gprs_needed * 4, 12); for (i = 0; i < gprs_needed; i++) { if (info->opnd.reg != (info->gpr + i)) { load_store_register( PC_LWZ, info->gpr + i, info->opnd.reg, info->opnd.object, info->opnd.immOffset + i * 4); } } if (info->opnd.reg >= info->gpr && info->opnd.reg < (info->gpr + gprs_needed)) { load_store_register( PC_LWZ, info->opnd.reg, info->opnd.reg, info->opnd.object, info->opnd.immOffset + (info->opnd.reg - info->gpr) * 4); } } } } static void pass_in_register_and_memory(ArgInfo *info) { Type *type; int gpr; SInt32 offset; type = info->expr->rtype; gpr = info->gpr; offset = 0; while (offset < type->size && gpr <= 10) { load_store_register( PC_LWZ, gpr, 1, NULL, offset + out_param_displ_to_offset(info->offset)); gpr++; offset += 4; } } static Boolean needs_TOC_reload(Object *func) { return 0; } static void load_virtual_function(TypeClass *tclass, SInt32 offset, int reg, Operand *opnd) { if (tclass->flags & CLASS_FLAGS_1) { load_store_register(PC_LWZ, 12, reg, NULL, 0); load_store_register(PC_LWZ, 12, 12, NULL, tclass->vtable->offset); } else { load_store_register(PC_LWZ, 12, reg, NULL, tclass->vtable->offset); } load_store_register(PC_LWZ, 12, 12, NULL, offset); opnd->optype = OpndType_GPR; opnd->reg = 12; } static void branch_subroutine_indirect(Object *func, Operand *addrOpnd, UInt32 *used_regs) { if (addrOpnd->reg != 12) emitpcode(PC_MR, 12, addrOpnd->reg); used_regs[RegClass_GPR] |= 1 << 12; branch_subroutine(func, 1, used_regs); } static void evaluate_nested_function_calls(ArgInfo *info) { ArgInfo *scan; scan = info->next; while (scan && !scan->expr->hascall) scan = scan->next; if (scan) evaluate_nested_function_calls(scan); if (info->expr->hascall) { GEN_NODE(info->expr, &info->opnd); info->evaluated = 1; } } void call_function(ENode *expr, Operand *output) { ArgInfo *infos; // r31 ENode *funcref = expr->data.funccall.funcref; // r27 Type *resultType = expr->data.funccall.functype->functype; // r26 ENode *node = NULL; // r25 char has_varargs; // r24 ArgInfo *info; // r22 Operand opnd; UInt32 used_regs[RegClassMax] = {0}; Boolean has_floats; FuncArg *arg; memclrw(&opnd, sizeof(Operand)); has_varargs = 0; for (arg = expr->data.funccall.functype->args; arg; arg = arg->next) { if (arg == &elipsis) { has_varargs = 1; break; } } if (expr->data.funccall.functype->flags & FUNC_FLAGS_80) { if (CMach_PassResultInHiddenArg(resultType)) node = expr->data.funccall.args->next->node; else node = expr->data.funccall.args->node; } infos = analyze_arguments( node, expr->data.funccall.args, expr->data.funccall.functype->args, used_regs, &has_floats, has_varargs); if (infos) evaluate_nested_function_calls(infos); if (funcref->hascall) { GEN_NODE_TO_GPR(funcref, &opnd, TYPE(&void_ptr), 0); } else if (node && node->hascall) { GEN_NODE_TO_GPR(node, &opnd, TYPE(&void_ptr), 0); } for (info = infos; info; info = info->next) { if (info->flags & AIF_PassOnStack) pass_in_memory(info); } for (info = infos; info; info = info->next) { if ((info->flags & AIF_PassMask) == (AIF_PassInGPR | AIF_PassOnStack)) pass_in_register_and_memory(info); } for (info = infos; info; info = info->next) { int flag = info->flags & AIF_PassMask; if ( flag == AIF_PassInGPR || flag == AIF_PassInFPR || flag == AIF_PassInVR ) pass_in_register(info); } if (funcref->type == EOBJREF) { TypeClass *tclass; SInt32 vfOffset; if (CParser_IsVirtualFunction(funcref->data.objref, &tclass, &vfOffset)) { load_virtual_function( tclass, vfOffset, CMach_PassResultInHiddenArg(resultType) ? Register4 : Register3, &opnd ); branch_subroutine_indirect_ctr(&opnd, used_regs); } else if (node) { if (!node->hascall) { GEN_NODE_TO_REG(node, 12, 0, &opnd); ENSURE_GPR(&opnd, TYPE(&void_ptr), 12); } branch_subroutine_indirect(funcref->data.objref, &opnd, used_regs); } else { branch_subroutine(funcref->data.objref, needs_TOC_reload(funcref->data.objref), used_regs); } } else { if (!funcref->hascall) GEN_NODE_TO_REG(funcref, 12, 0, &opnd); ENSURE_GPR(&opnd, TYPE(&void_ptr), 12); branch_subroutine_indirect_ctr(&opnd, used_regs); } if (IS_TYPE_FLOAT(resultType)) { output->optype = OpndType_FPR; output->reg = used_virtual_registers[RegClass_FPR]++; emitpcode(PC_FMR, output->reg, 1); } else if (IS_TYPE_VECTOR(resultType)) { output->optype = OpndType_VR; output->reg = used_virtual_registers[RegClass_VR]++; emitpcode(PC_VMR, output->reg, 2); } else if (TYPE_FITS_IN_REGISTER(resultType)) { if (resultType->size > 4) { output->optype = OpndType_GPRPair; output->reg = used_virtual_registers[RegClass_GPR]++; output->regHi = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_MR, output->reg, low_reg); emitpcode(PC_MR, output->regHi, high_reg); } else { output->optype = OpndType_GPR; output->reg = used_virtual_registers[RegClass_GPR]++; emitpcode(PC_MR, output->reg, 3); } } else { output->optype = OpndType_Absolute; output->immediate = 0; } } static void branch_subroutine_indirect_ctr(Operand *addrOpnd, UInt32 *used_regs) { if (addrOpnd->reg != 12) emitpcode(PC_MR, 12, addrOpnd->reg); emitpcode(PC_MTCTR, 12); used_regs[RegClass_GPR] |= 1 << 12; branch_subroutine_ctr(used_regs); }