From ec242c41d2424d882941f352bf419aa784138b2f Mon Sep 17 00:00:00 2001 From: Johannes Zottele Date: Wed, 13 May 2026 11:53:32 +0200 Subject: [PATCH] iss: Add strategy analysis infrastructure Adds the infrastructure for instruction analysis. It obtains facts about an instruction, and strategy evaluators use them to decide whether its strategy is viable for the given instruction. --- .../infoEnrichers/ViamEnricherCollection.java | 33 +++ .../passes/common/IssExecStrategyPass.java | 80 ------ .../planning/InstructionExecutionPlanner.java | 61 +++++ .../common/planning/IssExecStrategyPass.java | 74 +++++ .../common/planning/StrategyEvaluator.java | 31 +++ .../analysis/VectorAnalysisSupport.java | 233 ++++++++++++++++ .../planning/analysis/VectorCandidate.java | 31 +++ .../analysis/VectorFactExtractor.java | 65 +++++ .../planning/analysis/VectorFactStep.java | 25 ++ .../planning/analysis/VectorFactsBuilder.java | 105 ++++++++ .../analysis/VectorInstructionFacts.java | 196 ++++++++++++++ .../analysis/VectorStrategyIssueCode.java | 41 +++ .../steps/VectorAccessPatternStep.java | 76 ++++++ .../analysis/steps/VectorOperandStep.java | 107 ++++++++ .../analysis/steps/VectorOperationStep.java | 60 +++++ .../analysis/steps/VectorWriteShapeStep.java | 88 ++++++ .../DirectGvecStrategyEvaluator.java | 254 ++++++++++++++++++ .../HelperCallStrategyEvaluator.java | 43 +++ .../TcgScalarStrategyEvaluator.java | 96 +++++++ .../iss/passes/extensions/InstrExecPlan.java | 168 ++++++++++++ .../vadl/iss/passes/extensions/InstrInfo.java | 38 +++ .../passes/extensions/VectorTensorPlan.java | 135 ++++++++++ vadl/main/vadl/pass/order/IssPassOrder.java | 4 +- .../passes/IssVectorTcgAnalysisPassTest.java | 172 ++++++++++++ 24 files changed, 2134 insertions(+), 82 deletions(-) delete mode 100644 vadl/main/vadl/iss/passes/common/IssExecStrategyPass.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/InstructionExecutionPlanner.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/IssExecStrategyPass.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/StrategyEvaluator.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorAnalysisSupport.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorCandidate.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactExtractor.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactStep.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactsBuilder.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorInstructionFacts.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/VectorStrategyIssueCode.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorAccessPatternStep.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperandStep.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperationStep.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorWriteShapeStep.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/evaluators/DirectGvecStrategyEvaluator.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/evaluators/HelperCallStrategyEvaluator.java create mode 100644 vadl/main/vadl/iss/passes/common/planning/evaluators/TcgScalarStrategyEvaluator.java create mode 100644 vadl/main/vadl/iss/passes/extensions/InstrExecPlan.java create mode 100644 vadl/main/vadl/iss/passes/extensions/VectorTensorPlan.java create mode 100644 vadl/test/vadl/iss/passes/IssVectorTcgAnalysisPassTest.java diff --git a/vadl/main/vadl/dump/infoEnrichers/ViamEnricherCollection.java b/vadl/main/vadl/dump/infoEnrichers/ViamEnricherCollection.java index 8c4062049..c2e3f2201 100644 --- a/vadl/main/vadl/dump/infoEnrichers/ViamEnricherCollection.java +++ b/vadl/main/vadl/dump/infoEnrichers/ViamEnricherCollection.java @@ -29,6 +29,8 @@ import vadl.dump.InfoEnricher; import vadl.dump.InfoUtils; import vadl.dump.entities.DefinitionEntity; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrInfo; import vadl.utils.SourceLocation; import vadl.viam.DefProp; import vadl.viam.Encoding; @@ -295,6 +297,36 @@ private static String sourceHeader(SourceLocation location) { entity.addInfo(info); }); + public static InfoEnricher VECTOR_TCG_PLAN_SUPPLIER_TAGS = + forType(DefinitionEntity.class, (entity, passResult) -> { + if (!(entity.origin() instanceof Instruction instruction)) { + return; + } + + var info = instruction.extension(InstrInfo.class); + if (info == null || info.executionPlan() == null) { + return; + } + + var executionPlan = info.executionPlan(); + entity.addInfo(Info.Tag.of("SelectedExecutionStrategy", + executionPlan.selectedStrategy().name())); + + var directGvec = executionPlan.evaluation( + InstrExecPlan.StrategyKind.DIRECT_GVEC); + if (directGvec == null) { + return; + } + + entity.addInfo(Info.Tag.of("DirectGvecStatus", directGvec.status().name())); + entity.addInfo(Info.Tag.of("DirectGvecIssues", + directGvec.issues().isEmpty() + ? "-" + : directGvec.issues().stream() + .map(issue -> issue.code()) + .collect(Collectors.joining(", ")))); + }); + public static InfoEnricher BEHAVIOR_NO_LOCATION_EXPANDABLE = forType(DefinitionEntity.class, (entity, passResult) -> { @@ -405,6 +437,7 @@ private static String sourceHeader(SourceLocation location) { VERIFY_SUPPLIER_EXPANDABLE, SOURCE_CODE_SUPPLIER_EXPANDABLE, RESOURCE_ACCESS_SUPPLIER_EXPANDABLE, + VECTOR_TCG_PLAN_SUPPLIER_TAGS, BEHAVIOR_NO_LOCATION_EXPANDABLE, STAGE_ORDER_SUPPLIER, OPERATION_INSTRUCTIONS_SUPPLIER_EXPANDABLE, diff --git a/vadl/main/vadl/iss/passes/common/IssExecStrategyPass.java b/vadl/main/vadl/iss/passes/common/IssExecStrategyPass.java deleted file mode 100644 index 9eb3a1a88..000000000 --- a/vadl/main/vadl/iss/passes/common/IssExecStrategyPass.java +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText : © 2026 TU Wien -// SPDX-License-Identifier: GPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package vadl.iss.passes.common; - -import static vadl.iss.passes.TcgPassUtils.instrInfo; -import static vadl.iss.passes.TcgPassUtils.regInfo; - -import java.io.IOException; -import javax.annotation.Nullable; -import vadl.configuration.IssConfiguration; -import vadl.iss.passes.AbstractIssPass; -import vadl.iss.passes.extensions.InstrInfo; -import vadl.iss.passes.extensions.RegInfo; -import vadl.iss.passes.nodes.IssReadRegNode; -import vadl.iss.passes.nodes.IssWriteRegNode; -import vadl.pass.PassName; -import vadl.pass.PassResults; -import vadl.viam.Specification; -import vadl.viam.graph.control.ForallNode; -import vadl.viam.graph.dependency.FoldNode; -import vadl.viam.graph.dependency.TensorNode; - -/** - * Computes and stores ISS backend execution strategies for instructions. - * - *

This pass centralizes strategy classification so code generation only consumes the strategy - * and does not infer backend choices ad-hoc.

- */ -public class IssExecStrategyPass extends AbstractIssPass { - - public IssExecStrategyPass(IssConfiguration configuration) { - super(configuration); - } - - @Override - public PassName getName() { - return PassName.of("ISS Exec Strategy Classification"); - } - - @Override - public @Nullable Object execute(PassResults passResults, Specification viam) throws IOException { - if (viam.isa().isEmpty()) { - return null; - } - - var isa = viam.isa().get(); - isa.ownInstructions().forEach(instr -> { - var hasCpuVectorReads = instr.behavior().getNodes(IssReadRegNode.class) - .anyMatch(n -> regInfo(n.regTensor()).execClass() == RegInfo.ExecClass.CPU_VECTOR); - var hasCpuVectorWrites = instr.behavior().getNodes(IssWriteRegNode.class) - .anyMatch(n -> regInfo(n.regTensor()).execClass() == RegInfo.ExecClass.CPU_VECTOR); - - // TODO: Eventually this must be supported by TCG as well - var anyTensorExpr = instr.behavior().getNodes(ForallNode.class).findAny().isPresent() - || instr.behavior().getNodes(TensorNode.class).findAny().isPresent() - || instr.behavior().getNodes(FoldNode.class).findAny().isPresent(); - - var strategy = hasCpuVectorReads || hasCpuVectorWrites || anyTensorExpr - ? InstrInfo.ExecStrategy.HELPER_CALL - : InstrInfo.ExecStrategy.DIRECT_TCG; - - instrInfo(instr).setExecStrategy(strategy); - }); - return null; - } -} diff --git a/vadl/main/vadl/iss/passes/common/planning/InstructionExecutionPlanner.java b/vadl/main/vadl/iss/passes/common/planning/InstructionExecutionPlanner.java new file mode 100644 index 000000000..3e101c5fb --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/InstructionExecutionPlanner.java @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning; + +import java.util.Comparator; +import java.util.List; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyKind; +import vadl.viam.Instruction; + +/** + * Evaluates all configured execution strategies and selects the best viable one. + */ +final class InstructionExecutionPlanner { + + private final List evaluators; + + InstructionExecutionPlanner(List evaluators) { + this.evaluators = List.copyOf(evaluators); + } + + /** + * Plans instruction execution by evaluating all strategies and selecting the best viable one. + */ + InstrExecPlan plan(Instruction instruction) { + var evaluations = evaluators.stream() + .map(evaluator -> evaluator.evaluate(instruction)) + .toList(); + var selected = evaluations.stream() + .filter(StrategyEvaluation::isViable) + .min(Comparator + .comparingInt(StrategyEvaluation::estimatedCost) + .thenComparingInt(evaluation -> strategyPriority(evaluation.strategy()))) + .orElseThrow(() -> new IllegalStateException( + "Expected at least one viable execution strategy for " + instruction.simpleName())); + return new InstrExecPlan(evaluations, selected); + } + + private int strategyPriority(StrategyKind strategy) { + return switch (strategy) { + case DIRECT_GVEC -> 0; + case TCG_SCALAR -> 1; + case HELPER_CALL -> 2; + }; + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/IssExecStrategyPass.java b/vadl/main/vadl/iss/passes/common/planning/IssExecStrategyPass.java new file mode 100644 index 000000000..70b68be51 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/IssExecStrategyPass.java @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning; + +import static vadl.iss.passes.TcgPassUtils.instrInfo; + +import java.io.IOException; +import java.util.List; +import javax.annotation.Nullable; +import vadl.configuration.IssConfiguration; +import vadl.iss.passes.AbstractIssPass; +import vadl.iss.passes.common.planning.evaluators.DirectGvecStrategyEvaluator; +import vadl.iss.passes.common.planning.evaluators.HelperCallStrategyEvaluator; +import vadl.iss.passes.common.planning.evaluators.TcgScalarStrategyEvaluator; +import vadl.pass.PassName; +import vadl.pass.PassResults; +import vadl.viam.Specification; + +/** + * Computes and stores instruction execution plans plus the currently supported backend strategy. + * + *

The selected execution plan is the backend-independent source of truth. The legacy + * {@link vadl.iss.passes.extensions.InstrInfo.ExecStrategy} is derived from the selected plan so + * existing code generation can keep treating unsupported non-scalar strategies as helper calls.

+ */ +public class IssExecStrategyPass extends AbstractIssPass { + + private final InstructionExecutionPlanner executionPlanner; + + public IssExecStrategyPass(IssConfiguration configuration) { + super(configuration); + this.executionPlanner = new InstructionExecutionPlanner(defaultEvaluators()); + } + + @Override + public PassName getName() { + return PassName.of("ISS Exec Strategy Classification"); + } + + @Override + public @Nullable Object execute(PassResults passResults, Specification viam) throws IOException { + if (viam.isa().isEmpty()) { + return null; + } + + var isa = viam.isa().get(); + isa.ownInstructions().forEach( + instr -> instrInfo(instr).setExecutionPlan(executionPlanner.plan(instr)) + ); + return null; + } + + private List defaultEvaluators() { + return List.of( + new DirectGvecStrategyEvaluator(), + new TcgScalarStrategyEvaluator(), + new HelperCallStrategyEvaluator() + ); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/StrategyEvaluator.java b/vadl/main/vadl/iss/passes/common/planning/StrategyEvaluator.java new file mode 100644 index 000000000..dd2667024 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/StrategyEvaluator.java @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning; + +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.viam.Instruction; + +/** + * Evaluates whether one execution strategy can implement a given instruction. + */ +public interface StrategyEvaluator { + + /** + * Evaluates the strategy for the given instruction. + */ + StrategyEvaluation evaluate(Instruction instruction); +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorAnalysisSupport.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorAnalysisSupport.java new file mode 100644 index 000000000..3a45e077d --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorAnalysisSupport.java @@ -0,0 +1,233 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +import static vadl.iss.passes.TcgPassUtils.regInfo; + +import java.util.List; +import javax.annotation.Nullable; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.AccessBaseKind; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.AccessWindowKind; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.BindingFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.LayoutFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperationKind; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.SizeFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.StorageFacts; +import vadl.iss.passes.nodes.IssReadRegNode; +import vadl.iss.passes.nodes.IssWriteRegNode; +import vadl.types.BuiltInTable; +import vadl.viam.graph.Node; +import vadl.viam.graph.dependency.BuiltInCall; +import vadl.viam.graph.dependency.ConstantNode; +import vadl.viam.graph.dependency.ExpressionNode; +import vadl.viam.graph.dependency.ForIdxNode; + +/** + * Shared helper logic for vector-analysis steps. + */ +public final class VectorAnalysisSupport { + + private VectorAnalysisSupport() { + } + + /** + * Returns the read node when the expression is a lowered vector register read. + */ + public static @Nullable IssReadRegNode vectorRead(ExpressionNode node) { + return node instanceof IssReadRegNode read ? read : null; + } + + /** + * Creates a destination binding and strips the loop index from element-access indices. + */ + public static BindingFacts bindingFacts(IssWriteRegNode write, ForIdxNode idx) { + return new BindingFacts( + write.regTensor(), + vectorRegisterAccessorIndices(write.accessorIndices(), idx) + ); + } + + /** + * Creates a source binding and strips the loop index from element-access indices. + */ + public static BindingFacts bindingFacts(IssReadRegNode read, ForIdxNode idx) { + return new BindingFacts( + read.regTensor(), + vectorRegisterAccessorIndices(read.accessorIndices(), idx) + ); + } + + /** + * Removes the per-lane loop index from a lowered vector register access when present. + */ + public static List vectorRegisterAccessorIndices(List indices, + ForIdxNode idx) { + if (!indices.isEmpty() && indices.getLast() == idx) { + return List.copyOf(indices.subList(0, indices.size() - 1)); + } + return List.copyOf(indices); + } + + /** + * Derives vector shape facts from the destination binding and element/lane counts. + */ + public static SizeFacts sizeFacts(BindingFacts destination, int elementBits, int laneCount) { + var oprszBytes = elementBits * laneCount / 8; + var maxszBits = destination.registerTensor() + .resultType(destination.accessorIndices().size()) + .bitWidth(); + var maxszBytes = maxszBits / 8; + return new SizeFacts( + elementBits, + laneCount, + oprszBytes, + maxszBytes + ); + } + + /** + * Checks that the selected vector view is laid out as contiguous element lanes. + */ + public static LayoutFacts layoutFacts(BindingFacts binding, SizeFacts size) { + var dimensions = binding.registerTensor().dimensions(); + var baseIndexCount = binding.accessorIndices().size(); + var contiguousElements = dimensions.size() == baseIndexCount + 2 + && dimensions.get(baseIndexCount).size() == size.laneCount() + && dimensions.getLast().size() == size.elementBits(); + var fullRegisterRange = size.oprszBytes() == size.maxszBytes(); + return new LayoutFacts( + contiguousElements, + fullRegisterRange, + fullRegisterRange + ); + } + + /** + * Derives neutral storage facts for a register access. + */ + public static StorageFacts storageFacts(vadl.viam.RegisterTensor registerTensor) { + var info = regInfo(registerTensor); + return new StorageFacts( + info.execClass(), + info.isGvecCapable(), + registerTensor.totalWidth() % 8 == 0, + info.isCpuVector() ? info.cpuVectorAlignmentBytes() : 0 + ); + } + + /** + * Normalizes access-base classifications across read and write nodes. + */ + public static AccessBaseKind accessBaseKind(IssWriteRegNode.AccessKind accessKind) { + return switch (accessKind) { + case BASE -> AccessBaseKind.BASE; + case ALIAS -> AccessBaseKind.ALIAS; + }; + } + + /** + * Normalizes access-base classifications across read and write nodes. + */ + public static AccessBaseKind accessBaseKind(IssReadRegNode.AccessKind accessKind) { + return switch (accessKind) { + case BASE -> AccessBaseKind.BASE; + case ALIAS -> AccessBaseKind.ALIAS; + }; + } + + /** + * Normalizes access-window classifications across read and write nodes. + */ + public static AccessWindowKind accessWindowKind(IssWriteRegNode.WindowKind windowKind) { + return switch (windowKind) { + case CHUNK -> AccessWindowKind.CHUNK; + case FULL -> AccessWindowKind.FULL; + default -> AccessWindowKind.OTHER; + }; + } + + /** + * Normalizes access-window classifications across read and write nodes. + */ + public static AccessWindowKind accessWindowKind(IssReadRegNode.WindowKind windowKind) { + return switch (windowKind) { + case CHUNK -> AccessWindowKind.CHUNK; + case FULL -> AccessWindowKind.FULL; + default -> AccessWindowKind.OTHER; + }; + } + + /** + * Matches the normalized chunk offset shape {@code idx * elementBits}. + */ + public static boolean isLoopElementOffset(ExpressionNode expr, + ForIdxNode idx, + int elementBits) { + if (!(expr instanceof BuiltInCall call) || call.builtIn() != BuiltInTable.MUL + || call.arguments().size() != 2) { + return false; + } + return matchesLoopMul(call.arg(0), call.arg(1), idx, elementBits) + || matchesLoopMul(call.arg(1), call.arg(0), idx, elementBits); + } + + /** + * Checks whether the final accessor index selects the current loop lane. + */ + public static boolean isFullyIndexedElementAccess(List indices, ForIdxNode idx) { + return !indices.isEmpty() && indices.getLast() == idx; + } + + /** + * Checks whether an expression is an integer constant with the expected value. + */ + public static boolean isConstantInt(ExpressionNode expr, int expected) { + return expr instanceof ConstantNode c && c.constant().asVal().intValue() == expected; + } + + private static boolean matchesLoopMul(Node maybeIdx, + ExpressionNode maybeElementBits, + ForIdxNode idx, + int elementBits) { + return maybeIdx == idx && isConstantInt(maybeElementBits, elementBits); + } + + /** + * Maps supported lowered builtins to the current vector operation enum. + */ + public static OperationKind operationKindOf(BuiltInTable.BuiltIn builtIn) { + if (builtIn == BuiltInTable.ADD) { + return OperationKind.ADD; + } + if (builtIn == BuiltInTable.SUB) { + return OperationKind.SUB; + } + if (builtIn == BuiltInTable.AND) { + return OperationKind.AND; + } + if (builtIn == BuiltInTable.OR) { + return OperationKind.OR; + } + if (builtIn == BuiltInTable.XOR) { + return OperationKind.XOR; + } + if (builtIn == BuiltInTable.MUL) { + return OperationKind.MUL; + } + return OperationKind.OTHER; + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorCandidate.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorCandidate.java new file mode 100644 index 000000000..56121d1ab --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorCandidate.java @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +import vadl.iss.passes.nodes.IssWriteRegNode; +import vadl.viam.graph.dependency.ExpressionNode; +import vadl.viam.graph.dependency.ForIdxNode; + +/** + * The current vector instruction shape under analysis. + */ +public record VectorCandidate( + IssWriteRegNode write, + ExpressionNode valueExpression, + ForIdxNode idx +) { +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactExtractor.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactExtractor.java new file mode 100644 index 000000000..0d709de34 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactExtractor.java @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +import java.util.List; +import vadl.iss.passes.common.planning.analysis.steps.VectorAccessPatternStep; +import vadl.iss.passes.common.planning.analysis.steps.VectorOperandStep; +import vadl.iss.passes.common.planning.analysis.steps.VectorOperationStep; +import vadl.iss.passes.common.planning.analysis.steps.VectorWriteShapeStep; +import vadl.viam.Instruction; + +/** + * Extracts reusable vector facts for one instruction. + * + *

The extracted facts are strategy-neutral. Strategy evaluators decide which of these facts are + * required and which issue codes should reject a specific execution strategy.

+ */ +public final class VectorFactExtractor { + + private final List steps; + + /** + * Creates an extractor with the given ordered fact-extraction steps. + */ + public VectorFactExtractor(List steps) { + this.steps = List.copyOf(steps); + } + + /** + * Creates the default vector fact extractor used by current strategy evaluators. + */ + public static VectorFactExtractor defaultExtractor() { + return new VectorFactExtractor(List.of( + new VectorAccessPatternStep(), + new VectorWriteShapeStep(), + new VectorOperationStep(), + new VectorOperandStep() + )); + } + + /** + * Runs all configured fact-extraction steps for one instruction. + */ + public VectorInstructionFacts extract(Instruction instruction) { + var state = new VectorFactsBuilder(instruction); + for (var step : steps) { + step.extract(state); + } + return state.toFacts(); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactStep.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactStep.java new file mode 100644 index 000000000..c30dca8b4 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactStep.java @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +/** + * A single vector fact-extraction step. + */ +public interface VectorFactStep { + + void extract(VectorFactsBuilder builder); +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactsBuilder.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactsBuilder.java new file mode 100644 index 000000000..aac22a1b4 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorFactsBuilder.java @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.EffectFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.LoopFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperandAccessFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperationFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.WriteAccessFacts; +import vadl.viam.Instruction; + +/** + * Mutable assembly object while extracting vector facts for one instruction. + */ +public final class VectorFactsBuilder { + + private final Instruction instruction; + private int forallCount; + private boolean hasSingleForallRegisterWriteBody; + private int sideEffectCount; + private @Nullable VectorCandidate candidate; + private @Nullable WriteAccessFacts writeFacts; + private @Nullable OperationFacts operationFacts; + private final List operandFacts = new ArrayList<>(); + + public VectorFactsBuilder(Instruction instruction) { + this.instruction = instruction; + } + + public Instruction instruction() { + return instruction; + } + + public void setForallCount(int forallCount) { + this.forallCount = forallCount; + } + + public void setHasSingleForallRegisterWriteBody(boolean hasSingleForallRegisterWriteBody) { + this.hasSingleForallRegisterWriteBody = hasSingleForallRegisterWriteBody; + } + + public void setSideEffectCount(int sideEffectCount) { + this.sideEffectCount = sideEffectCount; + } + + public void setCandidate(VectorCandidate candidate) { + this.candidate = candidate; + } + + public @Nullable VectorCandidate candidate() { + return candidate; + } + + public void setWriteFacts(WriteAccessFacts writeFacts) { + this.writeFacts = writeFacts; + } + + public @Nullable WriteAccessFacts writeFacts() { + return writeFacts; + } + + public void setOperationFacts(OperationFacts operationFacts) { + this.operationFacts = operationFacts; + } + + public @Nullable OperationFacts operationFacts() { + return operationFacts; + } + + public void addOperandFact(OperandAccessFacts operandFact) { + operandFacts.add(operandFact); + } + + /** + * Materializes the immutable vector fact set extracted for this instruction. + */ + public VectorInstructionFacts toFacts() { + return new VectorInstructionFacts( + instruction, + new LoopFacts(forallCount, hasSingleForallRegisterWriteBody), + new EffectFacts(sideEffectCount), + candidate, + writeFacts, + operationFacts, + List.copyOf(operandFacts) + ); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorInstructionFacts.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorInstructionFacts.java new file mode 100644 index 000000000..4cadf5d18 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorInstructionFacts.java @@ -0,0 +1,196 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +import java.util.List; +import javax.annotation.Nullable; +import vadl.iss.passes.extensions.RegInfo; +import vadl.iss.passes.nodes.IssReadRegNode; +import vadl.iss.passes.nodes.IssWriteRegNode; +import vadl.viam.Instruction; +import vadl.viam.RegisterTensor; +import vadl.viam.graph.dependency.BuiltInCall; +import vadl.viam.graph.dependency.ExpressionNode; + +/** + * Reusable vector facts extracted from one instruction. + */ +public record VectorInstructionFacts( + Instruction instruction, + LoopFacts loop, + EffectFacts effects, + @Nullable VectorCandidate candidate, + @Nullable WriteAccessFacts write, + @Nullable OperationFacts operation, + List operands +) { + + public VectorInstructionFacts { + operands = List.copyOf(operands); + } + + /** + * Loop-shape facts extracted from the instruction body. + */ + public record LoopFacts( + int forallCount, + boolean hasSingleForallRegisterWriteBody + ) { + } + + /** + * Side-effect facts extracted from the instruction body. + */ + public record EffectFacts( + int sideEffectCount + ) { + } + + /** + * Write-side facts derived from the lowered destination access. + */ + public record WriteAccessFacts( + IssWriteRegNode write, + AccessBaseKind baseKind, + AccessWindowKind windowKind, + boolean elementShapeMatches, + boolean conditional, + StorageFacts storage, + BindingFacts binding, + LayoutFacts layout, + SizeFacts size, + OverlapFacts overlap + ) { + + public boolean usesSupportedWindowKind() { + return windowKind == AccessWindowKind.CHUNK || windowKind == AccessWindowKind.FULL; + } + } + + /** + * Operation-shape facts derived from the vector body value expression. + */ + public record OperationFacts( + boolean valueIsBuiltInCall, + @Nullable BuiltInCall binaryOperation, + @Nullable OperationKind operationKind + ) { + } + + /** + * Operand-side facts derived from one vector operation argument. + */ + public record OperandAccessFacts( + ExpressionNode expression, + @Nullable IssReadRegNode read, + AccessBaseKind baseKind, + AccessWindowKind windowKind, + boolean elementShapeMatches, + @Nullable StorageFacts storage, + boolean widthMatches, + @Nullable BindingFacts binding + ) { + + public boolean usesSupportedWindowKind() { + return windowKind == AccessWindowKind.CHUNK || windowKind == AccessWindowKind.FULL; + } + } + + /** + * Neutralized base-access classification for lowered register accesses. + */ + public enum AccessBaseKind { + BASE, + ALIAS, + OTHER + } + + /** + * Neutralized window classification for lowered register accesses. + */ + public enum AccessWindowKind { + CHUNK, + FULL, + OTHER + } + + /** + * Neutralized classification of storage properties relevant to later evaluators. + */ + public record StorageFacts( + RegInfo.ExecClass execClass, + boolean envOffsetAddressable, + boolean byteAddressable, + int alignmentBytes + ) { + } + + /** + * Neutralized binding facts for vector register selections. + */ + public record BindingFacts( + RegisterTensor registerTensor, + List accessorIndices + ) { + public BindingFacts { + accessorIndices = List.copyOf(accessorIndices); + } + } + + /** + * Neutralized layout facts derived from the selected vector view. + */ + public record LayoutFacts( + boolean contiguousElements, + boolean fullRegisterRange, + boolean paddingPreserved + ) { + } + + /** + * Neutralized size facts derived from the selected vector view. + */ + public record SizeFacts( + int elementBits, + int laneCount, + int oprszBytes, + int maxszBytes + ) { + } + + /** + * Neutralized overlap proof available from the extracted vector shape. + */ + public enum OverlapFacts { + NOT_ANALYZED, + EXACT_OR_DISJOINT_ONLY, + PARTIAL_POSSIBLE + } + + /** + * Neutralized vector-operation classification. + */ + public enum OperationKind { + ADD, + SUB, + AND, + OR, + XOR, + MUL, + OTHER + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/VectorStrategyIssueCode.java b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorStrategyIssueCode.java new file mode 100644 index 000000000..b5c31d823 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/VectorStrategyIssueCode.java @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis; + +/** + * Rejection reasons for the direct-gvec strategy evaluator. + */ +public enum VectorStrategyIssueCode { + NO_FORALL, + MULTIPLE_FORALLS, + EXTRA_SIDE_EFFECTS, + FORALL_WITHOUT_SINGLE_SIDE_EFFECT, + WRITE_NOT_BASE_CHUNK, + WRITE_HAS_CONDITION, + DESTINATION_NOT_GVEC_CAPABLE, + UNSUPPORTED_ELEMENT_WIDTH, + NON_BYTE_OPERATION_SIZE, + UNSUPPORTED_VALUE_SHAPE, + UNSUPPORTED_OPERATION, + OP_SIZE_NOT_FULL_RANGE, + LAYOUT_NOT_CONTIGUOUS, + OPERAND_NOT_VECTOR_READ, + READ_NOT_BASE_ELEMENT, + READ_NOT_GVEC_CAPABLE, + READ_WIDTH_MISMATCH, + READ_OFFSET_MISMATCH +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorAccessPatternStep.java b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorAccessPatternStep.java new file mode 100644 index 000000000..905f93f84 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorAccessPatternStep.java @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis.steps; + +import java.util.List; +import vadl.iss.passes.common.planning.analysis.VectorCandidate; +import vadl.iss.passes.common.planning.analysis.VectorFactStep; +import vadl.iss.passes.common.planning.analysis.VectorFactsBuilder; +import vadl.iss.passes.nodes.IssWriteRegNode; +import vadl.viam.graph.control.ForallEndNode; +import vadl.viam.graph.control.ForallNode; +import vadl.viam.graph.dependency.SideEffectNode; + +/** + * Detects the current vector instruction shape. + */ +public final class VectorAccessPatternStep implements VectorFactStep { + + @Override + public void extract(VectorFactsBuilder builder) { + var graph = builder.instruction().behavior(); + // Later evaluators want loop and side-effect counts even for rejected shapes so they can + // explain a rejection without re-walking the graph. + var foralls = graph.getNodes(ForallNode.class).toList(); + builder.setForallCount(foralls.size()); + builder.setSideEffectCount(graph.getNodes(SideEffectNode.class).toList().size()); + analyzeForallPattern(builder, foralls); + } + + private void analyzeForallPattern(VectorFactsBuilder builder, List foralls) { + // No forall means there is no lane-structured vector body for later strategies to reason about. + if (foralls.isEmpty()) { + builder.setHasSingleForallRegisterWriteBody(false); + return; + } + + // The first vector strategies only know how to reason about one elementwise vector write. + var forallEnds = builder.instruction().behavior().getNodes(ForallEndNode.class).toList(); + if (forallEnds.size() != 1 || forallEnds.getFirst().sideEffects().size() != 1) { + builder.setHasSingleForallRegisterWriteBody(false); + return; + } + + var sideEffect = forallEnds.getFirst().sideEffects().getFirst(); + // A non-register side effect does not expose the destination binding later strategies need for + // offset, layout, and overlap reasoning. + if (!(sideEffect instanceof IssWriteRegNode write)) { + builder.setHasSingleForallRegisterWriteBody(false); + return; + } + + // Candidate extraction stays intentionally minimal: later steps and evaluators decide which + // write shape, storage kind, and operation forms a strategy can actually accept. + builder.setHasSingleForallRegisterWriteBody(true); + var idx = foralls.getFirst().idx(); + builder.setCandidate(new VectorCandidate( + write, + write.value(), + idx + )); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperandStep.java b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperandStep.java new file mode 100644 index 000000000..248f721c0 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperandStep.java @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis.steps; + +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.accessBaseKind; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.accessWindowKind; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.bindingFacts; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isConstantInt; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isFullyIndexedElementAccess; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isLoopElementOffset; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.storageFacts; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.vectorRead; + +import javax.annotation.Nullable; +import vadl.iss.passes.common.planning.analysis.VectorFactStep; +import vadl.iss.passes.common.planning.analysis.VectorFactsBuilder; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperandAccessFacts; +import vadl.iss.passes.nodes.IssReadRegNode; + +/** + * Extracts operand-side access and binding facts from the matched vector operation. + */ +public final class VectorOperandStep implements VectorFactStep { + + @Override + public void extract(VectorFactsBuilder builder) { + var candidate = builder.candidate(); + var writeFacts = builder.writeFacts(); + var operationFacts = builder.operationFacts(); + var operationCall = operationFacts == null ? null : operationFacts.binaryOperation(); + if (candidate == null || writeFacts == null || operationCall == null) { + return; + } + + for (var arg : operationCall.arguments()) { + // Each operand is recorded independently so different strategies can make different decisions + // about vector, scalar, immediate, alias, or broadcast forms from the same fact set. + var read = vectorRead(arg); + builder.addOperandFact(operandFact( + arg, + read, + candidate.idx(), + writeFacts.size().elementBits() + )); + } + } + + private OperandAccessFacts operandFact(vadl.viam.graph.dependency.ExpressionNode expression, + @Nullable IssReadRegNode read, + vadl.viam.graph.dependency.ForIdxNode idx, + int elementBits) { + // Non-register operands are kept as explicit facts rather than dropped so evaluators can + // distinguish scalar/immediate rejection from "no analysis result". + if (read == null) { + return new OperandAccessFacts( + expression, + null, + vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.AccessBaseKind.OTHER, + vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.AccessWindowKind.OTHER, + false, + null, + false, + null + ); + } + + return new OperandAccessFacts( + expression, + read, + accessBaseKind(read.accessKind()), + accessWindowKind(read.windowKind()), + matchesElementShape(read, idx, elementBits), + storageFacts(read.regTensor()), + read.readBitWidth() == elementBits, + bindingFacts(read, idx) + ); + } + + private boolean matchesElementShape(IssReadRegNode read, + vadl.viam.graph.dependency.ForIdxNode idx, + int elementBits) { + // Read-side shape matching mirrors the write side so later evaluators can reason about exact + // lane correspondence without duplicating offset decoding. + if (read.windowKind() == IssReadRegNode.WindowKind.CHUNK) { + return isLoopElementOffset(read.bitOffset(), idx, elementBits) + && isConstantInt(read.bitWidth(), elementBits); + } + if (read.windowKind() == IssReadRegNode.WindowKind.FULL) { + return isFullyIndexedElementAccess(read.accessorIndices(), idx); + } + return false; + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperationStep.java b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperationStep.java new file mode 100644 index 000000000..5086cb420 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorOperationStep.java @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis.steps; + +import javax.annotation.Nullable; +import vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport; +import vadl.iss.passes.common.planning.analysis.VectorFactStep; +import vadl.iss.passes.common.planning.analysis.VectorFactsBuilder; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperationFacts; +import vadl.viam.graph.dependency.BuiltInCall; +import vadl.viam.graph.dependency.ExpressionNode; + +/** + * Extracts operation-shape facts from the vector body value expression. + */ +public final class VectorOperationStep implements VectorFactStep { + + @Override + public void extract(VectorFactsBuilder builder) { + var candidate = builder.candidate(); + if (candidate == null) { + return; + } + + // The fact layer records the operation shape without deciding yet whether any concrete + // strategy can lower it directly. + var valueIsBuiltInCall = candidate.valueExpression() instanceof BuiltInCall; + var operationCall = binaryOperation(candidate.valueExpression()); + builder.setOperationFacts(new OperationFacts( + valueIsBuiltInCall, + operationCall, + operationCall == null + ? null + : VectorAnalysisSupport.operationKindOf(operationCall.builtIn()) + )); + } + + private @Nullable BuiltInCall binaryOperation(ExpressionNode node) { + // The first vector strategies only reason about binary lane-local expressions. Broader shapes + // still flow through the fact model as rejected operation forms. + if (!(node instanceof BuiltInCall call) || call.arguments().size() != 2) { + return null; + } + return call; + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorWriteShapeStep.java b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorWriteShapeStep.java new file mode 100644 index 000000000..87533c5a4 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/analysis/steps/VectorWriteShapeStep.java @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.analysis.steps; + +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.accessBaseKind; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.accessWindowKind; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.bindingFacts; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isConstantInt; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isFullyIndexedElementAccess; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.isLoopElementOffset; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.layoutFacts; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.sizeFacts; +import static vadl.iss.passes.common.planning.analysis.VectorAnalysisSupport.storageFacts; + +import vadl.iss.passes.common.planning.analysis.VectorFactStep; +import vadl.iss.passes.common.planning.analysis.VectorFactsBuilder; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OverlapFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.WriteAccessFacts; +import vadl.iss.passes.nodes.IssWriteRegNode; + +/** + * Extracts write-side properties plus destination binding and shape facts. + */ +public final class VectorWriteShapeStep implements VectorFactStep { + + @Override + public void extract(VectorFactsBuilder builder) { + var candidate = builder.candidate(); + if (candidate == null) { + return; + } + + var elementBits = candidate.write().writeBitWidth(); + var laneCount = candidate.idx().toIdx() - candidate.idx().fromIdx() + 1; + // Binding extraction strips the lane index so later evaluators see the selected vector + // register rather than one per-lane access inside the lowered loop body. + var destination = bindingFacts(candidate.write(), candidate.idx()); + // Size and layout remain strategy-neutral here so later strategies can reuse them with + // different acceptance rules. + var size = sizeFacts(destination, elementBits, laneCount); + var layout = layoutFacts(destination, size); + var write = candidate.write(); + builder.setWriteFacts(new WriteAccessFacts( + write, + accessBaseKind(write.accessKind()), + accessWindowKind(write.windowKind()), + matchesElementShape(write, candidate.idx(), elementBits), + write.nullableCondition() != null, + storageFacts(write.regTensor()), + destination, + layout, + size, + // The extracted shape is a full-lane overwrite where equal register selectors overlap + // exactly and distinct selectors are disjoint. + OverlapFacts.EXACT_OR_DISJOINT_ONLY + )); + } + + private boolean matchesElementShape(IssWriteRegNode write, + vadl.viam.graph.dependency.ForIdxNode idx, + int elementBits) { + // Chunk windows are the normalized scalarized form of one lane inside a vector register. + if (write.windowKind() == IssWriteRegNode.WindowKind.CHUNK) { + return isLoopElementOffset(write.bitOffset(), idx, elementBits) + && isConstantInt(write.bitWidth(), elementBits); + } + // Full windows are still usable as long as the final accessor index selects the active lane. + if (write.windowKind() == IssWriteRegNode.WindowKind.FULL) { + return isFullyIndexedElementAccess(write.accessorIndices(), idx) + && write.writeBitWidth() == elementBits; + } + return false; + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/evaluators/DirectGvecStrategyEvaluator.java b/vadl/main/vadl/iss/passes/common/planning/evaluators/DirectGvecStrategyEvaluator.java new file mode 100644 index 000000000..011b249ef --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/evaluators/DirectGvecStrategyEvaluator.java @@ -0,0 +1,254 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.evaluators; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import vadl.iss.passes.common.planning.StrategyEvaluator; +import vadl.iss.passes.common.planning.analysis.VectorFactExtractor; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.AccessBaseKind; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.BindingFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.LayoutFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OperationKind; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.OverlapFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.SizeFacts; +import vadl.iss.passes.common.planning.analysis.VectorInstructionFacts.WriteAccessFacts; +import vadl.iss.passes.common.planning.analysis.VectorStrategyIssueCode; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyIssue; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyKind; +import vadl.iss.passes.extensions.VectorTensorPlan; +import vadl.iss.passes.extensions.VectorTensorPlan.OverlapPolicy; +import vadl.iss.passes.extensions.VectorTensorPlan.VectorOperand; +import vadl.iss.passes.extensions.VectorTensorPlan.VectorRegisterBinding; +import vadl.iss.passes.extensions.VectorTensorPlan.VectorShape; +import vadl.viam.Instruction; + +/** + * Evaluates the direct-gvec strategy for vector instructions. + */ +public final class DirectGvecStrategyEvaluator implements StrategyEvaluator { + + private static final int DIRECT_GVEC_ESTIMATED_COST = 10; + + private final VectorFactExtractor factExtractor = VectorFactExtractor.defaultExtractor(); + + @Override + public StrategyEvaluation evaluate(Instruction instruction) { + var facts = factExtractor.extract(instruction); + var issues = new ArrayList(); + + // Strategy evaluation is additive: each check only rejects DIRECT_GVEC, not the whole + // instruction. Other strategies still get the same neutral fact set. + recordGeneralShapeIssues(facts, issues); + + var candidate = facts.candidate(); + if (candidate == null) { + return rejected(issues); + } + + recordWriteIssues(facts.write(), issues); + + var operationFacts = facts.operation(); + var vectorOp = evaluateOperation(operationFacts, issues); + + var operands = recordOperandIssues(facts, issues); + var writeFacts = facts.write(); + if (!issues.isEmpty() || writeFacts == null || vectorOp == null) { + return rejected(issues); + } + + // The direct-gvec plan is assembled only after all direct-gvec-specific preconditions hold. + return InstrExecPlan.StrategyEvaluation.viable( + StrategyKind.DIRECT_GVEC, + DIRECT_GVEC_ESTIMATED_COST, + VectorTensorPlan.directGvecCandidate( + vectorOp, + vectorShape(writeFacts.size(), writeFacts.layout()), + vectorRegisterBinding(writeFacts.binding()), + overlapPolicy(writeFacts.overlap()), + operands + ) + ); + } + + private void recordGeneralShapeIssues(VectorInstructionFacts facts, + List issues) { + // Direct gvec starts from one normalized vector loop. More complex loop structures may still + // be usable by other vector strategies later. + if (facts.loop().forallCount() == 0) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.NO_FORALL)); + return; + } + if (facts.loop().forallCount() != 1) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.MULTIPLE_FORALLS)); + } + if (facts.effects().sideEffectCount() != 1) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.EXTRA_SIDE_EFFECTS)); + } + if (!facts.loop().hasSingleForallRegisterWriteBody()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.FORALL_WITHOUT_SINGLE_SIDE_EFFECT)); + } + } + + private @Nullable VectorTensorPlan.VectorOp evaluateOperation( + @Nullable VectorInstructionFacts.OperationFacts operationFacts, + List issues) { + // Distinguish "not a binary builtin shape" from "binary builtin but not one of the currently + // mapped direct-gvec ops" so diagnostics stay useful as more strategies are added. + if (operationFacts == null || operationFacts.binaryOperation() == null) { + if (operationFacts != null && operationFacts.valueIsBuiltInCall()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.UNSUPPORTED_OPERATION)); + } else { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.UNSUPPORTED_VALUE_SHAPE)); + } + return null; + } + + var vectorOp = vectorOpOf(operationFacts.operationKind()); + if (vectorOp == null) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.UNSUPPORTED_OPERATION)); + } + return vectorOp; + } + + private void recordWriteIssues(@Nullable WriteAccessFacts writeFacts, + List issues) { + if (writeFacts == null) { + return; + } + + var sizeFacts = writeFacts.size(); + // Direct gvec currently assumes byte-addressable lane sizes with one full vector operation + // region. Smaller or irregular shapes remain available to other strategies. + if (sizeFacts.elementBits() <= 0 + || sizeFacts.laneCount() <= 0 + || sizeFacts.elementBits() > 64) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.UNSUPPORTED_ELEMENT_WIDTH)); + } + if ((sizeFacts.elementBits() * sizeFacts.laneCount()) % 8 != 0) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.NON_BYTE_OPERATION_SIZE)); + } + + // Direct gvec needs a destination access that can be translated into a stable env offset plus + // a plain contiguous vector region. + if (writeFacts.baseKind() != AccessBaseKind.BASE || !writeFacts.usesSupportedWindowKind()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.WRITE_NOT_BASE_CHUNK)); + } else if (!writeFacts.elementShapeMatches()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.WRITE_NOT_BASE_CHUNK)); + } + if (writeFacts.conditional()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.WRITE_HAS_CONDITION)); + } + if (!writeFacts.storage().envOffsetAddressable()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.DESTINATION_NOT_GVEC_CAPABLE)); + } + if (!writeFacts.layout().fullRegisterRange()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.OP_SIZE_NOT_FULL_RANGE)); + } + if (!writeFacts.layout().contiguousElements()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.LAYOUT_NOT_CONTIGUOUS)); + } + } + + private List recordOperandIssues(VectorInstructionFacts facts, + List issues) { + var operands = new ArrayList(); + for (var operandFacts : facts.operands()) { + // Direct gvec currently only accepts vector-register operands. Keeping the extracted operand + // facts explicit lets later strategies reuse the same analysis for scalar or immediate forms. + var read = operandFacts.read(); + if (read == null) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.OPERAND_NOT_VECTOR_READ)); + continue; + } + if (operandFacts.baseKind() != AccessBaseKind.BASE + || !operandFacts.usesSupportedWindowKind()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.READ_NOT_BASE_ELEMENT)); + } else if (!operandFacts.elementShapeMatches()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.READ_OFFSET_MISMATCH)); + } + var storageFacts = operandFacts.storage(); + if (storageFacts == null || !storageFacts.envOffsetAddressable()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.READ_NOT_GVEC_CAPABLE)); + } + if (!operandFacts.widthMatches()) { + issues.add(StrategyIssue.of(VectorStrategyIssueCode.READ_WIDTH_MISMATCH)); + } + if (operandFacts.binding() != null) { + operands.add(VectorOperand.vectorRegister(vectorRegisterBinding(operandFacts.binding()))); + } + } + return List.copyOf(operands); + } + + private @Nullable VectorTensorPlan.VectorOp vectorOpOf(@Nullable OperationKind operationKind) { + if (operationKind == null) { + return null; + } + // The neutral operation kind is translated to a direct-gvec-specific opcode only here. + return switch (operationKind) { + case ADD -> VectorTensorPlan.VectorOp.ADD; + case SUB -> VectorTensorPlan.VectorOp.SUB; + case AND -> VectorTensorPlan.VectorOp.AND; + case OR -> VectorTensorPlan.VectorOp.OR; + case XOR -> VectorTensorPlan.VectorOp.XOR; + case MUL -> VectorTensorPlan.VectorOp.MUL; + case OTHER -> null; + }; + } + + private VectorRegisterBinding vectorRegisterBinding(BindingFacts bindingFacts) { + return new VectorRegisterBinding( + bindingFacts.registerTensor(), + bindingFacts.accessorIndices() + ); + } + + private VectorShape vectorShape(SizeFacts sizeFacts, LayoutFacts layoutFacts) { + return new VectorShape( + sizeFacts.elementBits(), + sizeFacts.laneCount(), + sizeFacts.oprszBytes(), + sizeFacts.maxszBytes(), + layoutFacts.fullRegisterRange(), + layoutFacts.contiguousElements(), + layoutFacts.paddingPreserved() + ); + } + + private OverlapPolicy overlapPolicy(OverlapFacts overlapFacts) { + // Strategy-neutral overlap facts are mapped to the narrower direct-gvec overlap policy only + // when this evaluator commits to a direct-gvec plan. + return switch (overlapFacts) { + case NOT_ANALYZED -> OverlapPolicy.NOT_ANALYZED; + case EXACT_OR_DISJOINT_ONLY -> OverlapPolicy.NO_PARTIAL_OVERLAP; + case PARTIAL_POSSIBLE -> OverlapPolicy.NOT_ANALYZED; + }; + } + + private StrategyEvaluation rejected(List issues) { + return InstrExecPlan.StrategyEvaluation.rejected( + StrategyKind.DIRECT_GVEC, + DIRECT_GVEC_ESTIMATED_COST, + List.copyOf(issues) + ); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/evaluators/HelperCallStrategyEvaluator.java b/vadl/main/vadl/iss/passes/common/planning/evaluators/HelperCallStrategyEvaluator.java new file mode 100644 index 000000000..7dc6e8263 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/evaluators/HelperCallStrategyEvaluator.java @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.evaluators; + +import vadl.iss.passes.common.planning.StrategyEvaluator; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrExecPlan.HelperCallPlan; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyKind; +import vadl.viam.Instruction; + +/** + * Fallback evaluator that always keeps whole-instruction helper execution available. + */ +public final class HelperCallStrategyEvaluator implements StrategyEvaluator { + + private static final int HELPER_CALL_ESTIMATED_COST = 1_000; + + @Override + public StrategyEvaluation evaluate(Instruction instruction) { + // Whole-instruction helper execution stays always viable so the planner can reject optimized + // strategies locally without losing a correct fallback. + return InstrExecPlan.StrategyEvaluation.viable( + StrategyKind.HELPER_CALL, + HELPER_CALL_ESTIMATED_COST, + new HelperCallPlan() + ); + } +} diff --git a/vadl/main/vadl/iss/passes/common/planning/evaluators/TcgScalarStrategyEvaluator.java b/vadl/main/vadl/iss/passes/common/planning/evaluators/TcgScalarStrategyEvaluator.java new file mode 100644 index 000000000..9b8e161f0 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/planning/evaluators/TcgScalarStrategyEvaluator.java @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common.planning.evaluators; + +import static vadl.iss.passes.TcgPassUtils.regInfo; + +import java.util.ArrayList; +import java.util.List; +import vadl.iss.passes.common.planning.StrategyEvaluator; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyIssue; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyKind; +import vadl.iss.passes.extensions.InstrExecPlan.TcgScalarPlan; +import vadl.iss.passes.extensions.RegInfo; +import vadl.iss.passes.nodes.IssReadRegNode; +import vadl.iss.passes.nodes.IssWriteRegNode; +import vadl.viam.Instruction; +import vadl.viam.graph.control.ForallNode; +import vadl.viam.graph.dependency.FoldNode; +import vadl.viam.graph.dependency.TensorNode; + +/** + * Evaluates whether the existing scalar TCG pipeline can lower the instruction directly. + */ +public final class TcgScalarStrategyEvaluator implements StrategyEvaluator { + + private static final int TCG_SCALAR_ESTIMATED_COST = 100; + + @Override + public StrategyEvaluation evaluate(Instruction instruction) { + var issues = new ArrayList(); + var behavior = instruction.behavior(); + + // Scalar TCG is treated as the existing baseline path. It rejects instructions only when the + // current scalar lowering pipeline would have to rediscover vector/tensor semantics it does + // not model directly. + var hasCpuVectorReads = behavior.getNodes(IssReadRegNode.class) + .anyMatch(node -> regInfo(node.regTensor()).execClass() == RegInfo.ExecClass.CPU_VECTOR); + var hasCpuVectorWrites = behavior.getNodes(IssWriteRegNode.class) + .anyMatch(node -> regInfo(node.regTensor()).execClass() == RegInfo.ExecClass.CPU_VECTOR); + var hasForall = behavior.getNodes(ForallNode.class).findAny().isPresent(); + var hasTensor = behavior.getNodes(TensorNode.class).findAny().isPresent(); + if (hasCpuVectorReads || hasCpuVectorWrites) { + issues.add(StrategyIssue.of(IssueCode.USES_CPU_VECTOR_STORAGE)); + } + if (hasForall) { + issues.add(StrategyIssue.of(IssueCode.HAS_FORALL)); + } + if (hasTensor) { + issues.add(StrategyIssue.of(IssueCode.HAS_TENSOR_EXPR)); + } + var hasFold = behavior.getNodes(FoldNode.class).findAny().isPresent(); + if (hasFold) { + issues.add(StrategyIssue.of(IssueCode.HAS_FOLD)); + } + + if (!issues.isEmpty()) { + return InstrExecPlan.StrategyEvaluation.rejected( + StrategyKind.TCG_SCALAR, + TCG_SCALAR_ESTIMATED_COST, + List.copyOf(issues) + ); + } + + return InstrExecPlan.StrategyEvaluation.viable( + StrategyKind.TCG_SCALAR, + TCG_SCALAR_ESTIMATED_COST, + new TcgScalarPlan() + ); + } + + /** + * Rejection reasons for the current scalar TCG pipeline. + */ + public enum IssueCode { + USES_CPU_VECTOR_STORAGE, + HAS_FORALL, + HAS_TENSOR_EXPR, + HAS_FOLD + } +} diff --git a/vadl/main/vadl/iss/passes/extensions/InstrExecPlan.java b/vadl/main/vadl/iss/passes/extensions/InstrExecPlan.java new file mode 100644 index 000000000..193b45478 --- /dev/null +++ b/vadl/main/vadl/iss/passes/extensions/InstrExecPlan.java @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.extensions; + +import java.util.List; +import javax.annotation.Nullable; + +/** + * General execution-planning result for one instruction. + * + *

It keeps all evaluated execution strategies, their rejection reasons, and the selected + * best-performing surviving strategy.

+ */ +public record InstrExecPlan( + List evaluations, + StrategyEvaluation selectedEvaluation +) { + + public InstrExecPlan { + evaluations = List.copyOf(evaluations); + } + + /** + * Returns the selected execution strategy. + */ + public StrategyKind selectedStrategy() { + return selectedEvaluation.strategy(); + } + + /** + * Returns the evaluation for the requested strategy, if present. + */ + public @Nullable StrategyEvaluation evaluation(StrategyKind strategy) { + return evaluations.stream() + .filter(evaluation -> evaluation.strategy() == strategy) + .findFirst() + .orElse(null); + } + + /** + * Backend-independent execution strategy kinds. + */ + public enum StrategyKind { + TCG_SCALAR, + DIRECT_GVEC, + HELPER_CALL + } + + /** + * Outcome of evaluating one candidate execution strategy. + */ + public enum EvaluationStatus { + VIABLE, + REJECTED + } + + /** + * A diagnostic issue recorded while evaluating one strategy. + */ + public record StrategyIssue(String code) { + + /** + * Creates a strategy issue from an enum constant. + */ + public static StrategyIssue of(Enum issueCode) { + return new StrategyIssue(issueCode.name()); + } + } + + /** + * One strategy evaluation including viability, issues, and the concrete strategy plan. + */ + public record StrategyEvaluation( + StrategyKind strategy, + EvaluationStatus status, + int estimatedCost, + List issues, + @Nullable StrategyPlan plan + ) { + + public StrategyEvaluation { + issues = List.copyOf(issues); + } + + /** + * Creates a viable strategy evaluation. + */ + public static StrategyEvaluation viable(StrategyKind strategy, + int estimatedCost, + StrategyPlan plan) { + return new StrategyEvaluation( + strategy, + EvaluationStatus.VIABLE, + estimatedCost, + List.of(), + plan + ); + } + + /** + * Creates a rejected strategy evaluation. + */ + public static StrategyEvaluation rejected(StrategyKind strategy, + int estimatedCost, + List issues) { + return new StrategyEvaluation( + strategy, + EvaluationStatus.REJECTED, + estimatedCost, + issues, + null + ); + } + + /** + * Returns whether this strategy survived evaluation. + */ + public boolean isViable() { + return status == EvaluationStatus.VIABLE; + } + + /** + * Returns whether this evaluation recorded the given issue code. + */ + public boolean hasIssue(String code) { + return issues.stream().anyMatch(issue -> issue.code().equals(code)); + } + + /** + * Returns the plan cast to the requested type, if it matches. + */ + public @Nullable T planAs(Class planType) { + return planType.isInstance(plan) ? planType.cast(plan) : null; + } + } + + /** + * Marker interface for concrete execution-strategy plans. + */ + public sealed interface StrategyPlan permits HelperCallPlan, TcgScalarPlan, VectorTensorPlan { + } + + /** + * Selected when the existing scalar TCG pipeline can lower the instruction directly. + */ + public record TcgScalarPlan() implements StrategyPlan { + } + + /** + * Selected when the instruction must still execute as a whole-instruction helper call. + */ + public record HelperCallPlan() implements StrategyPlan { + } +} diff --git a/vadl/main/vadl/iss/passes/extensions/InstrInfo.java b/vadl/main/vadl/iss/passes/extensions/InstrInfo.java index 11dcad78f..e57ce2198 100644 --- a/vadl/main/vadl/iss/passes/extensions/InstrInfo.java +++ b/vadl/main/vadl/iss/passes/extensions/InstrInfo.java @@ -57,6 +57,9 @@ public enum ExecStrategy { @Nullable private ExecStrategy execStrategy = null; + @Nullable + private InstrExecPlan executionPlan = null; + List extractedFunctions = new ArrayList<>(); /** @@ -84,7 +87,36 @@ public void setExecStrategy(ExecStrategy execStrategy) { this.execStrategy = execStrategy; } + /** + * Returns the execution plan computed for this instruction, if any. + */ + public @Nullable InstrExecPlan executionPlan() { + return executionPlan; + } + + /** + * Stores the execution plan computed for this instruction. + */ + public void setExecutionPlan(InstrExecPlan executionPlan) { + this.executionPlan = executionPlan; + this.execStrategy = mapExecutionPlanToExecStrategy(executionPlan); + } + + /** + * Returns the selected direct-gvec plan for this instruction, if any. + */ + public @Nullable VectorTensorPlan directGvecPlan() { + if (executionPlan == null) { + return null; + } + var evaluation = executionPlan.evaluation(InstrExecPlan.StrategyKind.DIRECT_GVEC); + return evaluation == null ? null : evaluation.planAs(VectorTensorPlan.class); + } + private ExecStrategy computeFallbackExecStrategy() { + if (executionPlan != null) { + return mapExecutionPlanToExecStrategy(executionPlan); + } var hasCpuVectorReads = instr().behavior().getNodes(IssReadRegNode.class) .anyMatch(n -> regInfo(n.regTensor()).execClass() == RegInfo.ExecClass.CPU_VECTOR); var hasCpuVectorWrites = instr().behavior().getNodes(IssWriteRegNode.class) @@ -94,6 +126,12 @@ private ExecStrategy computeFallbackExecStrategy() { : ExecStrategy.DIRECT_TCG; } + private ExecStrategy mapExecutionPlanToExecStrategy(InstrExecPlan executionPlan) { + return executionPlan.selectedStrategy() == InstrExecPlan.StrategyKind.TCG_SCALAR + ? ExecStrategy.DIRECT_TCG + : ExecStrategy.HELPER_CALL; + } + /** * Determines if the instruction's loops should be unrolled. */ diff --git a/vadl/main/vadl/iss/passes/extensions/VectorTensorPlan.java b/vadl/main/vadl/iss/passes/extensions/VectorTensorPlan.java new file mode 100644 index 000000000..4d5521eb9 --- /dev/null +++ b/vadl/main/vadl/iss/passes/extensions/VectorTensorPlan.java @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.extensions; + +import java.util.List; +import javax.annotation.Nullable; +import vadl.viam.RegisterTensor; +import vadl.viam.graph.dependency.ExpressionNode; + +/** + * Diagnostic ISS vector translation plan. + * + *

It stores instruction properties that are important to generate optimized QEMU vector + * instruction execution. + */ +public record VectorTensorPlan( + VectorOp op, + VectorShape shape, + VectorRegisterBinding destination, + OverlapPolicy overlapPolicy, + List operands +) implements InstrExecPlan.StrategyPlan { + + /** + * Creates a direct-gvec candidate plan. + */ + public static VectorTensorPlan directGvecCandidate(VectorOp op, + VectorShape shape, + VectorRegisterBinding destination, + OverlapPolicy overlapPolicy, + List operands) { + return new VectorTensorPlan( + op, + shape, + destination, + overlapPolicy, + List.copyOf(operands) + ); + } + + public int elementBits() { + return shape.elementBits(); + } + + public int laneCount() { + return shape.laneCount(); + } + + public int opBytes() { + return shape.oprszBytes(); + } + + /** + * Vector operation recognized by the first matcher. + */ + public enum VectorOp { + NONE, + ADD, + SUB, + AND, + OR, + XOR, + MUL + } + + /** + * Proven vector operation shape and size/layout facts for direct-gvec candidates. + */ + public record VectorShape( + int elementBits, + int laneCount, + int oprszBytes, + int maxszBytes, + boolean fullRange, + boolean contiguousLayout, + boolean paddingPreserved + ) { + } + + /** + * Register binding that preserves the accessor indices needed for future offset emission. + */ + public record VectorRegisterBinding( + RegisterTensor registerTensor, + List accessorIndices + ) { + public VectorRegisterBinding { + accessorIndices = List.copyOf(accessorIndices); + } + } + + /** + * Operand binding for recognized vector plans. + */ + public record VectorOperand(OperandKind kind, + @Nullable VectorRegisterBinding registerBinding) { + + public static VectorOperand vectorRegister(VectorRegisterBinding binding) { + return new VectorOperand(OperandKind.VECTOR_REGISTER, binding); + } + } + + /** + * Operand categories expected by later vector planning. + */ + public enum OperandKind { + VECTOR_REGISTER, + SCALAR_REGISTER, + IMMEDIATE, + BROADCAST, + PREDICATE + } + + /** + * Register-overlap proof available to a future direct-gvec emitter. + */ + public enum OverlapPolicy { + NOT_ANALYZED, + NO_PARTIAL_OVERLAP + } +} diff --git a/vadl/main/vadl/pass/order/IssPassOrder.java b/vadl/main/vadl/pass/order/IssPassOrder.java index a0c6ff110..5e643db4f 100644 --- a/vadl/main/vadl/pass/order/IssPassOrder.java +++ b/vadl/main/vadl/pass/order/IssPassOrder.java @@ -29,7 +29,6 @@ import vadl.iss.passes.common.IssBuiltInArgTruncOptPass; import vadl.iss.passes.common.IssCommonExprSavePass; import vadl.iss.passes.common.IssConfigurationPass; -import vadl.iss.passes.common.IssExecStrategyPass; import vadl.iss.passes.common.IssExtractOptimizationPass; import vadl.iss.passes.common.IssGdbInfoExtractionPass; import vadl.iss.passes.common.IssInfoRetrievalPass; @@ -42,6 +41,7 @@ import vadl.iss.passes.common.IssScheduleIndirectJumpsPass; import vadl.iss.passes.common.IssTensorAssignmentToForallPass; import vadl.iss.passes.common.opDecomposition.IssOpDecompositionPass; +import vadl.iss.passes.common.planning.IssExecStrategyPass; import vadl.iss.passes.common.safeResourceRead.IssSafeResourceReadPass; import vadl.iss.passes.helper.IssCFunctionExtractionPass; import vadl.iss.passes.scalar.IssHardcodedTcgAddOnPass; @@ -118,10 +118,10 @@ private static void addCommonPasses(PassOrder order, IssConfiguration config) { .add(new IssApplyMemoryEndiannessPass(config)) .add(new IssMemoryDetectionPass(config)) .add(new IssRegisterAccessLoweringPass(config)) - .add(new IssExecStrategyPass(config)) .add(new IssBitfieldWriteLoweringPass(config)) .add(new IssTensorAssignmentToForallPass(config)) .add(new CanonicalizationPass(config)) + .add(new IssExecStrategyPass(config)) .add(new IssOpDecompositionPass(config)) .add(new IssNormalizationPass(config)) .add(new IssExtractOptimizationPass(config)) diff --git a/vadl/test/vadl/iss/passes/IssVectorTcgAnalysisPassTest.java b/vadl/test/vadl/iss/passes/IssVectorTcgAnalysisPassTest.java new file mode 100644 index 000000000..717fea8a0 --- /dev/null +++ b/vadl/test/vadl/iss/passes/IssVectorTcgAnalysisPassTest.java @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static vadl.TestUtils.findDefinitionByNameIn; +import static vadl.iss.passes.TcgPassUtils.instrInfo; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; +import vadl.AbstractTest; +import vadl.configuration.DumpMode; +import vadl.configuration.GeneralConfiguration; +import vadl.configuration.IssConfiguration; +import vadl.iss.passes.common.planning.IssExecStrategyPass; +import vadl.iss.passes.extensions.InstrExecPlan; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyEvaluation; +import vadl.iss.passes.extensions.InstrExecPlan.StrategyKind; +import vadl.iss.passes.extensions.InstrInfo; +import vadl.iss.passes.extensions.VectorTensorPlan; +import vadl.iss.passes.extensions.VectorTensorPlan.OperandKind; +import vadl.iss.passes.extensions.VectorTensorPlan.OverlapPolicy; +import vadl.iss.passes.extensions.VectorTensorPlan.VectorOp; +import vadl.pass.PassOrders; +import vadl.pass.exception.DuplicatedPassKeyException; +import vadl.viam.Instruction; +import vadl.viam.Specification; +import vadl.viam.graph.dependency.ExpressionNode; +import vadl.viam.graph.dependency.ParamNode; + +public class IssVectorTcgAnalysisPassTest extends AbstractTest { + + @Test + void recognizesRv64vVaddVv() + throws IOException, DuplicatedPassKeyException { + var viam = analyze("sys/risc-v/rv64v.vadl"); + var instr = findInstruction(viam, "RV64IMV::VADD_VV"); + var executionPlan = executionPlan(instr); + var evaluation = strategyEvaluation(executionPlan, StrategyKind.DIRECT_GVEC); + var plan = vectorPlan(evaluation); + + assertTrue(evaluation.isViable(), evaluation::toString); + assertEquals(StrategyKind.DIRECT_GVEC, executionPlan.selectedStrategy()); + assertEquals(VectorOp.ADD, plan.op()); + assertEquals(32, plan.elementBits()); + assertEquals(32, plan.laneCount()); + assertEquals(128, plan.opBytes()); + assertNotNull(plan.shape()); + assertEquals(128, plan.shape().maxszBytes()); + assertTrue(plan.shape().fullRange()); + assertTrue(plan.shape().contiguousLayout()); + assertTrue(plan.shape().paddingPreserved()); + assertEquals(OverlapPolicy.NO_PARTIAL_OVERLAP, plan.overlapPolicy()); + assertNotNull(plan.destination()); + assertEquals(List.of("vd"), bindingParamNames(plan.destination().accessorIndices())); + assertEquals(2, plan.operands().size()); + assertEquals(OperandKind.VECTOR_REGISTER, plan.operands().get(0).kind()); + assertNotNull(plan.operands().get(0).registerBinding()); + assertNotNull(plan.operands().get(1).registerBinding()); + assertEquals(List.of("vs2"), + bindingParamNames(plan.operands().get(0).registerBinding().accessorIndices())); + assertEquals(List.of("vs1"), + bindingParamNames(plan.operands().get(1).registerBinding().accessorIndices())); + assertEquals(InstrInfo.ExecStrategy.HELPER_CALL, instrInfo(instr).execStrategy()); + } + + @Test + void recognizesRv64vVsubVv() + throws IOException, DuplicatedPassKeyException { + var viam = analyze("sys/risc-v/rv64v.vadl"); + var executionPlan = executionPlan(findInstruction(viam, "RV64IMV::VSUB_VV")); + var evaluation = strategyEvaluation(executionPlan, StrategyKind.DIRECT_GVEC); + var plan = vectorPlan(evaluation); + + assertTrue(evaluation.isViable(), evaluation::toString); + assertEquals(VectorOp.SUB, plan.op()); + assertEquals(32, plan.elementBits()); + assertEquals(32, plan.laneCount()); + } + + @Test + void keepsVectorScalarInstructionOnFallbackPlan() + throws IOException, DuplicatedPassKeyException { + var viam = analyze("sys/risc-v/rv64v.vadl"); + var executionPlan = executionPlan(findInstruction(viam, "RV64IMV::VADD_VX")); + var evaluation = strategyEvaluation(executionPlan, StrategyKind.DIRECT_GVEC); + + assertEquals(StrategyKind.HELPER_CALL, executionPlan.selectedStrategy()); + assertTrue(!evaluation.isViable(), evaluation::toString); + assertTrue(evaluation.hasIssue("OPERAND_NOT_VECTOR_READ"), evaluation::toString); + } + + @Test + void keepsAliasVectorInstructionOnFallbackPlan() + throws IOException, DuplicatedPassKeyException { + var viam = analyze("sys/vectorbench/vectorbench64.vadl"); + var executionPlan = executionPlan(findInstruction(viam, "VectorBench64::VADD_DO_VV")); + var evaluation = strategyEvaluation(executionPlan, StrategyKind.DIRECT_GVEC); + + assertEquals(StrategyKind.HELPER_CALL, executionPlan.selectedStrategy()); + assertTrue(!evaluation.isViable(), evaluation::toString); + assertTrue(evaluation.hasIssue("READ_NOT_BASE_ELEMENT"), evaluation::toString); + } + + @Test + void keepsScalarInstructionOnTcgScalarStrategy() + throws IOException, DuplicatedPassKeyException { + var viam = analyze("sys/risc-v/rv64v.vadl"); + var instr = findInstruction(viam, "RV64IMV::VSETVLI"); + var executionPlan = executionPlan(instr); + var scalarEvaluation = strategyEvaluation(executionPlan, StrategyKind.TCG_SCALAR); + + assertEquals(StrategyKind.TCG_SCALAR, executionPlan.selectedStrategy()); + assertTrue(scalarEvaluation.isViable(), scalarEvaluation::toString); + assertEquals(InstrInfo.ExecStrategy.DIRECT_TCG, instrInfo(instr).execStrategy()); + } + + private Specification analyze(String specPath) throws IOException, DuplicatedPassKeyException { + var config = + new IssConfiguration(new GeneralConfiguration(Path.of("build/test-output"), DumpMode.NONE)); + return setupPassManagerAndRunSpec(specPath, + PassOrders.iss(config).untilFirst(IssExecStrategyPass.class) + ).specification(); + } + + private Instruction findInstruction(Specification viam, String name) { + return findDefinitionByNameIn(name, viam, Instruction.class); + } + + private InstrExecPlan executionPlan(Instruction instr) { + var plan = instrInfo(instr).executionPlan(); + return plan == null ? fail("Expected execution plan for " + instr.simpleName()) : plan; + } + + private StrategyEvaluation strategyEvaluation(InstrExecPlan executionPlan, + StrategyKind strategyKind) { + var evaluation = executionPlan.evaluation(strategyKind); + return evaluation == null ? fail("Expected evaluation for strategy " + strategyKind) : + evaluation; + } + + private VectorTensorPlan vectorPlan(StrategyEvaluation evaluation) { + var plan = evaluation.planAs(VectorTensorPlan.class); + return plan == null ? fail("Expected VectorTensorPlan payload") : plan; + } + + private List bindingParamNames(List indices) { + return indices.stream() + .map(index -> assertInstanceOf(ParamNode.class, index).definition().simpleName()) + .toList(); + } +}