mirror of https://git.wuffs.org/MWCC
2437 lines
76 KiB
C
2437 lines
76 KiB
C
#include "compiler/CodeGen.h"
|
|
#include "compiler/CCompiler.h"
|
|
#include "compiler/CDecl.h"
|
|
#include "compiler/CError.h"
|
|
#include "compiler/CExpr.h"
|
|
#include "compiler/CFunc.h"
|
|
#include "compiler/CInt64.h"
|
|
#include "compiler/CMachine.h"
|
|
#include "compiler/CMangler.h"
|
|
#include "compiler/COptimizer.h"
|
|
#include "compiler/CParser.h"
|
|
#include "compiler/CPrep.h"
|
|
#include "compiler/CPrepTokenizer.h"
|
|
#include "compiler/Coloring.h"
|
|
#include "compiler/CompilerTools.h"
|
|
#include "compiler/uDump.h"
|
|
#include "compiler/Exceptions.h"
|
|
#include "compiler/InlineAsmPPC.h"
|
|
#include "compiler/Intrinsics.h"
|
|
#include "compiler/InstrSelection.h"
|
|
#include "compiler/GlobalOptimizer.h"
|
|
#include "compiler/ObjGenMachO.h"
|
|
#include "compiler/Operands.h"
|
|
#include "compiler/PCode.h"
|
|
#include "compiler/PCodeAssembly.h"
|
|
#include "compiler/PCodeInfo.h"
|
|
#include "compiler/PCodeListing.h"
|
|
#include "compiler/PCodeUtilities.h"
|
|
#include "compiler/Peephole.h"
|
|
#include "compiler/PPCError.h"
|
|
#include "compiler/RegisterInfo.h"
|
|
#include "compiler/Scheduler.h"
|
|
#include "compiler/StackFrame.h"
|
|
#include "compiler/Switch.h"
|
|
#include "compiler/TOC.h"
|
|
#include "compiler/ValueNumbering.h"
|
|
#include "compiler/enode.h"
|
|
#include "compiler/objc.h"
|
|
#include "compiler/objects.h"
|
|
#include "compiler/scopes.h"
|
|
#include "compiler/tokens.h"
|
|
#include "compiler/types.h"
|
|
|
|
static Macro powcM;
|
|
static Macro __powcM;
|
|
static Macro ppc_cpu;
|
|
static Macro profM;
|
|
static Macro hostM;
|
|
static Macro bendM;
|
|
static Macro _ppc_M;
|
|
static Macro longI;
|
|
static Macro IEEED;
|
|
Macro vecM;
|
|
Macro altivecM;
|
|
static Macro macM2;
|
|
static Macro appleM;
|
|
static Macro optM;
|
|
static Macro alignM;
|
|
static Macro _machM;
|
|
static Macro archM;
|
|
static Macro dynM;
|
|
static Macro ppcM;
|
|
Object *gFunction;
|
|
static ObjectList *temps;
|
|
CLabel *returnlabel;
|
|
CLabel *cleanreturnlabel;
|
|
Boolean needs_cleanup;
|
|
Statement *current_statement;
|
|
int has_catch_blocks;
|
|
int disable_optimizer;
|
|
SInt32 current_linenumber;
|
|
Boolean has_altivec_arrays;
|
|
short high_reg;
|
|
short low_reg;
|
|
short high_offset;
|
|
short low_offset;
|
|
short low_reg2;
|
|
short high_reg2;
|
|
PCodeLabel *pic_base_pcodelabel;
|
|
Object *dyld_stub_binding_helper;
|
|
Object *rt_cvt_fp2unsigned;
|
|
Object *rt_profile_entry;
|
|
Object *rt_profile_exit;
|
|
Object *rt_div2i;
|
|
Object *rt_div2u;
|
|
Object *rt_mod2i;
|
|
Object *rt_mod2u;
|
|
Object *rt_shr2i;
|
|
Object *rt_shr2u;
|
|
Object *rt_shl2i;
|
|
Object *rt_cvt_ull_dbl;
|
|
Object *rt_cvt_sll_dbl;
|
|
Object *rt_cvt_ull_flt;
|
|
Object *rt_cvt_sll_flt;
|
|
Object *rt_cvt_dbl_usll;
|
|
static heaperror_t saveheaperror;
|
|
|
|
enum {
|
|
GPRLimit = 10,
|
|
FPRLimit = 13,
|
|
VRLimit = 13
|
|
};
|
|
|
|
// forward decls
|
|
static void CodeGen_heaperror(void);
|
|
|
|
VarInfo *CodeGen_GetNewVarInfo(void) {
|
|
VarInfo *vi;
|
|
|
|
vi = lalloc(sizeof(VarInfo));
|
|
memclrw(vi, sizeof(VarInfo));
|
|
|
|
vi->deftoken = *CPrep_CurStreamElement();
|
|
vi->varnumber = localcount++;
|
|
|
|
return vi;
|
|
}
|
|
|
|
Object *maketemporary(Type *type) {
|
|
ObjectList *list;
|
|
Object *obj;
|
|
|
|
for (list = temps; list; list = list->next) {
|
|
obj = list->object;
|
|
if (obj->u.var.uid == 0 && obj->type == type) {
|
|
obj->u.var.uid = 1;
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
obj = lalloc(sizeof(Object));
|
|
memclrw(obj, sizeof(Object));
|
|
obj->otype = OT_OBJECT;
|
|
obj->access = ACCESSPUBLIC;
|
|
obj->datatype = DLOCAL;
|
|
obj->type = type;
|
|
obj->name = CParser_GetUniqueName();
|
|
obj->u.var.info = CodeGen_GetNewVarInfo();
|
|
obj->u.var.uid = 1;
|
|
|
|
list = lalloc(sizeof(ObjectList));
|
|
memclrw(list, sizeof(ObjectList));
|
|
list->next = temps;
|
|
list->object = obj;
|
|
temps = list;
|
|
|
|
return obj;
|
|
}
|
|
|
|
static void free_temporaries(void) {
|
|
ObjectList *list;
|
|
|
|
for (list = temps; list; list = list->next)
|
|
list->object->u.var.uid = 0;
|
|
}
|
|
|
|
static void allocate_temporaries(void) {
|
|
ObjectList *list;
|
|
|
|
for (list = temps; list; list = list->next)
|
|
assign_local_memory(list->object);
|
|
}
|
|
|
|
void process_arguments(ArgumentProcessor func, Boolean flag) {
|
|
short gpr = 3;
|
|
short fpr = 1;
|
|
short vr = 2;
|
|
Type *type;
|
|
ObjectList *list;
|
|
|
|
for (list = arguments; list; list = list->next) {
|
|
type = list->object->type;
|
|
if (IS_TYPE_FLOAT(type)) {
|
|
func(list->object, (fpr <= FPRLimit) ? fpr : 0);
|
|
fpr++;
|
|
if (type->size == 4)
|
|
gpr++;
|
|
else
|
|
gpr += 2;
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
func(list->object, (vr <= VRLimit) ? vr : 0);
|
|
vr++;
|
|
if (flag) {
|
|
if ((vr - 1) == 2)
|
|
gpr = 9;
|
|
else if ((vr - 1) > 2)
|
|
gpr = 11;
|
|
}
|
|
} else {
|
|
func(list->object, (gpr <= GPRLimit) ? gpr : 0);
|
|
if (TYPE_FITS_IN_REGISTER(type)) {
|
|
if (type->size <= 4)
|
|
gpr += 1;
|
|
else
|
|
gpr += 2;
|
|
} else {
|
|
gpr += (type->size >> 2);
|
|
if (type->size & 3)
|
|
gpr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
last_argument_register[RegClass_GPR] = gpr - 1;
|
|
last_argument_register[RegClass_FPR] = fpr - 1;
|
|
last_argument_register[RegClass_VR] = vr - 1;
|
|
if (flag)
|
|
move_varargs_to_memory();
|
|
}
|
|
|
|
static void retain_argument_register(Object *obj, short reg) {
|
|
VarInfo *vi = Registers_GetVarInfo(obj);
|
|
Type *type = obj->type;
|
|
|
|
if (reg && !vi->noregister && vi->used) {
|
|
if (TYPE_FITS_IN_REGISTER(type)) {
|
|
if (type->size <= 4) {
|
|
retain_register(obj, RegClass_GPR, reg);
|
|
} else if (reg < GPRLimit) {
|
|
if (copts.little_endian)
|
|
retain_GPR_pair(obj, reg, reg + 1);
|
|
else
|
|
retain_GPR_pair(obj, reg + 1, reg);
|
|
}
|
|
} else if (IS_TYPE_FLOAT(type)) {
|
|
retain_register(obj, RegClass_FPR, reg);
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
retain_register(obj, RegClass_VR, reg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void allocate_local_vregs(void) {
|
|
VarInfo *vi;
|
|
ObjectList *list;
|
|
Object *obj;
|
|
|
|
if (copts.codegen_pic && uses_globals && assignable_registers[RegClass_GPR]) {
|
|
if (assignable_registers[RegClass_GPR]) {
|
|
vi = pic_base.u.var.info;
|
|
vi->reg = 0;
|
|
assign_register_by_type(&pic_base);
|
|
pic_base_reg = vi->reg;
|
|
CError_ASSERT(497, pic_base_reg);
|
|
} else {
|
|
CError_FATAL(500);
|
|
}
|
|
} else {
|
|
pic_base_reg = 0;
|
|
}
|
|
|
|
for (list = exceptionlist; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
|
|
if (vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && !vi->reg)
|
|
assign_register_by_type(obj);
|
|
}
|
|
}
|
|
|
|
set_last_exception_registers();
|
|
|
|
for (list = arguments; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
|
|
if (vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && !vi->reg)
|
|
assign_register_by_type(obj);
|
|
}
|
|
}
|
|
|
|
for (list = locals; list; list = list->next) {
|
|
obj = list->object;
|
|
if (!IsTempName(obj->name)) {
|
|
vi = Registers_GetVarInfo(obj);
|
|
|
|
if (vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && !vi->reg)
|
|
assign_register_by_type(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
open_fe_temp_registers();
|
|
|
|
for (list = locals; list; list = list->next) {
|
|
obj = list->object;
|
|
if (IsTempName(obj->name)) {
|
|
vi = Registers_GetVarInfo(obj);
|
|
|
|
if (vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && !vi->reg)
|
|
assign_register_by_type(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (list = toclist; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
|
|
if (!vi->reg && vi->used && vi->usage > 1)
|
|
assign_register_by_type(obj);
|
|
}
|
|
}
|
|
|
|
static void allocate_local_GPRs(void) {
|
|
ObjectList *list;
|
|
Object *obj;
|
|
Object *winning_obj;
|
|
SInt32 winning_usage;
|
|
VarInfo *vi;
|
|
Type *type;
|
|
|
|
if (copts.codegen_pic && uses_globals && assignable_registers[RegClass_GPR]) {
|
|
vi = pic_base.u.var.info;
|
|
vi->reg = 0;
|
|
assign_register_by_type(&pic_base);
|
|
pic_base_reg = vi->reg;
|
|
CError_ASSERT(605, pic_base_reg);
|
|
} else {
|
|
pic_base_reg = 0;
|
|
}
|
|
|
|
while (assignable_registers[RegClass_GPR]) {
|
|
winning_obj = NULL;
|
|
winning_usage = -1;
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = arguments; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
type = obj->type;
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (TYPE_FITS_IN_REGISTER(type) && (!TYPE_IS_8BYTES(type) || assignable_registers[RegClass_GPR] >= 2)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = locals; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
type = obj->type;
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (TYPE_FITS_IN_REGISTER(type) && (!TYPE_IS_8BYTES(type) || assignable_registers[RegClass_GPR] >= 2)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (list = toclist; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used) {
|
|
if (vi->usage >= winning_usage && vi->usage >= 3) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!winning_obj)
|
|
break;
|
|
|
|
assign_register_by_type(winning_obj);
|
|
CError_ASSERT(698, Registers_GetVarInfo(winning_obj)->flags & VarInfoFlag2);
|
|
}
|
|
}
|
|
|
|
static void allocate_local_FPRs(void) {
|
|
ObjectList *list;
|
|
Object *obj;
|
|
Object *winning_obj;
|
|
SInt32 winning_usage;
|
|
VarInfo *vi;
|
|
Type *type;
|
|
|
|
while (assignable_registers[RegClass_FPR]) {
|
|
winning_obj = NULL;
|
|
winning_usage = -1;
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = arguments; list; list = list->next) {
|
|
obj = list->object;
|
|
type = obj->type;
|
|
vi = Registers_GetVarInfo(obj);
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (IS_TYPE_FLOAT(type)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = locals; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (IS_TYPE_FLOAT(obj->type)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!winning_obj)
|
|
break;
|
|
|
|
assign_register_by_type(winning_obj);
|
|
CError_ASSERT(782, Registers_GetVarInfo(winning_obj)->flags & VarInfoFlag2);
|
|
}
|
|
}
|
|
|
|
static void allocate_local_VRs(void) {
|
|
ObjectList *list;
|
|
Object *obj;
|
|
Object *winning_obj;
|
|
SInt32 winning_usage;
|
|
VarInfo *vi;
|
|
|
|
while (assignable_registers[RegClass_VR]) {
|
|
winning_obj = NULL;
|
|
winning_usage = -1;
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = arguments; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (IS_TYPE_VECTOR(obj->type)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!(disable_optimizer & 2)) {
|
|
for (list = locals; list; list = list->next) {
|
|
obj = list->object;
|
|
vi = Registers_GetVarInfo(obj);
|
|
if (vi->flags & VarInfoFlag40)
|
|
vi->usage = 100000;
|
|
if (!vi->reg && vi->used && !vi->noregister) {
|
|
if (!OBJ_GET_TARGET_VOLATILE(obj) && vi->usage >= winning_usage && vi->usage >= 2) {
|
|
if (IS_TYPE_VECTOR(obj->type)) {
|
|
winning_obj = obj;
|
|
winning_usage = vi->usage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!winning_obj)
|
|
break;
|
|
|
|
assign_register_by_type(winning_obj);
|
|
CError_ASSERT(846, Registers_GetVarInfo(winning_obj)->flags & VarInfoFlag2);
|
|
}
|
|
}
|
|
|
|
static void allocate_locals(void) {
|
|
has_altivec_arrays = 0;
|
|
|
|
if (!requires_frame && !optimizing)
|
|
process_arguments(retain_argument_register, 0);
|
|
|
|
if (optimizing) {
|
|
allocate_local_vregs();
|
|
} else {
|
|
allocate_local_GPRs();
|
|
allocate_local_FPRs();
|
|
allocate_local_VRs();
|
|
}
|
|
|
|
assign_locals_to_memory(locals);
|
|
}
|
|
|
|
void move_assigned_argument(Object *obj, short reg) {
|
|
VarInfo *vi;
|
|
Type *type;
|
|
SInt32 bytesLeft;
|
|
SInt32 offset;
|
|
|
|
vi = Registers_GetVarInfo(obj);
|
|
type = obj->type;
|
|
CError_ASSERT(901, obj->datatype == DLOCAL);
|
|
|
|
if (!vi->used)
|
|
return;
|
|
|
|
if (reg) {
|
|
if (vi->reg) {
|
|
if (TYPE_IS_8BYTES(type)) {
|
|
if (copts.little_endian) {
|
|
if (vi->reg != reg)
|
|
emitpcode(PC_MR, vi->reg, reg);
|
|
if (reg < GPRLimit) {
|
|
CError_FAIL(916, (vi->regHi == reg) || (vi->reg == (reg + 1)));
|
|
if (vi->regHi != (reg + 1))
|
|
emitpcode(PC_MR, vi->regHi, reg + 1);
|
|
} else {
|
|
load_store_register(PC_LWZ, vi->regHi, local_base_register(obj), obj, high_offset);
|
|
}
|
|
} else {
|
|
if (vi->regHi != reg)
|
|
emitpcode(PC_MR, vi->regHi, reg);
|
|
if (reg < GPRLimit) {
|
|
CError_FAIL(931, (vi->reg == reg) || (vi->regHi == (reg + 1)));
|
|
if (vi->reg != (reg + 1))
|
|
emitpcode(PC_MR, vi->reg, reg + 1);
|
|
} else {
|
|
load_store_register(PC_LWZ, vi->reg, local_base_register(obj), obj, low_offset);
|
|
}
|
|
}
|
|
} else if (vi->reg != reg) {
|
|
if (IS_TYPE_FLOAT(type)) {
|
|
emitpcode(PC_FMR, vi->reg, reg);
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
emitpcode(PC_VMR, vi->reg, reg);
|
|
} else {
|
|
emitpcode(PC_MR, vi->reg, reg);
|
|
}
|
|
}
|
|
} else {
|
|
if (IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)) {
|
|
load_store_register(PC_STW, reg, local_base_register(obj), obj, 0);
|
|
} else if (IS_TYPE_INT(type) || IS_TYPE_ENUM(type)) {
|
|
switch (type->size) {
|
|
case 1:
|
|
load_store_register(PC_STB, reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 2:
|
|
load_store_register(PC_STH, reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 4:
|
|
load_store_register(PC_STW, reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 8:
|
|
load_store_register(PC_STW, reg, local_base_register(obj), obj, 0);
|
|
if (reg < GPRLimit)
|
|
load_store_register(PC_STW, reg + 1, local_base_register(obj), obj, 4);
|
|
break;
|
|
default:
|
|
CError_FATAL(993);
|
|
}
|
|
} else if (IS_TYPE_FLOAT(type)) {
|
|
load_store_register((type->size == 4) ? PC_STFS : PC_STFD, reg, local_base_register(obj), obj, 0);
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
load_store_register(PC_STVX, reg, local_base_register(obj), obj, 0);
|
|
} else {
|
|
bytesLeft = (11 - reg) * 4;
|
|
if (bytesLeft > obj->type->size)
|
|
bytesLeft = obj->type->size;
|
|
offset = 0;
|
|
while (bytesLeft > 0) {
|
|
load_store_register(PC_STW, reg, local_base_register(obj), obj, offset);
|
|
reg++;
|
|
offset += 4;
|
|
bytesLeft -= 4;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (vi->reg) {
|
|
if (IS_TYPE_POINTER(type) || IS_TYPE_4BYTES_MEMBERPOINTER(type)) {
|
|
load_store_register(PC_LWZ, vi->reg, local_base_register(obj), obj, 0);
|
|
} else if (IS_TYPE_FLOAT(type)) {
|
|
load_store_register((type->size == 4) ? PC_LFS : PC_LFD, vi->reg, local_base_register(obj), obj, 0);
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
load_store_register(PC_LVX, vi->reg, local_base_register(obj), obj, 0);
|
|
} else {
|
|
switch (type->size) {
|
|
case 1:
|
|
load_store_register(PC_LBZ, vi->reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 2:
|
|
load_store_register(is_unsigned(type) ? PC_LHZ : PC_LHA, vi->reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 4:
|
|
load_store_register(PC_LWZ, vi->reg, local_base_register(obj), obj, 0);
|
|
break;
|
|
case 8:
|
|
load_store_register(PC_LWZ, vi->regHi, local_base_register(obj), obj, high_offset);
|
|
load_store_register(PC_LWZ, vi->reg, local_base_register(obj), obj, low_offset);
|
|
break;
|
|
default:
|
|
CError_FATAL(1095);
|
|
}
|
|
}
|
|
} else if (!optimizing) {
|
|
local_base_register(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void load_TOC_pointers(void) {
|
|
VarInfo *vi;
|
|
Object *obj;
|
|
ObjectList *list;
|
|
PCode *pc;
|
|
Operand opnd;
|
|
|
|
if (uses_globals && pic_base_reg) {
|
|
pic_base_pcodelabel = makepclabel();
|
|
pc = makepcode(PC_BC, 20, 7, 3, pic_base_pcodelabel);
|
|
pcsetlinkbit(pc);
|
|
pcsetsideeffects(pc);
|
|
appendpcode(pclastblock, pc);
|
|
pcbranch(pclastblock, pic_base_pcodelabel);
|
|
makepcblock();
|
|
pclabel(pclastblock, pic_base_pcodelabel);
|
|
emitpcode(PC_MFLR, pic_base_reg);
|
|
}
|
|
|
|
memclrw(&opnd, sizeof(Operand));
|
|
for (list = toclist; list; list = list->next) {
|
|
obj = list->object;
|
|
if ((vi = Registers_GetVarInfo(obj)) && (vi->flags & VarInfoFlag2)) {
|
|
switch (obj->datatype) {
|
|
case DNONLAZYPTR:
|
|
symbol_operand(&opnd, obj);
|
|
opnd.optype = OpndType_IndirectSymbol;
|
|
if (opnd.optype)
|
|
Coerce_to_register(&opnd, TYPE(&void_ptr), vi->reg);
|
|
if (opnd.reg != vi->reg)
|
|
emitpcode(PC_MR, vi->reg, opnd.reg);
|
|
break;
|
|
default:
|
|
CError_FATAL(1206);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int has_vararglist(Object *funcobj) {
|
|
FuncArg *arg;
|
|
|
|
arg = TYPE_FUNC(funcobj->type)->args;
|
|
while (arg && arg != &elipsis)
|
|
arg = arg->next;
|
|
|
|
return (arg == &elipsis);
|
|
}
|
|
|
|
void assign_labels(Statement *stmt) {
|
|
Statement *last;
|
|
|
|
last = NULL;
|
|
while (stmt) {
|
|
if (stmt->type == ST_LABEL && !stmt->label->pclabel) {
|
|
if (last && last->type == ST_LABEL)
|
|
stmt->label->pclabel = last->label->pclabel;
|
|
else
|
|
stmt->label->pclabel = makepclabel();
|
|
}
|
|
|
|
last = stmt;
|
|
stmt = stmt->next;
|
|
}
|
|
}
|
|
|
|
static Boolean islaststatement(Statement *stmt) {
|
|
for (stmt = stmt->next; stmt; stmt = stmt->next) {
|
|
if (stmt->type > ST_LABEL)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void newstatement(SInt32 sourceoffset, SInt32 value, int flag) {
|
|
PCodeBlock *block = pclastblock;
|
|
|
|
pcloopweight = value;
|
|
if (!block->pcodeCount)
|
|
block->loopWeight = value;
|
|
|
|
if (block->pcodeCount > 100)
|
|
branch_label(makepclabel());
|
|
|
|
if (flag)
|
|
block->flags |= fPCBlockFlag4000;
|
|
}
|
|
|
|
static void expressionstatement(ENode *expr) {
|
|
Operand opnd;
|
|
|
|
memclrw(&opnd, sizeof(Operand));
|
|
cgdispatch[expr->type](expr, 0, 0, &opnd);
|
|
|
|
if (ENODE_IS(expr, EINDIRECT) && (opnd.flags & OpndFlags_Volatile)) {
|
|
if (TYPE_FITS_IN_REGISTER_2(expr->rtype)) {
|
|
if (opnd.optype != OpndType_GPR)
|
|
Coerce_to_register(&opnd, expr->rtype, 0);
|
|
} else if (IS_TYPE_FLOAT(expr->rtype)) {
|
|
if (opnd.optype != OpndType_FPR)
|
|
Coerce_to_fp_register(&opnd, expr->rtype, 0);
|
|
} else if (IS_TYPE_VECTOR(expr->rtype)) {
|
|
if (opnd.optype != OpndType_VR)
|
|
Coerce_to_v_register(&opnd, expr->rtype, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void labelstatement(CLabel *label) {
|
|
if (!label->pclabel)
|
|
label->pclabel = makepclabel();
|
|
if (!label->pclabel->resolved)
|
|
branch_label(label->pclabel);
|
|
free_temporaries();
|
|
}
|
|
|
|
static void gotostatement(CLabel *label) {
|
|
if (!label->pclabel)
|
|
label->pclabel = makepclabel();
|
|
branch_always(label->pclabel);
|
|
free_temporaries();
|
|
}
|
|
|
|
static void gotoexpression(ENode *expr) {
|
|
Operand opnd;
|
|
CodeLabelList *list;
|
|
|
|
memclrw(&opnd, sizeof(Operand));
|
|
cgdispatch[expr->type](expr, 0, 0, &opnd);
|
|
|
|
if (opnd.optype != OpndType_GPR)
|
|
Coerce_to_register(&opnd, TYPE(&void_ptr), 0);
|
|
CError_ASSERT(1397, opnd.optype == OpndType_GPR);
|
|
|
|
for (list = codelabellist; list; list = list->next)
|
|
pcbranch(pclastblock, list->label->pclabel);
|
|
|
|
emitpcode(PC_MTCTR, opnd.reg);
|
|
branch_indirect(NULL);
|
|
}
|
|
|
|
static void conditionalstatement(ENode *cond, CLabel *label, short truthy) {
|
|
Operand opnd;
|
|
memclrw(&opnd, sizeof(Operand));
|
|
|
|
cond = evaluate_and_skip_comma(cond);
|
|
if (!label->pclabel)
|
|
label->pclabel = makepclabel();
|
|
|
|
if (TYPE_IS_8BYTES(cond->data.diadic.right->rtype) || TYPE_IS_8BYTES(cond->data.diadic.left->rtype))
|
|
I8_gen_condition(cond, &opnd, 0);
|
|
else
|
|
gen_condition(cond, &opnd);
|
|
|
|
branch_conditional(opnd.reg, opnd.regOffset, truthy, label->pclabel);
|
|
free_temporaries();
|
|
}
|
|
|
|
static void returnstatement(ENode *expr, Boolean dont_branch) {
|
|
Operand opnd;
|
|
Type *type;
|
|
memclrw(&opnd, sizeof(Operand));
|
|
|
|
if (expr) {
|
|
type = expr->rtype;
|
|
if (TYPE_FITS_IN_REGISTER(type)) {
|
|
if (TYPE_IS_8BYTES(type)) {
|
|
cgdispatch[expr->type](expr, low_reg, high_reg, &opnd);
|
|
coerce_to_register_pair(&opnd, type, low_reg, high_reg);
|
|
} else {
|
|
cgdispatch[expr->type](expr, 3, 0, &opnd);
|
|
if (opnd.optype != OpndType_GPR)
|
|
Coerce_to_register(&opnd, type, 3);
|
|
if (opnd.reg != 3)
|
|
emitpcode(PC_MR, 3, opnd.reg);
|
|
}
|
|
} else if (IS_TYPE_FLOAT(type)) {
|
|
cgdispatch[expr->type](expr, 1, 0, &opnd);
|
|
if (opnd.optype != OpndType_FPR)
|
|
Coerce_to_fp_register(&opnd, type, 1);
|
|
if (opnd.reg != 1)
|
|
emitpcode(PC_FMR, 1, opnd.reg);
|
|
} else if (IS_TYPE_VECTOR(type)) {
|
|
cgdispatch[expr->type](expr, 2, 0, &opnd);
|
|
if (opnd.optype != OpndType_VR)
|
|
Coerce_to_v_register(&opnd, type, 2);
|
|
if (opnd.reg != 2)
|
|
emitpcode(PC_VMR, 2, opnd.reg);
|
|
} else {
|
|
cgdispatch[expr->type](expr, 0, 0, &opnd);
|
|
}
|
|
}
|
|
|
|
if (!dont_branch) {
|
|
if (!returnlabel->pclabel)
|
|
returnlabel->pclabel = makepclabel();
|
|
branch_always(returnlabel->pclabel);
|
|
free_temporaries();
|
|
}
|
|
}
|
|
|
|
static void capturestackpointer(Object *obj) {
|
|
branch_label(makepclabel());
|
|
CError_ASSERT(1568, obj->datatype == DLOCAL);
|
|
|
|
load_store_register(PC_STW, 1, local_base_register(obj), obj, 20);
|
|
branch_label(makepclabel());
|
|
}
|
|
|
|
static void resetstackpointer(Object *obj) {
|
|
PCode *pc;
|
|
|
|
CError_ASSERT(1595, obj->datatype == DLOCAL);
|
|
|
|
branch_label(makepclabel());
|
|
|
|
load_store_register(PC_LWZ, 0, 1, NULL, 0);
|
|
|
|
pc = makepcode(PC_LWZ, 1, local_base_register(obj), obj, 20);
|
|
pcsetsideeffects(pc);
|
|
appendpcode(pclastblock, pc);
|
|
|
|
load_store_register(PC_STW, 0, 1, NULL, 0);
|
|
|
|
branch_label(makepclabel());
|
|
}
|
|
|
|
static void callprofiler(char *name) {
|
|
UInt32 masks[RegClassMax] = {0, 0, 0, 0, 0};
|
|
load_store_register(PC_LWZ, 3, 1, NULL, 0);
|
|
load_store_register(PC_LWZ, 3, 3, NULL, 8);
|
|
masks[RegClass_GPR] |= (1 << 3);
|
|
branch_subroutine(rt_profile_entry, 1, masks);
|
|
}
|
|
|
|
static void exitprofiler(void) {
|
|
}
|
|
|
|
void CodeGen_Generator(Statement *statements, Object *func, UInt8 mysteryFlag, Boolean callOnModuleBind) {
|
|
Statement *stmt;
|
|
Boolean has_varargs;
|
|
PCodeBlock *tmp;
|
|
Object *obj;
|
|
SInt32 size;
|
|
|
|
CodeGen_InitialSanityCheck();
|
|
if (!saveheaperror) {
|
|
saveheaperror = getheaperror();
|
|
setheaperror(CodeGen_heaperror);
|
|
}
|
|
|
|
if (cparamblkptr->isPrecompiling == 1)
|
|
CError_Error(CErrorStr180);
|
|
|
|
if (!func) {
|
|
func = createstaticinitobject();
|
|
} else if (func && func->name) {
|
|
PrintProgressFunction(func->name->name);
|
|
}
|
|
|
|
gFunction = func;
|
|
has_varargs = has_vararglist(func);
|
|
init_endian();
|
|
init_stack_globals(func);
|
|
assign_arguments_to_memory(func, mysteryFlag, has_varargs);
|
|
|
|
needs_cleanup = 0;
|
|
disable_optimizer = 0;
|
|
has_catch_blocks = 0;
|
|
current_statement = NULL;
|
|
current_linenumber = 0;
|
|
precomputedoperands = NULL;
|
|
switchtables = NULL;
|
|
temps = NULL;
|
|
initializeexceptiontables();
|
|
returnlabel = cleanreturnlabel = newlabel();
|
|
|
|
if (copts.debuglisting)
|
|
DumpIR(statements, func);
|
|
|
|
statements = COpt_Optimizer(func, statements);
|
|
if (copts.debuglisting)
|
|
DumpIR(statements, func);
|
|
|
|
resetTOCvarinfo();
|
|
init_registers();
|
|
expandTOCreferences(&statements->next);
|
|
if (copts.debuglisting)
|
|
DumpIR(statements, func);
|
|
|
|
if (copts.profile) {
|
|
makes_call = 1;
|
|
requires_frame = 1;
|
|
}
|
|
|
|
initpcode();
|
|
|
|
pclabel(prologue = makepcblock(), makepclabel());
|
|
prologue->flags |= fIsProlog;
|
|
|
|
pclabel(tmp = makepcblock(), makepclabel());
|
|
pcbranch(prologue, tmp->labels);
|
|
|
|
init_frame_sizes(has_varargs);
|
|
allocate_locals();
|
|
process_arguments(move_assigned_argument, has_varargs);
|
|
|
|
if (copts.schedule_mode || copts.altivec_model)
|
|
branch_label(makepclabel());
|
|
|
|
load_TOC_pointers();
|
|
|
|
if (copts.profile)
|
|
callprofiler(CMangler_GetLinkName(func)->name);
|
|
|
|
assign_labels(statements->next);
|
|
open_temp_registers();
|
|
|
|
for (stmt = statements->next; stmt; stmt = stmt->next) {
|
|
current_statement = stmt;
|
|
current_linenumber = stmt->sourceoffset;
|
|
switch (stmt->type) {
|
|
case ST_NOP:
|
|
break;
|
|
case ST_EXPRESSION:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
expressionstatement(stmt->expr);
|
|
break;
|
|
case ST_LABEL:
|
|
labelstatement(stmt->label);
|
|
break;
|
|
case ST_IFGOTO:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
conditionalstatement(stmt->expr, stmt->label, 1);
|
|
break;
|
|
case ST_IFNGOTO:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
conditionalstatement(stmt->expr, stmt->label, 0);
|
|
break;
|
|
case ST_GOTOEXPR:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
gotoexpression(stmt->expr);
|
|
break;
|
|
case ST_GOTO:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
gotostatement(stmt->label);
|
|
break;
|
|
case ST_RETURN:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
returnstatement(stmt->expr, islaststatement(stmt));
|
|
break;
|
|
case ST_SWITCH:
|
|
newstatement(stmt->sourceoffset, stmt->value, (stmt->flags & StmtFlag_10) != 0);
|
|
switchstatement(stmt->expr, (SwitchInfo *) stmt->label);
|
|
break;
|
|
case ST_BEGINCATCH:
|
|
capturestackpointer(stmt->expr->data.objref);
|
|
break;
|
|
case ST_ENDCATCHDTOR:
|
|
CError_ASSERT(2056, stmt->expr->data.objref->datatype == DLOCAL);
|
|
obj = stmt->expr->data.objref;
|
|
add_immediate(3, local_base_register(obj), obj, 0);
|
|
{
|
|
UInt32 masks[RegClassMax] = {0, 0, 0, 0, 0};
|
|
masks[RegClass_GPR] |= 1 << 3;
|
|
branch_subroutine(Xecth_func, 1, masks);
|
|
}
|
|
case ST_ENDCATCH:
|
|
resetstackpointer(stmt->expr->data.objref);
|
|
break;
|
|
case ST_ASM:
|
|
if (stmt->expr) {
|
|
if (((InlineAsm *) stmt->expr)->flags & IAFlag1) {
|
|
CError_FATAL(2076);
|
|
} else {
|
|
branch_label(makepclabel());
|
|
InlineAsm_TranslateIRtoPCode(stmt);
|
|
}
|
|
}
|
|
break;
|
|
case ST_BEGINLOOP:
|
|
CError_FATAL(2086);
|
|
break;
|
|
case ST_ENDLOOP:
|
|
CError_FATAL(2090);
|
|
break;
|
|
default:
|
|
CError_FATAL(2094);
|
|
}
|
|
check_temp_registers();
|
|
}
|
|
|
|
close_temp_registers();
|
|
labelstatement(returnlabel);
|
|
|
|
current_statement = NULL;
|
|
|
|
epilogue = pclastblock;
|
|
pclastblock->flags |= fIsEpilogue;
|
|
|
|
pccomputepredecessors();
|
|
deleteunreachableblocks();
|
|
|
|
if (copts.report_heap_info > 0)
|
|
CodeGen_reportheapinfo(0, CMangler_GetLinkName(func)->name, "before optimization");
|
|
|
|
if (copts.optimizationlevel > 0 && !disable_optimizer)
|
|
globallyoptimizepcode(func);
|
|
else
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "INITIAL CODE");
|
|
|
|
if (copts.schedule_mode == 2) {
|
|
if (copts.peephole)
|
|
peepholemergeblocks(func, 0);
|
|
if (copts.debuglisting)
|
|
pclistblocks_start_scheduler(CMangler_GetLinkName(func)->name, "BEFORE SCHEDULING");
|
|
scheduleinstructions(0);
|
|
if (copts.debuglisting)
|
|
pclistblocks_end_scheduler();
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER INSTRUCTION SCHEDULING");
|
|
}
|
|
|
|
if (copts.peephole) {
|
|
if (copts.schedule_mode == 0 && copts.optimizationlevel > 1)
|
|
peepholemergeblocks(func, 0);
|
|
peepholeoptimizeforward(func);
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER PEEPHOLE FORWARD");
|
|
}
|
|
|
|
allocate_temporaries();
|
|
colorinstructions(func);
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER REGISTER COLORING");
|
|
|
|
if (copts.optimizationlevel > 1 && !disable_optimizer) {
|
|
removecommonsubexpressions(func, 1);
|
|
if (removedcommonsubexpressions && copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER VALUE NUMBERING (POST COLORING)");
|
|
}
|
|
|
|
compute_frame_sizes();
|
|
generate_prologue(prologue, has_varargs);
|
|
if (copts.profile)
|
|
exitprofiler();
|
|
generate_epilogue(epilogue, 1);
|
|
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER GENERATING EPILOGUE, PROLOGUE");
|
|
|
|
if (copts.peephole) {
|
|
if (copts.schedule_mode) {
|
|
peepholemergeblocks(func, 1);
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER MERGING EPILOGUE, PROLOGUE");
|
|
}
|
|
peepholeoptimizepcode(func);
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "AFTER PEEPHOLE OPTIMIZATION");
|
|
}
|
|
|
|
if (copts.schedule_mode) {
|
|
if (copts.debuglisting)
|
|
pclistblocks_start_scheduler(CMangler_GetLinkName(func)->name, "BEFORE SCHEDULING");
|
|
scheduleinstructions(1);
|
|
if (copts.debuglisting)
|
|
pclistblocks_end_scheduler();
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "FINAL CODE AFTER INSTRUCTION SCHEDULING");
|
|
} else {
|
|
if (copts.debuglisting)
|
|
pclistblocks(CMangler_GetLinkName(func)->name, "FINAL CODE");
|
|
}
|
|
|
|
size = assemblefunction(func, NULL);
|
|
dumpswitchtables(func);
|
|
dumpcodelabels(func);
|
|
if (callOnModuleBind)
|
|
ObjGen_DeclareInitFunction(func);
|
|
|
|
pic_base_pcodelabel = NULL;
|
|
pic_base_reg = 0;
|
|
if (copts.exceptions && requires_frame)
|
|
dumpexceptiontables(func, size);
|
|
|
|
if (copts.report_heap_info > 0)
|
|
CodeGen_reportheapinfo(0, CMangler_GetLinkName(func)->name, "finished");
|
|
|
|
if (saveheaperror)
|
|
setheaperror(saveheaperror);
|
|
}
|
|
|
|
enum {
|
|
reg3 = 3,
|
|
reg4 = 4
|
|
};
|
|
|
|
void CodeGen_GenVDispatchThunk(Object *thunkobj, Object *obj, SInt32 a, SInt32 b, SInt32 c) {
|
|
Boolean save_debug;
|
|
Boolean save_peephole;
|
|
Boolean save_traceback;
|
|
char reg;
|
|
|
|
save_debug = copts.isGeneratingDebugInfo;
|
|
save_peephole = copts.peephole;
|
|
save_traceback = copts.traceback;
|
|
|
|
CodeGen_InitialSanityCheck();
|
|
CError_ASSERT(2270, b == 0);
|
|
|
|
initpcode();
|
|
makepcblock();
|
|
|
|
if (a) {
|
|
reg = CMach_PassResultInHiddenArg(TYPE_FUNC(obj->type)->functype) ? reg4 : reg3;
|
|
|
|
if (c >= 0) {
|
|
if (!FITS_IN_SHORT(c)) {
|
|
emitpcode(PC_ADDIS, 11, 0, 0, HIGH_PART(c));
|
|
if (c)
|
|
emitpcode(PC_ADDI, 11, 11, 0, LOW_PART(c));
|
|
} else {
|
|
emitpcode(PC_ADDI, 11, 0, 0, c);
|
|
}
|
|
emitpcode(PC_LWZX, 11, reg, 11);
|
|
emitpcode(PC_ADD, reg, reg, 11);
|
|
}
|
|
|
|
if (!FITS_IN_SHORT(a)) {
|
|
emitpcode(PC_ADDIS, reg, reg, 0, HIGH_PART(a));
|
|
if (a)
|
|
emitpcode(PC_ADDI, reg, reg, 0, LOW_PART(a));
|
|
} else {
|
|
emitpcode(PC_ADDI, reg, reg, 0, a);
|
|
}
|
|
}
|
|
|
|
emitpcode(PC_B, 0, obj);
|
|
|
|
copts.isGeneratingDebugInfo = 0;
|
|
copts.peephole = 0;
|
|
copts.traceback = 0;
|
|
assemblefunction(thunkobj, NULL);
|
|
copts.isGeneratingDebugInfo = save_debug;
|
|
copts.peephole = save_peephole;
|
|
copts.traceback = save_traceback;
|
|
}
|
|
|
|
void CodeGen_SetupRuntimeObjects(void) {
|
|
setupaddressing();
|
|
dyld_stub_binding_helper = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_fp2unsigned = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_profile_entry = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_profile_exit = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_div2i = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_div2u = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_mod2i = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_mod2u = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_shr2i = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_shr2u = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_shl2i = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_ull_dbl = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_sll_dbl = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_ull_flt = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_sll_flt = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
rt_cvt_dbl_usll = CParser_NewRTFunc(&stvoid, NULL, 2, 0);
|
|
Intrinsics_SetupRuntimeObjects();
|
|
}
|
|
|
|
Boolean CodeGen_ReInitRuntimeObjects(Boolean is_precompiler) {
|
|
dyld_stub_binding_helper->name = GetHashNameNodeExport("dyld_stub_binding_helper");
|
|
dyld_stub_binding_helper->u.func.linkname = dyld_stub_binding_helper->name;
|
|
|
|
rt_cvt_fp2unsigned->name = GetHashNameNodeExport("__cvt_fp2unsigned");
|
|
|
|
rt_profile_entry->name = GetHashNameNodeExport("mcount");
|
|
rt_profile_entry->u.func.linkname = rt_profile_entry->name;
|
|
|
|
rt_profile_exit->name = GetHashNameNodeExport("profile_exit");
|
|
|
|
rt_div2i->name = GetHashNameNodeExport("__div2i");
|
|
rt_div2u->name = GetHashNameNodeExport("__div2u");
|
|
rt_mod2i->name = GetHashNameNodeExport("__mod2i");
|
|
rt_mod2u->name = GetHashNameNodeExport("__mod2u");
|
|
rt_shr2i->name = GetHashNameNodeExport("__shr2i");
|
|
rt_shr2u->name = GetHashNameNodeExport("__shr2u");
|
|
rt_shl2i->name = GetHashNameNodeExport("__shl2i");
|
|
|
|
rt_cvt_ull_dbl->name = GetHashNameNodeExport("__cvt_ull_dbl");
|
|
rt_cvt_sll_dbl->name = GetHashNameNodeExport("__cvt_sll_dbl");
|
|
rt_cvt_ull_flt->name = GetHashNameNodeExport("__cvt_ull_flt");
|
|
rt_cvt_sll_flt->name = GetHashNameNodeExport("__cvt_sll_flt");
|
|
rt_cvt_dbl_usll->name = GetHashNameNodeExport("__cvt_dbl_usll");
|
|
|
|
CMach_ReInitRuntimeObjects();
|
|
return Intrinsics_ReInitRuntimeObjects(is_precompiler);
|
|
}
|
|
|
|
Boolean CodeGen_IsPublicRuntimeObject(Object *obj) {
|
|
return Intrinsics_IsPublicRuntimeObject(obj);
|
|
}
|
|
|
|
void CodeGen_SOMStub(Object *a, Object *b, Object *c, SInt32 offset) {
|
|
Boolean save_debug;
|
|
Boolean save_peephole;
|
|
Boolean save_traceback;
|
|
Object *tmp;
|
|
Operand opnd;
|
|
|
|
save_debug = copts.isGeneratingDebugInfo;
|
|
save_peephole = copts.peephole;
|
|
save_traceback = copts.traceback;
|
|
|
|
CodeGen_InitialSanityCheck();
|
|
memclrw(&opnd, sizeof(Operand));
|
|
CError_ASSERT(2528, offset <= 0x7FFF);
|
|
|
|
initpcode();
|
|
makepcblock();
|
|
|
|
tmp = createIndirect(c, 1, 1);
|
|
if (tmp) {
|
|
opnd.optype = OpndType_Symbol;
|
|
opnd.object = tmp;
|
|
indirect(&opnd, NULL);
|
|
} else {
|
|
opnd.optype = OpndType_Symbol;
|
|
opnd.object = c;
|
|
}
|
|
|
|
if (opnd.optype != OpndType_GPR)
|
|
Coerce_to_register(&opnd, TYPE(&void_ptr), 12);
|
|
|
|
if (opnd.optype != OpndType_GPR) {
|
|
CError_FATAL(2562);
|
|
} else if (opnd.reg != 12) {
|
|
emitpcode(PC_MR, 12, opnd.reg);
|
|
}
|
|
|
|
load_store_register(PC_LWZ, 12, 12, NULL, (short) offset);
|
|
emitpcode(PC_B, 0, b);
|
|
|
|
copts.isGeneratingDebugInfo = 0;
|
|
copts.peephole = 0;
|
|
copts.traceback = 0;
|
|
assemblefunction(a, NULL);
|
|
copts.isGeneratingDebugInfo = save_debug;
|
|
copts.peephole = save_peephole;
|
|
copts.traceback = save_traceback;
|
|
}
|
|
|
|
void CodeGen_ParseDeclSpec(HashNameNode *identifier, DeclInfo *declinfo) {
|
|
if (!strcmp(identifier->name, "private_extern")) {
|
|
declinfo->storageclass = TK_EXTERN;
|
|
declinfo->exportflags = EXPORT_FLAGS_INTERNAL;
|
|
} else {
|
|
CError_Error(CErrorStr176);
|
|
}
|
|
}
|
|
|
|
static void CodeGen_EOLCheck(void) {
|
|
short t;
|
|
|
|
if (plex() != TK_EOL) {
|
|
CPrep_Error(CErrorStr113);
|
|
do {
|
|
t = plex();
|
|
} while (t != TK_EOL && t != 0);
|
|
}
|
|
}
|
|
|
|
static void schedule_for(int what) {
|
|
CPrep_PushOption(OPT_OFFSET(schedule_cpu), what);
|
|
if (copts.schedule_mode == 0)
|
|
CPrep_PushOption(OPT_OFFSET(schedule_mode), 2);
|
|
}
|
|
|
|
static void pragma_scheduling(void) {
|
|
Boolean *flag;
|
|
int cpu;
|
|
|
|
tk = plex();
|
|
if (tk == TK_IDENTIFIER) {
|
|
flag = &copts.altivec_model;
|
|
if (!strcmp(tkidentifier->name, "vger")) {
|
|
schedule_for(10);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "altivec")) {
|
|
schedule_for(7);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "reset")) {
|
|
CPrep_PopOption(OPT_OFFSET(schedule_cpu));
|
|
CPrep_PopOption(OPT_OFFSET(schedule_mode));
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "off")) {
|
|
CPrep_PushOption(OPT_OFFSET(schedule_mode), 0);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "once")) {
|
|
CPrep_PushOption(OPT_OFFSET(schedule_mode), 1);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "twice")) {
|
|
CPrep_PushOption(OPT_OFFSET(schedule_mode), 2);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
CPrep_PushOption(OPT_OFFSET(schedule_mode), 2);
|
|
return;
|
|
} else if (!*flag) {
|
|
if (!strcmp(tkidentifier->name, "603e")) {
|
|
schedule_for(5);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "604e")) {
|
|
schedule_for(6);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "PPC603e")) {
|
|
schedule_for(5);
|
|
return;
|
|
} else if (!strcmp(tkidentifier->name, "PPC604e")) {
|
|
schedule_for(6);
|
|
return;
|
|
}
|
|
} else {
|
|
PPCError_Error(115);
|
|
return;
|
|
}
|
|
CPrep_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
|
|
if (tk == TK_INTCONST) {
|
|
switch (CInt64_GetULong(&tkintconst)) {
|
|
case 601:
|
|
cpu = 1;
|
|
break;
|
|
case 603:
|
|
cpu = 2;
|
|
break;
|
|
case 604:
|
|
cpu = 3;
|
|
break;
|
|
case 750:
|
|
cpu = 4;
|
|
break;
|
|
case 7400:
|
|
cpu = 7;
|
|
break;
|
|
case 7450:
|
|
cpu = 10;
|
|
break;
|
|
case 8240:
|
|
case 8260:
|
|
cpu = 5;
|
|
break;
|
|
case 801:
|
|
case 821:
|
|
case 823:
|
|
case 850:
|
|
case 860:
|
|
cpu = 9;
|
|
break;
|
|
default:
|
|
CPrep_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
schedule_for(cpu);
|
|
return;
|
|
}
|
|
|
|
if (copts.warn_illpragma)
|
|
CPrep_Warning(CErrorStr186);
|
|
}
|
|
|
|
static SInt32 CodeGen_ParseLongIntegerORonORoff(void) {
|
|
SInt32 result;
|
|
short t;
|
|
|
|
result = 0;
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
if (!tkintconst.hi)
|
|
result = CInt64_GetULong(&tkintconst);
|
|
else
|
|
CPrep_Error(CErrorStr154);
|
|
CodeGen_EOLCheck();
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "on")) {
|
|
CodeGen_EOLCheck();
|
|
return 1;
|
|
}
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
CodeGen_EOLCheck();
|
|
return 0;
|
|
}
|
|
if (copts.warn_illpragma)
|
|
CPrep_Warning(CErrorStr186);
|
|
return 0;
|
|
} else {
|
|
if (copts.warn_illpragma)
|
|
CPrep_Warning(CErrorStr186);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CodeGen_ParsePragma(HashNameNode *name) {
|
|
short t;
|
|
SInt32 value;
|
|
char *str;
|
|
NameSpace *nspace;
|
|
NameSpaceObjectList *list;
|
|
|
|
if (!strcmp(name->name, "debuglisting")) {
|
|
copts.debuglisting = CodeGen_ParseLongIntegerORonORoff();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "report_heap_info")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
copts.report_heap_info = CInt64_GetULong(&tkintconst);
|
|
if (copts.report_heap_info < 0) {
|
|
copts.report_heap_info = 0;
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.report_heap_info = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.report_heap_info = 1;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "scheduling")) {
|
|
pragma_scheduling();
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "ppc_unroll_speculative")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.ppc_unroll_speculative = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.ppc_unroll_speculative = 1;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "ppc_unroll_instructions_limit")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
copts.ppc_unroll_instructions_limit = CInt64_GetULong(&tkintconst);
|
|
if (copts.ppc_unroll_instructions_limit < 0) {
|
|
copts.ppc_unroll_instructions_limit = 0;
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.ppc_unroll_instructions_limit = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.ppc_unroll_instructions_limit = 70;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "gen_fsel")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
value = CInt64_GetULong(&tkintconst);
|
|
if (value < 0) {
|
|
copts.gen_fsel = 0;
|
|
CError_Error(CErrorStr186);
|
|
} else if (value > 255) {
|
|
copts.gen_fsel = 255;
|
|
CError_Error(CErrorStr186);
|
|
} else {
|
|
copts.gen_fsel = value;
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.gen_fsel = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.gen_fsel = 10;
|
|
} else if (!strcmp(tkidentifier->name, "always")) {
|
|
copts.gen_fsel = 255;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "ppc_unroll_factor_limit")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
copts.ppc_unroll_factor_limit = CInt64_GetULong(&tkintconst);
|
|
if (copts.ppc_unroll_factor_limit < 0) {
|
|
copts.ppc_unroll_factor_limit = 0;
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.ppc_unroll_factor_limit = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.ppc_unroll_factor_limit = 10;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "altivec_model")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.altivec_model = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.altivec_model = 1;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "altivec_vrsave")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
CPrep_PushOption(OPT_OFFSET(altivec_vrsave), 0);
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
CPrep_PushOption(OPT_OFFSET(altivec_vrsave), 1);
|
|
} else if (!strcmp(tkidentifier->name, "allon")) {
|
|
CPrep_PushOption(OPT_OFFSET(altivec_vrsave), 2);
|
|
} else if (!strcmp(tkidentifier->name, "reset")) {
|
|
CPrep_PopOption(OPT_OFFSET(altivec_vrsave));
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "function_align")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
value = CInt64_GetULong(&tkintconst);
|
|
switch (value) {
|
|
case 4:
|
|
case 8:
|
|
case 16:
|
|
case 32:
|
|
case 64:
|
|
case 128:
|
|
CPrep_PushOption(OPT_OFFSET(code_alignment), value);
|
|
break;
|
|
default:
|
|
PPCError_Warning(161);
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
} else if (t == TK_IDENTIFIER && !strcmp(tkidentifier->name, "reset")) {
|
|
CPrep_PopOption(OPT_OFFSET(code_alignment));
|
|
} else {
|
|
PPCError_Warning(161);
|
|
}
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "processor")) {
|
|
if (cscope_currentfunc) {
|
|
PPCError_Warning(156, "pragma processor");
|
|
return;
|
|
}
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
switch (CInt64_GetULong(&tkintconst)) {
|
|
case 401:
|
|
copts.cpu = 0;
|
|
break;
|
|
case 403:
|
|
copts.cpu = 1;
|
|
break;
|
|
case 505:
|
|
copts.cpu = 2;
|
|
break;
|
|
case 509:
|
|
copts.cpu = 3;
|
|
break;
|
|
case 555:
|
|
copts.cpu = 4;
|
|
break;
|
|
case 556:
|
|
copts.cpu = 25;
|
|
break;
|
|
case 565:
|
|
copts.cpu = 26;
|
|
break;
|
|
case 601:
|
|
copts.cpu = 5;
|
|
break;
|
|
case 602:
|
|
copts.cpu = 6;
|
|
break;
|
|
case 8240:
|
|
copts.cpu = 18;
|
|
break;
|
|
case 8260:
|
|
copts.cpu = 19;
|
|
break;
|
|
case 603:
|
|
copts.cpu = 7;
|
|
break;
|
|
case 604:
|
|
copts.cpu = 9;
|
|
break;
|
|
case 740:
|
|
copts.cpu = 11;
|
|
break;
|
|
case 750:
|
|
copts.cpu = 12;
|
|
break;
|
|
case 801:
|
|
copts.cpu = 13;
|
|
break;
|
|
case 821:
|
|
copts.cpu = 14;
|
|
break;
|
|
case 823:
|
|
copts.cpu = 15;
|
|
break;
|
|
case 850:
|
|
copts.cpu = 16;
|
|
break;
|
|
case 860:
|
|
copts.cpu = 17;
|
|
break;
|
|
case 7400:
|
|
copts.cpu = 21;
|
|
break;
|
|
default:
|
|
PPCError_Warning(208);
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "generic"))
|
|
copts.cpu = 20;
|
|
else if (!strcmp(tkidentifier->name, "603e"))
|
|
copts.cpu = 8;
|
|
else if (!strcmp(tkidentifier->name, "604e"))
|
|
copts.cpu = 10;
|
|
else if (!strcmp(tkidentifier->name, "PPC603e"))
|
|
copts.cpu = 8;
|
|
else if (!strcmp(tkidentifier->name, "PPC604e"))
|
|
copts.cpu = 10;
|
|
else
|
|
PPCError_Warning(208);
|
|
} else {
|
|
PPCError_Warning(208);
|
|
}
|
|
|
|
if ((str = CMach_GetCPU()))
|
|
CPrep_InsertSpecialMacro(&ppc_cpu, str);
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "min_struct_alignment")) {
|
|
t = plex();
|
|
if (t == TK_INTCONST) {
|
|
value = CInt64_GetULong(&tkintconst);
|
|
switch (value) {
|
|
case 4:
|
|
case 8:
|
|
case 16:
|
|
case 32:
|
|
case 64:
|
|
case 128:
|
|
CPrep_PushOption(OPT_OFFSET(some_alignment), value);
|
|
break;
|
|
default:
|
|
PPCError_Warning(191);
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
} else if (t == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "reset"))
|
|
CPrep_PopOption(OPT_OFFSET(some_alignment));
|
|
else if (!strcmp(tkidentifier->name, "on"))
|
|
CPrep_PushOption(OPT_OFFSET(some_alignment), 4);
|
|
else if (!strcmp(tkidentifier->name, "off"))
|
|
CPrep_PushOption(OPT_OFFSET(some_alignment), 1);
|
|
} else {
|
|
PPCError_Warning(161);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "tvectors")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
no_descriptors = 1;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
no_descriptors = 0;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "dynamic")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.codegen_dynamic = 0;
|
|
copts.codegen_pic = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.codegen_dynamic = 1;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "pic")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.codegen_pic = 0;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.codegen_pic = 1;
|
|
if (!copts.codegen_dynamic) {
|
|
PPCError_Error(189);
|
|
copts.codegen_pic = 0;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "implicit_templates")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.no_implicit_templates = 1;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.no_implicit_templates = 0;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "common")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
if (!strcmp(tkidentifier->name, "off")) {
|
|
copts.no_common = 1;
|
|
} else if (!strcmp(tkidentifier->name, "on")) {
|
|
copts.no_common = 0;
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
return;
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name->name, "CALL_ON_MODULE_BIND")) {
|
|
if (plex() == TK_IDENTIFIER) {
|
|
for (nspace = cscope_current; nspace; nspace = nspace->parent) {
|
|
list = CScope_GetLocalObject(nspace, tkidentifier);
|
|
if (list && list->object->otype == OT_OBJECT && OBJECT(list->object)->datatype == DFUNC) {
|
|
ObjGen_DeclareInitFunction(OBJECT(list->object));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
CError_Error(CErrorStr186);
|
|
}
|
|
|
|
CodeGen_EOLCheck();
|
|
return;
|
|
}
|
|
|
|
if (copts.warn_illpragma)
|
|
CPrep_Warning(CErrorStr186);
|
|
|
|
if (plex() != TK_EOL) {
|
|
do {
|
|
t = plex();
|
|
} while (t != TK_EOL && t != 0);
|
|
}
|
|
}
|
|
|
|
void CodeGen_UpdateObject(Object *object) {
|
|
if (object->datatype == DDATA && object->section == SECT_DEFAULT && object == rt_ptmf_null)
|
|
object->sclass = TK_EXTERN;
|
|
}
|
|
|
|
void CodeGen_UpdateBackEndOptions(void) {
|
|
copts.global_optimizer = 1;
|
|
}
|
|
|
|
SInt32 CodeGen_objc_method_self_offset(ObjCMethod *meth) {
|
|
SInt32 size;
|
|
|
|
if (!meth->return_type) {
|
|
size = 4;
|
|
} else if (
|
|
IS_TYPE_ARRAY(meth->return_type) ||
|
|
IS_TYPE_NONVECTOR_STRUCT(meth->return_type) ||
|
|
IS_TYPE_CLASS(meth->return_type) ||
|
|
IS_TYPE_12BYTES_MEMBERPOINTER(meth->return_type)
|
|
)
|
|
{
|
|
size = 8;
|
|
} else {
|
|
size = meth->return_type->size;
|
|
}
|
|
|
|
if (size == 0)
|
|
size = 1;
|
|
|
|
return (size + 3) & ~3;
|
|
}
|
|
|
|
SInt32 CodeGen_objc_method_sel_offset(ObjCMethod *meth) {
|
|
return (CodeGen_objc_method_self_offset(meth) + 7) & ~3;
|
|
}
|
|
|
|
SInt32 CodeGen_objc_method_arg_offset(ObjCMethod *meth, ObjCMethodArg *arg) {
|
|
SInt32 pos;
|
|
ObjCMethodArg *scan;
|
|
|
|
pos = CodeGen_objc_method_sel_offset(meth) + 4;
|
|
for (scan = meth->selector_args; scan; scan = scan->next) {
|
|
if (scan == arg)
|
|
return pos;
|
|
|
|
if (scan->type == NULL)
|
|
pos += 4;
|
|
else
|
|
pos += scan->type->size;
|
|
|
|
pos = (pos + 3) & ~3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SInt32 CodeGen_objc_method_args_size(ObjCMethod *meth) {
|
|
SInt32 size;
|
|
ObjCMethodArg *scan;
|
|
|
|
size = CodeGen_objc_method_self_offset(meth);
|
|
for (scan = meth->selector_args; scan; scan = scan->next) {
|
|
if (scan->next == NULL && scan->type == NULL)
|
|
return size;
|
|
|
|
size = (size + 3) & ~3;
|
|
if (scan->type == NULL)
|
|
size += 4;
|
|
else
|
|
size += scan->type->size;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
ENode *CodeGen_HandleIntrinsicCall(Object *func, ENodeList *arg_exprs) {
|
|
return Intrinsics_HandleIntrinsicCall(func, arg_exprs);
|
|
}
|
|
|
|
ENode *CodeGen_HandleTypeCast(ENode *expr, Type *type, UInt32 qual) {
|
|
short flags;
|
|
|
|
if (copts.altivec_model) {
|
|
flags = qual & ENODE_FLAG_QUALS;
|
|
if (IS_TYPE_STRUCT(type) && IS_TYPE_STRUCT(expr->rtype) && expr->flags == flags) {
|
|
switch (TYPE_STRUCT(type)->stype) {
|
|
case STRUCT_TYPE_4:
|
|
case STRUCT_TYPE_5:
|
|
case STRUCT_TYPE_6:
|
|
case STRUCT_TYPE_7:
|
|
case STRUCT_TYPE_8:
|
|
case STRUCT_TYPE_9:
|
|
case STRUCT_TYPE_A:
|
|
case STRUCT_TYPE_B:
|
|
case STRUCT_TYPE_C:
|
|
case STRUCT_TYPE_D:
|
|
case STRUCT_TYPE_E:
|
|
expr = makemonadicnode(expr, ETYPCON);
|
|
expr->rtype = type;
|
|
expr->flags = flags;
|
|
return expr;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
short CodeGen_AssignCheck(const ENode *expr, const Type *type, Boolean flag1, Boolean flag2) {
|
|
short result;
|
|
const Type *exprtype = expr->rtype;
|
|
|
|
if (
|
|
copts.altivec_model &&
|
|
IS_TYPE_VECTOR(type) &&
|
|
IS_TYPE_VECTOR(exprtype) &&
|
|
TYPE_STRUCT(type)->stype == TYPE_STRUCT(exprtype)->stype
|
|
)
|
|
result = CheckResult3;
|
|
else
|
|
result = CheckResult0;
|
|
return result;
|
|
}
|
|
|
|
Boolean CodeGen_CollapseVectorExpression(ENode *expr, MWVector128 *vec, Type *type) {
|
|
// this function is a mess and needs lots of fixing
|
|
Boolean result;
|
|
//int count;
|
|
int i;
|
|
ENode *escan;
|
|
|
|
result = 0;
|
|
for (i = 0; i < 4; i++)
|
|
vec->ul[i] = 0;
|
|
|
|
if (ENODE_IS(expr, ECOMMA)) {
|
|
i = 0;
|
|
escan = expr;
|
|
while (ENODE_IS(escan, ECOMMA)) {
|
|
i++;
|
|
escan = escan->data.diadic.left;
|
|
}
|
|
switch (TYPE_STRUCT(type)->stype) {
|
|
case STRUCT_TYPE_4:
|
|
case STRUCT_TYPE_5:
|
|
case STRUCT_TYPE_6:
|
|
if (i < 15) {
|
|
PPCError_Error(110, type, 0);
|
|
} else if (i > 15) {
|
|
PPCError_Error(111, type, 0);
|
|
} else {
|
|
escan = expr;
|
|
i = 15;
|
|
while (ENODE_IS(escan, ECOMMA)) {
|
|
CInt64 v;
|
|
expr = escan->data.diadic.right;
|
|
v = expr->data.intval;
|
|
if (!ENODE_IS(expr, EINTCONST)) {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_4) {
|
|
if (!CInt64_IsInURange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
|
|
vec->uc[i--] = (UInt8) v.lo;
|
|
escan = escan->data.diadic.left;
|
|
}
|
|
|
|
if (ENODE_IS(escan, EINTCONST)) {
|
|
CInt64 v = escan->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_4) {
|
|
if (!CInt64_IsInURange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
vec->uc[0] = (UInt8) v.lo;
|
|
} else {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
result = 1;
|
|
}
|
|
break;
|
|
|
|
case STRUCT_TYPE_7:
|
|
case STRUCT_TYPE_8:
|
|
case STRUCT_TYPE_9:
|
|
case STRUCT_TYPE_E:
|
|
if (i < 7) {
|
|
PPCError_Error(110, type, 0);
|
|
} else if (i > 7) {
|
|
PPCError_Error(111, type, 0);
|
|
} else {
|
|
escan = expr;
|
|
i = 7;
|
|
while (ENODE_IS(escan, ECOMMA)) {
|
|
ENode *e = escan->data.diadic.right;
|
|
CInt64 v = e->data.intval;
|
|
if (!ENODE_IS(e, EINTCONST)) {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_7 || TYPE_STRUCT(type)->stype == STRUCT_TYPE_E) {
|
|
if (!CInt64_IsInURange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
|
|
vec->us[i--] = (UInt16) e->data.intval.lo;
|
|
escan = escan->data.diadic.left;
|
|
}
|
|
|
|
if (ENODE_IS(escan, EINTCONST)) {
|
|
CInt64 v = escan->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_7 || TYPE_STRUCT(type)->stype == STRUCT_TYPE_E) {
|
|
if (!CInt64_IsInURange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
vec->us[0] = (UInt16) v.lo;
|
|
} else {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
result = 1;
|
|
}
|
|
break;
|
|
|
|
case STRUCT_TYPE_A:
|
|
case STRUCT_TYPE_B:
|
|
case STRUCT_TYPE_C:
|
|
if (i < 3) {
|
|
PPCError_Error(110, type, 0);
|
|
} else if (i > 3) {
|
|
PPCError_Error(111, type, 0);
|
|
} else {
|
|
escan = expr;
|
|
i = 3;
|
|
while (ENODE_IS(escan, ECOMMA)) {
|
|
CInt64 v;
|
|
expr = escan->data.diadic.right;
|
|
v = expr->data.intval;
|
|
if (!ENODE_IS(expr, EINTCONST)) {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_A) {
|
|
if (!CInt64_IsInURange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
|
|
vec->ul[i--] = expr->data.intval.lo;
|
|
escan = escan->data.diadic.left;
|
|
}
|
|
|
|
if (ENODE_IS(escan, EINTCONST)) {
|
|
CInt64 v = escan->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_A) {
|
|
if (!CInt64_IsInURange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
vec->ul[0] = v.lo;
|
|
} else {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
result = 1;
|
|
}
|
|
break;
|
|
|
|
case STRUCT_TYPE_D:
|
|
if (i < 3) {
|
|
PPCError_Error(110, type, 0);
|
|
} else if (i > 3) {
|
|
PPCError_Error(111, type, 0);
|
|
} else {
|
|
Float fv;
|
|
escan = expr;
|
|
i = 3;
|
|
while (ENODE_IS(escan, ECOMMA)) {
|
|
expr = escan->data.diadic.right;
|
|
if (ENODE_IS(expr, EFLOATCONST)) {
|
|
fv = expr->data.floatval;
|
|
} else if (ENODE_IS(escan->data.diadic.right, EINTCONST)) {
|
|
fv = CMach_CalcFloatConvertFromInt(expr->rtype,
|
|
expr->data.intval);
|
|
} else {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
|
|
CMach_InitFloatMem(TYPE(&stfloat), fv, &vec->f[i]);
|
|
i--;
|
|
escan = escan->data.diadic.left;
|
|
}
|
|
|
|
if (ENODE_IS(escan, EFLOATCONST)) {
|
|
fv = escan->data.floatval;
|
|
} else if (ENODE_IS(escan, EINTCONST)) {
|
|
fv = CMach_CalcFloatConvertFromInt(escan->rtype, escan->data.intval);
|
|
} else {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
|
|
CMach_InitFloatMem(TYPE(&stfloat), fv, &vec->f[0]);
|
|
result = 1;
|
|
}
|
|
break;
|
|
}
|
|
} else if (ENODE_IS(expr, EINTCONST)) {
|
|
int i = 0;
|
|
switch (TYPE_STRUCT(type)->stype) {
|
|
case STRUCT_TYPE_4:
|
|
case STRUCT_TYPE_5:
|
|
case STRUCT_TYPE_6:
|
|
{
|
|
CInt64 v = expr->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_4) {
|
|
if (!CInt64_IsInURange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 1))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
while (i < 16)
|
|
vec->uc[i++] = (UInt8) v.lo;
|
|
result = 1;
|
|
break;
|
|
}
|
|
case STRUCT_TYPE_7:
|
|
case STRUCT_TYPE_8:
|
|
case STRUCT_TYPE_9:
|
|
case STRUCT_TYPE_E:
|
|
{
|
|
CInt64 v = expr->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_7 || TYPE_STRUCT(type)->stype == STRUCT_TYPE_E) {
|
|
if (!CInt64_IsInURange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 2))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
while (i < 8)
|
|
vec->us[i++] = (UInt16) v.lo;
|
|
result = 1;
|
|
break;
|
|
}
|
|
case STRUCT_TYPE_A:
|
|
case STRUCT_TYPE_B:
|
|
case STRUCT_TYPE_C:
|
|
{
|
|
CInt64 v = expr->data.intval;
|
|
if (copts.pedantic) {
|
|
if (TYPE_STRUCT(type)->stype == STRUCT_TYPE_A) {
|
|
if (!CInt64_IsInURange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
} else {
|
|
if (!CInt64_IsInRange(v, 4))
|
|
PPCError_Warning(113, type, 0);
|
|
}
|
|
}
|
|
while (i < 4)
|
|
vec->ul[i++] = v.lo;
|
|
result = 1;
|
|
break;
|
|
}
|
|
case STRUCT_TYPE_D:
|
|
{
|
|
Float fv;
|
|
if (!CInt64_IsInRange(expr->data.intval, 4)) {
|
|
PPCError_Error(112);
|
|
break;
|
|
}
|
|
fv = CMach_CalcFloatConvertFromInt(expr->rtype, expr->data.intval);
|
|
for (; i < 4; i++)
|
|
CMach_InitFloatMem(TYPE(&stfloat), fv, &vec->f[i]);
|
|
result = 1;
|
|
break;
|
|
}
|
|
default:
|
|
PPCError_Error(112);
|
|
}
|
|
} else if (ENODE_IS(expr, EFLOATCONST)) {
|
|
switch (TYPE_STRUCT(type)->stype) {
|
|
default:
|
|
PPCError_Error(112);
|
|
break;
|
|
case STRUCT_TYPE_D:
|
|
{
|
|
Float fv;
|
|
i = 0;
|
|
fv = expr->data.floatval;
|
|
CMach_InitFloatMem(TYPE(&stfloat), fv, &vec->f[0]);
|
|
while (i < 4)
|
|
CMach_InitFloatMem(TYPE(&stfloat), fv, &vec->f[i++]);
|
|
result = 1;
|
|
break;
|
|
}
|
|
}
|
|
} else if (ENODE_IS2(expr, EINDIRECT, EFUNCCALL)) {
|
|
if (!IS_TYPE_STRUCT(expr->rtype))
|
|
PPCError_Error(112);
|
|
} else if (!ENODE_IS(expr, EVECTOR128CONST)) {
|
|
PPCError_Error(112);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CodeGen_InsertSpecialMacros(void) {
|
|
char *str;
|
|
|
|
CPrep_InsertSpecialMacro(&vecM, "__VEC__");
|
|
CPrep_InsertSpecialMacro(&altivecM, "__ALTIVEC__");
|
|
CPrep_InsertSpecialMacro(&powcM, "powerc");
|
|
CPrep_InsertSpecialMacro(&__powcM, "__powerc");
|
|
CPrep_InsertSpecialMacro(&hostM, "__POWERPC__");
|
|
CPrep_InsertSpecialMacro(&_ppc_M, "__ppc__");
|
|
|
|
CPrep_InsertSpecialMacro(&bendM, "__BIG_ENDIAN__");
|
|
|
|
if ((str = CMach_GetCPU()))
|
|
CPrep_InsertSpecialMacro(&ppc_cpu, str);
|
|
|
|
CPrep_InsertSpecialMacro(&profM, "__profile__");
|
|
CPrep_InsertSpecialMacro(&longI, "__fourbyteints__");
|
|
CPrep_InsertSpecialMacro(&IEEED, "__IEEEdoubles__");
|
|
CPrep_InsertSpecialMacro(&macM2, "__MACOS__");
|
|
CPrep_InsertSpecialMacro(&appleM, "__APPLE__");
|
|
CPrep_InsertSpecialMacro(&_machM, "__MACH__");
|
|
CPrep_InsertSpecialMacro(&archM, "__ARCHITECTURE__");
|
|
|
|
if (copts.optimizationlevel > 0)
|
|
CPrep_InsertSpecialMacro(&optM, "__OPTIMIZE__");
|
|
|
|
if (copts.codegen_dynamic)
|
|
CPrep_InsertSpecialMacro(&dynM, "__DYNAMIC__");
|
|
if (!copts.codegen_dynamic)
|
|
CPrep_InsertSpecialMacro(&dynM, "__STATIC__");
|
|
|
|
if (copts.oldalignment && copts.align_mode == AlignMode2_PPC)
|
|
CPrep_InsertSpecialMacro(&alignM, "__NATURAL_ALIGNMENT__");
|
|
|
|
if (!copts.ANSI_strict)
|
|
CPrep_InsertSpecialMacro(&ppcM, "ppc");
|
|
}
|
|
|
|
char *CodeGen_ExpandSpecialMacro(Macro *macro) {
|
|
if (macro == &vecM) return "10205";
|
|
if (macro == &altivecM) return "100000000";
|
|
if (macro == &powcM) return "1";
|
|
if (macro == &__powcM) return "1";
|
|
if (macro == &hostM) return "1";
|
|
if (macro == &bendM) return "1";
|
|
if (macro == &_ppc_M) return "1";
|
|
if (CMach_GetCPU() && macro == &ppc_cpu) return "1";
|
|
if (macro == &profM) return copts.profile ? "1" : "0";
|
|
if (macro == &longI) return "1";
|
|
if (macro == &IEEED) return "1";
|
|
if (macro == &macM2) return "1";
|
|
if (macro == &appleM) return "1";
|
|
if (macro == &alignM) return "1";
|
|
|
|
if (macro == &optM) {
|
|
switch (copts.optimizationlevel) {
|
|
case 1: return "1";
|
|
case 2: return "2";
|
|
case 3: return "3";
|
|
case 4: return "4";
|
|
case 0: return "0";
|
|
default: return "9";
|
|
}
|
|
}
|
|
|
|
if (macro == &_machM) return "1";
|
|
if (macro == &archM) return "ppc";
|
|
if (macro == &dynM) return "1";
|
|
if (!copts.ANSI_strict && macro == &ppcM) return "1";
|
|
if (macro == &_ppc_M) return "1";
|
|
|
|
CError_FATAL(4801);
|
|
return "0";
|
|
}
|
|
|
|
void CodeGen_reportheapinfo(Boolean release_flag, char *name, char *text) {
|
|
HeapInfo o;
|
|
HeapInfo all;
|
|
|
|
CTool_GetHeapInfo(&o, 3);
|
|
CTool_GetHeapInfo(&all, 5);
|
|
|
|
if (release_flag)
|
|
releaseoheap();
|
|
|
|
PPCError_Message(
|
|
"%n:%u HEAP STATUS\n"
|
|
" optimizer: %i blocks, used: %i, free: %i, total: %i, largest free: %i\n"
|
|
" all: %i blocks, used: %i, free: %i, total: %i, largest free: %i",
|
|
name, text,
|
|
o.blocks, o.total_size - o.total_free, o.total_free, o.total_size, o.largest_free_block,
|
|
all.blocks, all.total_size - all.total_free, all.total_free, all.total_size, all.largest_free_block
|
|
);
|
|
}
|
|
|
|
static void CodeGen_heaperror(void) {
|
|
CodeGen_reportheapinfo(1, "?", "out of memory");
|
|
if (saveheaperror) {
|
|
setheaperror(saveheaperror);
|
|
saveheaperror();
|
|
}
|
|
}
|
|
|
|
void CodeGen_InitialSanityCheck(void) {
|
|
}
|