Exemple #1
0
    void processBlock(AstNode* nodep) {
	if (!nodep) return;	// Empty lists are ignorable
	// Pass the first node in a list of block items, we'll process them
	// Check there's >= 2 sub statements, else nothing to analyze
	// Save recursion state
	AstNode* firstp = nodep;   // We may reorder, and nodep is no longer first.
	void* oldBlockUser3 = nodep->user3p();   // May be overloaded in below loop, save it
	nodep->user3p(NULL);
	if (!nodep->firstAbovep()) nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once");
	// Process it
	if (!nodep->nextp()) {
	    // Just one, so can't reorder.  Just look for more blocks/statements.
            iterate(nodep);
	} else {
	    UINFO(9,"  processBlock "<<nodep<<endl);
	    // Process block and followers
	    scanBlock(nodep);
	    if (m_noReorderWhy != "") {  // Jump or something nasty
		UINFO(9,"  NoReorderBlock because "<<m_noReorderWhy<<endl);
	    } else {
		// Reorder statements in this block
		cleanupBlockGraph(nodep);
		reorderBlock(nodep);
		// Delete old vertexes and edges only applying to this block
		while (firstp->backp()->nextp()==firstp) firstp = firstp->backp();  // Walk back to first in list
		for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) {
		    SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p();
		    vvertexp->unlinkDelete(&m_graph);
		}
	    }
	}
	// Again, nodep may no longer be first.
	firstp->user3p(oldBlockUser3);
    }
