From d3283ec05f51056faa18610e952ccc81cb738313 Mon Sep 17 00:00:00 2001 From: Eric Biederman Date: Wed, 18 Jun 2003 11:03:18 +0000 Subject: [PATCH] - A new test case for romcc - Minor romcc fixes - In smbus_wail_until_done a romcc glitch with || in romcc where it likes to run out of registers. Use | to be explicit that I don't need the short circuiting behavior. - Remove unused #defines from coherent_ht.c - Update the test in auto.c to 512M - Add definition of log2 to romcc_io.h - Implement SPD memory sizing in raminit.c - Reduce the number of memory devices back 2 to for the SOLO board. git-svn-id: svn://svn.coreboot.org/coreboot/trunk@883 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/arch/i386/include/arch/romcc_io.h | 12 ++ src/mainboard/amd/solo/auto.c | 57 ++++- src/northbridge/amd/amdk8/coherent_ht.c | 8 - src/northbridge/amd/amdk8/raminit.c | 202 +++++++++++++++++- .../amd/amd8111/amd8111_early_smbus.c | 2 +- util/romcc/Makefile | 1 + util/romcc/romcc.c | 192 +++++++++++++---- util/romcc/tests/simple_test33.c | 41 ++++ 8 files changed, 447 insertions(+), 68 deletions(-) create mode 100644 util/romcc/tests/simple_test33.c diff --git a/src/arch/i386/include/arch/romcc_io.h b/src/arch/i386/include/arch/romcc_io.h index 7d3d50e39..d67b3b60d 100644 --- a/src/arch/i386/include/arch/romcc_io.h +++ b/src/arch/i386/include/arch/romcc_io.h @@ -35,6 +35,18 @@ static void hlt(void) __builtin_hlt(); } +int log2(int value) +{ + /* __builtin_bsr is a exactly equivalent to the x86 machine + * instruction with the exception that it returns -1 + * when the value presented to it is zero. + * Otherwise __builtin_bsr returns the zero based index of + * the highest bit set. + */ + return __builtin_bsr(value); +} + + typedef __builtin_msr_t msr_t; static msr_t rdmsr(unsigned long index) diff --git a/src/mainboard/amd/solo/auto.c b/src/mainboard/amd/solo/auto.c index e8e3976ef..f61b79425 100644 --- a/src/mainboard/amd/solo/auto.c +++ b/src/mainboard/amd/solo/auto.c @@ -11,6 +11,13 @@ #include "northbridge/amd/amdk8/coherent_ht.c" #include "sdram/generic_sdram.c" +#define NODE_ID 0x60 +#define HT_INIT_CONTROL 0x6c + +#define HTIC_ColdR_Detect (1<<4) +#define HTIC_BIOSR_Detect (1<<5) +#define HTIC_INIT_Detect (1<<6) + static int boot_cpu(void) { volatile unsigned long *local_apic; @@ -59,6 +66,16 @@ static int cpu_init_detected(void) } +static void print_debug_pci_dev(unsigned dev) +{ + print_debug("PCI: "); + print_debug_hex8((dev >> 16) & 0xff); + print_debug_char(':'); + print_debug_hex8((dev >> 11) & 0x1f); + print_debug_char('.'); + print_debug_hex8((dev >> 8) & 7); +} + static void print_pci_devices(void) { device_t dev; @@ -72,15 +89,33 @@ static void print_pci_devices(void) (((id >> 16) & 0xffff) == 0x0000)) { continue; } - print_debug("PCI: 00:"); - print_debug_hex8(dev >> 11); - print_debug_char('.'); - print_debug_hex8((dev >> 8) & 7); + print_debug_pci_dev(dev); print_debug("\r\n"); } } +static void dump_pci_device(unsigned dev) +{ + int i; + print_debug_pci_dev(dev); + print_debug("\r\n"); + + for(i = 0; i <= 255; i++) { + unsigned char val; + if ((i & 0x0f) == 0) { + print_debug_hex8(i); + print_debug_char(':'); + } + val = pci_read_config8(dev, i); + print_debug_char(' '); + print_debug_hex8(val); + if ((i & 0x0f) == 0x0f) { + print_debug("\r\n"); + } + } +} + static void dump_spd_registers(void) { unsigned device; @@ -112,6 +147,7 @@ static void dump_spd_registers(void) } } + static void main(void) { uart_init(); @@ -132,7 +168,16 @@ static void main(void) sdram_initialize(); dump_spd_registers(); - /* Check the first 8M */ - ram_check(0x00100000, 0x00800000); + dump_pci_device(PCI_DEV(0, 0x18, 2)); + + /* Check the first 512M */ + msr_t msr; + msr = rdmsr(TOP_MEM); + print_debug("TOP_MEM: "); + print_debug_hex32(msr.hi); + print_debug_hex32(msr.lo); + print_debug("\r\n"); +#warning "FIXME if I pass msr.lo somehow I get the value 0x00000030 as stop in ram_check" + ram_check(0x00000000, 0x20000000); } } diff --git a/src/northbridge/amd/amdk8/coherent_ht.c b/src/northbridge/amd/amdk8/coherent_ht.c index 68d8b39e3..fe2f3723a 100644 --- a/src/northbridge/amd/amdk8/coherent_ht.c +++ b/src/northbridge/amd/amdk8/coherent_ht.c @@ -1,11 +1,3 @@ -#define COHERENT_AMD_SOLO 1 /* AMD Solo motherboard */ -#define COHERENT_ARIMA_HDAMA 2 /* Arima HDAMA motherboard */ - -#ifndef COHERENT_CONFIG -#define COHERENT_CONFIG COHERENT_AMD_SOLO -#endif - - static void setup_coherent_ht_domain(void) { static const unsigned int register_values[] = { diff --git a/src/northbridge/amd/amdk8/raminit.c b/src/northbridge/amd/amdk8/raminit.c index 6c757c993..54ca82e65 100644 --- a/src/northbridge/amd/amdk8/raminit.c +++ b/src/northbridge/amd/amdk8/raminit.c @@ -1,3 +1,4 @@ +#include #define MEMORY_SUSE_SOLO 1 /* SuSE Solo configuration */ #define MEMORY_LNXI_SOLO 2 /* LNXI Solo configuration */ #define MEMORY_LNXI_HDAMA 3 /* LNXI HDAMA configuration */ @@ -1112,6 +1113,192 @@ static void sdram_set_registers(void) print_debug("done.\r\n"); } + +struct dimm_size { + unsigned long side1; + unsigned long side2; +}; +static struct dimm_size spd_get_dimm_size(unsigned device) +{ + /* Calculate the log base 2 size of a DIMM in bits */ + struct dimm_size sz; + int value, low; + sz.side1 = 0; + sz.side2 = 0; + + /* Note it might be easier to use byte 31 here, it has the DIMM size as + * a multiple of 4MB. The way we do it now we can size both + * sides of an assymetric dimm. + */ + value = smbus_read_byte(device, 3); /* rows */ + if (value < 0) return sz; + sz.side1 += value & 0xf; + + value = smbus_read_byte(device, 4); /* columns */ + if (value < 0) return sz; + sz.side1 += value & 0xf; + + value = smbus_read_byte(device, 17); /* banks */ + if (value < 0) return sz; + sz.side1 += log2(value & 0xff); + + /* Get the module data widht and convert it to a power of two */ + value = smbus_read_byte(device, 7); /* (high byte) */ + if (value < 0) return sz; + value &= 0xff; + value <<= 8; + + low = smbus_read_byte(device, 6); /* (low byte) */ + if (low < 0) return sz; + value = value | (low & 0xff); + sz.side1 += log2(value); + + /* side 2 */ + value = smbus_read_byte(device, 5); /* number of physical banks */ + if (value <= 1) return sz; + + /* Start with the symmetrical case */ + sz.side2 = sz.side1; + + value = smbus_read_byte(device, 3); /* rows */ + if (value < 0) return sz; + if ((value & 0xf0) == 0) return sz; /* If symmetrical we are done */ + sz.side2 -= (value & 0x0f); /* Subtract out rows on side 1 */ + sz.side2 += ((value >> 4) & 0x0f); /* Add in rows on side 2 */ + + value = smbus_read_byte(device, 4); /* columns */ + if (value < 0) return sz; + sz.side2 -= (value & 0x0f); /* Subtract out columns on side 1 */ + sz.side2 += ((value >> 4) & 0x0f); /* Add in columsn on side 2 */ + return sz; +} + +static unsigned spd_to_dimm_side0(unsigned device) +{ + return (device - SMBUS_MEM_DEVICE_START) << 1; +} + +static unsigned spd_to_dimm_side1(unsigned device) +{ + return ((device - SMBUS_MEM_DEVICE_START) << 1) + 1; +} + +static void set_dimm_size(unsigned long size, unsigned index) +{ + unsigned value = 0; + /* Make certain the dimm is at least 32MB */ + if (size >= (25 + 3)) { + /* Place the dimm size in 32 MB quantities in the bits 31 - 21. + * The initialize dimm size is in bits. + * Set the base enable bit0. + */ + value = (1 << ((size - (25 + 3)) + 21)) | 1; + } + /* Set the appropriate DIMM base address register */ + pci_write_config32(PCI_DEV(0, 0x18, 2), 0x40 + (index << 2), value); +} + +static void spd_set_ram_size(void) +{ + unsigned device; + for(device = SMBUS_MEM_DEVICE_START; + device <= SMBUS_MEM_DEVICE_END; + device += SMBUS_MEM_DEVICE_INC) + { + struct dimm_size sz; + sz = spd_get_dimm_size(device); + set_dimm_size(sz.side1, spd_to_dimm_side0(device)); + set_dimm_size(sz.side2, spd_to_dimm_side1(device)); + } +} + +static void set_top_mem(unsigned tom_k) +{ + /* Error if I don't have memory */ + if (!tom_k) { + die("No memory"); + } + /* Now set top of memory */ + msr_t msr; + msr.lo = (tom_k & 0x003fffff) << 10; + msr.hi = (tom_k & 0xffc00000) >> 22; + wrmsr(TOP_MEM, msr); + +#if 1 + /* And report the amount of memory. (I run out of registers if i don't) */ + print_debug("RAM: 0x"); + print_debug_hex32(tom_k); + print_debug(" KB\r\n"); +#endif +} + +static void order_dimms(void) +{ + unsigned long tom; + unsigned mask; + unsigned index; + + /* Remember which registers we have used in the high 8 bits of tom */ + tom = 0; + for(;;) { + /* Find the largest remaining canidate */ + unsigned canidate; + uint32_t csbase, csmask; + unsigned size; + csbase = 0; + canidate = 0; + for(index = 0; index < 8; index++) { + uint32_t value; + value = pci_read_config32(PCI_DEV(0, 0x18, 2), 0x40 + (index << 2)); + + /* Is it enabled? */ + if (!(value & 1)) { + continue; + } + + /* Is it greater? */ + if (value <= csbase) { + continue; + } + + /* Has it already been selected */ + if (tom & (1 << (index + 24))) { + continue; + } + /* I have a new canidate */ + csbase = value; + canidate = index; + } + /* See if I have found a new canidate */ + if (csbase == 0) { + break; + } + + /* Remember I have used this register */ + tom |= (1 << (canidate + 24)); + + /* Remember the dimm size */ + size = csbase >> 21; + + /* Recompute the cs base register value */ + csbase = (tom << 21) | 1; + + /* Increment the top of memory */ + tom += size; + + /* Compute the memory mask */ + csmask = ((size -1) << 21); + csmask |= 0xfe00; /* For now don't optimize */ + + /* Write the new base register */ + pci_write_config32(PCI_DEV(0, 0x18, 2), 0x40 + (canidate << 2), csbase); + pci_write_config32(PCI_DEV(0, 0x18, 2), 0x60 + (canidate << 2), csmask); + + } + set_top_mem((tom & ~0xff000000) << 15); +} + + #define DRAM_CONFIG_LOW 0x90 #define DCL_DLL_Disable (1<<0) #define DCL_D_DRV (1<<1) @@ -1122,20 +1309,21 @@ static void sdram_set_registers(void) #define DCL_MemClrStatus (1<<11) #define DCL_DimmEcEn (1<<17) -#define NODE_ID 0x60 -#define HT_INIT_CONTROL 0x6c -#define HTIC_ColdR_Detect (1<<4) -#define HTIC_BIOSR_Detect (1<<5) -#define HTIC_INIT_Detect (1<<6) - -static void sdram_set_spd_registers(void) +static void spd_set_ecc_mode(void) { unsigned long dcl; dcl = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW); /* Until I know what is going on disable ECC support */ dcl &= ~DCL_DimmEcEn; pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW, dcl); + +} +static void sdram_set_spd_registers(void) +{ + spd_set_ram_size(); + spd_set_ecc_mode(); + order_dimms(); } #define TIMEOUT_LOOPS 300000 diff --git a/src/southbridge/amd/amd8111/amd8111_early_smbus.c b/src/southbridge/amd/amd8111/amd8111_early_smbus.c index 6b6d9ad14..33f558896 100644 --- a/src/southbridge/amd/amd8111/amd8111_early_smbus.c +++ b/src/southbridge/amd/amd8111/amd8111_early_smbus.c @@ -53,7 +53,7 @@ static int smbus_wait_until_done(void) smbus_delay(); val = inw(SMBUS_IO_BASE + SMBGSTATUS); - if (((val & 0x8) == 0) || ((val & 0x437) != 0)) { + if (((val & 0x8) == 0) | ((val & 0x437) != 0)) { break; } } while(--loops); diff --git a/util/romcc/Makefile b/util/romcc/Makefile index be2d03105..9722ca3fc 100644 --- a/util/romcc/Makefile +++ b/util/romcc/Makefile @@ -51,6 +51,7 @@ TESTS=\ simple_test30.c \ simple_test31.c \ simple_test32.c \ + simple_test33.c \ raminit_test.c \ raminit_test2.c \ raminit_test3.c \ diff --git a/util/romcc/romcc.c b/util/romcc/romcc.c index 7aea1b40b..24fe206b1 100644 --- a/util/romcc/romcc.c +++ b/util/romcc/romcc.c @@ -1428,6 +1428,10 @@ static struct triple *pre_triple(struct compile_state *state, { struct block *block; struct triple *ret; + /* If I am an OP_PIECE jump to the real instruction */ + if (base->op == OP_PIECE) { + base = MISC(base, 0); + } block = block_of_triple(state, base); ret = build_triple(state, op, type, left, right, base->filename, base->line, base->col); @@ -1447,6 +1451,17 @@ static struct triple *post_triple(struct compile_state *state, { struct block *block; struct triple *ret; + int zlhs; + /* If I am an OP_PIECE jump to the real instruction */ + if (base->op == OP_PIECE) { + base = MISC(base, 0); + } + /* If I have a left hand side skip over it */ + zlhs = TRIPLE_LHS(base->sizes); + if (zlhs && (base->op != OP_WRITE) && (base->op != OP_STORE)) { + base = LHS(base, zlhs - 1); + } + block = block_of_triple(state, base); ret = build_triple(state, op, type, left, right, base->filename, base->line, base->col); @@ -1491,7 +1506,7 @@ static void display_triple(FILE *fp, struct triple *ins) fprintf(fp, " %-10p", ins->param[i]); } for(; i < 2; i++) { - printf(" "); + fprintf(fp, " "); } fprintf(fp, " @ %s:%d.%d\n", ins->filename, ins->line, ins->col); @@ -12248,6 +12263,7 @@ struct least_conflict { struct triple *ins; struct triple_reg_set *live; size_t count; + int constraints; }; static void least_conflict(struct compile_state *state, struct reg_block *blocks, struct triple_reg_set *live, @@ -12257,19 +12273,13 @@ static void least_conflict(struct compile_state *state, struct live_range_edge *edge; struct triple_reg_set *set; size_t count; - -#if 0 -#define HI() fprintf(stderr, "%-10p(%-15s) %d\n", ins, tops(ins->op), __LINE__) -#else -#define HI() -#endif + int constraints; #warning "FIXME handle instructions with left hand sides..." /* Only instructions that introduce a new definition * can be the conflict instruction. */ if (!triple_is_def(state, ins)) { -HI(); return; } @@ -12280,22 +12290,25 @@ HI(); for(set = live; set; set = set->next) { struct live_range *lr; lr = conflict->rstate->lrd[set->member->id].lr; + /* Ignore it if there cannot be an edge between these two nodes */ + if (!arch_regcm_intersect(conflict->ref_range->classes, lr->classes)) { + continue; + } for(edge = conflict->ref_range->edges; edge; edge = edge->next) { if (edge->node == lr) { break; } } if (!edge && (lr != conflict->ref_range)) { -HI(); return; } count++; } if (count <= 1) { -HI(); return; } +#if 0 /* See if there is an uncolored member in this subset. */ for(set = live; set; set = set->next) { @@ -12306,11 +12319,79 @@ HI(); } } if (!set && (conflict->ref_range != REG_UNSET)) { -HI(); + return; + } +#endif + + /* See if any of the live registers are constrained, + * if not it won't be productive to pick this as + * a conflict instruction. + */ + constraints = 0; + for(set = live; set; set = set->next) { + struct triple_set *uset; + struct reg_info info; + unsigned classes; + unsigned cur_size, size; + /* Skip this instruction */ + if (set->member == ins) { + continue; + } + /* Find how many registers this value can potentially + * be assigned to. + */ + classes = arch_type_to_regcm(state, set->member->type); + size = regc_max_size(state, classes); + + /* Find how many registers we allow this value to + * be assigned to. + */ + info = arch_reg_lhs(state, set->member, 0); + + /* If the value does not live in a register it + * isn't constrained. + */ + if (info.reg == REG_UNNEEDED) { + continue; + } + + if ((info.reg == REG_UNSET) || (info.reg >= MAX_REGISTERS)) { + cur_size = regc_max_size(state, info.regcm); + } else { + cur_size = 1; + } + + /* If there is no difference between potential and + * actual register count there is not a constraint + */ + if (cur_size >= size) { + continue; + } + + /* If this live_range feeds into conflict->inds + * it isn't a constraint we can relieve. + */ + for(uset = set->member->use; uset; uset = uset->next) { + if (uset->member == ins) { + break; + } + } + if (uset) { + continue; + } + constraints = 1; + break; + } + /* Don't drop canidates with constraints */ + if (conflict->constraints && !constraints) { return; } +#if 0 + fprintf(stderr, "conflict ins? %p %s count: %d constraints: %d\n", + ins, tops(ins->op), count, constraints); +#endif /* Find the instruction with the largest possible subset of * conflict ranges and that dominates any other instruction * with an equal sized set of conflicting ranges. @@ -12322,6 +12403,7 @@ HI(); /* Remember the canidate instruction */ conflict->ins = ins; conflict->count = count; + conflict->constraints = constraints; /* Free the old collection of live registers */ for(set = conflict->live; set; set = next) { next = set->next; @@ -12353,7 +12435,6 @@ HI(); ; } } -HI(); return; } @@ -12362,12 +12443,6 @@ static void find_range_conflict(struct compile_state *state, struct least_conflict *conflict) { -#if 0 - static void verify_blocks(struct compile_state *stae); - verify_blocks(state); - print_blocks(state, stderr); - print_dominators(state, stderr); -#endif /* there are 3 kinds ways conflicts can occure. * 1) the life time of 2 values simply overlap. * 2) the 2 values feed into the same instruction. @@ -12387,37 +12462,25 @@ static void find_range_conflict(struct compile_state *state, * is at or after the instruction. */ memset(conflict, 0, sizeof(*conflict)); - conflict->rstate = rstate; - conflict->ref_range = ref_range; - conflict->ins = 0; - conflict->count = 0; - conflict->live = 0; + conflict->rstate = rstate; + conflict->ref_range = ref_range; + conflict->ins = 0; + conflict->live = 0; + conflict->count = 0; + conflict->constraints = 0; walk_variable_lifetimes(state, rstate->blocks, least_conflict, conflict); if (!conflict->ins) { - struct live_range_edge *edge; - struct live_range_def *lrd; - fprintf(stderr, "edges:\n"); - for(edge = ref_range->edges; edge; edge = edge->next) { - lrd = edge->node->defs; - do { - fprintf(stderr, " %-10p(%s)", lrd->def, tops(lrd->def->op)); - lrd = lrd->next; - } while(lrd != edge->node->defs); - fprintf(stderr, "|\n"); - } - fprintf(stderr, "range:\n"); - lrd = ref_range->defs; - do { - fprintf(stderr, " %-10p(%s)", lrd->def, tops(lrd->def->op)); - lrd = lrd->next; - } while(lrd != ref_range->defs); - fprintf(stderr,"\n"); internal_error(state, ref_range->defs->def, "No conflict ins?"); } if (!conflict->live) { internal_error(state, ref_range->defs->def, "No conflict live?"); } +#if 0 + fprintf(stderr, "conflict ins: %p %s count: %d constraints: %d\n", + conflict->ins, tops(conflict->ins->op), + conflict->count, conflict->constraints); +#endif return; } @@ -12452,6 +12515,13 @@ static struct triple *split_constrained_range(struct compile_state *state, * be assigned to. */ info = arch_reg_lhs(state, cset->member, 0); + + /* If the register doesn't need a register + * splitting it can't help. + */ + if (info.reg == REG_UNNEEDED) { + continue; + } #warning "FIXME do I need a call to arch_reg_rhs around here somewhere?" if ((info.reg == REG_UNSET) || (info.reg >= MAX_REGISTERS)) { cur_size = regc_max_size(state, info.regcm); @@ -12497,6 +12567,10 @@ static int split_ranges( { struct triple *new; +#if 0 + fprintf(stderr, "split_ranges %d %s %p\n", + rstate->passes, tops(range->defs->def->op), range->defs->def); +#endif if ((range->color == REG_UNNEEDED) || (rstate->passes >= rstate->max_passes)) { return 0; @@ -12506,6 +12580,9 @@ static int split_ranges( if (arch_select_free_register(state, used, range->classes) == REG_UNSET) { struct least_conflict conflict; +#if 0 + fprintf(stderr, "find_range_conflict\n"); +#endif /* Find where in the set of registers the conflict * actually occurs. */ @@ -12528,6 +12605,11 @@ static int split_ranges( * */ #warning "WISHLIST implement live range splitting..." +#if 0 + print_blocks(state, stderr); + print_dominators(state, stderr); + +#endif return 0; } } @@ -12536,7 +12618,13 @@ static int split_ranges( new->id = rstate->defs; rstate->defs++; #if 0 - fprintf(stderr, "new: %p\n", new); + fprintf(stderr, "new: %p old: %s %p\n", + new, tops(RHS(new, 0)->op), RHS(new, 0)); +#endif +#if 0 + print_blocks(state, stderr); + print_dominators(state, stderr); + #endif return 1; } @@ -12717,17 +12805,29 @@ static int select_free_color(struct compile_state *state, arch_select_free_register(state, used, range->classes); } if (range->color == REG_UNSET) { + struct live_range_def *lrd; int i; if (split_ranges(state, rstate, used, range)) { return 0; } for(edge = range->edges; edge; edge = edge->next) { - if (edge->node->color == REG_UNSET) { - continue; - } - warning(state, edge->node->defs->def, "reg %s", + warning(state, edge->node->defs->def, "edge reg %s", arch_reg_str(edge->node->color)); + lrd = edge->node->defs; + do { + warning(state, lrd->def, " %s", + tops(lrd->def->op)); + lrd = lrd->next; + } while(lrd != edge->node->defs); } + warning(state, range->defs->def, "range: "); + lrd = range->defs; + do { + warning(state, lrd->def, " %s", + tops(lrd->def->op)); + lrd = lrd->next; + } while(lrd != range->defs); + warning(state, range->defs->def, "classes: %x", range->classes); for(i = 0; i < MAX_REGISTERS; i++) { diff --git a/util/romcc/tests/simple_test33.c b/util/romcc/tests/simple_test33.c new file mode 100644 index 000000000..4caaa3a9e --- /dev/null +++ b/util/romcc/tests/simple_test33.c @@ -0,0 +1,41 @@ +static void main(void) +{ + unsigned long loops0, loops1, loops2; + unsigned long accum; + + accum = 0; + + loops0 = 10; + do { + unsigned short val; + val = __builtin_inw(0x10e0); + if (((val & 0x08) == 0) || (val == 1)) { + break; + } + } while(--loops0); + if (loops0 < 0) return; + accum += loops0; + + + loops1 = 20; + do { + unsigned short val; + val = __builtin_inw(0x10e0); + if (((val & 0x08) == 0) || (val == 1)) { + break; + } + } while(--loops1); + + loops2 = 30; + do { + unsigned short val; + val = __builtin_inw(0x10e0); + if (((val & 0x08) == 0) || (val == 1)) { + break; + } + } while(--loops2); + + accum += loops1 + loops0; +} + + -- 2.25.1