void MPII_Segment_manipulate(struct MPIR_Segment *segp, MPI_Aint first, MPI_Aint * lastp, int (*contigfn) (MPI_Aint * blocks_p, MPI_Datatype el_type, MPI_Aint rel_off, void *bufp, void *v_paramp), int (*vectorfn) (MPI_Aint * blocks_p, MPI_Aint count, MPI_Aint blklen, MPI_Aint stride, MPI_Datatype el_type, MPI_Aint rel_off, void *bufp, void *v_paramp), int (*blkidxfn) (MPI_Aint * blocks_p, MPI_Aint count, MPI_Aint blklen, MPI_Aint * offsetarray, MPI_Datatype el_type, MPI_Aint rel_off, void *bufp, void *v_paramp), int (*indexfn) (MPI_Aint * blocks_p, MPI_Aint count, MPI_Aint * blockarray, MPI_Aint * offsetarray, MPI_Datatype el_type, MPI_Aint rel_off, void *bufp, void *v_paramp), MPI_Aint(*sizefn) (MPI_Datatype el_type), void *pieceparams) { /* these four are the "local values": cur_sp, valid_sp, last, stream_off */ int cur_sp, valid_sp; MPI_Aint last, stream_off; struct MPII_Dataloop_stackelm *cur_elmp; enum { PF_NULL, PF_CONTIG, PF_VECTOR, PF_BLOCKINDEXED, PF_INDEXED } piecefn_type = PF_NULL; SEGMENT_LOAD_LOCAL_VALUES; if (first == *lastp) { /* nothing to do */ MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "dloop_segment_manipulate: warning: first == last (" MPI_AINT_FMT_DEC_SPEC ")\n", first)); return; } /* first we ensure that stream_off and first are in the same spot */ if (first != stream_off) { #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "first=" MPI_AINT_FMT_DEC_SPEC "; stream_off=" MPI_AINT_FMT_DEC_SPEC "; resetting.\n", first, stream_off)); #endif if (first < stream_off) { SEGMENT_RESET_VALUES; stream_off = 0; } if (first != stream_off) { MPI_Aint tmp_last = first; /* use manipulate function with a NULL piecefn to advance * stream offset */ MPII_Segment_manipulate(segp, stream_off, &tmp_last, NULL, /* contig fn */ NULL, /* vector fn */ NULL, /* blkidx fn */ NULL, /* index fn */ sizefn, NULL); /* --BEGIN ERROR HANDLING-- */ /* verify that we're in the right location */ MPIR_Assert(tmp_last == first); /* --END ERROR HANDLING-- */ } SEGMENT_LOAD_LOCAL_VALUES; #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "done repositioning stream_off; first=" MPI_AINT_FMT_DEC_SPEC ", stream_off=" MPI_AINT_FMT_DEC_SPEC ", last=" MPI_AINT_FMT_DEC_SPEC "\n", first, stream_off, last)); #endif } for (;;) { #ifdef MPII_DATALOOP_DEBUG_MANIPULATE #if 0 MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "looptop; cur_sp=%d, cur_elmp=%x\n", cur_sp, (unsigned) cur_elmp)); #endif #endif if (cur_elmp->loop_p->kind & MPII_DATALOOP_FINAL_MASK) { int piecefn_indicated_exit = -1; MPI_Aint myblocks, local_el_size, stream_el_size; MPI_Datatype el_type; /* structs are never finals (leaves) */ MPIR_Assert((cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) != MPII_DATALOOP_KIND_STRUCT); /* pop immediately on zero count */ if (cur_elmp->curcount == 0) SEGMENT_POP_AND_MAYBE_EXIT; /* size on this system of the int, double, etc. that is * the elementary type. */ local_el_size = cur_elmp->loop_p->el_size; el_type = cur_elmp->loop_p->el_type; stream_el_size = (sizefn) ? sizefn(el_type) : local_el_size; /* calculate number of elem. types to work on and function to use. * default is to use the contig piecefn (if there is one). */ myblocks = cur_elmp->curblock; piecefn_type = (contigfn ? PF_CONTIG : PF_NULL); /* check for opportunities to use other piecefns */ switch (cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_CONTIG: break; case MPII_DATALOOP_KIND_BLOCKINDEXED: /* only use blkidx piecefn if at start of blkidx type */ if (blkidxfn && cur_elmp->orig_block == cur_elmp->curblock && cur_elmp->orig_count == cur_elmp->curcount) { /* TODO: RELAX CONSTRAINTS */ myblocks = cur_elmp->curblock * cur_elmp->curcount; piecefn_type = PF_BLOCKINDEXED; } break; case MPII_DATALOOP_KIND_INDEXED: /* only use index piecefn if at start of the index type. * count test checks that we're on first block. * block test checks that we haven't made progress on first block. */ if (indexfn && cur_elmp->orig_count == cur_elmp->curcount && cur_elmp->curblock == STACKELM_INDEXED_BLOCKSIZE(cur_elmp, 0)) { /* TODO: RELAX CONSTRAINT ON COUNT? */ myblocks = cur_elmp->loop_p->loop_params.i_t.total_blocks; piecefn_type = PF_INDEXED; } break; case MPII_DATALOOP_KIND_VECTOR: /* only use the vector piecefn if at the start of a * contiguous block. */ if (vectorfn && cur_elmp->orig_block == cur_elmp->curblock) { myblocks = cur_elmp->curblock * cur_elmp->curcount; piecefn_type = PF_VECTOR; } break; default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); break; /* --END ERROR HANDLING-- */ } #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\thit leaf; cur_sp=%d, elmp=%x, piece_sz=" MPI_AINT_FMT_DEC_SPEC "\n", cur_sp, (unsigned) cur_elmp, myblocks * local_el_size)); #endif /* enforce the last parameter if necessary by reducing myblocks */ if (last != MPIR_SEGMENT_IGNORE_LAST && (stream_off + (myblocks * stream_el_size) > last)) { myblocks = ((last - stream_off) / stream_el_size); #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\tpartial block count=" MPI_AINT_FMT_DEC_SPEC " (" MPI_AINT_FMT_DEC_SPEC " bytes)\n", myblocks, myblocks * stream_el_size)); #endif if (myblocks == 0) { SEGMENT_SAVE_LOCAL_VALUES; return; } } /* call piecefn to perform data manipulation */ switch (piecefn_type) { case PF_NULL: piecefn_indicated_exit = 0; #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG("\tNULL piecefn for this piece\n"); #endif break; case PF_CONTIG: MPIR_Assert(myblocks <= cur_elmp->curblock); piecefn_indicated_exit = contigfn(&myblocks, el_type, cur_elmp->curoffset, /* relative to segp->ptr */ segp->ptr, /* start of buffer (from segment) */ pieceparams); break; case PF_VECTOR: piecefn_indicated_exit = vectorfn(&myblocks, cur_elmp->curcount, cur_elmp->orig_block, cur_elmp->loop_p->loop_params.v_t.stride, el_type, cur_elmp->curoffset, segp->ptr, pieceparams); break; case PF_BLOCKINDEXED: piecefn_indicated_exit = blkidxfn(&myblocks, cur_elmp->curcount, cur_elmp->orig_block, cur_elmp->loop_p->loop_params.bi_t.offset_array, el_type, cur_elmp->orig_offset, /* blkidxfn adds offset */ segp->ptr, pieceparams); break; case PF_INDEXED: piecefn_indicated_exit = indexfn(&myblocks, cur_elmp->curcount, cur_elmp->loop_p->loop_params.i_t.blocksize_array, cur_elmp->loop_p->loop_params.i_t.offset_array, el_type, cur_elmp->orig_offset, /* indexfn adds offset value */ segp->ptr, pieceparams); break; } /* update local values based on piecefn returns (myblocks and * piecefn_indicated_exit) */ MPIR_Assert(piecefn_indicated_exit >= 0); MPIR_Assert(myblocks >= 0); stream_off += myblocks * stream_el_size; /* myblocks of 0 or less than cur_elmp->curblock indicates * that we should stop processing and return. */ if (myblocks == 0) { SEGMENT_SAVE_LOCAL_VALUES; return; } else if (myblocks < (MPI_Aint) (cur_elmp->curblock)) { cur_elmp->curoffset += myblocks * local_el_size; cur_elmp->curblock -= myblocks; SEGMENT_SAVE_LOCAL_VALUES; return; } else { /* myblocks >= cur_elmp->curblock */ MPI_Aint count_index = 0; /* this assumes we're either *just* processing the last parts * of the current block, or we're processing as many blocks as * we like starting at the beginning of one. */ switch (cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_INDEXED: while (myblocks > 0 && myblocks >= (MPI_Aint) (cur_elmp->curblock)) { myblocks -= (MPI_Aint) (cur_elmp->curblock); cur_elmp->curcount--; MPIR_Assert(cur_elmp->curcount >= 0); count_index = cur_elmp->orig_count - cur_elmp->curcount; cur_elmp->curblock = STACKELM_INDEXED_BLOCKSIZE(cur_elmp, count_index); } if (cur_elmp->curcount == 0) { /* don't bother to fill in values; we're popping anyway */ MPIR_Assert(myblocks == 0); SEGMENT_POP_AND_MAYBE_EXIT; } else { cur_elmp->orig_block = cur_elmp->curblock; cur_elmp->curoffset = cur_elmp->orig_offset + STACKELM_INDEXED_OFFSET(cur_elmp, count_index); cur_elmp->curblock -= myblocks; cur_elmp->curoffset += myblocks * local_el_size; } break; case MPII_DATALOOP_KIND_VECTOR: /* this math relies on assertions at top of code block */ cur_elmp->curcount -= myblocks / (MPI_Aint) (cur_elmp->curblock); if (cur_elmp->curcount == 0) { MPIR_Assert(myblocks % ((MPI_Aint) (cur_elmp->curblock)) == 0); SEGMENT_POP_AND_MAYBE_EXIT; } else { /* this math relies on assertions at top of code * block */ cur_elmp->curblock = cur_elmp->orig_block - (myblocks % (MPI_Aint) (cur_elmp->curblock)); /* new offset = original offset + * stride * whole blocks + * leftover bytes */ cur_elmp->curoffset = cur_elmp->orig_offset + (((MPI_Aint) (cur_elmp->orig_count - cur_elmp->curcount)) * cur_elmp->loop_p->loop_params.v_t.stride) + (((MPI_Aint) (cur_elmp->orig_block - cur_elmp->curblock)) * local_el_size); } break; case MPII_DATALOOP_KIND_CONTIG: /* contigs that reach this point have always been * completely processed */ MPIR_Assert(myblocks == (MPI_Aint) (cur_elmp->curblock) && cur_elmp->curcount == 1); SEGMENT_POP_AND_MAYBE_EXIT; break; case MPII_DATALOOP_KIND_BLOCKINDEXED: while (myblocks > 0 && myblocks >= (MPI_Aint) (cur_elmp->curblock)) { myblocks -= (MPI_Aint) (cur_elmp->curblock); cur_elmp->curcount--; MPIR_Assert(cur_elmp->curcount >= 0); count_index = cur_elmp->orig_count - cur_elmp->curcount; cur_elmp->curblock = cur_elmp->orig_block; } if (cur_elmp->curcount == 0) { /* popping */ MPIR_Assert(myblocks == 0); SEGMENT_POP_AND_MAYBE_EXIT; } else { /* cur_elmp->orig_block = cur_elmp->curblock; */ cur_elmp->curoffset = cur_elmp->orig_offset + STACKELM_BLOCKINDEXED_OFFSET(cur_elmp, count_index); cur_elmp->curblock -= myblocks; cur_elmp->curoffset += myblocks * local_el_size; } break; } } if (piecefn_indicated_exit) { /* piece function indicated that we should quit processing */ SEGMENT_SAVE_LOCAL_VALUES; return; } } /* end of if leaf */ else if (cur_elmp->curblock == 0) { #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\thit end of block; elmp=%x [%d]\n", (unsigned) cur_elmp, cur_sp)); #endif cur_elmp->curcount--; /* new block. for indexed and struct reset orig_block. * reset curblock for all types */ switch (cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_CONTIG: case MPII_DATALOOP_KIND_VECTOR: case MPII_DATALOOP_KIND_BLOCKINDEXED: break; case MPII_DATALOOP_KIND_INDEXED: cur_elmp->orig_block = STACKELM_INDEXED_BLOCKSIZE(cur_elmp, cur_elmp->curcount ? cur_elmp->orig_count - cur_elmp->curcount : 0); break; case MPII_DATALOOP_KIND_STRUCT: cur_elmp->orig_block = STACKELM_STRUCT_BLOCKSIZE(cur_elmp, cur_elmp->curcount ? cur_elmp->orig_count - cur_elmp->curcount : 0); break; default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); break; /* --END ERROR HANDLING-- */ } cur_elmp->curblock = cur_elmp->orig_block; if (cur_elmp->curcount == 0) { #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\talso hit end of count; elmp=%x [%d]\n", (unsigned) cur_elmp, cur_sp)); #endif SEGMENT_POP_AND_MAYBE_EXIT; } } else { /* push the stackelm */ MPII_Dataloop_stackelm *next_elmp; MPI_Aint count_index, block_index; count_index = cur_elmp->orig_count - cur_elmp->curcount; block_index = cur_elmp->orig_block - cur_elmp->curblock; /* reload the next stackelm if necessary */ next_elmp = &(segp->stackelm[cur_sp + 1]); if (cur_elmp->may_require_reloading) { MPIR_Dataloop *load_dlp = NULL; switch (cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_CONTIG: case MPII_DATALOOP_KIND_VECTOR: case MPII_DATALOOP_KIND_BLOCKINDEXED: case MPII_DATALOOP_KIND_INDEXED: load_dlp = cur_elmp->loop_p->loop_params.cm_t.dataloop; break; case MPII_DATALOOP_KIND_STRUCT: load_dlp = STACKELM_STRUCT_DATALOOP(cur_elmp, count_index); break; default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); break; /* --END ERROR HANDLING-- */ } #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\tloading dlp=%x, elmp=%x [%d]\n", (unsigned) load_dlp, (unsigned) next_elmp, cur_sp + 1)); #endif MPII_Dataloop_stackelm_load(next_elmp, load_dlp, 1); } #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\tpushing type, elmp=%x [%d], count=%d, block=%d\n", (unsigned) cur_elmp, cur_sp, count_index, block_index)); #endif /* set orig_offset and all cur values for new stackelm. * this is done in two steps: first set orig_offset based on * current stackelm, then set cur values based on new stackelm. */ switch (cur_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_CONTIG: next_elmp->orig_offset = cur_elmp->curoffset + (MPI_Aint) block_index *cur_elmp->loop_p->el_extent; break; case MPII_DATALOOP_KIND_VECTOR: /* note: stride is in bytes */ next_elmp->orig_offset = cur_elmp->orig_offset + (MPI_Aint) count_index *cur_elmp->loop_p->loop_params.v_t.stride + (MPI_Aint) block_index *cur_elmp->loop_p->el_extent; break; case MPII_DATALOOP_KIND_BLOCKINDEXED: next_elmp->orig_offset = cur_elmp->orig_offset + (MPI_Aint) block_index *cur_elmp->loop_p->el_extent + STACKELM_BLOCKINDEXED_OFFSET(cur_elmp, count_index); break; case MPII_DATALOOP_KIND_INDEXED: next_elmp->orig_offset = cur_elmp->orig_offset + (MPI_Aint) block_index *cur_elmp->loop_p->el_extent + STACKELM_INDEXED_OFFSET(cur_elmp, count_index); break; case MPII_DATALOOP_KIND_STRUCT: next_elmp->orig_offset = cur_elmp->orig_offset + (MPI_Aint) block_index *STACKELM_STRUCT_EL_EXTENT(cur_elmp, count_index) + STACKELM_STRUCT_OFFSET(cur_elmp, count_index); break; default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); break; /* --END ERROR HANDLING-- */ } #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\tstep 1: next orig_offset = " MPI_AINT_FMT_DEC_SPEC " (0x" MPI_AINT_FMT_HEX_SPEC ")\n", next_elmp->orig_offset, next_elmp->orig_offset)); #endif switch (next_elmp->loop_p->kind & MPII_DATALOOP_KIND_MASK) { case MPII_DATALOOP_KIND_CONTIG: case MPII_DATALOOP_KIND_VECTOR: next_elmp->curcount = next_elmp->orig_count; next_elmp->curblock = next_elmp->orig_block; next_elmp->curoffset = next_elmp->orig_offset; break; case MPII_DATALOOP_KIND_BLOCKINDEXED: next_elmp->curcount = next_elmp->orig_count; next_elmp->curblock = next_elmp->orig_block; next_elmp->curoffset = next_elmp->orig_offset + STACKELM_BLOCKINDEXED_OFFSET(next_elmp, 0); break; case MPII_DATALOOP_KIND_INDEXED: next_elmp->curcount = next_elmp->orig_count; next_elmp->curblock = STACKELM_INDEXED_BLOCKSIZE(next_elmp, 0); next_elmp->curoffset = next_elmp->orig_offset + STACKELM_INDEXED_OFFSET(next_elmp, 0); break; case MPII_DATALOOP_KIND_STRUCT: next_elmp->curcount = next_elmp->orig_count; next_elmp->curblock = STACKELM_STRUCT_BLOCKSIZE(next_elmp, 0); next_elmp->curoffset = next_elmp->orig_offset + STACKELM_STRUCT_OFFSET(next_elmp, 0); break; default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); break; /* --END ERROR HANDLING-- */ } #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG_FMT(MPIR_DBG_DATATYPE, VERBOSE, (MPL_DBG_FDEST, "\tstep 2: next curoffset = " MPI_AINT_FMT_DEC_SPEC " (0x" MPI_AINT_FMT_HEX_SPEC ")\n", next_elmp->curoffset, next_elmp->curoffset)); #endif cur_elmp->curblock--; SEGMENT_PUSH; } /* end of else push the stackelm */ } /* end of for (;;) */ #ifdef MPII_DATALOOP_DEBUG_MANIPULATE MPL_DBG_MSG("hit end of datatype\n"); #endif SEGMENT_SAVE_LOCAL_VALUES; return; }
/*@ Dataloop_stream_size - return the size of the data described by the dataloop Input Parameters: + dl_p - pointer to dataloop for which we will return the size - sizefn - function for determining size of types in the corresponding stream (passing NULL will instead result in el_size values being used) @*/ DLOOP_Offset PREPEND_PREFIX(Dataloop_stream_size)(struct DLOOP_Dataloop *dl_p, DLOOP_Offset (*sizefn)(DLOOP_Type el_type)) { DLOOP_Offset tmp_sz, tmp_ct = 1; for (;;) { if ((dl_p->kind & DLOOP_KIND_MASK) == DLOOP_KIND_STRUCT) { int i; tmp_sz = 0; for (i = 0; i < dl_p->loop_params.s_t.count; i++) { tmp_sz += (DLOOP_Offset)(dl_p->loop_params.s_t.blocksize_array[i]) * PREPEND_PREFIX(Dataloop_stream_size)(dl_p->loop_params.s_t.dataloop_array[i], sizefn); } return tmp_sz * tmp_ct; } switch (dl_p->kind & DLOOP_KIND_MASK) { case DLOOP_KIND_CONTIG: tmp_ct *= (DLOOP_Offset)(dl_p->loop_params.c_t.count); #ifdef DLOOP_DEBUG_SIZE DLOOP_dbg_printf("stream_size: contig: ct = %d; new tot_ct = " DLOOP_OFFSET_FMT_DEC_SPEC "\n", (int) dl_p->loop_params.c_t.count, (DLOOP_Offset) tmp_ct); #endif break; case DLOOP_KIND_VECTOR: tmp_ct *= (DLOOP_Offset)(dl_p->loop_params.v_t.count) * (DLOOP_Offset)(dl_p->loop_params.v_t.blocksize); #ifdef DLOOP_DEBUG_SIZE DLOOP_dbg_printf("stream_size: vector: ct = %d; blk = %d; new tot_ct = " DLOOP_OFFSET_FMT_DEC_SPEC "\n", (int) dl_p->loop_params.v_t.count, (int) dl_p->loop_params.v_t.blocksize, (DLOOP_Offset) tmp_ct); #endif break; case DLOOP_KIND_BLOCKINDEXED: tmp_ct *= (DLOOP_Offset)(dl_p->loop_params.bi_t.count) * (DLOOP_Offset)(dl_p->loop_params.bi_t.blocksize); #ifdef DLOOP_DEBUG_SIZE DLOOP_dbg_printf("stream_size: blkindexed: blks = %d; new tot_ct = " DLOOP_OFFSET_FMT_DEC_SPEC "\n", (int) dl_p->loop_params.bi_t.count * (int) dl_p->loop_params.bi_t.blocksize, (DLOOP_Offset) tmp_ct); #endif break; case DLOOP_KIND_INDEXED: tmp_ct *= (DLOOP_Offset)(dl_p->loop_params.i_t.total_blocks); #ifdef DLOOP_DEBUG_SIZE DLOOP_dbg_printf("stream_size: contig: blks = %d; new tot_ct = " DLOOP_OFFSET_FMT_DEC_SPEC "\n", (int) dl_p->loop_params.i_t.total_blocks, (DLOOP_Offset) tmp_ct); #endif break; default: /* --BEGIN ERROR HANDLING-- */ DLOOP_Assert(0); break; /* --END ERROR HANDLING-- */ } if (dl_p->kind & DLOOP_FINAL_MASK) break; else { DLOOP_Assert(dl_p->loop_params.cm_t.dataloop != NULL); dl_p = dl_p->loop_params.cm_t.dataloop; } } /* call fn for size using bottom type, or use size if fnptr is NULL */ tmp_sz = ((sizefn) ? sizefn(dl_p->el_type) : dl_p->el_size); return tmp_sz * tmp_ct; }
Node * fold(Node *n, int foldvar) { Node **args, *r; Type *t; vlong a, b; Ucon *uc; size_t i; if (!n) return NULL; if (n->type != Nexpr) return n; r = NULL; args = n->expr.args; if (n->expr.idx) n->expr.idx = fold(n->expr.idx, foldvar); for (i = 0; i < n->expr.nargs; i++) args[i] = fold(args[i], foldvar); switch (exprop(n)) { case Outag: if (exprop(args[0]) != Oucon) break; uc = finducon(tybase(exprtype(args[0])), args[0]->expr.args[0]); r = val(n->loc, uc->id, exprtype(n)); break; case Oudata: if (exprop(args[0]) != Oucon || args[0]->expr.nargs != 2) break; r = args[0]->expr.args[1]; break; case Ovar: if (foldvar && issmallconst(decls[n->expr.did])) r = fold(decls[n->expr.did]->decl.init, foldvar); break; case Oadd: /* x + 0 = 0 */ if (isintval(args[0], 0)) r = args[1]; if (isintval(args[1], 0)) r = args[0]; if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a + b, exprtype(n)); break; case Osub: /* x - 0 = 0 */ if (isintval(args[1], 0)) r = args[0]; if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, (uint64_t)a - b, exprtype(n)); break; case Omul: /* 1 * x = x */ if (isintval(args[0], 1)) r = args[1]; if (isintval(args[1], 1)) r = args[0]; /* 0 * x = 0 */ if (isintval(args[0], 0)) r = args[0]; if (isintval(args[1], 0)) r = args[1]; if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a * b, exprtype(n)); break; case Odiv: /* x/0 = error */ if (isintval(args[1], 0)) fatal(args[1], "division by zero"); /* x/1 = x */ if (isintval(args[1], 1)) r = args[0]; /* 0/x = 0 */ if (isintval(args[1], 0)) r = args[1]; if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a / b, exprtype(n)); break; case Omod: /* x%0 = error */ if (isintval(args[1], 0)) fatal(args[1], "division by zero"); /* x%1 = 0 */ if (isintval(args[1], 1)) r = val(n->loc, 0, exprtype(n)); if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a % b, exprtype(n)); break; case Oneg: if (getintlit(args[0], &a)) r = val(n->loc, -a, exprtype(n)); break; case Obsl: if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, (uint64_t)a << b, exprtype(n)); break; case Obsr: if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a >> b, exprtype(n)); break; case Obor: if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a | b, exprtype(n)); break; case Oband: if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a & b, exprtype(n)); break; case Obxor: if (getintlit(args[0], &a) && getintlit(args[1], &b)) r = val(n->loc, a ^ b, exprtype(n)); break; case Omemb: t = tybase(exprtype(args[0])); /* we only fold lengths right now */ if (t->type == Tyarray && !strcmp(namestr(args[1]), "len")) { r = t->asize; r->expr.type = exprtype(n); } break; case Oarr: if (n->expr.nargs > 0) qsort(n->expr.args, n->expr.nargs, sizeof(Node*), idxcmp); break; case Ocast: r = foldcast(n); break; case Osize: if (sizefn) r = val(n->loc, sizefn(n->expr.args[0]), exprtype(n)); break; default: break; } if (r && n->expr.idx) r->expr.idx = n->expr.idx; if (r) return r; else return n; }