Exemple #2
0
    virtual void visit(AstDefImplicitDType* nodep, AstNUser*) {
	cleanFileline(nodep);
	UINFO(8,"   DEFIMPLICIT "<<nodep<<endl);
	// Must remember what names we've already created, and combine duplicates
	// so that for "var enum {...} a,b" a & b will share a common typedef
	// Unique name space under each containerp() so that an addition of a new type won't change every verilated module.
	AstTypedef* defp = NULL;
	ImplTypedefMap::iterator it = m_implTypedef.find(make_pair(nodep->containerp(), nodep->name()));
	if (it != m_implTypedef.end()) {
	    defp = it->second;
	} else {
	    // Definition must be inserted right after the variable (etc) that needed it
	    // AstVar, AstTypedef, AstNodeFTask are common containers
	    AstNode* backp = nodep->backp();
	    for (; backp; backp=backp->backp()) {
		if (backp->castVar()) break;
		else if (backp->castTypedef()) break;
		else if (backp->castNodeFTask()) break;
	    }
	    if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type");
	    AstNodeDType* dtypep = nodep->dtypep(); dtypep->unlinkFrBack();
	    if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing
		// For typedefs just remove the AstRefDType level of abstraction
		nodep->replaceWith(dtypep);
		nodep->deleteTree(); nodep=NULL;
		return;
	    } else {
		defp = new AstTypedef(nodep->fileline(), nodep->name(), dtypep);
		m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp));
		backp->addNextHere(defp);
	    }
	}
	nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name()));
	nodep->deleteTree(); nodep=NULL;
    }
    void detectDuplicates() {
	UINFO(9,"Finding duplicates\n");
	// Note uses user4
	V3Hashed  hashed;	// Duplicate code detection
	// Hash all of the original signals we toggle cover
	for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
	    AstCoverToggle* nodep = *it;
	    hashed.hashAndInsert(nodep->origp());
	}
	// Find if there are any duplicates
	for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
	    AstCoverToggle* nodep = *it;
	    if (nodep->backp()) {   // nodep->backp() is null if we already detected it's a duplicate and unlinked it
		// Want to choose a base node, and keep finding duplicates that are identical
		// This prevents making chains where a->b, then c->d, then b->c, as we'll find a->b, a->c, a->d directly.
		while (1) {
		    V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp());
		    if (dupit == hashed.end()) break;
		    //
		    AstNode* duporigp = hashed.iteratorNodep(dupit);
		    // Note hashed will point to the original variable (what's duplicated), not the covertoggle,
		    // but we need to get back to the covertoggle which is immediately above, so:
		    AstCoverToggle* removep = duporigp->backp()->castCoverToggle();
		    if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
		    UINFO(8,"  Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
		    UINFO(8,"   dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
		    // The CoverDecl the duplicate pointed to now needs to point to the original's data
		    // IE the duplicate will get the coverage number from the non-duplicate
		    AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
		    removep->incp()->declp()->dataDeclp (datadeclp);
		    UINFO(8,"   new "<<removep->incp()->declp()<<endl);
		    // Mark the found node as a duplicate of the first node
		    // (Not vice-versa as we have the iterator for the found node)
		    removep->unlinkFrBack();  pushDeletep(removep); removep=NULL;
		    // Remove node from comparison so don't hit it again
		    hashed.erase(dupit);
		    ++m_statToggleJoins;
		}
	    }
	}
    }
    virtual void visit(AstSenItem* nodep, AstNUser*) {
	// Remove bit selects, and bark if it's not a simple variable
	nodep->iterateChildren(*this);
	if (nodep->isClocked()) {
	    // If it's not a simple variable wrap in a temporary
	    // This is a bit unfortunate as we haven't done width resolution
	    // and any width errors will look a bit odd, but it works.
	    AstNode* sensp = nodep->sensp();
	    if (sensp
		&& !sensp->castNodeVarRef()
		&& !sensp->castConst()) {
		// Make a new temp wire
		string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum);
		AstVar* newvarp = new AstVar (sensp->fileline(), AstVarType::MODULETEMP, newvarname,
					      VFlagLogicPacked(), 1);
		// We can't just add under the module, because we may be inside a generate, begin, etc.
		// We know a SenItem should be under a SenTree/Always etc, we we'll just hunt upwards
		AstNode* addwherep = nodep;  // Add to this element's next
		while (addwherep->castSenItem()
		       || addwherep->castSenTree()) {
		    addwherep = addwherep->backp();
		}
		if (!addwherep->castAlways()) {  // Assertion perhaps?
		    sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block");
		    addwherep = m_modp;
		}
		addwherep->addNext(newvarp);

		sensp->replaceWith(new AstVarRef (sensp->fileline(), newvarp, false));
		AstAssignW* assignp = new AstAssignW
		    (sensp->fileline(),
		     new AstVarRef(sensp->fileline(), newvarp, true),
		     sensp);
		addwherep->addNext(assignp);
	    }
	} else {  // Old V1995 sensitivity list; we'll probably mostly ignore
	    bool did=1;
	    while (did) {
		did=0;
		if (AstNodeSel* selp = nodep->sensp()->castNodeSel()) {
		    AstNode* fromp = selp->fromp()->unlinkFrBack();
		    selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
		    did=1;
		}
		// NodeSel doesn't include AstSel....
		if (AstSel* selp = nodep->sensp()->castSel()) {
		    AstNode* fromp = selp->fromp()->unlinkFrBack();
		    selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
		    did=1;
		}
		if (AstNodePreSel* selp = nodep->sensp()->castNodePreSel()) {
		    AstNode* fromp = selp->lhsp()->unlinkFrBack();
		    selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
		    did=1;
		}
	    }
	}
	if (!nodep->sensp()->castNodeVarRef()
	    && !nodep->sensp()->castEnumItemRef()) {  // V3Const will cleanup
	    if (debug()) nodep->dumpTree(cout,"-tree: ");
	    nodep->v3error("Unsupported: Complex statement in sensitivity list");
	}
    }
    void replaceBoundLvalue(AstNode* nodep, AstNode* condp) {
	// Spec says a out-of-range LHS SEL results in a NOP.
	// This is a PITA.  We could:
	//  1. IF(...) around an ASSIGN,
	//     but that would break a "foo[TOO_BIG]=$fopen(...)".
	//  2. Hack to extend the size of the output structure
	//     by one bit, and write to that temporary, but never read it.
	//     That makes there be two widths() and is likely a bug farm.
	//  3. Make a special SEL to choose between the real lvalue
	//     and a temporary NOP register.
	//  4. Assign to a temp, then IF that assignment.
	//     This is suspected to be nicest to later optimizations.
	// 4 seems best but breaks later optimizations.  3 was tried,
	// but makes a mess in the emitter as lvalue switching is needed.  So 4.
	// SEL(...) -> temp
	//             if (COND(LTE(bit<=maxlsb))) ASSIGN(SEL(...)),temp)
	if (m_assignwp) {
	    // Wire assigns must become always statements to deal with insertion
	    // of multiple statements.  Perhaps someday make all wassigns into always's?
	    UINFO(5,"     IM_WireRep  "<<m_assignwp<<endl);
	    m_assignwp->convertToAlways(); pushDeletep(m_assignwp); m_assignwp=NULL;
	}
	bool needDly = (m_assigndlyp != NULL);
	if (m_assigndlyp) {
	    // Delayed assignments become normal assignments,
	    // then the temp created becomes the delayed assignment
	    AstNode* newp = new AstAssign(m_assigndlyp->fileline(),
					  m_assigndlyp->lhsp()->unlinkFrBackWithNext(),
					  m_assigndlyp->rhsp()->unlinkFrBackWithNext());
	    m_assigndlyp->replaceWith(newp); pushDeletep(m_assigndlyp); m_assigndlyp=NULL;
	}
	AstNode* prep = nodep;

	// Scan back to put the condlvalue above all selects (IE top of the lvalue)
	while (prep->backp()->castNodeSel()
	       || prep->backp()->castSel()) {
	    prep=prep->backp();
	}
	FileLine* fl = nodep->fileline();
	VL_DANGLING(nodep);  // Zap it so we don't use it by mistake - use prep

	// Already exists; rather than IF(a,... IF(b... optimize to IF(a&&b,
	// Saves us teaching V3Const how to optimize, and it won't be needed again.
	if (AstIf* ifp = prep->user2p()->castIf()) {
	    if (needDly) prep->v3fatalSrc("Should have already converted to non-delay");
	    AstNRelinker replaceHandle;
	    AstNode* earliercondp = ifp->condp()->unlinkFrBack(&replaceHandle);
	    AstNode* newp = new AstLogAnd (condp->fileline(),
					   condp,
					   earliercondp);
	    UINFO(4, "Edit BOUNDLVALUE "<<newp<<endl);
	    replaceHandle.relink(newp);
	}
	else {
	    string name = ((string)"__Vlvbound"+cvtToStr(m_modp->varNumGetInc()));
	    AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name,
				      prep->dtypep());
	    m_modp->addStmtp(varp);

	    AstNode* abovep = prep->backp();  // Grab above point before lose it w/ next replace
	    prep->replaceWith(new AstVarRef(fl, varp, true));
	    AstNode* newp = new AstIf(fl, condp,
				      (needDly
				       ? static_cast<AstNode*>
				       (new AstAssignDly(fl, prep,
							 new AstVarRef(fl, varp, false)))
				       : static_cast<AstNode*>
				       (new AstAssign   (fl, prep,
							 new AstVarRef(fl, varp, false)))),
				      NULL);
	    if (debug()>=9) newp->dumpTree(cout,"     _new: ");
	    abovep->addNextStmt(newp,abovep);
	    prep->user2p(newp);  // Save so we may LogAnd it next time
	}
    }
