MWCC/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/StackFrame.c

1253 lines
39 KiB
C

#include "compiler/StackFrame.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/PCode.h"
#include "compiler/PCodeInfo.h"
#include "compiler/PCodeUtilities.h"
#include "compiler/RegisterInfo.h"
#include "compiler/objects.h"
#include "compiler/types.h"
#define ALIGN(thing, alignment) ( ~((alignment) - 1) & ((thing) + (alignment) - 1) )
#define ALIGN_REMAINDER(thing, alignment) ( ALIGN(thing, alignment) - (thing) )
Boolean requires_frame;
Boolean makes_call;
Boolean uses_globals;
Boolean dynamic_stack;
Boolean large_stack;
static SInt32 out_param_alignment;
static SInt32 in_parameter_size;
static SInt32 in_param_alignment;
static SInt32 frame_alignment;
static SInt32 alloca_alignment;
static SInt32 linkage_area_size;
static SInt32 parameter_area_size;
static SInt32 parameter_area_size_estimate;
static SInt32 local_data_size;
static SInt32 local_data_limit;
static SInt32 large_data_near_size;
static SInt32 large_data_far_size;
static ObjectList *local_objects[ObjClassMax];
static ObjectList *local_objects_tail[ObjClassMax];
static SInt32 non_volatile_save_offset[RegClassMax];
static SInt32 VRSAVE_save_offset;
static SInt32 LR_save_offset;
static SInt32 nonvolatile_save_size;
static UInt32 vrsave_mask;
static SInt32 frame_size;
static SInt32 frame_size_estimate;
static SInt32 genuine_frame_size;
static Object *dummylocal;
static Boolean dynamic_align_stack;
static Boolean has_varargs;
static Boolean compressing_data_area;
static PCode *setup_caller_sp;
static PCode *align_instr1;
static PCode *align_instr2;
static short vrsave_register;
static PCode *loadvrsave;
static PCode *storevrsave;
Object *dummyvaparam;
void *dummyprofiler;
// forward declarations
static void insert_local_object(UInt8 oclass, Object *obj);
static void compress_data_area(void);
static UInt32 align_bits(UInt32 value, UInt8 bitcount);
static Boolean use_helper_function(char rclass);
static Boolean need_link_register(void);
static void call_helper_function(char *name, char rclass, short effect);
static void save_nonvolatile_FPRs(int reg, SInt32 offset);
static void save_nonvolatile_VRs(int reg, SInt32 offset);
static void restore_nonvolatile_FPRs(int reg, SInt32 offset);
static void restore_nonvolatile_VRs(int reg, SInt32 offset);
static void save_nonvolatile_GPRs(int reg, SInt32 offset);
static void restore_nonvolatile_GPRs(int reg, SInt32 offset);
static void do_allocate_dynamic_stack_space(Boolean isConstantSize, int reg1, int reg2, SInt32 size);
void init_stack_globals(Object *funcobj) {
RegClass rclass;
ObjClass oclass;
requires_frame = 0;
makes_call = 0;
uses_globals = 0;
dynamic_stack = 0;
large_stack = 0;
vrsave_register = -1;
dynamic_align_stack = 0;
compressing_data_area = 0;
align_instr1 = NULL;
align_instr2 = NULL;
setup_caller_sp = NULL;
dummyvaparam = NULL;
loadvrsave = NULL;
storevrsave = NULL;
local_data_size = 0;
local_data_limit = 0x2000;
large_data_near_size = 0;
large_data_far_size = 0;
frame_size_estimate = 0;
in_parameter_size = 0;
parameter_area_size = 0;
parameter_area_size_estimate = 0;
frame_alignment = 8;
out_param_alignment = 8;
in_param_alignment = 8;
alloca_alignment = 0;
has_varargs = 0;
linkage_area_size = 0;
frame_size = 0;
genuine_frame_size = 0;
nonvolatile_save_size = -1;
VRSAVE_save_offset = -1;
LR_save_offset = -1;
for (rclass = 0; rclass < RegClassMax; rclass++)
non_volatile_save_offset[rclass] = -1;
dummyprofiler = NULL;
dummyvaparam = NULL;
dummylocal = NULL;
for (oclass = 0; oclass < ObjClassMax; oclass++) {
local_objects[oclass] = NULL;
local_objects_tail[oclass] = NULL;
}
}
void init_frame_sizes(Boolean has_varargs) {
ObjectList *scan;
Object *obj;
SInt32 r30;
SInt32 align;
SInt32 mask;
r30 = in_parameter_size + parameter_area_size_estimate;
frame_size_estimate = r30 + 2484;
for (scan = locals; scan; scan = scan->next) {
obj = scan->object;
{
align = CMach_AllocationAlignment(obj->type, obj->qual) - 1;
mask = ~align;
}
frame_size_estimate = (frame_size_estimate + align) & mask;
frame_size_estimate += obj->type->size;
}
if (frame_size_estimate > 0x8000) {
dynamic_stack = 1;
large_stack = 1;
requires_frame = 1;
}
local_data_limit = 0x8000 - (r30 + 2484);
if (dynamic_stack) {
requires_frame = 1;
dummylocal = galloc(sizeof(Object));
memclrw(dummylocal, sizeof(Object));
dummylocal->type = (Type *) &stvoid;
dummylocal->otype = OT_OBJECT;
dummylocal->name = GetHashNameNode("<dummy>");
dummylocal->datatype = DLOCAL;
dummylocal->u.var.info = CodeGen_GetNewVarInfo();
dummylocal->u.var.info->flags |= VarInfoFlag80;
dummylocal->u.var.info->noregister = 1;
}
if (dynamic_stack) {
retain_register(NULL, RegClass_GPR, 31);
_FP_ = 31;
} else {
_FP_ = 1;
}
}
void assign_local_memory(Object *obj) {
// some misassigned registers x.x
short align;
VarInfo *vi;
align = CMach_AllocationAlignment(obj->type, obj->qual);
if (!compressing_data_area && (obj->u.var.info->flags & VarInfoFlag80))
return;
update_frame_align(align);
if (local_data_size + (ALIGN_REMAINDER(local_data_size, align) + ALIGN(obj->type->size, align)) < local_data_limit) {
local_data_size = ALIGN(local_data_size, align);
vi = Registers_GetVarInfo(obj);
vi->flags &= ~VarInfoFlag2;
vi->flags |= VarInfoFlag80;
obj->u.var.uid = local_data_size;
local_data_size += ALIGN(obj->type->size, align);
insert_local_object(ObjClass0, obj);
return;
}
if (compressing_data_area || obj->type->size <= 32) {
large_data_near_size = ALIGN(large_data_near_size, align);
vi = Registers_GetVarInfo(obj);
vi->flags &= ~VarInfoFlag2;
vi->flags |= VarInfoFlag80;
obj->u.var.uid = 0x8000 + large_data_near_size;
large_data_near_size += ALIGN(obj->type->size, align);
insert_local_object(ObjClass1, obj);
} else {
large_data_far_size = ALIGN(large_data_far_size, align);
vi = Registers_GetVarInfo(obj);
vi->flags &= ~VarInfoFlag2;
vi->flags |= VarInfoFlag80;
obj->u.var.uid = 0x10000 + large_data_far_size;
large_data_far_size += ALIGN(obj->type->size, align);
insert_local_object(ObjClass2, obj);
}
}
void assign_locals_to_memory(ObjectList *first) {
ObjectList *list;
Object *obj;
SInt32 i;
for (i = 1; i < 1024; i <<= 1) {
for (list = first; list; list = list->next) {
obj = list->object;
if (Registers_GetVarInfo(obj)->used) {
if ((Registers_GetVarInfo(obj) ? Registers_GetVarInfo(obj)->reg : 0) == 0) {
if (obj->type->size <= i)
assign_local_memory(obj);
}
}
}
}
for (list = first; list; list = list->next) {
obj = list->object;
if (Registers_GetVarInfo(obj)->used) {
if ((Registers_GetVarInfo(obj) ? Registers_GetVarInfo(obj)->reg : 0) == 0) {
assign_local_memory(list->object);
}
}
if (obj->type && IS_TYPE_ARRAY(obj->type) && IS_TYPE_VECTOR(TYPE_POINTER(obj->type)->target))
has_altivec_arrays = 1;
}
}
void compute_frame_sizes(void) {
SInt32 altivec_size;
SInt32 altivec_offset;
CError_ASSERT(897, alloca_alignment == 0 || alloca_alignment == frame_alignment);
update_asm_nonvolatile_registers();
LR_save_offset = 8;
non_volatile_save_offset[RegClass_FPR] = -(used_nonvolatile_registers[RegClass_FPR] * 8);
non_volatile_save_offset[RegClass_GPR] = -(((15 - non_volatile_save_offset[RegClass_FPR]) & ~15) + used_nonvolatile_registers[RegClass_GPR] * 4);
nonvolatile_save_size = -non_volatile_save_offset[RegClass_GPR];
non_volatile_save_offset[RegClass_CRFIELD] = 4;
VRSAVE_save_offset = -1;
non_volatile_save_offset[RegClass_VR] = -1;
if (copts.altivec_model) {
if (vrsave_mask) {
VRSAVE_save_offset = non_volatile_save_offset[RegClass_GPR] - 4;
nonvolatile_save_size = nonvolatile_save_size + 4;
}
altivec_size = used_nonvolatile_registers[RegClass_VR] * 16;
if (altivec_size > 0)
nonvolatile_save_size = ALIGN(nonvolatile_save_size + altivec_size, frame_alignment);
}
if (parameter_area_size)
requires_frame = 1;
compress_data_area();
local_data_size = ALIGN(local_data_size, frame_alignment);
nonvolatile_save_size = ALIGN(nonvolatile_save_size, frame_alignment);
if (!requires_frame && (local_data_size + nonvolatile_save_size) <= 224) {
CError_ASSERT(1005, !dynamic_align_stack);
linkage_area_size = 0;
frame_size = 0;
genuine_frame_size = local_data_size + nonvolatile_save_size;
} else {
requires_frame = 1;
if (parameter_area_size < 32)
parameter_area_size = 32;
parameter_area_size = ALIGN(parameter_area_size + 24, frame_alignment) - 24;
if (large_stack) {
CError_ASSERT(1019, !large_data_far_size);
large_data_near_size += parameter_area_size;
parameter_area_size = 0;
}
linkage_area_size = 24;
frame_size = nonvolatile_save_size + (altivec_offset = parameter_area_size + 24 + local_data_size);
if (copts.altivec_model && used_nonvolatile_registers[RegClass_VR])
non_volatile_save_offset[RegClass_VR] = altivec_offset;
frame_size += ALIGN_REMAINDER(frame_size, 16);
frame_size = ALIGN(frame_size, frame_alignment);
genuine_frame_size = frame_size;
}
if (!large_stack && frame_size > 0x7FFF)
CError_ErrorTerm(CErrorStr210);
}
static void allocate_new_frame(int reg1, int reg2) {
if (dynamic_align_stack) {
CError_ASSERT(1116, reg1 != _CALLER_SP_);
emitpcode(PC_RLWINM, reg1, 1, 0, align_bits(frame_alignment, 1), 31);
if (frame_size > 0x7FFF) {
CError_FATAL(1122);
return;
}
if (frame_size)
emitpcode(PC_SUBFIC, reg1, reg1, -frame_size);
else
emitpcode(PC_SUBFIC, reg1, reg1, -genuine_frame_size);
if (reg2)
emitpcode(PC_MR, reg2, 1);
emitpcode(PC_STWUX, 1, 1, reg1);
} else {
if (frame_size > 0x7FFF)
CError_FATAL(1153);
else
emitpcode(PC_STWU, 1, 1, 0, -frame_size);
if (reg2)
emitpcode(PC_MR, reg2, 1);
}
}
void generate_prologue(PCodeBlock *block, Boolean has_varargs) {
PCodeBlock *save_block;
Boolean needs_lr;
Statement *save_statement;
Statement stmt;
UInt32 vrsave_low;
UInt32 vrsave_high;
save_block = pclastblock;
needs_lr = need_link_register();
save_statement = current_statement;
stmt.sourceoffset = functionbodyoffset;
current_statement = &stmt;
pclastblock = block;
if (setup_caller_sp && setup_caller_sp->block) {
if (
setup_caller_sp->op == PC_MR &&
setup_caller_sp->args[1].kind == PCOp_REGISTER &&
setup_caller_sp->args[1].arg == RegClass_GPR &&
setup_caller_sp->args[1].data.reg.reg == _FP_
)
CError_FATAL(1197);
_CALLER_SP_ = setup_caller_sp->args[0].data.reg.reg;
deletepcode(setup_caller_sp);
setup_caller_sp = NULL;
} else if (_CALLER_SP_ != _FP_) {
_CALLER_SP_ = -1;
}
if (align_instr1 && align_instr1->block) {
deletepcode(align_instr1);
align_instr1 = NULL;
}
if (align_instr2 && align_instr2->block) {
deletepcode(align_instr2);
align_instr2 = NULL;
}
if (loadvrsave && loadvrsave->block) {
deletepcode(loadvrsave);
loadvrsave = NULL;
}
if (storevrsave && storevrsave->block) {
deletepcode(storevrsave);
storevrsave = NULL;
}
if (needs_lr)
emitpcode(PC_MFLR, 0);
if (used_nonvolatile_registers[RegClass_CRFIELD]) {
emitpcode(PC_MFCR, 12);
emitpcode(PC_STW, 12, 1, 0, non_volatile_save_offset[RegClass_CRFIELD]);
}
if (used_nonvolatile_registers[RegClass_FPR])
save_nonvolatile_FPRs(1, 0);
if (used_nonvolatile_registers[RegClass_GPR])
save_nonvolatile_GPRs(1, 0);
if (needs_lr)
emitpcode(PC_STW, 0, 1, 0, 8);
if (frame_size) {
if (vrsave_mask) {
emitpcode(PC_MFSPR, 0, 256);
emitpcode(PC_STW, 0, 1, 0, VRSAVE_save_offset);
vrsave_register = 0;
}
allocate_new_frame(12, (_CALLER_SP_ > 0 && _CALLER_SP_ != _FP_) ? _CALLER_SP_ : 0);
} else {
CError_ASSERT(1326, !dynamic_align_stack);
if (vrsave_mask)
emitpcode(PC_MFSPR, vrsave_register, 256);
}
if (vrsave_mask) {
vrsave_high = vrsave_mask >> 16;
vrsave_low = vrsave_mask & 0xFFFF;
if (vrsave_mask == 0xFFFFFFFF) {
emitpcode(PC_LI, 0, -1);
} else {
if (vrsave_high)
emitpcode(PC_ORIS, 0, vrsave_register, vrsave_high);
if (vrsave_low)
emitpcode(PC_ORI, 0, 0, vrsave_low);
}
emitpcode(PC_MTSPR, 256, 0);
}
if (used_nonvolatile_registers[RegClass_VR])
save_nonvolatile_VRs(1, 0);
if (dynamic_stack)
emitpcode(PC_MR, 31, 1);
if (large_stack)
do_allocate_dynamic_stack_space(1, 11, 0, large_data_near_size);
block->flags |= fIsProlog;
pclastblock = save_block;
current_statement = save_statement;
}
void generate_epilogue(PCodeBlock *block, Boolean add_blr) {
PCodeBlock *save_block;
Boolean needs_lr;
Statement *save_statement;
Statement stmt;
save_block = pclastblock;
needs_lr = need_link_register();
save_statement = current_statement;
if (!save_statement) {
stmt.sourceoffset = current_linenumber;
current_statement = &stmt;
}
pclastblock = block;
if (used_nonvolatile_registers[RegClass_VR])
restore_nonvolatile_VRs(_FP_, 0);
if (dynamic_align_stack) {
load_store_register(PC_LWZ, 1, 1, NULL, 0);
setpcodeflags(fSideEffects);
if (needs_lr)
load_store_register(PC_LWZ, 0, 1, 0, 8);
} else {
if (needs_lr)
load_store_register(PC_LWZ, 0, _FP_, 0, frame_size + 8);
if (frame_size > 0) {
if (dynamic_stack) {
load_store_register(PC_LWZ, 1, 1, 0, 0);
setpcodeflags(fSideEffects);
} else {
emitpcode(PC_ADDI, 1, 1, 0, frame_size);
setpcodeflags(fSideEffects);
}
}
}
if (used_nonvolatile_registers[RegClass_CRFIELD]) {
load_store_register(PC_LWZ, 12, 1, NULL, non_volatile_save_offset[RegClass_CRFIELD]);
emitpcode(PC_MTCRF, 255, 12);
}
if (vrsave_mask) {
if (!requires_frame) {
emitpcode(PC_MTSPR, 256, vrsave_register);
} else {
emitpcode(PC_LWZ, 11, 1, 0, VRSAVE_save_offset);
emitpcode(PC_MTSPR, 256, 11);
}
}
if (used_nonvolatile_registers[RegClass_FPR])
restore_nonvolatile_FPRs(1, 0);
if (needs_lr && !use_helper_function(RegClass_GPR))
emitpcode(PC_MTLR, 0);
if (used_nonvolatile_registers[RegClass_GPR])
restore_nonvolatile_GPRs(1, 0);
if (needs_lr && use_helper_function(RegClass_GPR))
emitpcode(PC_MTLR, 0);
if (add_blr) {
emitpcode(PC_BLR);
setpcodeflags(fIsVolatile);
}
block->flags |= fIsEpilogue;
pclastblock = save_block;
current_statement = save_statement;
}
static void load_base_offset(int dest_reg, int base_reg, SInt32 offset) {
if (offset)
emitpcode(PC_ADDI, dest_reg, base_reg, 0, offset);
else
emitpcode(PC_MR, dest_reg, base_reg);
}
static void save_nonvolatile_FPRs(int reg, SInt32 offset) {
short i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_FPR];
if (!use_helper_function(RegClass_FPR)) {
for (i = 1; i <= used_nonvolatile_registers[RegClass_FPR]; i++) {
emitpcode(PC_STFD, 32 - i, reg, NULL, o + (used_nonvolatile_registers[RegClass_FPR] - i) * 8);
setpcodeflags(fIsVolatile);
}
} else {
load_base_offset(11, reg, o + used_nonvolatile_registers[RegClass_FPR] * 8);
call_helper_function("__save_fpr_%d", RegClass_FPR, EffectRead);
}
}
static void save_nonvolatile_VRs(int reg, SInt32 offset) {
short i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_VR];
if (!use_helper_function(RegClass_VR)) {
for (i = 1; i <= used_nonvolatile_registers[RegClass_VR]; i++) {
emitpcode(PC_LI, 0, o + (used_nonvolatile_registers[RegClass_VR] - i) * 16);
emitpcode(PC_STVX, 32 - i, reg, 0);
setpcodeflags(fIsVolatile);
}
} else {
load_base_offset(0, reg, o + used_nonvolatile_registers[RegClass_VR] * 16);
call_helper_function("__savev%d", RegClass_VR, EffectRead);
}
}
static void restore_nonvolatile_FPRs(int reg, SInt32 offset) {
short i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_FPR];
if (!use_helper_function(RegClass_FPR)) {
for (i = 1; i <= used_nonvolatile_registers[RegClass_FPR]; i++) {
load_store_register(PC_LFD, 32 - i, reg, NULL, o + (used_nonvolatile_registers[RegClass_FPR] - i) * 8);
setpcodeflags(fIsVolatile);
}
} else {
load_base_offset(11, reg, o + used_nonvolatile_registers[RegClass_FPR] * 8);
call_helper_function("__restore_fpr_%d", RegClass_FPR, EffectWrite);
}
}
static void restore_nonvolatile_VRs(int reg, SInt32 offset) {
short i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_VR];
if (!use_helper_function(RegClass_VR)) {
for (i = 1; i <= used_nonvolatile_registers[RegClass_VR]; i++) {
emitpcode(PC_LI, 0, o + (used_nonvolatile_registers[RegClass_VR] - i) * 16);
setpcodeflags(fIsVolatile);
emitpcode(PC_LVX, 32 - i, reg, 0);
setpcodeflags(fIsVolatile);
}
} else {
load_base_offset(0, reg, o + used_nonvolatile_registers[RegClass_VR] * 16);
call_helper_function("__restv%d", RegClass_VR, EffectWrite);
}
}
static void save_nonvolatile_GPRs(int reg, SInt32 offset) {
int i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_GPR];
if (!use_helper_function(RegClass_GPR)) {
if (copts.use_lmw_stmw && ((used_nonvolatile_registers[RegClass_GPR] > 4) || (copts.optimizesize && (used_nonvolatile_registers[RegClass_GPR] > 1)))) {
emitpcode(PC_STMW, used_nonvolatile_registers[RegClass_GPR] - 1, 32 - used_nonvolatile_registers[RegClass_GPR], reg, 0, o);
} else {
for (i = 1; i <= used_nonvolatile_registers[RegClass_GPR]; i++) {
emitpcode(PC_STW, 32 - i, reg, 0, o + (used_nonvolatile_registers[RegClass_GPR] - i) * 4);
}
}
} else {
load_base_offset(11, reg, o + used_nonvolatile_registers[RegClass_GPR] * 4);
call_helper_function("__savegpr_%d", RegClass_GPR, EffectRead);
}
}
static void restore_nonvolatile_GPRs(int reg, SInt32 offset) {
int i;
SInt32 o;
o = offset + non_volatile_save_offset[RegClass_GPR];
if (!use_helper_function(RegClass_GPR)) {
if (copts.use_lmw_stmw && ((used_nonvolatile_registers[RegClass_GPR] > 4) || (copts.optimizesize && (used_nonvolatile_registers[RegClass_GPR] > 1)))) {
emitpcode(PC_LMW, used_nonvolatile_registers[RegClass_GPR] - 1, 32 - used_nonvolatile_registers[RegClass_GPR], reg, 0, o);
setpcodeflags(fIsVolatile);
} else {
for (i = 1; i <= used_nonvolatile_registers[RegClass_GPR]; i++) {
emitpcode(PC_LWZ, 32 - i, reg, 0, o + (used_nonvolatile_registers[RegClass_GPR] - i) * 4);
setpcodeflags(fIsVolatile);
}
}
} else {
load_base_offset(11, reg, o + used_nonvolatile_registers[RegClass_GPR] * 4);
call_helper_function("__restgpr_%d", RegClass_GPR, EffectWrite);
}
}
static void do_allocate_dynamic_stack_space(Boolean isConstantSize, int reg1, int reg2, SInt32 size) {
load_store_register(PC_LWZ, reg2, 1, NULL, 0);
if (isConstantSize) {
size = ALIGN(size, frame_alignment);
if (size < 0x8000) {
emitpcode(PC_STWU, reg2, 1, 0, -size);
} else {
emitpcode(PC_LIS, reg1, 0, (short) HIGH_PART(-size));
if (-size)
emitpcode(PC_ADDI, reg1, reg1, 0, LOW_PART(-size));
emitpcode(PC_STWUX, reg2, 1, reg1);
setpcodeflags(fIsVolatile | fSideEffects);
}
} else {
emitpcode(PC_STWUX, reg2, 1, reg1);
setpcodeflags(fIsVolatile | fSideEffects);
}
}
void allocate_dynamic_stack_space(Boolean isConstantSize, int reg1, int reg2, SInt32 size) {
if (copts.altivec_model)
update_frame_align(16);
do_allocate_dynamic_stack_space(isConstantSize, reg1, reg2, size);
add_immediate(reg1, 1, dummylocal, 0);
}
#ifdef __MWERKS__
#pragma options align=mac68k
#endif
typedef struct Traceback {
UInt8 x0;
UInt8 x1;
UInt8 x2;
UInt8 x3;
UInt8 x4;
UInt8 x5;
UInt8 x6_0 : 2;
UInt8 x6_1 : 1; // set to 1
UInt8 x6_2 : 5;
UInt8 x7_0 : 1;
UInt8 x7_1 : 1; // set to 1
UInt8 has_dynamic_stack : 1; // set to 1 if dynamic_stack
UInt8 x7_3 : 3;
UInt8 uses_CRs : 1; // set to 1 if CRs used
UInt8 needs_link_register : 1; // set to 1 if link register used
UInt8 has_frame_size : 1; // set to 1 if frame_size is nonzero
UInt8 x8_1 : 1; // set to 0
UInt8 used_FPRs : 6; // stores non-volatile FPRs used
UInt8 x9_0 : 1; // set to 0
UInt8 x9_1 : 1; // set to 1 if VRs or vrsave used
UInt8 used_GPRs : 6; // stores non-volatile GPRs used
UInt8 xA;
UInt8 xB;
SInt32 funcsize;
SInt16 namelen;
char name[0];
} Traceback;
typedef struct TracebackExtra {
UInt8 used_VRs : 6;
UInt8 has_vrsave_mask : 1;
UInt8 is_varargs : 1;
UInt8 vec_arg_count : 7;
UInt8 has_vrsave_mask_or_used_VRs : 1;
} TracebackExtra;
#ifdef __MWERKS__
#pragma options align=reset
#endif
char *generate_traceback(SInt32 funcsize, char *funcname, SInt32 *tbsize, Object *func) {
char *work;
short namelen;
Traceback *buf;
SInt32 bufsize;
namelen = strlen(funcname);
bufsize = ALIGN(sizeof(Traceback) + namelen + (dynamic_stack ? 1 : 0) + ((used_nonvolatile_registers[RegClass_VR] || vrsave_mask) ? sizeof(TracebackExtra) : 0), 4);
buf = lalloc(bufsize);
memclrw(buf, bufsize);
buf->x4 = 0;
buf->x5 = copts.cplusplus ? 9 : 0;
buf->x6_1 = 1;
buf->x7_1 = 1;
if (dynamic_stack)
buf->has_dynamic_stack = 1;
if (used_nonvolatile_registers[RegClass_CRFIELD])
buf->uses_CRs = 1;
if (need_link_register())
buf->needs_link_register = 1;
if (frame_size)
buf->has_frame_size = 1;
buf->used_FPRs = used_nonvolatile_registers[RegClass_FPR];
buf->used_GPRs = used_nonvolatile_registers[RegClass_GPR];
buf->x8_1 = 0;
buf->x9_0 = 0;
buf->x9_1 = (used_nonvolatile_registers[RegClass_VR] || vrsave_mask) != 0;
buf->funcsize = funcsize;
buf->namelen = namelen;
work = buf->name;
strcpy(work, funcname);
work += namelen;
if (dynamic_stack) {
*(work++) = 31;
}
if (vrsave_mask || used_nonvolatile_registers[RegClass_VR]) {
TracebackExtra *extra;
Boolean is_varargs;
int vec_count;
FuncArg *args, *scan;
Type *type;
extra = (TracebackExtra *) work;
vec_count = 0;
args = TYPE_FUNC(func->type)->args;
scan = args;
while (scan && scan != &elipsis)
scan = scan->next;
is_varargs = scan == &elipsis;
while (args) {
if ((type = args->type) && IS_TYPE_VECTOR(type))
vec_count++;
args = args->next;
}
extra->used_VRs = used_nonvolatile_registers[RegClass_VR];
extra->has_vrsave_mask = vrsave_mask != 0;
extra->is_varargs = is_varargs;
extra->vec_arg_count = vec_count;
extra->has_vrsave_mask_or_used_VRs = vrsave_mask || used_nonvolatile_registers[RegClass_VR];
}
*tbsize = bufsize;
return (char *) buf;
}
static SInt32 localsbase(void) {
SInt32 size = parameter_area_size;
if (frame_size || dynamic_align_stack)
size += linkage_area_size;
else
size -= genuine_frame_size;
return size;
}
static SInt32 parametersbase(int flag) {
if (flag)
return 24;
return frame_size ? (genuine_frame_size + 24) : 24;
}
void check_dynamic_aligned_frame(void) {
PCode *pc;
if (used_nonvolatile_registers[RegClass_VR]) {
update_frame_align(16);
requires_frame = 1;
}
if (frame_alignment > in_param_alignment) {
dynamic_align_stack = 1;
requires_frame = 1;
CError_ASSERT(2091, !has_varargs || _CALLER_SP_ != -1);
CError_ASSERT(2096, _CALLER_SP_ != _FP_);
if (setup_caller_sp && setup_caller_sp->block) {
align_instr1 = makepcode(PC_RLWINM, 12, 1, 0, 5, 31);
insertpcodebefore(setup_caller_sp, align_instr1);
align_instr2 = makepcode(PC_STWUX, 1, 1, 12);
insertpcodeafter(setup_caller_sp, align_instr2);
}
} else {
dynamic_align_stack = 0;
if (setup_caller_sp && setup_caller_sp->block) {
pc = makepcode(PC_MR, _CALLER_SP_, _FP_);
insertpcodebefore(setup_caller_sp, pc);
deletepcode(setup_caller_sp);
setup_caller_sp = pc;
}
_CALLER_SP_ = _FP_;
}
vrsave_mask = 0;
if (copts.altivec_model) {
vrsave_mask = colored_vrs_as_vrsave(pcbasicblocks);
if (!requires_frame && vrsave_mask) {
vrsave_register = 11;
loadvrsave = makepcode(PC_LWZ, 11, 1, 0, -4);
appendpcode(prologue, loadvrsave);
storevrsave = makepcode(PC_STW, 11, 1, 0, -4);
appendpcode(epilogue, storevrsave);
}
}
}
void move_varargs_to_memory(void) {
short reg;
has_varargs = 1;
dummyvaparam = galloc(sizeof(Object));
memclrw(dummyvaparam, sizeof(Object));
dummyvaparam->type = TYPE(&stvoid);
dummyvaparam->otype = OT_OBJECT;
dummyvaparam->name = GetHashNameNode("<vaparam>");
dummyvaparam->datatype = DLOCAL;
dummyvaparam->u.var.info = CodeGen_GetNewVarInfo();
dummyvaparam->u.var.uid = 0;
dummyvaparam->u.var.info->noregister = 1;
Registers_GetVarInfo(dummyvaparam)->flags = (Registers_GetVarInfo(dummyvaparam)->flags & ~VarInfoFlag1) | VarInfoFlag1;
for (reg = last_argument_register[RegClass_GPR] + 1; (int)reg <= 10; reg++) {
emitpcode(PC_STW, reg, local_base_register(dummyvaparam), dummyvaparam, (reg - 3) * 4);
setpcodeflags(fIsPtrOp | fIsArgInit);
}
}
void assign_arguments_to_memory(Object *func, UInt8 mysteryFlag, Boolean hasVarargs) {
// almost matches except for the not/andc issue
SInt32 pos;
ObjectList *list;
Object *obj;
Type *type;
short reg;
SInt32 chk;
Boolean flag;
pos = 0;
reg = 2;
for (list = arguments; list; list = list->next) {
obj = list->object;
type = obj->type;
if (!IS_TYPE_VECTOR(type)) {
obj->datatype = DLOCAL;
obj->u.var.info = CodeGen_GetNewVarInfo();
if (IS_TYPE_ARRAY(type) || IS_TYPE_NONVECTOR_STRUCT(type) || IS_TYPE_CLASS(type) ||
IS_TYPE_12BYTES_MEMBERPOINTER(type)) {
chk = CMach_ArgumentAlignment(type);
if (chk > 4) {
pos = ALIGN(pos, chk);
update_in_param_align(chk);
}
}
obj->u.var.uid = pos;
Registers_GetVarInfo(obj)->flags = (Registers_GetVarInfo(obj)->flags & ~VarInfoFlag1) | VarInfoFlag1;
if (!copts.littleendian && (IS_TYPE_INT(obj->type) || IS_TYPE_ENUM(obj->type)) && obj->type->size < 4)
obj->u.var.uid += 4 - obj->type->size;
pos += type->size;
pos = ALIGN(pos, 4);
} else {
obj->u.var.info = CodeGen_GetNewVarInfo();
obj->u.var.uid = 0;
obj->datatype = DLOCAL;
flag = 1;
if (reg <= 13)
flag = hasVarargs;
if (flag) {
pos = ALIGN(pos + 24, 16) - 24;
obj->u.var.uid = pos;
pos += 16;
update_in_param_align(16);
Registers_GetVarInfo(obj)->flags = (Registers_GetVarInfo(obj)->flags & ~VarInfoFlag1) | VarInfoFlag1;
} else {
assign_local_memory(obj);
Registers_GetVarInfo(obj)->flags = Registers_GetVarInfo(obj)->flags & ~VarInfoFlag1;
}
reg++;
}
}
in_parameter_size = (in_parameter_size < pos) ? pos : in_parameter_size;
CError_ASSERT(2408, !dummyvaparam);
}
SInt32 set_out_param_displ(SInt32 a, Type *type, Boolean flag, SInt32 *outvar, SInt32 b) {
// does not match due to errant andc
SInt32 argAlign;
if (!flag && !b) {
*outvar = 0;
return a;
}
if (IS_TYPE_VECTOR(type)) {
update_out_param_align(16);
a = ALIGN(a + 16 + 24, 16) - 24;
} else if (IS_TYPE_ARRAY(type) || IS_TYPE_NONVECTOR_STRUCT(type) || IS_TYPE_CLASS(type) || IS_TYPE_12BYTES_MEMBERPOINTER(type)) {
argAlign = CMach_ArgumentAlignment(type);
if (argAlign > 4) {
a = ALIGN(a + 24, argAlign) - 24;
update_in_param_align(argAlign);
}
}
*outvar = a;
a = ALIGN(a + b, 4);
return a;
}
SInt32 out_param_displ_to_offset(SInt32 displ) {
return displ + 24;
}
Boolean needs_frame(void) {
return (frame_size > 224) || requires_frame;
}
void update_out_param_size(SInt32 size) {
if (size < 32)
size = 32;
if (parameter_area_size < size)
parameter_area_size = size;
}
void estimate_out_param_size(SInt32 size) {
if (parameter_area_size_estimate < size)
parameter_area_size_estimate = size;
}
void update_out_param_align(SInt32 align) {
if (out_param_alignment < align)
out_param_alignment = align;
update_frame_align(align);
}
void update_in_param_align(SInt32 align) {
if (in_param_alignment < align)
in_param_alignment = align;
}
void update_frame_align(SInt32 align) {
if (frame_alignment < align)
frame_alignment = align;
}
SInt32 local_offset_32(Object *obj) {
short align;
SInt32 offset;
if (obj->u.var.info->flags & VarInfoFlag1)
align = CMach_ArgumentAlignment(obj->type);
else
align = CMach_AllocationAlignment(obj->type, obj->qual);
offset = obj->u.var.uid;
if (offset > 0x7FFF)
offset = 0x8000 - offset - ALIGN(obj->type->size, align);
if (obj->u.var.info->flags & VarInfoFlag1)
return offset + parametersbase(local_base_register(obj) != _FP_);
else
return offset + localsbase();
}
SInt32 local_offset_lo(Object *obj, SInt32 offset) {
SInt32 combo = offset + local_offset_32(obj);
return LOW_PART(combo);
//return (SInt16) (offset + local_offset_32(obj));
}
SInt32 local_offset_ha(Object *obj, SInt32 offset) {
SInt32 combo = offset + local_offset_32(obj);
return HIGH_PART(combo);
//return (SInt16) ((combo >> 16) + ((combo & 0x8000) >> 15));
}
SInt32 local_offset_16(Object *obj) {
SInt32 offset32 = local_offset_32(obj);
SInt16 offset16 = (SInt16) offset32;
CError_ASSERT(2662, offset32 == offset16);
return offset16;
}
Boolean local_is_16bit_offset(Object *obj) {
SInt32 offset32 = local_offset_32(obj);
SInt16 offset16 = (SInt16) offset32;
return offset32 == offset16;
}
int local_base_register(Object *obj) {
PCode *pc;
if (obj->u.var.info->flags & VarInfoFlag1) {
if (coloring && _CALLER_SP_ == -1) {
_CALLER_SP_ = used_virtual_registers[RegClass_GPR]++;
pc = makepcode(PC_LWZ, _CALLER_SP_, 1, 0, 0);
setup_caller_sp = pc;
appendpcode(prologue, pc);
}
return _CALLER_SP_;
} else {
return _FP_;
}
}
static UInt32 align_bits(UInt32 value, UInt8 bitcount) {
UInt32 base = bitcount != 0;
switch (value) {
case 0x0002: return base + 30;
case 0x0004: return base + 29;
case 0x0008: return base + 28;
case 0x0010: return base + 27;
case 0x0020: return base + 26;
case 0x0040: return base + 25;
case 0x0080: return base + 24;
case 0x0100: return base + 23;
case 0x0200: return base + 22;
case 0x0400: return base + 21;
case 0x0800: return base + 20;
case 0x1000: return base + 19;
case 0x2000: return base + 18;
default:
CError_FATAL(2754);
return base + 27;
}
}
Boolean is_large_frame(void) {
CError_ASSERT(2769, frame_size != -1);
return large_stack;
}
void no_frame_for_asm(void) {
frame_size = 0;
}
Boolean can_add_displ_to_local(Object *obj, SInt32 displ) {
if (obj->datatype != DLOCAL)
return 0;
if (local_offset_32(obj) == (short) local_offset_32(obj))
if ((displ + local_offset_32(obj)) == (short) (displ + local_offset_32(obj)))
return 1;
return 0;
}
SInt32 get_alloca_alignment(void) {
SInt32 align = frame_alignment;
if (copts.altivec_model)
align = ALIGN(align, 16);
if (!alloca_alignment)
alloca_alignment = align;
else
CError_ASSERT(2825, alloca_alignment == align);
return align_bits(align, 0);
}
static Boolean use_helper_function(char rclass) {
if (copts.no_register_save_helpers)
return 0;
switch (rclass) {
case RegClass_GPR:
if (copts.use_lmw_stmw)
return 0;
return (used_nonvolatile_registers[RegClass_GPR] > 4) || (copts.optimizesize && used_nonvolatile_registers[RegClass_GPR] > 2);
case RegClass_FPR:
return (used_nonvolatile_registers[RegClass_FPR] > 3) || (copts.optimizesize && used_nonvolatile_registers[RegClass_FPR] > 2);
case RegClass_VR:
return (used_nonvolatile_registers[RegClass_VR] > 3) || (copts.optimizesize && used_nonvolatile_registers[RegClass_VR] > 2);
default:
CError_FATAL(2862);
return 0;
}
}
static Boolean need_link_register(void) {
if (copts.codegen_pic && uses_globals)
return 1;
if (makes_call)
return 1;
return use_helper_function(RegClass_FPR) || use_helper_function(RegClass_GPR) || use_helper_function(RegClass_VR);
}
static void call_helper_function(char *name, char rclass, short effect) {
char str[32];
Object *func;
NameSpace *save_scope;
PCode *pc;
int extra_args;
PCodeArg *arg;
short i;
extra_args = 1;
if (rclass == RegClass_VR)
extra_args = 2;
sprintf(str, name, 32 - used_nonvolatile_registers[rclass]);
save_scope = cscope_current;
cscope_current = cscope_root;
func = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
cscope_current = save_scope;
func->name = GetHashNameNodeExport(str);
pc = makepcode(PC_BL, extra_args + used_nonvolatile_registers[rclass], func, 0);
for (i = 1, arg = &pc->args[1]; i <= used_nonvolatile_registers[rclass]; i++, arg++) {
arg->kind = PCOp_REGISTER;
arg->arg = rclass;
arg->data.reg.reg = n_real_registers[rclass] - i;
arg->data.reg.effect = effect;
}
if (rclass == RegClass_VR) {
arg[1].kind = PCOp_REGISTER;
arg[1].arg = RegClass_GPR;
arg[1].data.reg.reg = 12;
arg[1].data.reg.effect = EffectWrite;
arg[2].kind = PCOp_REGISTER;
arg[2].arg = RegClass_GPR;
arg[2].data.reg.reg = 0;
arg[2].data.reg.effect = EffectRead;
} else {
arg[1].kind = PCOp_REGISTER;
arg[1].arg = RegClass_GPR;
arg[1].data.reg.reg = 11;
arg[1].data.reg.effect = EffectRead;
}
appendpcode(pclastblock, pc);
setpcodeflags(fSideEffects);
}
static SInt32 nearest_power_of_two(SInt32 n) {
SInt32 power = 1;
do {
power <<= 1;
} while (power && power < n);
CError_ASSERT(2933, power != 0);
return power;
}
static void compress_data_area(void) {
// doesn't quite match
SInt32 r0;
SInt32 r7;
ObjectList *list;
Object *obj;
PCodeBlock *block;
PCode *pc;
int i;
compressing_data_area = 1;
if (large_stack) {
r0 = 0;
} else {
r0 = parameter_area_size;
if (r0 < 32)
r0 = 32;
}
r7 = ALIGN(r0 + 24, frame_alignment) - 24;
local_data_limit = 0x8000 - ALIGN(24 + in_parameter_size + nonvolatile_save_size + r7, frame_alignment);
if (local_objects_tail[ObjClass0]) {
if (local_objects[ObjClass1]) {
local_objects_tail[ObjClass0]->next = local_objects[ObjClass1];
local_objects_tail[ObjClass0] = local_objects_tail[ObjClass1];
}
if (local_objects[ObjClass2]) {
local_objects_tail[ObjClass0]->next = local_objects[ObjClass2];
local_objects_tail[ObjClass0] = local_objects_tail[ObjClass2];
}
} else if (local_objects_tail[ObjClass1]) {
local_objects[ObjClass0] = local_objects[ObjClass1];
local_objects_tail[ObjClass0] = local_objects_tail[ObjClass1];
if (local_objects[ObjClass2]) {
local_objects_tail[ObjClass0]->next = local_objects[ObjClass2];
local_objects_tail[ObjClass0] = local_objects_tail[ObjClass2];
}
} else {
local_objects[ObjClass0] = local_objects[ObjClass2];
local_objects_tail[ObjClass0] = local_objects_tail[ObjClass2];
}
for (list = local_objects[ObjClass0]; list; list = list->next)
Registers_GetVarInfo(list->object)->used = 0;
for (block = pcbasicblocks; block; block = block->nextBlock) {
for (pc = block->firstPCode; pc; pc = pc->nextPCode) {
for (i = 0; i < pc->argCount; i++) {
if (pc->args[i].kind == PCOp_MEMORY && pc->args[i].data.mem.obj && pc->args[i].data.mem.obj->datatype == DLOCAL)
Registers_GetVarInfo(pc->args[i].data.mem.obj)->used = 1;
}
}
}
local_data_size = 0;
large_data_near_size = 0;
large_data_far_size = 0;
for (list = local_objects[ObjClass0]; list; list = list->next) {
obj = list->object;
if (Registers_GetVarInfo(obj)->used)
assign_local_memory(obj);
}
}
static void insert_local_object(UInt8 oclass, Object *obj) {
ObjectList *list;
if (!compressing_data_area) {
list = lalloc(sizeof(ObjectList));
memclrw(list, sizeof(ObjectList));
list->object = obj;
if (!local_objects[oclass])
local_objects[oclass] = list;
if (local_objects_tail[oclass])
local_objects_tail[oclass]->next = list;
local_objects_tail[oclass] = list;
}
}