DeadLock
This commit is contained in:
@@ -35,7 +35,8 @@ static FailureOr<ArrayRef<int64_t>> getWeightShapeForWeightedOp(Value weight) {
|
||||
return shapedType.getShape();
|
||||
}
|
||||
|
||||
static bool isBatchOutputArgument(SpatComputeBatch batchOp, Value value) {
|
||||
template <typename ComputeBatchOpTy>
|
||||
static bool isBatchOutputArgument(ComputeBatchOpTy batchOp, Value value) {
|
||||
if (batchOp.getNumResults() == 0)
|
||||
return false;
|
||||
auto blockArg = dyn_cast<BlockArgument>(value);
|
||||
@@ -58,8 +59,28 @@ static LogicalResult verifyStaticWeights(ComputeOpTy computeOp, StringRef kind)
|
||||
return success();
|
||||
}
|
||||
|
||||
static bool isStaticIndexExpr(Value value) {
|
||||
if (matchConstantIndexValue(value))
|
||||
return true;
|
||||
|
||||
auto affineApply = value.getDefiningOp<affine::AffineApplyOp>();
|
||||
if (affineApply) {
|
||||
if (!isSingleResultSymbolFreeAffineMap(affineApply.getAffineMap()))
|
||||
return false;
|
||||
return llvm::all_of(affineApply.getMapOperands(), isStaticIndexExpr);
|
||||
}
|
||||
|
||||
if (auto addOp = value.getDefiningOp<arith::AddIOp>())
|
||||
return isStaticIndexExpr(addOp.getLhs()) && isStaticIndexExpr(addOp.getRhs());
|
||||
|
||||
if (auto mulOp = value.getDefiningOp<arith::MulIOp>())
|
||||
return isStaticIndexExpr(mulOp.getLhs()) && isStaticIndexExpr(mulOp.getRhs());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isSupportedLaneOffsetExpr(Value value, BlockArgument laneArg) {
|
||||
if (value == laneArg || matchConstantIndexValue(value))
|
||||
if (value == laneArg || isStaticIndexExpr(value))
|
||||
return true;
|
||||
|
||||
auto affineApply = value.getDefiningOp<affine::AffineApplyOp>();
|
||||
@@ -83,10 +104,15 @@ static bool isSupportedLaneOffsetExpr(Value value, BlockArgument laneArg) {
|
||||
}
|
||||
|
||||
auto addOp = value.getDefiningOp<arith::AddIOp>();
|
||||
if (!addOp)
|
||||
if (addOp)
|
||||
return (isSupportedLaneOffsetExpr(addOp.getLhs(), laneArg) && isStaticIndexExpr(addOp.getRhs()))
|
||||
|| (isSupportedLaneOffsetExpr(addOp.getRhs(), laneArg) && isStaticIndexExpr(addOp.getLhs()));
|
||||
|
||||
auto mulOp = value.getDefiningOp<arith::MulIOp>();
|
||||
if (!mulOp)
|
||||
return false;
|
||||
return (addOp.getLhs() == laneArg && matchConstantIndexValue(addOp.getRhs()))
|
||||
|| (addOp.getRhs() == laneArg && matchConstantIndexValue(addOp.getLhs()));
|
||||
return (isSupportedLaneOffsetExpr(mulOp.getLhs(), laneArg) && isStaticIndexExpr(mulOp.getRhs()))
|
||||
|| (isSupportedLaneOffsetExpr(mulOp.getRhs(), laneArg) && isStaticIndexExpr(mulOp.getLhs()));
|
||||
}
|
||||
|
||||
static LogicalResult
|
||||
@@ -158,17 +184,27 @@ static LogicalResult verifyOnlyConstantExternalValues(Operation* ownerOp, Region
|
||||
if (isDefinedInsideRegion(value, region) || isConstantExternalValue(value))
|
||||
continue;
|
||||
|
||||
InFlightDiagnostic diagnostic = ownerOp->emitOpError()
|
||||
<< kind << " body may only directly reference external constants";
|
||||
InFlightDiagnostic diagnostic =
|
||||
ownerOp->emitOpError() << kind << " body may not capture external values";
|
||||
diagnostic.attachNote(op->getLoc())
|
||||
<< "non-constant external operand #" << operand.getOperandNumber() << " is used by " << op->getName();
|
||||
<< "owner='" << ownerOp->getName() << "' nestedOp='" << op->getName() << "' operand#"
|
||||
<< operand.getOperandNumber() << " type=" << value.getType()
|
||||
<< " category=" << (isa<TensorType>(value.getType()) ? "tensor" : (value.getType().isIndex() ? "index"
|
||||
: "scalar"));
|
||||
if (Operation* definingOp = value.getDefiningOp())
|
||||
diagnostic.attachNote(definingOp->getLoc()) << "defining op is '" << definingOp->getName() << "'";
|
||||
else if (auto blockArg = dyn_cast<BlockArgument>(value))
|
||||
diagnostic.attachNote(blockArg.getOwner()->getParentOp()->getLoc())
|
||||
<< "value is block argument #" << blockArg.getArgNumber() << " of '"
|
||||
<< blockArg.getOwner()->getParentOp()->getName() << "'";
|
||||
hasFailure = true;
|
||||
}
|
||||
});
|
||||
return success(!hasFailure);
|
||||
}
|
||||
|
||||
static LogicalResult verifyBatchBody(SpatComputeBatch batchOp, Block& block) {
|
||||
template <typename ComputeBatchOpTy>
|
||||
static LogicalResult verifyBatchBody(ComputeBatchOpTy batchOp, Block& block) {
|
||||
if (batchOp.getNumResults() == 0) {
|
||||
auto yieldOp = dyn_cast_or_null<SpatYieldOp>(block.getTerminator());
|
||||
if (!yieldOp)
|
||||
@@ -344,144 +380,266 @@ LogicalResult SpatConcatOp::verify() {
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult verifyComputeResultsUses(Operation* op) {
|
||||
if (!isa<SpatCompute, SpatComputeBatch>(op))
|
||||
return op->emitError("verifyComputeResultUses: Op is not a SpatCompute/SpatComputeBatch operation");
|
||||
if (!llvm::all_of(op->getResults(), [](Value result) {
|
||||
return llvm::all_of(result.getUsers(), [](Operation* op) {
|
||||
return !(op->getParentOfType<SpatCompute>() || op->getParentOfType<SpatComputeBatch>());
|
||||
});
|
||||
})) {
|
||||
return op->emitError("ComputeResult used directly inside another Compute");
|
||||
static bool isKnownLogicalLayout(StringRef layout) { return layout == "nchw"; }
|
||||
|
||||
static bool isKnownPhysicalLayout(StringRef layout) {
|
||||
return layout == "dense_nchw" || layout == "nchw_row_strip";
|
||||
}
|
||||
|
||||
static LogicalResult verifyPlanTensorTypes(Operation* op, Value input, Value output, StringRef kind) {
|
||||
auto inputType = dyn_cast<RankedTensorType>(input.getType());
|
||||
auto outputType = dyn_cast<RankedTensorType>(output.getType());
|
||||
if (!inputType || !outputType)
|
||||
return op->emitOpError() << kind << " requires ranked tensor input and output types";
|
||||
if (inputType.getElementType() != outputType.getElementType())
|
||||
return op->emitOpError() << kind << " requires matching input/output element types";
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatConv2DPlanOp::verify() {
|
||||
auto inputType = dyn_cast<RankedTensorType>(getInput().getType());
|
||||
auto weightType = dyn_cast<RankedTensorType>(getWeight().getType());
|
||||
auto outputType = dyn_cast<RankedTensorType>(getOutput().getType());
|
||||
if (!inputType || !weightType || !outputType)
|
||||
return emitError("requires ranked tensor input, weight, and output");
|
||||
if (inputType.getRank() != 4 || weightType.getRank() != 4 || outputType.getRank() != 4)
|
||||
return emitError("requires rank-4 input, weight, and output tensors");
|
||||
if (!isKnownLogicalLayout(getLogicalLayout()))
|
||||
return emitError("requires a known logical layout");
|
||||
if (getPads().size() != 4)
|
||||
return emitError("requires exactly four pad values");
|
||||
if (getStrides().size() != 2)
|
||||
return emitError("requires exactly two stride values");
|
||||
if (getDilations().size() != 2)
|
||||
return emitError("requires exactly two dilation values");
|
||||
if (getGroup() < 1)
|
||||
return emitError("requires group >= 1");
|
||||
if (inputType.getElementType() != weightType.getElementType()
|
||||
|| inputType.getElementType() != outputType.getElementType()) {
|
||||
return emitError("requires matching input, weight, and output element types");
|
||||
}
|
||||
if (getBias()) {
|
||||
auto biasType = dyn_cast<RankedTensorType>(getBias().getType());
|
||||
if (!biasType)
|
||||
return emitError("requires ranked tensor bias type");
|
||||
if (biasType.getElementType() != outputType.getElementType())
|
||||
return emitError("requires bias element type to match output element type");
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatCompute::verify() {
|
||||
auto& block = getBody().front();
|
||||
unsigned expectedArgCount = getWeights().size() + getInputs().size();
|
||||
if (block.getNumArguments() != expectedArgCount)
|
||||
return emitError("compute body must have weight and input block arguments");
|
||||
LogicalResult SpatReluPlanOp::verify() {
|
||||
if (failed(verifyPlanTensorTypes(getOperation(), getInput(), getOutput(), "spat.relu_plan")))
|
||||
return failure();
|
||||
if (!isKnownLogicalLayout(getLogicalLayout()))
|
||||
return emitError("requires a known logical layout");
|
||||
return success();
|
||||
}
|
||||
|
||||
for (auto [weightIndex, weight] : llvm::enumerate(getWeights())) {
|
||||
auto blockArg = getWeightArgument(weightIndex);
|
||||
if (!blockArg || blockArg->getType() != weight.getType())
|
||||
return emitError("compute weight block argument types must match weight operand types exactly");
|
||||
LogicalResult SpatReconciliatorOp::verify() {
|
||||
if (failed(verifyPlanTensorTypes(getOperation(), getInput(), getOutput(), "spat.reconciliator")))
|
||||
return failure();
|
||||
if (!isKnownLogicalLayout(getLogicalLayout()))
|
||||
return emitError("requires a known logical layout");
|
||||
if (!isKnownPhysicalLayout(getPhysicalLayout()))
|
||||
return emitError("requires a known physical layout");
|
||||
|
||||
auto logicalType = dyn_cast<RankedTensorType>(getOutput().getType());
|
||||
if (!logicalType)
|
||||
return emitError("requires ranked tensor output");
|
||||
|
||||
auto offsets = getFragmentOffsets();
|
||||
auto sizes = getFragmentSizes();
|
||||
if (offsets.size() != sizes.size())
|
||||
return emitError("fragment offset and size arrays must have the same length");
|
||||
if (offsets.empty())
|
||||
return success();
|
||||
|
||||
int64_t rank = logicalType.getRank();
|
||||
if (rank <= 0 || offsets.size() % rank != 0)
|
||||
return emitError("fragment metadata must be a whole number of rank-sized fragments");
|
||||
|
||||
ArrayRef<int64_t> shape = logicalType.getShape();
|
||||
for (int64_t index = 0; index < static_cast<int64_t>(offsets.size()); ++index) {
|
||||
int64_t dim = index % rank;
|
||||
int64_t offset = offsets[index];
|
||||
int64_t size = sizes[index];
|
||||
if (offset < 0 || size < 0)
|
||||
return emitError("fragment offsets and sizes must be non-negative");
|
||||
int64_t logicalDim = shape[dim];
|
||||
if (!ShapedType::isDynamic(logicalDim) && offset + size > logicalDim)
|
||||
return emitError("fragment bounds must stay within the logical tensor shape");
|
||||
}
|
||||
for (auto [inputIndex, input] : llvm::enumerate(getInputs())) {
|
||||
auto blockArg = getInputArgument(inputIndex);
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatMaterializeLayoutOp::verify() {
|
||||
if (failed(verifyPlanTensorTypes(getOperation(), getInput(), getOutput(), "spat.materialize_layout")))
|
||||
return failure();
|
||||
if (!isKnownLogicalLayout(getLogicalLayout()))
|
||||
return emitError("requires a known logical layout");
|
||||
if (!isKnownPhysicalLayout(getSourcePhysicalLayout()))
|
||||
return emitError("requires a known source physical layout");
|
||||
if (!isKnownPhysicalLayout(getTargetPhysicalLayout()))
|
||||
return emitError("requires a known target physical layout");
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult verifyComputeResultsUses(Operation* op) {
|
||||
if (!isAnySpatialComputeLike(op))
|
||||
return op->emitError("verifyComputeResultUses: op is not a Spatial compute-like operation");
|
||||
if (!llvm::all_of(op->getResults(), [](Value result) {
|
||||
return llvm::all_of(result.getUsers(), [](Operation* op) {
|
||||
return !isAnySpatialComputeLike(op->getParentOp());
|
||||
});
|
||||
})) {
|
||||
return op->emitError("compute result used directly inside another Spatial compute body");
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
template <typename ComputeOpTy>
|
||||
LogicalResult verifyComputeLikeOp(ComputeOpTy compute, StringRef opName) {
|
||||
auto& block = compute.getBody().front();
|
||||
unsigned expectedArgCount = compute.getWeights().size() + compute.getInputs().size();
|
||||
if (block.getNumArguments() != expectedArgCount)
|
||||
return compute.emitOpError("compute body must have weight and input block arguments");
|
||||
|
||||
for (auto [weightIndex, weight] : llvm::enumerate(compute.getWeights())) {
|
||||
auto blockArg = compute.getWeightArgument(weightIndex);
|
||||
if (!blockArg || blockArg->getType() != weight.getType())
|
||||
return compute.emitOpError("compute weight block argument types must match weight operand types exactly");
|
||||
}
|
||||
for (auto [inputIndex, input] : llvm::enumerate(compute.getInputs())) {
|
||||
auto blockArg = compute.getInputArgument(inputIndex);
|
||||
if (!blockArg || blockArg->getType() != input.getType())
|
||||
return emitError("compute input block argument types must match input operand types exactly");
|
||||
return compute.emitOpError("compute input block argument types must match input operand types exactly");
|
||||
}
|
||||
|
||||
if (block.mightHaveTerminator()) {
|
||||
auto yieldOp = dyn_cast_or_null<SpatYieldOp>(block.getTerminator());
|
||||
if (!yieldOp)
|
||||
return emitError("ComputeOp must have a single yield operation");
|
||||
return compute.emitOpError("ComputeOp must have a single yield operation");
|
||||
|
||||
auto resultTypes = getResultTypes();
|
||||
auto resultTypes = compute.getResultTypes();
|
||||
auto yieldTypes = yieldOp->getOperandTypes();
|
||||
if (resultTypes.size() != yieldTypes.size())
|
||||
return emitError("ComputeOp must have same number of results as yieldOp operands");
|
||||
return compute.emitOpError("ComputeOp must have same number of results as yieldOp operands");
|
||||
|
||||
for (auto it : llvm::reverse(llvm::zip(resultTypes, yieldTypes))) {
|
||||
auto resultType = std::get<0>(it);
|
||||
auto yieldType = std::get<1>(it);
|
||||
|
||||
if (resultType != yieldType || failed(verifyCompatibleShape(resultType, yieldType)))
|
||||
return emitError("ComputeOp output must be of the same type as yieldOp operand");
|
||||
return compute.emitOpError("ComputeOp output must be of the same type as yieldOp operand");
|
||||
|
||||
if (auto resultRankedType = dyn_cast<RankedTensorType>(resultType)) {
|
||||
if (auto yieldRankedType = dyn_cast<RankedTensorType>(yieldType)) {
|
||||
if (resultRankedType.getEncoding() != yieldRankedType.getEncoding())
|
||||
return emitError("ComputeOp output must have the same encoding as yieldOp operand");
|
||||
return compute.emitOpError("ComputeOp output must have the same encoding as yieldOp operand");
|
||||
}
|
||||
else {
|
||||
return emitError("ComputeOp output has an encoding while yieldOp operand does not have one");
|
||||
return compute.emitOpError("ComputeOp output has an encoding while yieldOp operand does not have one");
|
||||
}
|
||||
}
|
||||
else if (dyn_cast<RankedTensorType>(yieldType)) {
|
||||
return emitError("ComputeOp output must not have an encoding if yieldOp operand has one");
|
||||
return compute.emitOpError("ComputeOp output must not have an encoding if yieldOp operand has one");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned inputIndex = 0; inputIndex < getInputs().size(); ++inputIndex)
|
||||
if (auto inputArg = getInputArgument(inputIndex); !inputArg || inputArg->use_empty())
|
||||
return emitError("ComputeOp block argument is not used");
|
||||
if (failed(verifyStaticWeights(*this, "compute")))
|
||||
for (unsigned inputIndex = 0; inputIndex < compute.getInputs().size(); ++inputIndex)
|
||||
if (auto inputArg = compute.getInputArgument(inputIndex); !inputArg || inputArg->use_empty())
|
||||
return compute.emitOpError("ComputeOp block argument is not used");
|
||||
if (failed(verifyStaticWeights(compute, opName)))
|
||||
return failure();
|
||||
if (failed(verifyOnlyConstantExternalValues(this->getOperation(), getBody(), "spat.compute")))
|
||||
if (failed(verifyOnlyConstantExternalValues(compute.getOperation(), compute.getBody(), opName)))
|
||||
return failure();
|
||||
if (failed(verifyComputeResultsUses(this->getOperation())))
|
||||
if (failed(verifyComputeResultsUses(compute.getOperation())))
|
||||
return failure();
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatComputeBatch::verify() {
|
||||
int32_t count = getLaneCount();
|
||||
LogicalResult SpatGraphCompute::verify() { return verifyComputeLikeOp(*this, "spat.graph_compute"); }
|
||||
|
||||
LogicalResult SpatScheduledCompute::verify() { return verifyComputeLikeOp(*this, "spat.scheduled_compute"); }
|
||||
|
||||
template <typename ComputeBatchOpTy>
|
||||
LogicalResult verifyComputeBatchLikeOp(ComputeBatchOpTy batch, StringRef opName) {
|
||||
int32_t count = batch.getLaneCount();
|
||||
if (count <= 0)
|
||||
return emitError("laneCount must be positive");
|
||||
return batch.emitOpError("laneCount must be positive");
|
||||
|
||||
auto laneCountSz = static_cast<size_t>(count);
|
||||
|
||||
if (auto coreIdAttr = (*this)->getAttr(kCoreIdsAttrName)) {
|
||||
if (auto coreIdAttr = batch->getAttr(kCoreIdsAttrName)) {
|
||||
auto coreIdsAttr = dyn_cast<DenseI32ArrayAttr>(coreIdAttr);
|
||||
if (!coreIdsAttr)
|
||||
return emitError("compute_batch coreIds attribute must be a dense i32 array");
|
||||
return batch.emitOpError("compute_batch coreIds attribute must be a dense i32 array");
|
||||
if (coreIdsAttr.size() != static_cast<int64_t>(laneCountSz))
|
||||
return emitError("compute_batch coreIds array length must match laneCount");
|
||||
return batch.emitOpError("compute_batch coreIds array length must match laneCount");
|
||||
if (llvm::any_of(coreIdsAttr.asArrayRef(), [](int32_t coreId) { return coreId < 0; }))
|
||||
return emitError("compute_batch coreIds values must be non-negative");
|
||||
return batch.emitOpError("compute_batch coreIds values must be non-negative");
|
||||
DenseSet<int32_t> seenCoreIds;
|
||||
for (int32_t coreId : coreIdsAttr.asArrayRef())
|
||||
if (!seenCoreIds.insert(coreId).second)
|
||||
return emitError("compute_batch coreIds values must be unique");
|
||||
return batch.emitOpError("compute_batch coreIds values must be unique");
|
||||
}
|
||||
|
||||
Block& block = getBody().front();
|
||||
Block& block = batch.getBody().front();
|
||||
if (block.getNumArguments() == 0)
|
||||
return emitError("compute_batch body must have exactly one lane block argument");
|
||||
unsigned expectedArgCount = 1 + getWeights().size() + getInputs().size() + getNumResults();
|
||||
return batch.emitOpError("compute_batch body must have exactly one lane block argument");
|
||||
unsigned expectedArgCount = 1 + batch.getWeights().size() + batch.getInputs().size() + batch.getNumResults();
|
||||
if (block.getNumArguments() != expectedArgCount)
|
||||
return emitError("compute_batch body block arguments must match lane, weight, input, and output operands/results");
|
||||
auto laneArg = getLaneArgument();
|
||||
return batch.emitOpError("compute_batch body block arguments must match lane, weight, input, and output operands/results");
|
||||
auto laneArg = batch.getLaneArgument();
|
||||
if (!laneArg || !laneArg->getType().isIndex())
|
||||
return emitError("compute_batch first block argument must have index type");
|
||||
return batch.emitOpError("compute_batch first block argument must have index type");
|
||||
|
||||
for (auto [weightIndex, weight] : llvm::enumerate(getWeights())) {
|
||||
auto blockArg = getWeightArgument(weightIndex);
|
||||
for (auto [weightIndex, weight] : llvm::enumerate(batch.getWeights())) {
|
||||
auto blockArg = batch.getWeightArgument(weightIndex);
|
||||
if (!blockArg || blockArg->getType() != weight.getType())
|
||||
return emitError("compute_batch weight block argument types must match weight operand types exactly");
|
||||
return batch.emitOpError("compute_batch weight block argument types must match weight operand types exactly");
|
||||
}
|
||||
for (auto [inputIndex, input] : llvm::enumerate(getInputs())) {
|
||||
auto blockArg = getInputArgument(inputIndex);
|
||||
for (auto [inputIndex, input] : llvm::enumerate(batch.getInputs())) {
|
||||
auto blockArg = batch.getInputArgument(inputIndex);
|
||||
if (!blockArg || blockArg->getType() != input.getType())
|
||||
return emitError("compute_batch input block argument types must match input operand types exactly");
|
||||
return batch.emitOpError("compute_batch input block argument types must match input operand types exactly");
|
||||
}
|
||||
for (auto [resultIndex, resultType] : llvm::enumerate(getResultTypes())) {
|
||||
auto blockArg = getOutputArgument(resultIndex);
|
||||
for (auto [resultIndex, resultType] : llvm::enumerate(batch.getResultTypes())) {
|
||||
auto blockArg = batch.getOutputArgument(resultIndex);
|
||||
if (!blockArg || blockArg->getType() != resultType)
|
||||
return emitError("compute_batch output block argument types must match result types exactly");
|
||||
return batch.emitOpError("compute_batch output block argument types must match result types exactly");
|
||||
}
|
||||
|
||||
if (failed(verifyComputeResultsUses(this->getOperation())))
|
||||
if (failed(verifyComputeResultsUses(batch.getOperation())))
|
||||
return failure();
|
||||
if (failed(verifyStaticWeights(*this, "compute_batch")))
|
||||
if (failed(verifyStaticWeights(batch, opName)))
|
||||
return failure();
|
||||
if (failed(verifyOnlyConstantExternalValues(this->getOperation(), getBody(), "spat.compute_batch")))
|
||||
if (failed(verifyOnlyConstantExternalValues(batch.getOperation(), batch.getBody(), opName)))
|
||||
return failure();
|
||||
return verifyBatchBody(*this, block);
|
||||
return verifyBatchBody(batch, block);
|
||||
}
|
||||
|
||||
LogicalResult SpatGraphComputeBatch::verify() { return verifyComputeBatchLikeOp(*this, "spat.graph_compute_batch"); }
|
||||
|
||||
LogicalResult SpatScheduledComputeBatch::verify() {
|
||||
return verifyComputeBatchLikeOp(*this, "spat.scheduled_compute_batch");
|
||||
}
|
||||
|
||||
LogicalResult SpatInParallelOp::verify() {
|
||||
auto batchOp = getOperation()->getParentOfType<SpatComputeBatch>();
|
||||
if (!batchOp)
|
||||
return emitOpError("expected spat.compute_batch parent");
|
||||
if (batchOp.getNumResults() == 0)
|
||||
Operation* parent = getOperation()->getParentOp();
|
||||
if (!isAnySpatialComputeBatchLike(parent))
|
||||
return emitOpError("expected spat.graph_compute_batch or spat.scheduled_compute_batch parent");
|
||||
if (parent->getNumResults() == 0)
|
||||
return emitOpError("requires a resultful spat.compute_batch parent");
|
||||
|
||||
auto laneArg = batchOp.getLaneArgument();
|
||||
std::optional<BlockArgument> laneArg;
|
||||
if (auto graphBatch = dyn_cast<SpatGraphComputeBatch>(parent))
|
||||
laneArg = graphBatch.getLaneArgument();
|
||||
else
|
||||
laneArg = cast<SpatScheduledComputeBatch>(parent).getLaneArgument();
|
||||
if (!laneArg)
|
||||
return emitOpError("expected compute_batch lane block argument");
|
||||
for (Operation& op : getRegion().front().getOperations()) {
|
||||
@@ -494,7 +652,10 @@ LogicalResult SpatInParallelOp::verify() {
|
||||
|
||||
MutableOperandRange destinations = insertSliceOp.getUpdatedDestinations();
|
||||
for (OpOperand& destination : destinations)
|
||||
if (!isBatchOutputArgument(batchOp, destination.get()))
|
||||
if ((isa<SpatGraphComputeBatch>(parent)
|
||||
&& !isBatchOutputArgument(cast<SpatGraphComputeBatch>(parent), destination.get()))
|
||||
|| (isa<SpatScheduledComputeBatch>(parent)
|
||||
&& !isBatchOutputArgument(cast<SpatScheduledComputeBatch>(parent), destination.get())))
|
||||
return op.emitOpError("may only insert into a compute_batch output block argument");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user