#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(""); 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.optimize_for_size && (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.optimize_for_size && (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(""); 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.little_endian && (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.optimize_for_size && used_nonvolatile_registers[RegClass_GPR] > 2); case RegClass_FPR: return (used_nonvolatile_registers[RegClass_FPR] > 3) || (copts.optimize_for_size && used_nonvolatile_registers[RegClass_FPR] > 2); case RegClass_VR: return (used_nonvolatile_registers[RegClass_VR] > 3) || (copts.optimize_for_size && 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; } }