Changeset 5650 for icGREP/icgrep-devel


Ignore:
Timestamp:
Sep 28, 2017, 11:47:58 AM (21 months ago)
Author:
cameron
Message:

Multiblock kernel builder support for ExternalBuffer? inputs; clean-up

Location:
icGREP/icgrep-devel/icgrep/kernels
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • icGREP/icgrep-devel/icgrep/kernels/kernel.cpp

    r5646 r5650  
    910910        inputBlockPtr[i] = kb->getInputStreamBlockPtr(mStreamSetInputs[i].name, kb->getInt32(0));
    911911        Value * avail = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
    912         Value * linearlyAvail = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, processedItemCount[i]);
    913         linearlyAvailItems[i] = kb->CreateSelect(kb->CreateICmpULT(avail, linearlyAvail), avail, linearlyAvail);
     912        linearlyAvailItems[i] = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, processedItemCount[i], avail);
    914913        auto & rate = mStreamSetInputs[i].rate;
    915914        if (rate.isUnknownRate()) continue;  // No calculation possible for unknown rates.
     
    10691068            else {
    10701069                Value * neededItems = kb->CreateSub(finalItemCountNeeded[i], blockBasePos);
    1071                 Value * availFromBase = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, blockBasePos);
    1072                 Value * allAvail = kb->CreateICmpULE(neededItems, availFromBase);
    1073                 Value * copyItems1 = kb->CreateSelect(allAvail, neededItems, availFromBase);
    1074                 //mStreamSetInputBuffers[i]->createBlockAlignedCopy(kb.get(), tempBufPtr, inputPtr, copyItems1);
     1070                Value * copyItems1 = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, blockBasePos, neededItems);
     1071                Value * allAvail = kb->CreateICmpEQ(neededItems, copyItems1);
    10751072                Value * copyBlocks1 = kb->CreateUDivCeil(copyItems1, blockSize);
    10761073                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, inputPtr, copyBlocks1);
  • icGREP/icgrep-devel/icgrep/kernels/kernel_builder.cpp

    r5647 r5650  
    129129}
    130130
    131 Value * KernelBuilder::getLinearlyAccessibleItems(const std::string & name, Value * fromPosition) {
     131Value * KernelBuilder::getLinearlyAccessibleItems(const std::string & name, Value * fromPosition, Value * avail, bool reverse) {
    132132    Kernel::Port port; unsigned index;
    133133    std::tie(port, index) = mKernel->getStreamPort(name);
    134134    const StreamSetBuffer * buf = nullptr;
    135     if (port == Kernel::Port::Input) {       
     135    if (port == Kernel::Port::Input) {
    136136        const auto lookAhead = mKernel->getLookAhead(index);
    137137        if (LLVM_UNLIKELY(lookAhead != 0)) {
     
    143143    }
    144144    assert (buf);
    145     return buf->getLinearlyAccessibleItems(this, getStreamSetBufferPtr(name), fromPosition);
    146 }
    147 
    148 Value * KernelBuilder::getLinearlyAccessibleBlocks(const std::string & name, Value * fromBlock) {
    149     Kernel::Port port; unsigned index;
    150     std::tie(port, index) = mKernel->getStreamPort(name);
    151     const StreamSetBuffer * buf = nullptr;
    152     if (port == Kernel::Port::Input) {       
    153         const auto lookAhead = mKernel->getLookAhead(index);
    154         if (LLVM_UNLIKELY(lookAhead != 0)) {
    155             const auto blocksAhead = (lookAhead + getBitBlockWidth() - 1) / getBitBlockWidth();
    156             fromBlock = CreateAdd(ConstantInt::get(fromBlock->getType(), blocksAhead), fromBlock);
    157         }
    158         buf = mKernel->getInputStreamSetBuffer(name);
    159     } else {
    160         buf = mKernel->getOutputStreamSetBuffer(name);
    161     }
    162     assert (buf);
    163     return buf->getLinearlyAccessibleBlocks(this, getStreamSetBufferPtr(name), fromBlock);
    164 }
    165 
    166 Value * KernelBuilder::getLinearlyWritableItems(const std::string & name, Value * fromPosition) {
    167     const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
    168     return buf->getLinearlyWritableItems(this, getStreamSetBufferPtr(name), fromPosition);
    169 }
    170 
    171 Value * KernelBuilder::getLinearlyWritableBlocks(const std::string & name, Value * fromBlock) {
    172     const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
    173     return buf->getLinearlyWritableBlocks(this, getStreamSetBufferPtr(name), fromBlock);
     145    return buf->getLinearlyAccessibleItems(this, getStreamSetBufferPtr(name), fromPosition, avail, reverse);
     146}
     147
     148Value * KernelBuilder::getLinearlyWritableItems(const std::string & name, Value * fromPosition, bool reverse) {
     149    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
     150    return buf->getLinearlyWritableItems(this, getStreamSetBufferPtr(name), fromPosition, reverse);
    174151}
    175152
  • icGREP/icgrep-devel/icgrep/kernels/kernel_builder.h

    r5501 r5650  
    9494    llvm::Value * getAvailableItemCount(const std::string & name);
    9595
    96     llvm::Value * getLinearlyAccessibleItems(const std::string & name, llvm::Value * fromPosition);
     96    llvm::Value * getLinearlyAccessibleItems(const std::string & name, llvm::Value * fromPos, llvm::Value * avail, bool reverse = false);
    9797   
    98     llvm::Value * getLinearlyAccessibleBlocks(const std::string & name, llvm::Value * fromBlock);
    99    
    100     llvm::Value * getLinearlyWritableItems(const std::string & name, llvm::Value * fromPosition);
    101    
    102     llvm::Value * getLinearlyWritableBlocks(const std::string & name, llvm::Value * fromBlock);
     98    llvm::Value * getLinearlyWritableItems(const std::string & name, llvm::Value * fromPos, bool reverse = false);
    10399   
    104100    llvm::BasicBlock * CreateConsumerWait();
  • icGREP/icgrep-devel/icgrep/kernels/streamset.cpp

    r5647 r5650  
    140140}
    141141
    142 Value * StreamSetBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
     142Value * StreamSetBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const b, Value * self, Value * fromPosition, Value * availItems, bool reverse) const {
     143    Constant * bufSize = b->getSize(mBufferBlocks * b->getStride());
     144    Value * itemsFromBase = b->CreateURem(fromPosition, bufSize);
     145    if (reverse) {
     146        Value * bufAvail = b->CreateSelect(b->CreateICmpEQ(itemsFromBase, b->getSize(0)), bufSize, itemsFromBase);
     147        return b->CreateSelect(b->CreateICmpULT(bufAvail, availItems), bufAvail, availItems);
     148    }
     149    else {
     150        Value * linearSpace = b->CreateSub(bufSize, itemsFromBase, "linearSpace");
     151        return b->CreateSelect(b->CreateICmpULT(availItems, linearSpace), availItems, linearSpace);
     152    }
     153}
     154
     155Value * StreamSetBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
    143156    Constant * bufSize = iBuilder->getSize(mBufferBlocks * iBuilder->getStride());
    144157    Value * bufRem = iBuilder->CreateURem(fromPosition, bufSize);
     
    146159        return iBuilder->CreateSelect(iBuilder->CreateICmpEQ(bufRem, iBuilder->getSize(0)), bufSize, bufRem);
    147160    }
    148     else return iBuilder->CreateSub(bufSize, bufRem, "linearItems");
    149 }
    150 
    151 Value * StreamSetBuffer::getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromBlock, bool reverse) const {
    152     Constant * bufBlocks = iBuilder->getSize(mBufferBlocks);
    153     Value * bufRem = iBuilder->CreateURem(fromBlock, bufBlocks);
    154     if (reverse) {
    155         return iBuilder->CreateSelect(iBuilder->CreateICmpEQ(bufRem, iBuilder->getSize(0)), bufBlocks, bufRem);
    156     }
    157     else return iBuilder->CreateSub(bufBlocks, bufRem, "linearBlocks");
    158 }
    159 
    160 Value * StreamSetBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
    161     return getLinearlyAccessibleItems(iBuilder, self, fromPosition, reverse);
    162 }
    163 
    164 Value * StreamSetBuffer::getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromBlock, bool reverse) const {
    165     return getLinearlyAccessibleBlocks(iBuilder, self, fromBlock, reverse);
     161    else return iBuilder->CreateSub(bufSize, bufRem, "linearSpace");
    166162}
    167163
     
    264260}
    265261
    266 Value * SourceBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
     262Value * SourceBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, Value * availItems, bool reverse) const {
    267263    if (reverse) report_fatal_error("SourceBuffer cannot be accessed in reverse");
    268     return iBuilder->CreateSub(getCapacity(iBuilder, self), fromPosition);
    269 }
    270 
    271 Value * SourceBuffer::getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromBlock, bool reverse) const {
    272     if (reverse) report_fatal_error("SourceBuffer cannot be accessed in reverse");
    273     return iBuilder->CreateSub(iBuilder->CreateUDiv(getCapacity(iBuilder, self), iBuilder->getSize(iBuilder->getBitBlockWidth())), fromBlock);
     264    Value * maxAvail = iBuilder->CreateSub(getCapacity(iBuilder, self), fromPosition);
     265    return iBuilder->CreateSelect(iBuilder->CreateICmpULT(availItems, maxAvail), availItems, maxAvail);
     266}
     267
     268Value * SourceBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
     269    report_fatal_error("SourceBuffers cannot be written");
    274270}
    275271
     
    301297}
    302298
    303 Value * ExternalBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const, Value *, Value *, bool) const {
    304     report_fatal_error("External buffers: getLinearlyAccessibleItems is not supported.");
     299// All available items can be accessed.
     300Value * ExternalBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const, Value *, Value *, Value * availItems, bool) const {
     301    return availItems;
    305302}
    306303
     
    333330
    334331Value * CircularCopybackBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
    335     Value * accessibleItems = getLinearlyAccessibleItems(iBuilder, self, fromPosition, reverse);
    336     if (reverse) return accessibleItems;
    337     return iBuilder->CreateAdd(accessibleItems, iBuilder->getSize(mOverflowBlocks * iBuilder->getBitBlockWidth()));
    338 }
    339 
    340 Value * CircularCopybackBuffer::getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromBlock, bool reverse) const {
    341     Value * accessibleBlocks = getLinearlyAccessibleBlocks(iBuilder, self, fromBlock);
    342     if (reverse) return accessibleBlocks;
    343     return iBuilder->CreateAdd(accessibleBlocks, iBuilder->getSize(mOverflowBlocks));
     332    Value * writableProper = StreamSetBuffer::getLinearlyWritableItems(iBuilder, self, fromPosition, reverse);
     333    if (reverse) return writableProper;
     334    return iBuilder->CreateAdd(writableProper, iBuilder->getSize(mOverflowBlocks * iBuilder->getBitBlockWidth()));
    344335}
    345336
     
    412403
    413404Value * SwizzledCopybackBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromPosition, bool reverse) const {
    414     Value * accessibleItems = getLinearlyAccessibleItems(iBuilder, self, fromPosition, reverse);
    415     if (reverse) return accessibleItems;
    416     return iBuilder->CreateAdd(accessibleItems, iBuilder->getSize(mOverflowBlocks * iBuilder->getBitBlockWidth()));
    417 }
    418 
    419 Value * SwizzledCopybackBuffer::getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, Value * self, Value * fromBlock, bool reverse) const {
    420     Value * accessibleBlocks = getLinearlyAccessibleBlocks(iBuilder, self, fromBlock);
    421     if (reverse) return accessibleBlocks;
    422     return iBuilder->CreateAdd(accessibleBlocks, iBuilder->getSize(mOverflowBlocks));
    423 }
     405    Value * writableProper = StreamSetBuffer::getLinearlyWritableItems(iBuilder, self, fromPosition, reverse);
     406    if (reverse) return writableProper;
     407    return iBuilder->CreateAdd(writableProper, iBuilder->getSize(mOverflowBlocks * iBuilder->getBitBlockWidth()));
     408}
     409
    424410void SwizzledCopybackBuffer::genCopyBackLogic(IDISA::IDISA_Builder * const b, Value * handle, Value * priorProduced, Value * newProduced, const std::string Name) {
    425411    Constant * bufSize = b->getSize(mBufferBlocks * b->getBitBlockWidth());
     
    591577}
    592578
    593 Value * ExpandableBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const, Value *, Value *, bool) const {
     579Value * ExpandableBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const, Value *, Value *, Value *, bool) const {
    594580    report_fatal_error("Expandable buffers: getLinearlyAccessibleItems is not supported.");
    595581}
     
    682668
    683669
    684 Value * DynamicBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const b, Value * handle, Value * fromPosition, bool reverse) const {
     670Value * DynamicBuffer::getLinearlyAccessibleItems(IDISA::IDISA_Builder * const b, Value * handle, Value * fromPosition, Value * availItems, bool reverse) const {
    685671    Constant * blockSize = b->getSize(b->getBitBlockWidth());
    686672    Value * const bufBlocks = b->CreateLoad(b->CreateGEP(handle, {b->getInt32(0), b->getInt32(int(Field::WorkingBlocks))}));
     673    Value * bufSize = b->CreateMul(bufBlocks, blockSize);
     674    Value * itemsFromBase = b->CreateURem(fromPosition, bufSize);
     675    if (reverse) {
     676        Value * bufAvail = b->CreateSelect(b->CreateICmpEQ(itemsFromBase, b->getSize(0)), bufSize, itemsFromBase);
     677        return b->CreateSelect(b->CreateICmpULT(bufAvail, availItems), bufAvail, availItems);
     678    }
     679    else {
     680        Value * linearSpace = b->CreateSub(bufSize, itemsFromBase, "linearSpace");
     681        return b->CreateSelect(b->CreateICmpULT(availItems, linearSpace), availItems, linearSpace);
     682    }
     683}
     684
     685Value * DynamicBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const b, Value * handle, Value * fromPosition, bool reverse) const {
     686    Constant * blockSize = b->getSize(b->getBitBlockWidth());
     687    Value * bufBlocks = b->CreateLoad(b->CreateGEP(handle, {b->getInt32(0), b->getInt32(int(Field::WorkingBlocks))}));
    687688    Value * bufSize = b->CreateMul(bufBlocks, blockSize);
    688689    Value * bufRem = b->CreateURem(fromPosition, bufSize);
     
    690691        return b->CreateSelect(b->CreateICmpEQ(bufRem, b->getSize(0)), bufSize, bufRem);
    691692    }
    692     return b->CreateSub(bufSize, bufRem, "linearItems");
    693 }
    694 
    695 Value * DynamicBuffer::getLinearlyWritableItems(IDISA::IDISA_Builder * const b, Value * handle, Value * fromPosition, bool reverse) const {
    696     Value * accessibleItems = getLinearlyAccessibleItems(b, handle, fromPosition, reverse);
    697     if (reverse || (mOverflowBlocks == 0))  return accessibleItems;
    698     return b->CreateAdd(accessibleItems, b->getSize(mOverflowBlocks * b->getBitBlockWidth()));
    699 }
    700 
    701 Value * DynamicBuffer::getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const b, Value * handle, Value * fromBlock, bool reverse) const {
    702     Value * const bufBlocks = b->CreateLoad(b->CreateGEP(handle, {b->getInt32(0), b->getInt32(int(Field::WorkingBlocks))}));
    703     Value * bufRem = b->CreateURem(fromBlock, bufBlocks);
    704     if (reverse) {
    705         return b->CreateSelect(b->CreateICmpEQ(bufRem, b->getSize(0)), bufBlocks, bufRem);
    706     }
    707     return b->CreateSub(bufBlocks, bufRem, "linearBlocks");
     693    bufSize = b->CreateMul(b->CreateAdd(bufBlocks, b->getSize(mOverflowBlocks)), blockSize);
     694    return b->CreateSub(bufSize, bufRem, "linearWritable");
    708695}
    709696
  • icGREP/icgrep-devel/icgrep/kernels/streamset.h

    r5647 r5650  
    8383   
    8484    // The number of items that cam be linearly accessed from a given logical stream position.
    85     virtual llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const;
    86    
    87     virtual llvm::Value * getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const;
     85    virtual llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPos, llvm::Value * avail, bool reverse = false) const;
    8886   
    8987    void createBlockCopy(IDISA::IDISA_Builder * const iBuilder, llvm::Value * targetBlockPtr, llvm::Value * sourceBlockPtr, llvm::Value * blocksToCopy) const;
     
    9290
    9391    virtual llvm::Value * getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const;
    94    
    95     virtual llvm::Value * getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const;
    9692   
    9793    virtual bool supportsCopyBack() const {
     
    163159    llvm::Value * getCapacity(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self) const override;
    164160   
    165     llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    166    
    167     llvm::Value * getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const override;
     161    llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, llvm::Value * avail, bool reverse = false) const override;
     162
     163    llvm::Value * getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    168164
    169165    llvm::Type * getStreamSetBlockType() const override;
     
    191187    ExternalBuffer(const std::unique_ptr<kernel::KernelBuilder> & b, llvm::Type * type, llvm::Value * addr, unsigned AddressSpace = 0);
    192188
    193     llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    194 
     189    llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, llvm::Value * avail, bool reverse = false) const override;
     190   
    195191    void allocateBuffer(const std::unique_ptr<kernel::KernelBuilder> & iBuilder) override;
    196192
     
    234230    llvm::Value * getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    235231   
    236     llvm::Value * getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const override;
    237 
    238232    void allocateBuffer(const std::unique_ptr<kernel::KernelBuilder> & iBuilder) override;
    239233
     
    259253
    260254    llvm::Value * getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    261    
    262     llvm::Value * getLinearlyWritableBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const override;
    263255   
    264256    void allocateBuffer(const std::unique_ptr<kernel::KernelBuilder> & iBuilder) override;
     
    291283    llvm::Value * getStreamPackPtr(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * streamIndex, llvm::Value * blockIndex, llvm::Value * packIndex, const bool readOnly) const override;
    292284
    293     llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    294 
     285    llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, llvm::Value * avail, bool reverse = false) const override;
     286   
    295287    llvm::Value * getStreamSetCount(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self) const override;
    296288
     
    322314    DynamicBuffer(const std::unique_ptr<kernel::KernelBuilder> & b, llvm::Type * type, size_t initialCapacity, size_t overflowBlocks = 0, unsigned swizzleFactor = 1, unsigned addrSpace = 0);
    323315   
    324     llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const b, llvm::Value * handle, llvm::Value * fromPosition, bool reverse = false) const override;
    325    
    326     llvm::Value * getLinearlyAccessibleBlocks(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromBlock, bool reverse = false) const override;
    327 
     316    llvm::Value * getLinearlyAccessibleItems(IDISA::IDISA_Builder * const b, llvm::Value * handle, llvm::Value * fromPosition, llvm::Value * avail, bool reverse = false) const override;
     317   
    328318    llvm::Value * getLinearlyWritableItems(IDISA::IDISA_Builder * const iBuilder, llvm::Value * self, llvm::Value * fromPosition, bool reverse = false) const override;
    329319   
Note: See TracChangeset for help on using the changeset viewer.