Exemple #6
0
    void replaceSelPlusMinus(AstNodePreSel* nodep) {
	// Select of a range specified with +: or -:, i.e. "array[2+:3], [2-:3]"
	// This select style has a lsb and width
	UINFO(6,"SELPLUS/MINUS "<<nodep<<endl);
	// Below 2 lines may change nodep->widthp()
	if (debug()>=9) nodep->dumpTree(cout,"--SELPM0: ");
	V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node
	checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit extract isn't a constant");
	if (debug()>=9) nodep->dumpTree(cout,"--SELPM3: ");
	// Now replace it with an AstSel
	AstNode* fromp = nodep->lhsp()->unlinkFrBack();
	AstNode* rhsp  = nodep->rhsp()->unlinkFrBack();
	AstNode* widthp = nodep->thsp()->unlinkFrBack();
	int width = widthp->castConst()->toSInt();
	if (width > (1<<28)) nodep->v3error("Width of :+ or :- is huge; vector of over 1billion bits: "<<widthp->prettyName());
	if (width<0) nodep->v3error("Width of :+ or :- is < 0: "<<widthp->prettyName());
	FromData fromdata = fromDataForArray(nodep, fromp, width!=1);
	AstNodeDType* ddtypep = fromdata.m_dtypep;
	VNumRange fromRange = fromdata.m_fromRange;
	if (ddtypep->castBasicDType()
	    || ddtypep->castPackArrayDType()
	    || (ddtypep->castNodeClassDType()
		&& ddtypep->castNodeClassDType()->packedUnsup())) {
	    int elwidth = 1;
	    AstNode* newwidthp = widthp;
	    if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
		elwidth = adtypep->width() / fromRange.elements();
		newwidthp = new AstConst (nodep->fileline(),AstConst::Unsized32(), width * elwidth);
	    }
	    AstNode* newlsbp = NULL;
	    if (nodep->castSelPlus()) {
		if (fromRange.littleEndian()) {
		    // SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width)
		    newlsbp = newSubNeg((fromRange.hi()-width+1), rhsp);
		} else {
		    // SELPLUS(from,lsb,width) -> SEL(from, lsb-vector_lsb, width)
		    newlsbp = newSubNeg(rhsp, fromRange.lo());
		}
	    } else if (nodep->castSelMinus()) {
		if (fromRange.littleEndian()) {
		    // SELMINUS(from,msb,width) -> SEL(from, msb-[bit])
		    newlsbp = newSubNeg(fromRange.hi(), rhsp);
		} else {
		    // SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#)
		    newlsbp = newSubNeg(rhsp, fromRange.lo()+(width-1));
		}
	    } else {
		nodep->v3fatalSrc("Bad Case");
	    }
	    if (elwidth != 1) newlsbp = new AstMul (nodep->fileline(), newlsbp,
						    new AstConst (nodep->fileline(), elwidth));
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp, newlsbp, newwidthp);
	    newp->declRange(fromRange);
	    newp->declElWidth(elwidth);
	    UINFO(6,"   new "<<newp<<endl);
	    if (debug()>=9) newp->dumpTree(cout,"--SELNEW: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else {  // NULL=bad extract, or unknown node type
	    nodep->v3error("Illegal +: or -: select; type already selected, or bad dimension: type is "
			   <<fromdata.m_errp->prettyTypeName());
	    // How to recover?  We'll strip a dimension.
	    nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	// delete whataver we didn't use in reconstruction
	if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); }
	if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); }
	if (!widthp->backp()) { pushDeletep(widthp); VL_DANGLING(widthp); }
    }
