Optimize bufferization

This commit is contained in:
ilgeco
2026-07-30 18:43:43 +02:00
parent c12f69133d
commit 1e7699f3e5
7 changed files with 78 additions and 9 deletions
+5 -2
View File
@@ -620,10 +620,13 @@ void PimCodeGen::codeGenLmvOp(pim::PimMemCopyOp lmvOp, const StaticValueKnowledg
} }
void PimCodeGen::codeGenReceiveOp(pim::PimReceiveOp receiveOp, const StaticValueKnowledge& knowledge) const { void PimCodeGen::codeGenReceiveOp(pim::PimReceiveOp receiveOp, const StaticValueKnowledge& knowledge) const {
auto outputOffset = indexOf(receiveOp.getOutputOffset(), knowledge);
auto sourceCoreId = indexOf(receiveOp.getSourceCoreId(), knowledge); auto sourceCoreId = indexOf(receiveOp.getSourceCoreId(), knowledge);
assert(succeeded(sourceCoreId) && "pim.receive source core id must be statically resolvable during codegen"); assert(succeeded(outputOffset) && succeeded(sourceCoreId)
&& "pim.receive offset and source core id must be statically resolvable during codegen");
emitCommunicationOp( emitCommunicationOp(
pim_binary::Opcode::recv, addressOf(receiveOp.getOutputBuffer(), knowledge), *sourceCoreId, receiveOp.getSize()); pim_binary::Opcode::recv, addressOf(receiveOp.getOutputBuffer(), knowledge) + *outputOffset,
*sourceCoreId, receiveOp.getSize());
} }
void PimCodeGen::codeGenSendOp(pim::PimSendOp sendOp, const StaticValueKnowledge& knowledge) const { void PimCodeGen::codeGenSendOp(pim::PimSendOp sendOp, const StaticValueKnowledge& knowledge) const {
@@ -348,7 +348,9 @@ LogicalResult raptor::SpatialToPimPass::lowerComputeOp(spatial::SpatScheduledCom
return failure(); return failure();
Value received = Value received =
PimReceiveOp::create( PimReceiveOp::create(
rewriter, receiveOp.getLoc(), outputBuffer.getType(), outputBuffer, *sizeAttr, receiveOp.getSourceCoreId()) rewriter, receiveOp.getLoc(), outputBuffer.getType(), outputBuffer,
arith::ConstantIndexOp::create(rewriter, receiveOp.getLoc(), 0),
*sizeAttr, receiveOp.getSourceCoreId())
.getOutput(); .getOutput();
blockArg->replaceAllUsesWith(received); blockArg->replaceAllUsesWith(received);
markOpToRemove(receiveOp); markOpToRemove(receiveOp);
@@ -85,8 +85,9 @@ struct ChannelReceiveLowering : OpRewritePattern<spatial::SpatChannelReceiveOp>
auto sizeAttr = getTensorSizeInBytesAttr(rewriter, op.getOperation(), op.getResult()); auto sizeAttr = getTensorSizeInBytesAttr(rewriter, op.getOperation(), op.getResult());
if (failed(sizeAttr)) if (failed(sizeAttr))
return failure(); return failure();
Value zero = arith::ConstantIndexOp::create(rewriter, op.getLoc(), 0);
auto receive = pim::PimReceiveOp::create( auto receive = pim::PimReceiveOp::create(
rewriter, op.getLoc(), op.getResult().getType(), outputBuffer, *sizeAttr, op.getSourceCoreId()); rewriter, op.getLoc(), op.getResult().getType(), outputBuffer, zero, *sizeAttr, op.getSourceCoreId());
copyRaptorDebugAttrs(op.getOperation(), receive.getOperation()); copyRaptorDebugAttrs(op.getOperation(), receive.getOperation());
Value received = receive.getOutput(); Value received = receive.getOutput();
if (!destinationInsert) { if (!destinationInsert) {
@@ -96,7 +97,6 @@ struct ChannelReceiveLowering : OpRewritePattern<spatial::SpatChannelReceiveOp>
rewriter.setInsertionPoint(destinationInsert); rewriter.setInsertionPoint(destinationInsert);
Value targetOffset = createDestinationByteOffset(rewriter, destinationInsert); Value targetOffset = createDestinationByteOffset(rewriter, destinationInsert);
Value zero = arith::ConstantIndexOp::create(rewriter, op.getLoc(), 0);
auto copy = pim::PimMemCopyOp::create( auto copy = pim::PimMemCopyOp::create(
rewriter, op.getLoc(), destinationInsert.getDestType(), targetOffset, zero, rewriter, op.getLoc(), destinationInsert.getDestType(), targetOffset, zero,
destinationInsert.getDest(), received, *sizeAttr); destinationInsert.getDest(), received, *sizeAttr);
+3 -1
View File
@@ -97,6 +97,7 @@ def PimReceiveOp : PimOp<"receive", [DestinationStyleOpInterface]> {
let arguments = (ins let arguments = (ins
PimTensor:$outputBuffer, PimTensor:$outputBuffer,
Index:$outputOffset,
I32Attr:$size, I32Attr:$size,
Index:$sourceCoreId Index:$sourceCoreId
); );
@@ -112,7 +113,8 @@ def PimReceiveOp : PimOp<"receive", [DestinationStyleOpInterface]> {
}]; }];
let assemblyFormat = [{ let assemblyFormat = [{
`(` $outputBuffer `,` $sourceCoreId `)` attr-dict `:` type($outputBuffer) `->` type($output) `[` $outputOffset `]` `(` $outputBuffer `,` $sourceCoreId `)` attr-dict
`:` type($outputBuffer) `->` type($output)
}]; }];
} }
@@ -189,7 +189,8 @@ struct ReceiveOpInterface : DstBufferizableOpInterfaceExternalModel<ReceiveOpInt
Value contiguousOutput = allocateContiguousResultMemRefLike(*outputBufferOpt, op->getLoc(), rewriter); Value contiguousOutput = allocateContiguousResultMemRefLike(*outputBufferOpt, op->getLoc(), rewriter);
replaceOpWithNewBufferizedOp<PimReceiveOp>( replaceOpWithNewBufferizedOp<PimReceiveOp>(
rewriter, op, contiguousOutput.getType(), contiguousOutput, receiveOp.getSizeAttr(), receiveOp.getSourceCoreId()); rewriter, op, contiguousOutput.getType(), contiguousOutput, receiveOp.getOutputOffset(),
receiveOp.getSizeAttr(), receiveOp.getSourceCoreId());
return success(); return success();
} }
}; };
@@ -9,6 +9,7 @@
#include "mlir/IR/Dominance.h" #include "mlir/IR/Dominance.h"
#include "mlir/IR/PatternMatch.h" #include "mlir/IR/PatternMatch.h"
#include "mlir/Interfaces/DestinationStyleOpInterface.h" #include "mlir/Interfaces/DestinationStyleOpInterface.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Pass/Pass.h" #include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
@@ -177,12 +178,22 @@ static void forwardSingleConsumerContiguousInputCopies(func::FuncOp funcOp) {
if (hasOtherUse || !consumerUse) if (hasOtherUse || !consumerUse)
continue; continue;
Value output = getForwardedInputConsumerOutput(*consumerUse);
Value source = copy.getSource(); Value source = copy.getSource();
if (!output || !isDeviceLocalPimAddress(source) if (!isDeviceLocalPimAddress(source)
|| (failed(resolveContiguousAddress(source)) && failed(compileContiguousAddressExpr(source)))) || (failed(resolveContiguousAddress(source)) && failed(compileContiguousAddressExpr(source))))
continue; continue;
if (isa<PimSendOp>(consumerUse->getOwner())) {
consumerUse->set(source);
copy.erase();
targetAlloc.erase();
continue;
}
Value output = getForwardedInputConsumerOutput(*consumerUse);
if (!output)
continue;
FailureOr<Value> sourceBase = getPimAddressBase(source); FailureOr<Value> sourceBase = getPimAddressBase(source);
FailureOr<Value> outputBase = getPimAddressBase(output); FailureOr<Value> outputBase = getPimAddressBase(output);
if (failed(sourceBase) || failed(outputBase) || *sourceBase == *outputBase) if (failed(sourceBase) || failed(outputBase) || *sourceBase == *outputBase)
@@ -251,6 +262,46 @@ static void forwardSingleConsumerPimOutputCopies(func::FuncOp funcOp) {
} }
} }
static void forwardSingleConsumerReceiveCopies(func::FuncOp funcOp) {
SmallVector<PimMemCopyOp> copies;
funcOp.walk([&](PimMemCopyOp copy) { copies.push_back(copy); });
for (PimMemCopyOp copy : copies) {
auto receive = copy.getSource().getDefiningOp<PimReceiveOp>();
auto outputAlloc = receive
? receive.getOutputBuffer().getDefiningOp<memref::AllocOp>()
: memref::AllocOp();
auto sourceOffset = getConstantIntValue(copy.getSourceOffset());
if (!receive || !outputAlloc || !receive.getOutput().hasOneUse()
|| !outputAlloc.getResult().hasOneUse()
|| !sourceOffset || *sourceOffset != 0
|| copy.getSize() != receive.getSize()
|| receive->getBlock() != copy->getBlock())
continue;
bool canSinkReceive = true;
for (Operation* between = receive->getNextNode(); between != copy;
between = between->getNextNode()) {
if (!isMemoryEffectFree(between)) {
canSinkReceive = false;
break;
}
}
if (!canSinkReceive)
continue;
OpBuilder builder(copy);
auto forwarded = PimReceiveOp::create(
builder, receive.getLoc(), copy.getOutput().getType(), copy.getTarget(),
copy.getTargetOffset(), receive.getSizeAttr(), receive.getSourceCoreId());
forwarded->setAttrs(receive->getAttrs());
copy.getOutput().replaceAllUsesWith(forwarded.getOutput());
copy.erase();
receive.erase();
outputAlloc.erase();
}
}
enum class ExpectedPimCopyDirection { HostToDevice, DeviceToHost, DeviceToDevice }; enum class ExpectedPimCopyDirection { HostToDevice, DeviceToHost, DeviceToDevice };
static LogicalResult verifyPimCopyEndpoints(Operation* copy, static LogicalResult verifyPimCopyEndpoints(Operation* copy,
@@ -406,6 +457,7 @@ void PimBufferizationPass::runOnOperation() {
return; return;
} }
forwardSingleConsumerReceiveCopies(funcOp);
forwardSingleConsumerContiguousInputCopies(funcOp); forwardSingleConsumerContiguousInputCopies(funcOp);
forwardSingleConsumerPimOutputCopies(funcOp); forwardSingleConsumerPimOutputCopies(funcOp);
@@ -990,6 +990,15 @@ private:
hasFailure = true; hasFailure = true;
} }
} }
if (auto receiveOp = dyn_cast<pim::PimReceiveOp>(op);
receiveOp
&& failed(resolveIndexValue(receiveOp.getOutputOffset(), knowledge))) {
diagnostics.report(&op, [](Operation* illegalOp) {
illegalOp->emitOpError(
"output offset must be statically evaluable for PIM codegen");
});
hasFailure = true;
}
return success(!hasFailure); return success(!hasFailure);
}); });
} }