mirror of https://git.wuffs.org/MWCC
1251 lines
38 KiB
C
1251 lines
38 KiB
C
#include "compiler/IroUtil.h"
|
|
#include "compiler/IroCSE.h"
|
|
#include "compiler/IroDump.h"
|
|
#include "compiler/IroFlowgraph.h"
|
|
#include "compiler/IroLinearForm.h"
|
|
#include "compiler/IroLoop.h"
|
|
#include "compiler/IroVars.h"
|
|
#include "compiler/InlineAsmPPC.h"
|
|
#include "compiler/CompilerTools.h"
|
|
#include "compiler/CCompiler.h"
|
|
#include "compiler/CError.h"
|
|
#include "compiler/CException.h"
|
|
#include "compiler/CExpr.h"
|
|
#include "compiler/CFunc.h"
|
|
#include "compiler/CInt64.h"
|
|
#include "compiler/CMachine.h"
|
|
#include "compiler/CParser.h"
|
|
#include "compiler/Exceptions.h"
|
|
#include "compiler/objects.h"
|
|
#include "compiler/types.h"
|
|
#include "cos.h"
|
|
|
|
static UInt32 IRO_LastUserBreakTick;
|
|
static IROLinear *ExprStart;
|
|
static IROLinear *ExprEnd;
|
|
static IRONode *IRO_Node;
|
|
static SInt32 FuncLevel;
|
|
Object *FunctionName;
|
|
Boolean IRO_IsLeafFunction;
|
|
Boolean IRO_FunctionHasReturn;
|
|
Boolean DisableDueToAsm;
|
|
Boolean LoopOptimizerRun;
|
|
|
|
Object *IRO_IsVariable(IROLinear *linear) {
|
|
if (linear->type == IROLinearOp1Arg &&
|
|
linear->nodetype == EINDIRECT &&
|
|
linear->u.monadic->type == IROLinearOperand &&
|
|
linear->u.monadic->u.node->type == EOBJREF)
|
|
return linear->u.monadic->u.node->data.objref;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Boolean IRO_IsConstant(IROLinear *linear) {
|
|
if (linear->type == IROLinearOperand && ENODE_IS3(linear->u.node, EINTCONST, EVECTOR128CONST, EFLOATCONST))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_IsPow2(IROLinear *linear, SInt32 *powvalue) {
|
|
UInt32 desired;
|
|
UInt32 value;
|
|
SInt32 i;
|
|
|
|
*powvalue = -1;
|
|
if (linear->type == IROLinearOperand && linear->u.node->type == EINTCONST) {
|
|
if (CTool_EndianReadWord32(&linear->u.node->data.intval.hi))
|
|
return 0;
|
|
|
|
desired = CInt64_GetULong(&linear->u.node->data.intval);
|
|
value = 1;
|
|
for (i = 0; i < 31; i++) {
|
|
if (value == desired) {
|
|
*powvalue = i;
|
|
return 1;
|
|
}
|
|
value += value;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_IsIntConstant(IROLinear *linear) {
|
|
if (linear->type == IROLinearOperand && ENODE_IS(linear->u.node, EINTCONST))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_IsFloatConstant(IROLinear *linear) {
|
|
if (linear->type == IROLinearOperand && ENODE_IS(linear->u.node, EFLOATCONST))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_IsVector128Constant(IROLinear *linear) {
|
|
if (linear->type == IROLinearOperand && ENODE_IS(linear->u.node, EVECTOR128CONST))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_IsAssignment(IROLinear *linear) {
|
|
if (linear->type == IROLinearOp1Arg || linear->type == IROLinearOp2Arg) {
|
|
if (IRO_IsAssignOp[linear->nodetype])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Boolean IRO_OperandsSame(ENode *a, ENode *b) {
|
|
if (a->type == b->type) {
|
|
switch (a->type) {
|
|
case EINTCONST:
|
|
return CInt64_Equal(a->data.intval, b->data.intval);
|
|
case ESTRINGCONST:
|
|
return 0;
|
|
case EFLOATCONST:
|
|
return a->data.floatval.value == b->data.floatval.value;
|
|
case EVECTOR128CONST:
|
|
return (a->data.vector128val.ul[0] == b->data.vector128val.ul[0]) &&
|
|
(a->data.vector128val.ul[1] == b->data.vector128val.ul[1]) &&
|
|
(a->data.vector128val.ul[2] == b->data.vector128val.ul[2]) &&
|
|
(a->data.vector128val.ul[3] == b->data.vector128val.ul[3]);
|
|
case EOBJREF:
|
|
return a->data.objref == b->data.objref;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Boolean IRO_TypesEqual(Type *a, Type *b) {
|
|
if (IS_TYPE_BITFIELD(a)) {
|
|
if (IS_TYPE_BITFIELD(b)) {
|
|
if (
|
|
(TYPE_BITFIELD(a)->bitfieldtype == TYPE_BITFIELD(b)->bitfieldtype) &&
|
|
(TYPE_BITFIELD(a)->unkA == TYPE_BITFIELD(b)->unkA) &&
|
|
(TYPE_BITFIELD(a)->unkB == TYPE_BITFIELD(b)->unkB))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (IS_TYPE_POINTER_ONLY(a) && IS_TYPE_POINTER_ONLY(b))
|
|
return 1;
|
|
|
|
return is_typeequal(a, b) != 0;
|
|
}
|
|
|
|
Type *IRO_UnsignedType(Type *type) {
|
|
if (IS_TYPE_ENUM(type) || IS_TYPE_POINTER_ONLY(type)) {
|
|
if (type->size == stunsignedchar.size)
|
|
return TYPE(&stunsignedchar);
|
|
if (type->size == stunsignedint.size)
|
|
return TYPE(&stunsignedint);
|
|
if (type->size == stunsignedshort.size)
|
|
return TYPE(&stunsignedshort);
|
|
if (type->size == stunsignedlong.size)
|
|
return TYPE(&stunsignedlong);
|
|
if (type->size == stunsignedlonglong.size)
|
|
return TYPE(&stunsignedlonglong);
|
|
#line 281
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
if (!IS_TYPE_INT(type)) {
|
|
#line 287
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
if (type == TYPE(&stbool) || type == TYPE(&stwchar))
|
|
return type;
|
|
|
|
if (type == TYPE(&stchar) || type == TYPE(&stsignedchar) || type == TYPE(&stunsignedchar))
|
|
return TYPE(&stunsignedchar);
|
|
if (type == TYPE(&stsignedshort) || type == TYPE(&stunsignedshort))
|
|
return TYPE(&stunsignedshort);
|
|
if (type == TYPE(&stsignedint) || type == TYPE(&stunsignedint))
|
|
return TYPE(&stunsignedint);
|
|
if (type == TYPE(&stsignedlong) || type == TYPE(&stunsignedlong))
|
|
return TYPE(&stunsignedlong);
|
|
if (type == TYPE(&stsignedlonglong) || type == TYPE(&stunsignedlonglong))
|
|
return TYPE(&stunsignedlonglong);
|
|
|
|
#line 319
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
Type *IRO_SignedType(Type *type) {
|
|
if (IS_TYPE_ENUM(type) || IS_TYPE_POINTER_ONLY(type)) {
|
|
if (type->size == stsignedchar.size)
|
|
return TYPE(&stsignedchar);
|
|
if (type->size == stsignedint.size)
|
|
return TYPE(&stsignedint);
|
|
if (type->size == stsignedshort.size)
|
|
return TYPE(&stsignedshort);
|
|
if (type->size == stsignedlong.size)
|
|
return TYPE(&stsignedlong);
|
|
if (type->size == stsignedlonglong.size)
|
|
return TYPE(&stsignedlonglong);
|
|
#line 357
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
if (!IS_TYPE_INT(type)) {
|
|
#line 363
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
if (type == TYPE(&stbool) && type->size == stsignedchar.size)
|
|
return TYPE(&stsignedchar);
|
|
|
|
if (type == TYPE(&stwchar) && type->size == stsignedshort.size)
|
|
return TYPE(&stsignedshort);
|
|
|
|
if (type == TYPE(&stchar) || type == TYPE(&stsignedchar) || type == TYPE(&stunsignedchar))
|
|
return TYPE(&stsignedchar);
|
|
if (type == TYPE(&stsignedshort) || type == TYPE(&stunsignedshort))
|
|
return TYPE(&stsignedshort);
|
|
if (type == TYPE(&stsignedint) || type == TYPE(&stunsignedint))
|
|
return TYPE(&stsignedint);
|
|
if (type == TYPE(&stsignedlong) || type == TYPE(&stunsignedlong))
|
|
return TYPE(&stsignedlong);
|
|
if (type == TYPE(&stsignedlonglong) || type == TYPE(&stunsignedlonglong))
|
|
return TYPE(&stsignedlonglong);
|
|
|
|
#line 399
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
|
|
Boolean IRO_is_CPtypeequal(Type *a, Type *b) {
|
|
if (IS_TYPE_POINTER_ONLY(a) && IS_TYPE_POINTER_ONLY(b))
|
|
return 1;
|
|
|
|
return is_typeequal(a, b) != 0;
|
|
}
|
|
|
|
Boolean IRO_ExprsSame(IROLinear *a, IROLinear *b) {
|
|
if (a->type == b->type && IRO_TypesEqual(a->rtype, b->rtype)) {
|
|
switch (a->type) {
|
|
case IROLinearOperand:
|
|
return IRO_OperandsSame(a->u.node, b->u.node);
|
|
case IROLinearOp1Arg:
|
|
if (a->nodetype == b->nodetype)
|
|
return IRO_ExprsSame(a->u.monadic, b->u.monadic);
|
|
return 0;
|
|
case IROLinearOp2Arg:
|
|
if (a->nodetype == b->nodetype)
|
|
return IRO_ExprsSame(a->u.diadic.left, b->u.diadic.left) &&
|
|
IRO_ExprsSame(a->u.diadic.right, b->u.diadic.right);
|
|
return 0;
|
|
case IROLinearOp3Arg:
|
|
if (a->nodetype == b->nodetype)
|
|
return IRO_ExprsSame(a->u.args3.a, b->u.args3.a) &&
|
|
IRO_ExprsSame(a->u.args3.b, b->u.args3.b) &&
|
|
IRO_ExprsSame(a->u.args3.c, b->u.args3.c);
|
|
return 0;
|
|
case IROLinearFunccall:
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CLabel *IRO_NewLabel(void) {
|
|
CLabel *label = newlabel();
|
|
label->next = Labels;
|
|
Labels = label;
|
|
return label;
|
|
}
|
|
|
|
Boolean IRO_ExprsSameSemantically(IROLinear *a, IROLinear *b) {
|
|
if (a->type == b->type && IRO_TypesEqual(a->rtype, b->rtype)) {
|
|
Boolean flag = 0;
|
|
|
|
switch (a->type) {
|
|
case IROLinearOperand:
|
|
return IRO_OperandsSame(a->u.node, b->u.node);
|
|
case IROLinearOp1Arg:
|
|
if (a->nodetype == b->nodetype)
|
|
return IRO_ExprsSameSemantically(a->u.monadic, b->u.monadic);
|
|
return 0;
|
|
case IROLinearOp2Arg:
|
|
if (a->nodetype == b->nodetype) {
|
|
switch (a->nodetype) {
|
|
case EMUL:
|
|
case EADD:
|
|
case EAND:
|
|
case EXOR:
|
|
case EOR:
|
|
case ELAND:
|
|
case ELOR:
|
|
if (!IRO_HasSideEffect(a)) {
|
|
flag = IRO_ExprsSameSemantically(a->u.diadic.left, b->u.diadic.right) &&
|
|
IRO_ExprsSameSemantically(a->u.diadic.right, b->u.diadic.left);
|
|
}
|
|
}
|
|
|
|
return flag || (IRO_ExprsSameSemantically(a->u.diadic.left, b->u.diadic.left) &&
|
|
IRO_ExprsSameSemantically(a->u.diadic.right, b->u.diadic.right));
|
|
}
|
|
return 0;
|
|
case IROLinearOp3Arg:
|
|
if (a->nodetype == b->nodetype)
|
|
return IRO_ExprsSameSemantically(a->u.args3.a, b->u.args3.a) &&
|
|
IRO_ExprsSameSemantically(a->u.args3.b, b->u.args3.b) &&
|
|
IRO_ExprsSameSemantically(a->u.args3.c, b->u.args3.c);
|
|
return 0;
|
|
case IROLinearFunccall:
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
IROLinear *IRO_FindPrecedAfter(IROLinear *a, IROLinear *iter) {
|
|
IROLinear *prev = iter;
|
|
while (iter && iter != a) {
|
|
prev = iter;
|
|
iter = iter->next;
|
|
}
|
|
|
|
if (iter)
|
|
return prev;
|
|
return iter;
|
|
}
|
|
|
|
IROLinear *IRO_FindPreced(IROLinear *a) {
|
|
IROLinear *iter;
|
|
IROLinear *prev;
|
|
|
|
prev = iter = IRO_FirstLinear;
|
|
while (iter && iter != a) {
|
|
prev = iter;
|
|
iter = iter->next;
|
|
}
|
|
|
|
if (iter)
|
|
return prev;
|
|
return iter;
|
|
}
|
|
|
|
IROLinear *IRO_FindFirst(IROLinear *linear) {
|
|
short i;
|
|
|
|
switch (linear->type) {
|
|
case IROLinearOperand:
|
|
return linear;
|
|
case IROLinearOp1Arg:
|
|
return IRO_FindFirst(linear->u.monadic);
|
|
case IROLinearOp2Arg:
|
|
if (linear->u.diadic.right->index < linear->u.diadic.left->index)
|
|
return IRO_FindFirst(linear->u.diadic.right);
|
|
else
|
|
return IRO_FindFirst(linear->u.diadic.left);
|
|
case IROLinearOp3Arg:
|
|
if (linear->u.args3.a->index < linear->u.args3.b->index) {
|
|
if (linear->u.args3.b->index < linear->u.args3.c->index)
|
|
return IRO_FindFirst(linear->u.args3.a);
|
|
else
|
|
return IRO_FindFirst(linear->u.args3.c);
|
|
} else {
|
|
if (linear->u.args3.b->index < linear->u.args3.c->index)
|
|
return IRO_FindFirst(linear->u.args3.b);
|
|
else
|
|
return IRO_FindFirst(linear->u.args3.c);
|
|
}
|
|
case IROLinearFunccall:
|
|
i = linear->u.funccall.argCount - 1;
|
|
return IRO_FindFirst(linear->u.funccall.args[i]);
|
|
default:
|
|
#line 641
|
|
CError_FATAL();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void IRO_CutAndPasteAfter(IROLinear *a, IROLinear *b, IROLinear *c) {
|
|
IROLinear *preced = IRO_FindPreced(a);
|
|
preced->next = b->next;
|
|
b->next = c->next;
|
|
c->next = a;
|
|
}
|
|
|
|
Boolean IRO_IsConstantZero(IROLinear *linear) {
|
|
return (IRO_IsIntConstant(linear) && CInt64_IsZero(&linear->u.node->data.intval)) ||
|
|
(IRO_IsFloatConstant(linear) && CMach_FloatIsZero(linear->u.node->data.floatval));
|
|
}
|
|
|
|
Boolean IRO_IsConstantOne(IROLinear *linear) {
|
|
return (IRO_IsIntConstant(linear) && CInt64_IsOne(&linear->u.node->data.intval)) ||
|
|
(IRO_IsFloatConstant(linear) && CMach_FloatIsOne(linear->u.node->data.floatval));
|
|
}
|
|
|
|
Boolean IRO_IsConstantNegativeOne(IROLinear *linear) {
|
|
CInt64 neg = CInt64_Neg(linear->u.node->data.intval);
|
|
return (IRO_IsIntConstant(linear) && CInt64_IsOne(&neg)) ||
|
|
(IRO_IsFloatConstant(linear) && CMach_FloatIsNegOne(linear->u.node->data.floatval));
|
|
}
|
|
|
|
static void ActNop(IROLinear *linear, Boolean isEntry) {
|
|
if (!isEntry) {
|
|
linear->type = IROLinearNop;
|
|
linear->expr = NULL;
|
|
}
|
|
}
|
|
|
|
void IRO_NopOut(IROLinear *linear) {
|
|
IRO_WalkTree(linear, ActNop);
|
|
}
|
|
|
|
static Boolean NotNoppable(IROLinear *linear) {
|
|
return (
|
|
(linear->type == IROLinearFunccall) ||
|
|
IRO_IsAssignment(linear) ||
|
|
((linear->type == IROLinearOperand) && ENODE_IS(linear->u.node, EINSTRUCTION)) ||
|
|
(linear->rtype && CParser_IsVolatile(linear->rtype, linear->nodeflags & ENODE_FLAG_QUALS)) ||
|
|
((linear->type == IROLinearOperand) && ENODE_IS(linear->u.node, EOBJREF) && is_volatile_object(linear->u.node->data.objref))
|
|
);
|
|
}
|
|
|
|
static void ActNopNonSideEffects(IROLinear *linear, Boolean isEntry) {
|
|
if (isEntry) {
|
|
if (NotNoppable(linear)) {
|
|
if (!FuncLevel)
|
|
linear->flags &= ~IROLF_Reffed;
|
|
FuncLevel++;
|
|
}
|
|
} else {
|
|
if (NotNoppable(linear)) {
|
|
FuncLevel--;
|
|
} else if (!FuncLevel) {
|
|
linear->type = IROLinearNop;
|
|
}
|
|
}
|
|
}
|
|
|
|
void IRO_NopNonSideEffects(IROLinear *linear, SInt32 level) {
|
|
FuncLevel = level;
|
|
IRO_WalkTree(linear, ActNopNonSideEffects);
|
|
}
|
|
|
|
void IRO_BuildList(IROLinear *linear, Boolean isEntry) {
|
|
if (!isEntry) {
|
|
linear->next = NULL;
|
|
IRO_AddToList(linear, &IRO_InitLList);
|
|
}
|
|
}
|
|
|
|
void IRO_WalkTree(IROLinear *linear, IROWalkTreeFunc func) {
|
|
int i;
|
|
|
|
func(linear, 1);
|
|
switch (linear->type) {
|
|
case IROLinearOperand:
|
|
break;
|
|
case IROLinearOp1Arg:
|
|
IRO_WalkTree(linear->u.monadic, func);
|
|
break;
|
|
case IROLinearOp2Arg:
|
|
IRO_WalkTree(linear->u.diadic.left, func);
|
|
IRO_WalkTree(linear->u.diadic.right, func);
|
|
break;
|
|
case IROLinearOp3Arg:
|
|
IRO_WalkTree(linear->u.args3.a, func);
|
|
IRO_WalkTree(linear->u.args3.b, func);
|
|
IRO_WalkTree(linear->u.args3.c, func);
|
|
break;
|
|
case IROLinearFunccall:
|
|
IRO_WalkTree(linear->u.funccall.linear8, func);
|
|
for (i = 0; i < linear->u.funccall.argCount; i++)
|
|
IRO_WalkTree(linear->u.funccall.args[i], func);
|
|
break;
|
|
}
|
|
func(linear, 0);
|
|
}
|
|
|
|
void IRO_WalkTreeToPropagateFlags(IROLinear *linear, IROWalkTreeFunc func) {
|
|
int i;
|
|
|
|
switch (linear->type) {
|
|
case IROLinearOperand:
|
|
break;
|
|
case IROLinearOp1Arg:
|
|
IRO_WalkTreeToPropagateFlags(linear->u.monadic, func);
|
|
break;
|
|
case IROLinearOp2Arg:
|
|
IRO_WalkTreeToPropagateFlags(linear->u.diadic.left, func);
|
|
IRO_WalkTreeToPropagateFlags(linear->u.diadic.right, func);
|
|
break;
|
|
case IROLinearOp3Arg:
|
|
IRO_WalkTreeToPropagateFlags(linear->u.args3.a, func);
|
|
IRO_WalkTreeToPropagateFlags(linear->u.args3.b, func);
|
|
IRO_WalkTreeToPropagateFlags(linear->u.args3.c, func);
|
|
break;
|
|
case IROLinearFunccall:
|
|
IRO_WalkTreeToPropagateFlags(linear->u.funccall.linear8, func);
|
|
for (i = 0; i < linear->u.funccall.argCount; i++)
|
|
IRO_WalkTreeToPropagateFlags(linear->u.funccall.args[i], func);
|
|
break;
|
|
}
|
|
func(linear, 0);
|
|
}
|
|
|
|
void IRO_WalkInts(IROLinear *a, IROLinear *b, IROWalkTreeFunc func) {
|
|
IROLinear *scan;
|
|
|
|
for (scan = a; scan; scan = scan->next) {
|
|
switch (scan->type) {
|
|
case IROLinearBeginCatch:
|
|
case IROLinearEndCatch:
|
|
case IROLinearEndCatchDtor:
|
|
IRO_WalkTree(scan->u.ctch.linear, func);
|
|
break;
|
|
case IROLinearOperand:
|
|
case IROLinearOp1Arg:
|
|
case IROLinearOp2Arg:
|
|
case IROLinearOp3Arg:
|
|
case IROLinearFunccall:
|
|
IRO_WalkTree(scan, func);
|
|
break;
|
|
case IROLinearIf:
|
|
case IROLinearIfNot:
|
|
IRO_WalkTree(scan->u.label.x4, func);
|
|
break;
|
|
case IROLinearReturn:
|
|
if (scan->u.monadic)
|
|
IRO_WalkTree(scan->u.monadic, func);
|
|
break;
|
|
case IROLinearSwitch:
|
|
IRO_WalkTree(scan->u.swtch.x4, func);
|
|
break;
|
|
case IROLinearAsm:
|
|
func(scan, 1);
|
|
func(scan, 0);
|
|
break;
|
|
case IROLinearEnd:
|
|
break;
|
|
}
|
|
|
|
if (scan == b)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void IRO_Cut(IROLinear *a, IROLinear *b) {
|
|
IROLinear *scan;
|
|
IROLinear *prev;
|
|
IRONode *node;
|
|
|
|
scan = a;
|
|
while (1) {
|
|
scan->stmt = NULL;
|
|
if (scan == b)
|
|
break;
|
|
scan = scan->next;
|
|
}
|
|
|
|
prev = NULL;
|
|
for (scan = IRO_FirstLinear; scan && scan != a; scan = scan->next)
|
|
prev = scan;
|
|
|
|
#line 951
|
|
CError_ASSERT(scan);
|
|
|
|
for (node = IRO_FirstNode; node; node = node->nextnode) {
|
|
if (node->first == a) {
|
|
if (node->last == b) {
|
|
node->first = node->last = NULL;
|
|
break;
|
|
} else {
|
|
node->first = b->next;
|
|
break;
|
|
}
|
|
} else if (node->last == b) {
|
|
node->last = prev;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prev)
|
|
prev->next = b->next;
|
|
else
|
|
IRO_FirstLinear = b->next;
|
|
}
|
|
|
|
void IRO_Paste(IROLinear *a, IROLinear *b, IROLinear *c) {
|
|
IROLinear *prev;
|
|
IROLinear *scan;
|
|
IRONode *node;
|
|
|
|
#line 1002
|
|
CError_ASSERT(c && c->type != IROLinearLabel);
|
|
|
|
prev = NULL;
|
|
for (scan = IRO_FirstLinear; scan && scan != c; scan = scan->next)
|
|
prev = scan;
|
|
|
|
#line 1016
|
|
CError_ASSERT(scan);
|
|
|
|
for (node = IRO_FirstNode; node; node = node->nextnode) {
|
|
if (node->first == c) {
|
|
node->first = a;
|
|
break;
|
|
}
|
|
}
|
|
|
|
b->next = c;
|
|
if (prev)
|
|
prev->next = a;
|
|
else
|
|
IRO_FirstLinear = a;
|
|
}
|
|
|
|
void IRO_PasteAfter(IROLinear *a, IROLinear *b, IROLinear *c) {
|
|
IRONode *node;
|
|
|
|
switch (c->type) {
|
|
case IROLinearGoto:
|
|
case IROLinearIf:
|
|
case IROLinearIfNot:
|
|
case IROLinearSwitch:
|
|
#line 1060
|
|
CError_FATAL();
|
|
}
|
|
|
|
for (node = IRO_FirstNode; node; node = node->nextnode) {
|
|
if (node->last == c) {
|
|
node->last = b;
|
|
break;
|
|
}
|
|
}
|
|
|
|
b->next = c->next;
|
|
c->next = a;
|
|
}
|
|
|
|
static void FindStart(IROLinear *linear, Boolean isEntry) {
|
|
IROLinear *scan;
|
|
if (isEntry) {
|
|
scan = linear;
|
|
do {
|
|
if (scan == ExprStart) {
|
|
ExprStart = linear;
|
|
return;
|
|
}
|
|
if (scan == ExprEnd)
|
|
return;
|
|
} while ((scan = scan->next));
|
|
}
|
|
}
|
|
|
|
void IRO_ClipExpr(IROExpr *expr) {
|
|
ExprStart = ExprEnd = expr->linear;
|
|
IRO_WalkTree(expr->linear, FindStart);
|
|
IRO_Cut(ExprStart, ExprEnd);
|
|
}
|
|
|
|
void IRO_ClipExprTree(IROLinear *linear) {
|
|
ExprStart = ExprEnd = linear;
|
|
IRO_WalkTree(linear, FindStart);
|
|
IRO_Cut(ExprStart, ExprEnd);
|
|
}
|
|
|
|
static void SetNodeNumInExprList(IROLinear *linear, Boolean isEntry) {
|
|
if (isEntry && linear->expr)
|
|
linear->expr->node = IRO_Node;
|
|
}
|
|
|
|
void IRO_MoveExpression(IROExpr *expr, IROLinear *linear) {
|
|
IRONode *node;
|
|
IROLinear *scan;
|
|
|
|
ExprStart = ExprEnd = expr->linear;
|
|
IRO_WalkTree(expr->linear, FindStart);
|
|
|
|
if (ExprStart != linear) {
|
|
IRO_Cut(ExprStart, ExprEnd);
|
|
IRO_Paste(ExprStart, ExprEnd, linear);
|
|
for (node = IRO_FirstNode; node; node = node->nextnode) {
|
|
for (scan = node->first; scan; scan = scan->next) {
|
|
if (scan == expr->linear) {
|
|
expr->node = node;
|
|
break;
|
|
}
|
|
if (scan == node->last)
|
|
break;
|
|
}
|
|
}
|
|
|
|
IRO_Node = expr->node;
|
|
IRO_WalkTree(expr->linear, SetNodeNumInExprList);
|
|
}
|
|
}
|
|
|
|
void IRO_InitList(IROList *list) {
|
|
list->head = list->tail = NULL;
|
|
}
|
|
|
|
void IRO_AddToList(IROLinear *linear, IROList *list) {
|
|
if (list->head)
|
|
list->tail->next = linear;
|
|
else
|
|
list->head = linear;
|
|
|
|
list->tail = linear;
|
|
while (list->tail->next)
|
|
list->tail = list->tail->next;
|
|
}
|
|
|
|
IROLinear *IRO_FindLabelNode(CLabel *label, IROLinear *linear) {
|
|
IROLinear *scan;
|
|
|
|
for (scan = linear; scan; scan = scan->next) {
|
|
if (scan->type == IROLinearLabel && scan->u.label.label == label)
|
|
break;
|
|
}
|
|
|
|
#line 1244
|
|
CError_ASSERT(scan);
|
|
return scan;
|
|
}
|
|
|
|
void IRO_DuplicateExprRange(IROLinear *start, IROLinear *end, IROList *list) {
|
|
IROLinear *scan;
|
|
|
|
for (scan = start; scan; scan = scan->next) {
|
|
if (scan->type != IROLinearNop && !(scan->flags & IROLF_Reffed))
|
|
IRO_DuplicateExpr(scan, list);
|
|
if (scan == end)
|
|
break;
|
|
}
|
|
}
|
|
|
|
IROLinear *IRO_DuplicateExpr(IROLinear *linear, IROList *list) {
|
|
IROLinear *copy;
|
|
ENode *copynode;
|
|
int i;
|
|
|
|
copy = IRO_NewLinear(linear->type);
|
|
*copy = *linear;
|
|
|
|
copy->index = ++IRO_NumLinear;
|
|
copy->next = NULL;
|
|
copy->expr = NULL;
|
|
|
|
switch (copy->type) {
|
|
case IROLinearOperand:
|
|
copynode = lalloc(sizeof(ENode));
|
|
*copynode = *linear->u.node;
|
|
copy->u.node = copynode;
|
|
break;
|
|
case IROLinearOp1Arg:
|
|
copy->u.monadic = IRO_DuplicateExpr(copy->u.monadic, list);
|
|
break;
|
|
case IROLinearOp2Arg:
|
|
if (linear->flags & IROLF_8000) {
|
|
copy->u.diadic.right = IRO_DuplicateExpr(copy->u.diadic.right, list);
|
|
copy->u.diadic.left = IRO_DuplicateExpr(copy->u.diadic.left, list);
|
|
} else {
|
|
copy->u.diadic.left = IRO_DuplicateExpr(copy->u.diadic.left, list);
|
|
copy->u.diadic.right = IRO_DuplicateExpr(copy->u.diadic.right, list);
|
|
}
|
|
break;
|
|
case IROLinearOp3Arg:
|
|
copy->u.args3.a = IRO_DuplicateExpr(copy->u.args3.a, list);
|
|
copy->u.args3.b = IRO_DuplicateExpr(copy->u.args3.b, list);
|
|
copy->u.args3.c = IRO_DuplicateExpr(copy->u.args3.c, list);
|
|
break;
|
|
case IROLinearFunccall:
|
|
copy->u.funccall.linear8 = IRO_DuplicateExpr(copy->u.funccall.linear8, list);
|
|
copy->u.funccall.args = oalloc(sizeof(IROLinear *) * copy->u.funccall.argCount);
|
|
for (i = 0; i < copy->u.funccall.argCount; i++) {
|
|
copy->u.funccall.args[i] = IRO_DuplicateExpr(linear->u.funccall.args[i], list);
|
|
}
|
|
break;
|
|
case IROLinearAsm:
|
|
copy->u.asm_stmt = CodeGen_CopyAsmStat(linear->u.asm_stmt);
|
|
break;
|
|
}
|
|
|
|
IRO_AddToList(copy, list);
|
|
return copy;
|
|
}
|
|
|
|
IROLinear *IRO_TempReference(Object *obj, IROList *list) {
|
|
IROLinear *op;
|
|
IROLinear *ind;
|
|
|
|
op = IRO_NewLinear(IROLinearOperand);
|
|
op->u.node = create_objectrefnode(obj);
|
|
op->rtype = op->u.node->data.objref->type;
|
|
op->index = ++IRO_NumLinear;
|
|
op->flags |= IROLF_Reffed | IROLF_Ind;
|
|
IRO_AddToList(op, list);
|
|
|
|
ind = IRO_NewLinear(IROLinearOp1Arg);
|
|
ind->nodetype = EINDIRECT;
|
|
ind->rtype = obj->type;
|
|
ind->u.monadic = op;
|
|
ind->index = ++IRO_NumLinear;
|
|
ind->next = NULL;
|
|
IRO_AddToList(ind, list);
|
|
|
|
return ind;
|
|
}
|
|
|
|
inline IROLinear *LocateFatherHelper(IROLinear *linear, Boolean a, IROLinear ***b) {
|
|
IROLinear *scan;
|
|
SInt32 index;
|
|
int i;
|
|
|
|
for (scan = linear->next, index = 0; index < (a ? 2 : 1); index++) {
|
|
while (scan) {
|
|
switch (scan->type) {
|
|
case IROLinearIf:
|
|
case IROLinearIfNot:
|
|
if (scan->u.label.x4 == linear) {
|
|
if (b)
|
|
*b = &scan->u.label.x4;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearReturn:
|
|
if (scan->u.monadic == linear) {
|
|
if (b)
|
|
*b = &scan->u.monadic;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearOp1Arg:
|
|
if (scan->u.monadic == linear) {
|
|
if (b)
|
|
*b = &scan->u.monadic;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearSwitch:
|
|
if (scan->u.swtch.x4 == linear) {
|
|
if (b)
|
|
*b = &scan->u.swtch.x4;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearOp2Arg:
|
|
if (scan->u.diadic.left == linear) {
|
|
if (b)
|
|
*b = &scan->u.diadic.left;
|
|
return scan;
|
|
}
|
|
if (scan->u.diadic.right == linear) {
|
|
if (b)
|
|
*b = &scan->u.diadic.right;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearOp3Arg:
|
|
if (scan->u.args3.a == linear) {
|
|
if (b)
|
|
*b = &scan->u.args3.a;
|
|
return scan;
|
|
}
|
|
if (scan->u.args3.b == linear) {
|
|
if (b)
|
|
*b = &scan->u.args3.b;
|
|
return scan;
|
|
}
|
|
if (scan->u.args3.c == linear) {
|
|
if (b)
|
|
*b = &scan->u.args3.c;
|
|
return scan;
|
|
}
|
|
break;
|
|
case IROLinearFunccall:
|
|
if (scan->u.funccall.linear8 == linear) {
|
|
if (b)
|
|
*b = &scan->u.funccall.linear8;
|
|
return scan;
|
|
}
|
|
for (i = 0; i < scan->u.funccall.argCount; i++) {
|
|
if (scan->u.funccall.args[i] == linear) {
|
|
if (b)
|
|
*b = &scan->u.funccall.args[i];
|
|
return scan;
|
|
}
|
|
}
|
|
break;
|
|
case IROLinearNop:
|
|
case IROLinearOperand:
|
|
case IROLinearGoto:
|
|
case IROLinearLabel:
|
|
case IROLinearEntry:
|
|
case IROLinearExit:
|
|
case IROLinearBeginCatch:
|
|
case IROLinearEndCatch:
|
|
case IROLinearEndCatchDtor:
|
|
case IROLinearAsm:
|
|
case IROLinearEnd:
|
|
break;
|
|
default:
|
|
#line 1536
|
|
CError_FATAL();
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
|
|
scan = IRO_FirstLinear;
|
|
}
|
|
|
|
if (b)
|
|
*b = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
IROLinear *IRO_LocateFather(IROLinear *linear) {
|
|
return LocateFatherHelper(linear, 0, NULL);
|
|
}
|
|
|
|
IROLinear *IRO_LocateFather_Cut_And_Paste(IROLinear *a, IROLinear *b) {
|
|
IROLinear **p;
|
|
IROLinear *l = LocateFatherHelper(a, 0, &p);
|
|
if (l) {
|
|
#line 1568
|
|
CError_ASSERT(p && *p);
|
|
IRO_NopOut(a);
|
|
*p = b;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
IROLinear *IRO_LocateFather_Cut_And_Paste_Without_Nopping(IROLinear *a, IROLinear *b) {
|
|
IROLinear **p;
|
|
IROLinear *l = LocateFatherHelper(a, 0, &p);
|
|
if (l) {
|
|
#line 1585
|
|
CError_ASSERT(p && *p);
|
|
*p = b;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
void IRO_ReplaceReference(IROLinear *a, Object *obj, IROLinear *b) {
|
|
IROLinear **ptr;
|
|
IROList list;
|
|
|
|
if (LocateFatherHelper(a, 1, &ptr)) {
|
|
#line 1605
|
|
CError_ASSERT(ptr && *ptr);
|
|
IRO_InitList(&list);
|
|
*ptr = IRO_TempReference(obj, &list);
|
|
IRO_PasteAfter(list.head, list.tail, b);
|
|
if (a->flags & IROLF_LoopInvariant)
|
|
list.tail->flags |= IROLF_LoopInvariant;
|
|
} else {
|
|
IRO_Dump("Oh, oh, did not find reference to replace\n");
|
|
}
|
|
}
|
|
|
|
void IRO_ReplaceReferenceWithNode(IROLinear *a, IROLinear *b) {
|
|
IROLinear **ptr;
|
|
IROList list;
|
|
|
|
if (LocateFatherHelper(a, 1, &ptr)) {
|
|
#line 1664
|
|
CError_ASSERT(ptr && *ptr);
|
|
*ptr = b;
|
|
b->flags |= IROLF_Reffed;
|
|
} else {
|
|
IRO_Dump("Oh, oh, did not find reference to replace\n");
|
|
}
|
|
}
|
|
|
|
void IRO_GetTemp(IROExpr *expr) {
|
|
}
|
|
|
|
IROLinear *IRO_AssignToTemp(IROExpr *expr) {
|
|
}
|
|
|
|
IROLinear *IRO_FindStart(IROLinear *linear) {
|
|
ExprStart = ExprEnd = linear;
|
|
IRO_WalkTree(linear, FindStart);
|
|
return ExprStart;
|
|
}
|
|
|
|
void IRO_DeleteCommaNode(IROLinear *linear, IROExpr *expr) {
|
|
IROLinear *scan;
|
|
for (scan = linear; scan; scan = scan->next) {
|
|
if (scan->nodetype == ECOMMA) {
|
|
if (scan != expr->linear) {
|
|
IRO_NopOut(scan->u.diadic.left);
|
|
IRO_LocateFather_Cut_And_Paste(scan, scan->u.diadic.right);
|
|
} else {
|
|
IRO_NopOut(scan->u.diadic.left);
|
|
expr->linear = scan->u.diadic.right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IRO_RemoveCommaNodeFromIR(void) {
|
|
IRONode *node;
|
|
IROLinear *linear;
|
|
|
|
for (node = IRO_FirstNode; node; node = node->nextnode) {
|
|
linear = node->first;
|
|
do {
|
|
if (!linear)
|
|
break;
|
|
if (linear->nodetype == ECOMMA) {
|
|
linear->u.diadic.left->flags &= ~IROLF_Reffed;
|
|
IRO_LocateFather_Cut_And_Paste_Without_Nopping(linear, linear->u.diadic.right);
|
|
linear->type = IROLinearNop;
|
|
}
|
|
linear = linear->next;
|
|
} while (linear != node->last);
|
|
}
|
|
}
|
|
|
|
IROAddrRecord *IRO_InitAddrRecordPointer(IROLinear *linear) {
|
|
IROAddrRecord *rec = oalloc(sizeof(IROAddrRecord));
|
|
rec->numObjRefs = 0;
|
|
rec->objRefs = NULL;
|
|
rec->numMisc = 0;
|
|
rec->misc = NULL;
|
|
rec->numInts = 0;
|
|
rec->ints = NULL;
|
|
rec->x16 = 0;
|
|
rec->linear = linear;
|
|
return rec;
|
|
}
|
|
|
|
IROLinear *IRO_HasSideEffect(IROLinear *linear) {
|
|
IROLinear *tmp;
|
|
|
|
if (!linear)
|
|
return linear;
|
|
|
|
if (linear->rtype && CParser_IsVolatile(linear->rtype, linear->nodeflags & ENODE_FLAG_QUALS))
|
|
return linear;
|
|
|
|
switch (linear->type) {
|
|
case IROLinearAsm:
|
|
return linear;
|
|
case IROLinearOperand:
|
|
if (ENODE_IS(linear->u.node, EOBJREF) && is_volatile_object(linear->u.node->data.objref))
|
|
return linear;
|
|
return NULL;
|
|
case IROLinearOp1Arg:
|
|
if (IRO_IsAssignOp[linear->nodetype])
|
|
return linear;
|
|
else
|
|
return IRO_HasSideEffect(linear->u.monadic);
|
|
case IROLinearOp2Arg:
|
|
if (IRO_IsAssignOp[linear->nodetype])
|
|
return linear;
|
|
else if ((tmp = IRO_HasSideEffect(linear->u.diadic.left)))
|
|
return tmp;
|
|
else
|
|
return IRO_HasSideEffect(linear->u.diadic.right);
|
|
case IROLinearOp3Arg:
|
|
if ((tmp = IRO_HasSideEffect(linear->u.args3.a)))
|
|
return tmp;
|
|
else if ((tmp = IRO_HasSideEffect(linear->u.args3.b)))
|
|
return tmp;
|
|
else
|
|
return IRO_HasSideEffect(linear->u.args3.c);
|
|
case IROLinearFunccall:
|
|
return linear;
|
|
default:
|
|
return linear;
|
|
}
|
|
}
|
|
|
|
IROLinear *IRO_CheckSideEffect(IROLinear *linear) {
|
|
IROLinear *result;
|
|
IROLinear *tmp;
|
|
IROLinear *tmp2;
|
|
IROLinear *tmp3;
|
|
|
|
if (!linear)
|
|
return linear;
|
|
|
|
if (linear->rtype && CParser_IsVolatile(linear->rtype, linear->nodeflags & ENODE_FLAG_QUALS)) {
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
}
|
|
|
|
result = NULL;
|
|
switch (linear->type) {
|
|
case IROLinearAsm:
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
case IROLinearOperand:
|
|
if (ENODE_IS(linear->u.node, EOBJREF) && is_volatile_object(linear->u.node->data.objref)) {
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
}
|
|
break;
|
|
case IROLinearOp1Arg:
|
|
if (IRO_IsAssignOp[linear->nodetype]) {
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
}
|
|
if ((result = IRO_CheckSideEffect(linear->u.monadic))) {
|
|
if (linear->nodetype == EINDIRECT && linear->u.monadic->type == IROLinearOperand) {
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
}
|
|
}
|
|
break;
|
|
case IROLinearOp2Arg:
|
|
if (IRO_IsAssignOp[linear->nodetype]) {
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
}
|
|
tmp = IRO_CheckSideEffect(linear->u.diadic.left);
|
|
tmp2 = IRO_CheckSideEffect(linear->u.diadic.right);
|
|
if (tmp)
|
|
result = tmp;
|
|
else
|
|
result = tmp2;
|
|
break;
|
|
case IROLinearOp3Arg:
|
|
tmp = IRO_CheckSideEffect(linear->u.args3.a);
|
|
tmp2 = IRO_CheckSideEffect(linear->u.args3.b);
|
|
tmp3 = IRO_CheckSideEffect(linear->u.args3.c);
|
|
result = tmp ? tmp : tmp2 ? tmp2 : tmp3;
|
|
break;
|
|
case IROLinearFunccall:
|
|
linear->flags &= ~IROLF_Reffed;
|
|
return linear;
|
|
default:
|
|
return linear;
|
|
}
|
|
|
|
linear->type = IROLinearNop;
|
|
IRO_Dump("Nop out with side-effects checking at: %d\n", linear->index);
|
|
return result;
|
|
}
|
|
|
|
void IRO_WalkExcActions(ExceptionAction *action, WalkObjFunc func) {
|
|
while (action) {
|
|
switch (action->type) {
|
|
case EAT_DESTROYLOCAL:
|
|
func(action->data.destroy_local.local);
|
|
break;
|
|
case EAT_DESTROYLOCALCOND:
|
|
func(action->data.destroy_local_cond.local);
|
|
func(action->data.destroy_local_cond.cond);
|
|
break;
|
|
case EAT_DESTROYLOCALOFFSET:
|
|
func(action->data.destroy_local_offset.local);
|
|
break;
|
|
case EAT_DESTROYLOCALPOINTER:
|
|
func(action->data.destroy_local_pointer.pointer);
|
|
break;
|
|
case EAT_DESTROYLOCALARRAY:
|
|
func(action->data.destroy_local_array.localarray);
|
|
break;
|
|
case EAT_DESTROYPARTIALARRAY:
|
|
func(action->data.destroy_partial_array.arraypointer);
|
|
func(action->data.destroy_partial_array.arraycounter);
|
|
func(action->data.destroy_partial_array.element_size);
|
|
break;
|
|
case EAT_DESTROYBASE:
|
|
func(action->data.destroy_member.objectptr); // wrong union?
|
|
break;
|
|
case EAT_DESTROYMEMBER:
|
|
func(action->data.destroy_member.objectptr);
|
|
break;
|
|
case EAT_DESTROYMEMBERCOND:
|
|
func(action->data.destroy_member_cond.objectptr);
|
|
func(action->data.destroy_member_cond.cond);
|
|
break;
|
|
case EAT_DESTROYMEMBERARRAY:
|
|
func(action->data.destroy_member_array.objectptr);
|
|
break;
|
|
case EAT_DELETEPOINTER:
|
|
func(action->data.delete_pointer.pointerobject);
|
|
break;
|
|
case EAT_DELETEPOINTERCOND:
|
|
func(action->data.delete_pointer_cond.pointerobject);
|
|
func(action->data.delete_pointer_cond.cond);
|
|
break;
|
|
}
|
|
action = action->prev;
|
|
}
|
|
}
|
|
|
|
Boolean IRO_FunctionCallMightThrowException(IROLinear *linear) {
|
|
Object *obj;
|
|
if (linear->type == IROLinearFunccall) {
|
|
obj = NULL;
|
|
if (linear->u.funccall.linear8->type == IROLinearOperand && ENODE_IS(linear->u.funccall.linear8->u.node, EOBJREF))
|
|
obj = linear->u.funccall.linear8->u.node->data.objref;
|
|
if (!obj || CExcept_CanThrowException(obj, obj->datatype == DVFUNC && !(linear->nodeflags & ENODE_FLAG_80)))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
IROLinear *IRO_NewIntConst(CInt64 val, Type *type) {
|
|
ENode *node;
|
|
IROLinear *linear;
|
|
|
|
node = IRO_NewENode(EINTCONST);
|
|
node->data.intval = val;
|
|
node->rtype = type;
|
|
|
|
linear = IRO_NewLinear(IROLinearOperand);
|
|
linear->index = ++IRO_NumLinear;
|
|
linear->rtype = type;
|
|
linear->u.node = node;
|
|
return linear;
|
|
}
|
|
|
|
IROLinear *IRO_NewFloatConst(Float val, Type *type) {
|
|
ENode *node;
|
|
IROLinear *linear;
|
|
|
|
node = IRO_NewENode(EFLOATCONST);
|
|
node->data.floatval = val;
|
|
node->rtype = type;
|
|
|
|
linear = IRO_NewLinear(IROLinearOperand);
|
|
linear->index = ++IRO_NumLinear;
|
|
linear->rtype = type;
|
|
linear->u.node = node;
|
|
return linear;
|
|
}
|
|
|
|
Boolean IRO_IsAddressMultiply(IROLinear *linear) {
|
|
return 0;
|
|
}
|
|
|
|
void IRO_SetupForUserBreakChecking(void) {
|
|
IRO_LastUserBreakTick = 0;
|
|
}
|
|
|
|
void IRO_CheckForUserBreak(void) {
|
|
if (IRO_LastUserBreakTick + 8 < COS_GetTicks()) {
|
|
if (CWUserBreak(cparams.context))
|
|
CError_UserBreak();
|
|
IRO_LastUserBreakTick = COS_GetTicks();
|
|
}
|
|
}
|
|
|