Exemple #7
0
    virtual void visit(AstSelExtract* nodep, AstNUser*) {
	// Select of a range specified part of an array, i.e. "array[2:3]"
	// SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb)
	// This select style has a (msb or lsb) and width
	UINFO(6,"SELEXTRACT "<<nodep<<endl);
	//if (debug()>=9) nodep->dumpTree(cout,"--SELEX0: ");
	// Below 2 lines may change nodep->widthp()
	V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node
	V3Const::constifyParamsEdit(nodep->msbp()); // May relink pointed to node
	//if (debug()>=9) nodep->dumpTree(cout,"--SELEX3: ");
	checkConstantOrReplace(nodep->lsbp(), "First value of [a:b] isn't a constant, maybe you want +: or -:");
	checkConstantOrReplace(nodep->msbp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:");
	AstNode* fromp = nodep->lhsp()->unlinkFrBack();
	AstNode* msbp = nodep->rhsp()->unlinkFrBack();
	AstNode* lsbp = nodep->thsp()->unlinkFrBack();
	vlsint32_t msb = msbp->castConst()->toSInt();
	vlsint32_t lsb = lsbp->castConst()->toSInt();
	FromData fromdata = fromDataForArray(nodep, fromp, false);
	AstNodeDType* ddtypep = fromdata.m_dtypep;
	VNumRange fromRange = fromdata.m_fromRange;
	if (ddtypep->castUnpackArrayDType()) {
	    // Slice extraction
	    if (fromRange.elements() == (msb-lsb+1)
		&& fromRange.lo() == lsb) { // Extracting whole of original array
		nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
	    } else {
		// TODO when unpacked arrays fully supported probably need new data type here
		AstArraySel* newp = new AstArraySel (nodep->fileline(), fromp, lsbp);
		newp->start(lsb);
		newp->length((msb - lsb) + 1);
		nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	    }
	}
	else if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
	    // SELEXTRACT(array, msb, lsb) -> SEL(array, lsb*width-of-subindex, width-of-subindex*(msb-lsb))
	    if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0)
		adtypep->v3fatalSrc("Array extraction with width miscomputed "
				    <<adtypep->width()<<"/"<<fromRange.elements());
	    int elwidth = adtypep->width() / fromRange.elements();
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       new AstConst(nodep->fileline(),AstConst::Unsized32(),lsb*elwidth),
				       new AstConst(nodep->fileline(),AstConst::Unsized32(),(msb-lsb+1)*elwidth));
	    newp->declRange(fromRange);
	    newp->declElWidth(elwidth);
	    newp->dtypeFrom(sliceDType(adtypep, msb, lsb));
	    //if (debug()>=9) newp->dumpTree(cout,"--EXTBTn: ");
	    if (newp->widthMin()!=(int)newp->widthConst()) nodep->v3fatalSrc("Width mismatch");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else if (ddtypep->castBasicDType()) {
	    if (fromRange.littleEndian()) {
		// Below code assumes big bit endian; just works out if we swap
		int x = msb; msb = lsb; lsb = x;
	    }
	    if (lsb > msb) {
		nodep->v3error("["<<msb<<":"<<lsb<<"] Range extract has backward bit ordering, perhaps you wanted ["<<lsb<<":"<<msb<<"]");
		int x = msb; msb = lsb; lsb = x;
	    }
	    AstNode* widthp = new AstConst (msbp->fileline(), AstConst::Unsized32(), // Unsized so width from user
					    msb +1-lsb);
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       newSubLsbOf(lsbp, fromRange),
				       widthp);
	    newp->declRange(fromRange);
	    UINFO(6,"   new "<<newp<<endl);
	    //if (debug()>=9) newp->dumpTree(cout,"--SELEXnew: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else if (ddtypep->castNodeClassDType()) {
	    // Classes aren't little endian
	    if (lsb > msb) {
		nodep->v3error("["<<msb<<":"<<lsb<<"] Range extract has backward bit ordering, perhaps you wanted ["<<lsb<<":"<<msb<<"]");
		int x = msb; msb = lsb; lsb = x;
	    }
	    AstNode* widthp = new AstConst (msbp->fileline(), AstConst::Unsized32(), // Unsized so width from user
					    msb +1-lsb);
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       newSubLsbOf(lsbp, fromRange),
				       widthp);
	    newp->declRange(fromRange);
	    UINFO(6,"   new "<<newp<<endl);
	    //if (debug()>=9) newp->dumpTree(cout,"--SELEXnew: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else {  // NULL=bad extract, or unknown node type
	    nodep->v3error("Illegal range select; type already selected, or bad dimension: type is "
			   <<fromdata.m_errp->prettyName());
	    UINFO(1,"    Related ddtype: "<<ddtypep<<endl);
	    // How to recover?  We'll strip a dimension.
	    nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	// delete whataver we didn't use in reconstruction
	if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); }
	if (!msbp->backp()) { pushDeletep(msbp); VL_DANGLING(msbp); }
	if (!lsbp->backp()) { pushDeletep(lsbp); VL_DANGLING(lsbp); }
    }
