diff options
Diffstat (limited to 'compiler_and_linker/BackEnd/PowerPC/CodeGenerator/Switch.c')
-rw-r--r-- | compiler_and_linker/BackEnd/PowerPC/CodeGenerator/Switch.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/Switch.c b/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/Switch.c new file mode 100644 index 0000000..4bbd82e --- /dev/null +++ b/compiler_and_linker/BackEnd/PowerPC/CodeGenerator/Switch.c @@ -0,0 +1,518 @@ +#include "compiler/Switch.h" +#include "compiler/CError.h" +#include "compiler/CFunc.h" +#include "compiler/CInt64.h" +#include "compiler/CParser.h" +#include "compiler/InstrSelection.h" +#include "compiler/ObjGenMachO.h" +#include "compiler/Operands.h" +#include "compiler/PCode.h" +#include "compiler/PCodeUtilities.h" +#include "compiler/RegisterInfo.h" +#include "compiler/TOC.h" +#include "compiler/CompilerTools.h" +#include "compiler/objects.h" + +ObjectList *switchtables; +static SwitchCase **caselabels; +static CaseRange *caseranges; +static SInt32 ncases; +static SInt32 nranges_minus1; +static CInt64 min; +static CInt64 max; +static CInt64 first; +static short selector_gpr; +static short selector_gprHi; +static Type *selector_type; +static PCodeLabel *defaultlabel; +static CInt64 range; + +static int compare_cases(const void *a, const void *b) { + const SwitchCase **casea = (const SwitchCase **) a; + const SwitchCase **caseb = (const SwitchCase **) b; + + if (CInt64_Less((*casea)->min, (*caseb)->min)) + return -1; + if (CInt64_Greater((*casea)->min, (*caseb)->min)) + return 1; + return 0; +} + +static void build_case_ranges(Type *type, SwitchCase *cases, CLabel *label) { + SwitchCase **caseptr; + SInt32 i; + SwitchCase *curcase; + CaseRange *currange; + + if (type->size == 8) { + min.lo = 0; + min.hi = 0x80000000; + max.lo = 0xFFFFFFFF; + max.hi = 0x7FFFFFFF; + } else if (type->size == 4) { + CInt64_SetLong(&min, 0x80000000); + CInt64_SetLong(&max, 0x7FFFFFFF); + } else if (is_unsigned(type)) { + min.hi = 0; + min.lo = 0; + max.hi = 0; + max.lo = 0xFFFF; + } else { + CInt64_SetLong(&min, -0x8000); + CInt64_SetLong(&max, 0x7FFF); + } + + caselabels = lalloc(sizeof(SwitchCase *) * ncases); + caseptr = caselabels; + while (cases) { + *caseptr = cases; + cases = cases->next; + ++caseptr; + } + + caseranges = lalloc(((ncases * 2) + 2) * sizeof(CaseRange)); + if (type->size < 8) { + for (i = 0; i < ncases; i++) + CInt64_SetLong(&caselabels[i]->min, caselabels[i]->min.lo); + } + + qsort(caselabels, ncases, sizeof(SwitchCase *), &compare_cases); + + currange = caseranges; + currange->min = min; + currange->range = CInt64_Sub(max, min); + currange->label = label->pclabel; + + for (i = 0; i < ncases; i++) { + curcase = caselabels[i]; + if (CInt64_GreaterEqual(curcase->min, min) && CInt64_LessEqual(curcase->min, max)) { + if (CInt64_Equal(currange->min, min)) + first = curcase->min; + range = CInt64_Sub(curcase->min, first); + + if (CInt64_Greater(curcase->min, currange->min)) { + currange->range = CInt64_Sub(CInt64_Sub(curcase->min, currange->min), cint64_one); + (++currange)->min = curcase->min; + } else if (CInt64_Greater(currange->min, min) && curcase->label->pclabel == currange[-1].label) { + currange[-1].range = CInt64_Add(currange[-1].range, cint64_one); + if (CInt64_Equal(currange->range, cint64_zero)) { + currange--; + } else { + currange->min = CInt64_Add(currange->min, cint64_one); + currange->range = CInt64_Sub(currange->range, cint64_one); + } + continue; + } + + currange->range = cint64_zero; + currange->label = curcase->label->pclabel; + + if (CInt64_Less(curcase->min, max)) { + currange++; + currange->min = CInt64_Add(curcase->min, cint64_one); + currange->range = CInt64_Sub(max, currange->min); + currange->label = label->pclabel; + } + } + } + + nranges_minus1 = currange - caseranges; +} + +static void treecompare(SInt32 start, SInt32 end) { + SInt32 r30; + SInt32 r29; + CaseRange *currange; + int count; + + count = end - start; + CError_ASSERT(175, selector_type->size <= 4); + + r29 = start + (count >> 1) + 1; + currange = caseranges + r29; + + if (CInt64_Equal(currange[-1].range, cint64_zero) && (!(count & 1) || (CInt64_NotEqual(currange->range, cint64_zero) && count > 1))) { + currange--; + r29--; + } + + r30 = r29 - 1; + + if (selector_type->size < 4 && is_unsigned(selector_type)) { + emitpcode(PC_CMPLI, 0, selector_gpr, CInt64_GetULong(&currange->min)); + } else if (FITS_IN_SHORT((SInt32) CInt64_GetULong(&currange->min))) { + emitpcode(PC_CMPI, 0, selector_gpr, CInt64_GetULong(&currange->min)); + } else { + SInt32 value = CInt64_GetULong(&currange->min); + int reg = ALLOC_GPR(); + load_immediate(reg, value); + emitpcode(PC_CMP, 0, selector_gpr, reg); + } + + if (CInt64_Equal(currange->range, cint64_zero) && r29 < end) { + branch_conditional(0, EEQU, 1, currange->label); + r29++; + } + + if (r29 == end) { + if (start == r30) { + if (caseranges[start].label == caseranges[end].label) { + branch_always(caseranges[start].label); + } else { + branch_conditional(0, EGREATEREQU, 1, caseranges[end].label); + branch_always(caseranges[start].label); + } + } else { + branch_conditional(0, EGREATEREQU, 1, caseranges[end].label); + treecompare(start, r30); + } + } else { + if (start == r30) { + branch_conditional(0, ELESS, 1, caseranges[start].label); + treecompare(r29, end); + } else { + PCodeLabel *label = makepclabel(); + branch_conditional(0, EGREATEREQU, 1, label); + treecompare(start, r30); + branch_label(label); + treecompare(r29, end); + } + } +} + +static void I8_treecompare(SInt32 start, SInt32 end) { + SInt32 r30; + SInt32 r29; + CaseRange *currange; + int count; + + count = end - start; + + r29 = start + (count >> 1) + 1; + currange = caseranges + r29; + + if (CInt64_Equal(currange[-1].range, cint64_zero) && (!(count & 1) || (CInt64_NotEqual(currange->range, cint64_zero) && count > 1))) { + currange--; + r29--; + } + + r30 = r29 - 1; + + if (CInt64_Equal(currange->range, cint64_zero) && r29 < end) { + short a = ALLOC_GPR(); + short b = ALLOC_GPR(); + load_immediate(a, currange->min.lo); + load_immediate(b, currange->min.hi); + emitpcode(PC_XOR, a, selector_gpr, a); + emitpcode(PC_XOR, b, selector_gprHi, b); + emitpcode(PC_OR, b, a, b); + emitpcode(PC_CMPI, 0, b, 0); + branch_conditional(0, EEQU, 1, currange->label); + r29++; + } + + if (r29 == end) { + if (start == r30) { + if (caseranges[start].label == caseranges[end].label) { + branch_always(caseranges[start].label); + } else { + short a = ALLOC_GPR(); + short b = ALLOC_GPR(); + short c = ALLOC_GPR(); + short d = ALLOC_GPR(); + load_immediate(a, currange->min.lo); + load_immediate(b, currange->min.hi); + if (TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG && TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG) { + emitpcode(PC_XORIS, c, selector_gprHi, 0x8000); + emitpcode(PC_XORIS, d, b, 0x8000); + } else { + c = selector_gprHi; + d = b; + } + emitpcode(PC_SUBFC, a, a, selector_gpr); + emitpcode(PC_SUBFE, b, d, c); + emitpcode(PC_SUBFE, b, a, a); + emitpcode(PC_NEG, b, b); + emitpcode(PC_CMPI, 0, b, 0); + branch_conditional(0, EEQU, 1, caseranges[end].label); + branch_always(caseranges[start].label); + } + } else { + short a = ALLOC_GPR(); + short b = ALLOC_GPR(); + short c = ALLOC_GPR(); + short d = ALLOC_GPR(); + load_immediate(a, currange->min.lo); + load_immediate(b, currange->min.hi); + if (TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG && TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG) { + emitpcode(PC_XORIS, c, selector_gprHi, 0x8000); + emitpcode(PC_XORIS, d, b, 0x8000); + } else { + c = selector_gprHi; + d = b; + } + emitpcode(PC_SUBFC, a, a, selector_gpr); + emitpcode(PC_SUBFE, b, d, c); + emitpcode(PC_SUBFE, b, a, a); + emitpcode(PC_NEG, b, b); + emitpcode(PC_CMPI, 0, b, 0); + branch_conditional(0, EEQU, 1, caseranges[end].label); + I8_treecompare(start, r30); + } + } else { + if (start == r30) { + short a = ALLOC_GPR(); + short b = ALLOC_GPR(); + short c = ALLOC_GPR(); + short d = ALLOC_GPR(); + load_immediate(a, currange->min.lo); + load_immediate(b, currange->min.hi); + if (TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG && TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG) { + emitpcode(PC_XORIS, c, selector_gprHi, 0x8000); + emitpcode(PC_XORIS, d, b, 0x8000); + } else { + c = selector_gprHi; + d = b; + } + emitpcode(PC_SUBFC, a, selector_gpr, a); + emitpcode(PC_SUBFE, b, c, d); + emitpcode(PC_SUBFE, b, a, a); + emitpcode(PC_NEG, b, b); + emitpcode(PC_CMPI, 0, b, 0); + branch_conditional(0, ENOTEQU, 1, caseranges[end].label); + I8_treecompare(r29, end); + } else { + PCodeLabel *label; + short a = ALLOC_GPR(); + short b = ALLOC_GPR(); + short c = ALLOC_GPR(); + short d = ALLOC_GPR(); + load_immediate(a, currange->min.lo); + load_immediate(b, currange->min.hi); + if (TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG && TYPE_INTEGRAL(selector_type)->integral != IT_ULONGLONG) { + emitpcode(PC_XORIS, c, selector_gprHi, 0x8000); + emitpcode(PC_XORIS, d, b, 0x8000); + } else { + c = selector_gprHi; + d = b; + } + emitpcode(PC_SUBFC, a, a, selector_gpr); + emitpcode(PC_SUBFE, b, d, c); + emitpcode(PC_SUBFE, b, a, a); + emitpcode(PC_NEG, b, b); + emitpcode(PC_CMPI, 0, b, 0); + label = makepclabel(); + branch_conditional(0, EEQU, 1, label); + I8_treecompare(start, r30); + branch_label(label); + I8_treecompare(r29, end); + } + } +} + +static void generate_tree(ENode *expr) { + Operand op; + + memclrw(&op, sizeof(Operand)); + if (TYPE_IS_8BYTES(expr->rtype)) { + GEN_NODE(expr, &op); + coerce_to_register_pair(&op, expr->rtype, 0, 0); + selector_type = expr->rtype; + selector_gpr = op.reg; + selector_gprHi = op.regHi; + I8_treecompare(0, nranges_minus1); + } else { + GEN_NODE(expr, &op); + if (expr->rtype->size < 4) + extend32(&op, expr->rtype, 0); + ENSURE_GPR(&op, expr->rtype, 0); + selector_type = expr->rtype; + selector_gpr = op.reg; + treecompare(0, nranges_minus1); + } +} + +static Object *create_switch_table(void) { + Object *obj; + ObjectList *list; + UInt32 *outptr; + CaseRange *currange; + SInt32 size; + CInt64 value; + + obj = galloc(sizeof(Object)); + list = galloc(sizeof(ObjectList)); + memclrw(obj, sizeof(Object)); + memclrw(list, sizeof(ObjectList)); + + obj->otype = OT_OBJECT; + obj->access = ACCESSPUBLIC; + obj->datatype = DDATA; + obj->name = CParser_GetUniqueName(); + obj->toc = NULL; + obj->sclass = TK_STATIC; + obj->qual = Q_CONST; + obj->flags |= OBJECT_FLAGS_2 | OBJECT_DEFINED; + obj->u.data.linkname = obj->name; + obj->type = NULL; + createIndirect(obj, 0, 0); + obj->type = TYPE(&void_ptr); + + size = CInt64_GetULong(&range) + 1; + obj->u.data.u.switchtable.size = size; + obj->u.data.u.switchtable.data = lalloc(4 * size); + + currange = caseranges; + outptr = (UInt32 *) obj->u.data.u.switchtable.data; + value = cint64_zero; + while (CInt64_LessEqual(value, range)) { + while (CInt64_Greater(CInt64_Add(first, value), CInt64_Add(currange->min, currange->range))) + currange++; + *outptr = CTool_CreateIndexFromPointer(currange->label); + value = CInt64_Add(value, cint64_one); + outptr++; + } + + list->object = obj; + list->next = switchtables; + switchtables = list; + return list->object; +} + +static void generate_table(ENode *expr, SwitchInfo *info) { + Object *table; + SwitchCase *curcase; + short reg; + short reg2; + short reg3; + Operand op1; + Operand op2; + + CInt64 val3 = {0, 3}; + memclrw(&op1, sizeof(Operand)); + memclrw(&op2, sizeof(Operand)); + + if (CInt64_Greater(first, cint64_zero) && CInt64_Less(first, val3)) { + range = CInt64_Add(range, first); + first = cint64_zero; + } + + table = create_switch_table(); + CError_ASSERT(553, !TYPE_IS_8BYTES(expr->rtype)); + + GEN_NODE(expr, &op1); + if (expr->rtype->size < 4) + extend32(&op1, expr->rtype, 0); + ENSURE_GPR(&op1, expr->rtype, 0); + + reg = op1.reg; + if (CInt64_NotEqual(first, cint64_zero)) { + SInt32 value; + reg = ALLOC_GPR(); + value = -CInt64_GetULong(&first); + if (!FITS_IN_SHORT(value)) { + emitpcode(PC_ADDIS, reg, op1.reg, 0, HIGH_PART(value)); + if (value) + emitpcode(PC_ADDI, reg, reg, 0, LOW_PART(value)); + } else { + emitpcode(PC_ADDI, reg, op1.reg, 0, value); + } + } + + if (!FITS_IN_SHORT(CInt64_GetULong(&range))) { + short tmp = ALLOC_GPR(); + load_immediate(tmp, CInt64_GetULong(&range)); + emitpcode(PC_CMPL, 0, reg, tmp); + } else { + emitpcode(PC_CMPLI, 0, reg, CInt64_GetULong(&range)); + } + + branch_conditional(0, EGREATER, 1, defaultlabel); + if (table->toc) { + op2.optype = OpndType_Symbol; + op2.object = table->toc; + indirect(&op2, NULL); + } else { + op2.optype = OpndType_Symbol; + op2.object = table; + } + + if (op2.optype != OpndType_GPR) { + Coerce_to_register(&op2, TYPE(&void_ptr), reg2 = ALLOC_GPR()); + } + + if (op2.optype != OpndType_GPR) { + CError_FATAL(599); + } else { + if (op2.reg != reg2) + emitpcode(PC_MR, reg2, op2.reg); + } + + if (CInt64_Equal(first, cint64_zero)) { + reg = ALLOC_GPR(); + emitpcode(PC_RLWINM, reg, op1.reg, 2, 0, 29); + } else { + emitpcode(PC_RLWINM, reg, reg, 2, 0, 29); + } + + reg3 = reg2; + emitpcode(PC_LWZX, reg3, reg3, reg); + for (curcase = info->cases; curcase; curcase = curcase->next) + pcbranch(pclastblock, curcase->label->pclabel); + pcbranch(pclastblock, info->defaultlabel->pclabel); + emitpcode(PC_MTCTR, reg3); + branch_indirect(table); +} + +void switchstatement(ENode *expr, SwitchInfo *info) { + Boolean use_table; + SwitchCase *swcase; + + use_table = copts.switch_tables; + + ncases = 0; + for (swcase = info->cases; swcase; swcase = swcase->next) { + if (!swcase->label->pclabel) + swcase->label->pclabel = makepclabel(); + ncases++; + } + + CError_ASSERT(656, ncases >= 0 && ncases <= 0x3333332U); + + if (!info->defaultlabel->pclabel) + info->defaultlabel->pclabel = makepclabel(); + defaultlabel = info->defaultlabel->pclabel; + + build_case_ranges(expr->rtype, info->cases, info->defaultlabel); + + if (TYPE_IS_8BYTES(expr->rtype)) { + generate_tree(expr); + return; + } + + if (!use_table || nranges_minus1 < 8 || (nranges_minus1 * 2) < ((range.lo / 2) + 4)) + generate_tree(expr); + else + generate_table(expr, info); +} + +void dumpswitchtables(Object *funcobj) { + Object *table; + ObjectList *list; + SInt32 size; + UInt32 *array; + + for (list = switchtables; list; list = list->next) { + table = list->object; + CError_ASSERT(694, table->otype == OT_OBJECT && table->access == ACCESSPUBLIC && table->datatype == DDATA); + + size = table->u.data.u.switchtable.size; + array = (UInt32 *) table->u.data.u.switchtable.data; + while (size--) { + *array = CTool_EndianConvertWord32(((PCodeLabel *) CTool_ResolveIndexToPointer(*array))->block->codeOffset); + array++; + } + + ObjGen_DeclareSwitchTable(table, funcobj); + } +} |