2022-12-29 12:32:55 +00:00
|
|
|
#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;
|
2023-01-15 12:14:05 +00:00
|
|
|
if (copts.littleendian) {
|
2022-12-29 12:32:55 +00:00
|
|
|
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);
|
2023-01-15 12:14:05 +00:00
|
|
|
if (copts.littleendian) {
|
2022-12-29 12:32:55 +00:00
|
|
|
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);
|
|
|
|
}
|