Exemple #8
0
    virtual void visit(AstSelBit* nodep, AstNUser*) {
	// Select of a non-width specified part of an array, i.e. "array[2]"
	// This select style has a lsb and msb (no user specified width)
	UINFO(6,"SELBIT "<<nodep<<endl);
	if (debug()>=9) nodep->backp()->dumpTree(cout,"--SELBT0: ");
	// lhsp/rhsp do not need to be constant
	AstNode* fromp = nodep->lhsp()->unlinkFrBack();
	AstNode* rhsp = nodep->rhsp()->unlinkFrBack();  // bit we're extracting
	if (debug()>=9) nodep->dumpTree(cout,"--SELBT2: ");
	FromData fromdata = fromDataForArray(nodep, fromp, false);
	AstNodeDType* ddtypep = fromdata.m_dtypep;
	VNumRange fromRange = fromdata.m_fromRange;
	UINFO(6,"  ddtypep "<<ddtypep<<endl);
	if (AstUnpackArrayDType* adtypep = ddtypep->castUnpackArrayDType()) {
	    // SELBIT(array, index) -> ARRAYSEL(array, index)
	    AstNode* subp = rhsp;
	    if (fromRange.lo()!=0 || fromRange.hi()<0) {
		subp = newSubNeg (subp, fromRange.lo());
	    }
	    AstArraySel* newp = new AstArraySel (nodep->fileline(),
						 fromp, subp);
	    newp->dtypeFrom(adtypep->subDTypep());  // Need to strip off array reference
	    if (debug()>=9) newp->dumpTree(cout,"--SELBTn: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
	    // SELBIT(array, index) -> SEL(array, index*width-of-subindex, width-of-subindex)
	    AstNode* subp = rhsp;
	    if (fromRange.lo()!=0 || fromRange.hi()<0) {
		subp = newSubNeg (subp, fromRange.lo());
	    }
	    if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0)
		adtypep->v3fatalSrc("Array extraction with width miscomputed "
				    <<adtypep->width()<<"/"<<fromRange.elements());
	    int elwidth = adtypep->width() / fromRange.elements();
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       new AstMul(nodep->fileline(),
						  new AstConst(nodep->fileline(),AstConst::Unsized32(),elwidth),
						  subp),
				       new AstConst (nodep->fileline(),AstConst::Unsized32(),elwidth));
	    newp->declRange(fromRange);
	    newp->declElWidth(elwidth);
	    newp->dtypeFrom(adtypep->subDTypep());  // Need to strip off array reference
	    if (debug()>=9) newp->dumpTree(cout,"--SELBTn: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else if (ddtypep->castBasicDType()) {
	    // SELBIT(range, index) -> SEL(array, index, 1)
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       newSubLsbOf(rhsp, fromRange),
				       // Unsized so width from user
				       new AstConst (nodep->fileline(),AstConst::Unsized32(),1));
	    newp->declRange(fromRange);
	    UINFO(6,"   new "<<newp<<endl);
	    if (debug()>=9) newp->dumpTree(cout,"--SELBTn: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else if (ddtypep->castNodeClassDType()) {  // It's packed, so a bit from the packed struct
	    // SELBIT(range, index) -> SEL(array, index, 1)
	    AstSel* newp = new AstSel (nodep->fileline(),
				       fromp,
				       newSubLsbOf(rhsp, fromRange),
				       // Unsized so width from user
				       new AstConst (nodep->fileline(),AstConst::Unsized32(),1));
	    newp->declRange(fromRange);
	    UINFO(6,"   new "<<newp<<endl);
	    if (debug()>=9) newp->dumpTree(cout,"--SELBTn: ");
	    nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	else {  // NULL=bad extract, or unknown node type
	    nodep->v3error("Illegal bit or array select; type already selected, or bad dimension: type is"
			   <<fromdata.m_errp->prettyName());
	    // How to recover?  We'll strip a dimension.
	    nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
	}
	if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); }
    }