Skip to content

Commit 40abc1a

Browse files
authored
Merge pull request #584 from OpenVADL/feature/dynamic-slicing
lowering: Add dynamic slicing and tensor reading with missing indices
2 parents 94a67ee + d77d33f commit 40abc1a

File tree

14 files changed

+439
-108
lines changed

14 files changed

+439
-108
lines changed

vadl/main/vadl/ast/BehaviorLowering.java

Lines changed: 260 additions & 53 deletions
Large diffs are not rendered by default.

vadl/main/vadl/ast/ConstantEvaluator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ public ConstantValue visit(CallIndexExpr expr) {
376376

377377
// Slicing/Indexing
378378
for (var sliceArg : expr.slices()) {
379-
result = slice(result, requireNonNull(sliceArg.computedBitSlice), (DataType) sliceArg.type());
379+
result = slice(result, requireNonNull(sliceArg.computedstaticBitSlice),
380+
(DataType) sliceArg.type());
380381
}
381382

382383
return result;

vadl/main/vadl/ast/Expr.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,12 +1892,13 @@ static final class Arguments implements TypedNode {
18921892
Type type;
18931893

18941894
/**
1895-
* The computed slices.
1895+
* The computed static slices.
18961896
* Will be set by the typechecker and only if it slicing is staticly known at compiletime,
18971897
* and not dynamic.
1898+
* If the slice is dynamic this will be left null.
18981899
*/
18991900
@Nullable
1900-
Constant.BitSlice computedBitSlice;
1901+
Constant.BitSlice computedstaticBitSlice;
19011902

19021903
Arguments(List<Expr> values, SourceLocation location) {
19031904
this.values = values;

vadl/main/vadl/ast/TypeChecker.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ public Void visit(AliasDefinition definition) {
12201220
visitSliceIndexCall(expr, typeBeforeSlice, List.of(slice));
12211221
var innerMostType = (BitsType) expr.type();
12221222
expr.type = setInnerMostType(oldType, innerMostType);
1223-
definition.slice = slice.computedBitSlice;
1223+
definition.slice = slice.computedstaticBitSlice;
12241224
}
12251225
}
12261226

@@ -3046,7 +3046,7 @@ private void visitSliceIndexCall(CallIndexExpr expr, Type typeBeforeSlice,
30463046
}
30473047

30483048
currType = Type.bits(bitSlice.bitSize());
3049-
slice.computedBitSlice = bitSlice;
3049+
slice.computedstaticBitSlice = bitSlice;
30503050
slice.type = currType;
30513051
expr.type = currType;
30523052
}
@@ -3094,7 +3094,7 @@ private void visitSliceIndexCall(CallIndexExpr expr, Type typeBeforeSlice,
30943094
case TensorType tt -> tt.flattenBitsType().bitWidth();
30953095
default -> throw new IllegalStateException();
30963096
};
3097-
slice.computedBitSlice =
3097+
slice.computedstaticBitSlice =
30983098
Constant.BitSlice.of(bitWidth * staticIndex + bitWidth - 1, bitWidth * staticIndex);
30993099
slice.type = currType;
31003100

@@ -3113,10 +3113,7 @@ private void visitSliceIndexCall(CallIndexExpr expr, Type typeBeforeSlice,
31133113
underlying problem because our typesystem isn't strong enough (and I would argue it
31143114
also shouldn't be that strong/complex).
31153115
*/
3116-
if (!indexExpr.type().isDataType()) {
3117-
addErrorAndStopChecking(
3118-
typeMismatchError(indexExpr, "numerical datatype", indexExpr.type()));
3119-
}
3116+
slice.type = currType;
31203117
}
31213118
}
31223119
}
@@ -3488,6 +3485,12 @@ public Void visit(LetExpr expr) {
34883485
valType)
34893486
.build());
34903487
}
3488+
3489+
for (int i = 0; i < expr.identifiers.size(); i++) {
3490+
expr.identifiers.get(i).type = valTupleType.get(i);
3491+
}
3492+
} else {
3493+
expr.identifiers.getFirst().type = valType;
34913494
}
34923495

34933496
expr.type = check(expr.body);
@@ -3724,6 +3727,12 @@ public Void visit(LetStatement statement) {
37243727
valType)
37253728
.build());
37263729
}
3730+
3731+
for (int i = 0; i < statement.identifiers.size(); i++) {
3732+
statement.identifiers.get(i).type = valTupleType.get(i);
3733+
}
3734+
} else {
3735+
statement.identifiers.getFirst().type = valType;
37273736
}
37283737

37293738
check(statement.body);

vadl/main/vadl/gcb/passes/operands/GenerateInstructionOperandsPass.java

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import vadl.viam.Specification;
5454
import vadl.viam.graph.Graph;
5555
import vadl.viam.graph.HasRegisterTensor;
56+
import vadl.viam.graph.IsInstructionOperand;
5657
import vadl.viam.graph.Node;
5758
import vadl.viam.graph.control.InstrCallNode;
5859
import vadl.viam.graph.dependency.AsmBuiltInCall;
@@ -281,21 +282,7 @@ private List<GcbInstructionOperand> getTableGenInputOperandsForMachineInstructio
281282

282283
var inputOperands = getInputOperands(graph)
283284
.stream()
284-
.filter(node -> {
285-
// Why?
286-
// Because LLVM cannot handle static registers in input or output operands.
287-
// They belong to defs and uses instead.
288-
if (node instanceof ReadRegTensorNode readRegTensorNode
289-
&& readRegTensorNode.regTensor().isRegisterFile()) {
290-
return !readRegTensorNode.hasConstantAddress();
291-
} else if (node instanceof ReadArtificialResNode artificialResNode
292-
&& artificialResNode.resourceDefinition()
293-
.innerResourceRef() instanceof RegisterTensor tensor
294-
&& tensor.isRegisterFile()) {
295-
return !artificialResNode.hasConstantAddress();
296-
}
297-
return true;
298-
})
285+
.filter(GenerateInstructionOperandsPass::checkWhetherNodeCanBeOperand)
299286
.map(this::map)
300287
.toList();
301288

@@ -317,28 +304,25 @@ private List<GcbInstructionOperand> getTableGenInputOperandsForPseudoInstruction
317304

318305
var inputOperands = getInputOperandsForPseudoInstructions(instruction, graph)
319306
.stream()
320-
.filter(node -> {
321-
// Why?
322-
// Because LLVM cannot handle static registers in input or output operands.
323-
// They belong to defs and uses instead.
324-
if (node instanceof ReadRegTensorNode readRegTensorNode
325-
&& readRegTensorNode.regTensor().isRegisterFile()) {
326-
return !readRegTensorNode.hasConstantAddress();
327-
} else if (node instanceof ReadArtificialResNode artificialResNode
328-
&& artificialResNode.resourceDefinition()
329-
.innerResourceRef() instanceof RegisterTensor tensor
330-
&& tensor.isRegisterFile()) {
331-
return !artificialResNode.hasConstantAddress();
332-
}
333-
return true;
334-
})
307+
.filter(GenerateInstructionOperandsPass::checkWhetherNodeCanBeOperand)
335308
.map(this::map)
336309
.toList();
337310

338311
return filterOutputs(outputOperands, inputOperands.stream())
339312
.toList();
340313
}
341314

315+
private static boolean checkWhetherNodeCanBeOperand(Node node) {
316+
// Why?
317+
// Because LLVM cannot handle static registers in input or output operands.
318+
// They belong to defs and uses instead.
319+
if (node instanceof IsInstructionOperand operandCandidate) {
320+
return operandCandidate.canBeInstructionOperand();
321+
} else {
322+
return node instanceof FuncParamNode || node instanceof FieldAccessRefNode;
323+
}
324+
}
325+
342326
private GcbInstructionOperand map(Node operand) {
343327
return switch (operand) {
344328
case ReadRegTensorNode node when node.regTensor().isRegisterFile() -> mapFrom(node);
@@ -413,11 +397,10 @@ private GcbInstructionOperand mapFrom(
413397
} else if (node.address() instanceof FuncParamNode funcParamNode) {
414398
return new GcbInstructionIndexedRegisterFileOperand(node, funcParamNode);
415399
} else if (node.address() instanceof ConstantNode constantNode) {
416-
var tensor = (RegisterTensor) node.resourceDefinition().innerResourceRef();
417400
// The register file has a constant as address.
418401
// This is ok as long as the value of the register file at the address is also constant.
419402
// For example, the X0 register in RISC-V which always has a constant value.
420-
var constraints = tensor.constraints();
403+
var constraints = node.getAllConstraintsRecursively();
421404
var constraintValue = constraints.stream()
422405
.filter(
423406
x -> x.indices().getFirst().intValue() == constantNode.constant().asVal().intValue())

vadl/main/vadl/gcb/passes/operands/model/GcbConstantOperand.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package vadl.gcb.passes.operands.model;
1818

19-
import static vadl.viam.ViamError.ensure;
2019
import static vadl.viam.ViamError.ensurePresent;
2120

2221
import vadl.error.Diagnostic;
@@ -37,8 +36,6 @@ public final class GcbConstantOperand extends GcbInstructionOperand {
3736
*/
3837
public GcbConstantOperand(ConstantNode constantNode, Constant value) {
3938
super(constantNode);
40-
ensure(constantNode.constant().equals(value),
41-
"This is definitely wrong because index and constraint value are mismatched");
4239
this.constant = value;
4340
}
4441

vadl/main/vadl/lcb/passes/llvmLowering/strategies/nodeLowering/LcbNodeReplacementHandler.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,14 @@ public void handle(ForallEndNode node) {
175175
@Handler
176176
@SuppressWarnings("MissingJavadocMethod")
177177
public void handle(ReadArtificialResNode node) {
178-
node.replaceAndDelete(
179-
new LlvmReadArtificialResourceNode(node.resourceDefinition(),
180-
node.indices().getFirst(),
181-
node.type()));
178+
if (node.indices().isEmpty()) {
179+
Objects.requireNonNull(node.graph()).add(new LlvmUnlowerableSD());
180+
} else {
181+
node.replaceAndDelete(
182+
new LlvmReadArtificialResourceNode(node.resourceDefinition(),
183+
node.indices().getFirst(),
184+
node.type()));
185+
}
182186
}
183187

184188
@Handler
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-FileCopyrightText : © 2025 TU Wien <vadl@tuwien.ac.at>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package vadl.viam.graph;
18+
19+
/**
20+
* Interface to indicate whether a read resource is an instruction operand.
21+
*/
22+
public interface IsInstructionOperand {
23+
/**
24+
* Check whether the node can become an operand.
25+
*
26+
* @return {@code true} when the node's has a non-constant address and is a register file.
27+
*/
28+
boolean canBeInstructionOperand();
29+
}

vadl/main/vadl/viam/graph/dependency/ReadArtificialResNode.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,24 @@
1616

1717
package vadl.viam.graph.dependency;
1818

19+
import java.util.ArrayList;
1920
import java.util.List;
2021
import vadl.javaannotations.viam.DataValue;
2122
import vadl.types.DataType;
2223
import vadl.viam.ArtificialResource;
2324
import vadl.viam.RegisterResource;
2425
import vadl.viam.RegisterTensor;
2526
import vadl.viam.graph.GraphNodeVisitor;
27+
import vadl.viam.graph.IsInstructionOperand;
2628
import vadl.viam.graph.Node;
2729
import vadl.viam.graph.NodeList;
2830
import vadl.viam.graph.ReadsRegisterTensor;
2931

3032
/**
3133
* A read of an {@link ArtificialResource}.
3234
*/
33-
public class ReadArtificialResNode extends ReadResourceNode implements ReadsRegisterTensor {
35+
public class ReadArtificialResNode extends ReadResourceNode implements ReadsRegisterTensor,
36+
IsInstructionOperand {
3437

3538
@DataValue
3639
private final ArtificialResource resource;
@@ -84,4 +87,58 @@ public boolean hasRegisterFile() {
8487
// If parameter's length is 0 then it is a register.
8588
return resource.isRegisterFile() && resource.readFunction().parameters().length == 1;
8689
}
90+
91+
/**
92+
* Theoretically, it is possible to have aliases to aliases. This method returns the
93+
* underlying {@link RegisterTensor}.
94+
*/
95+
public RegisterTensor getBaseTensor() {
96+
ArtificialResource resource = resourceDefinition();
97+
98+
while (!(resource.innerResourceRef() instanceof RegisterTensor)) {
99+
resource = (ArtificialResource) resource.innerResourceRef();
100+
}
101+
102+
return (RegisterTensor) resource.innerResourceRef();
103+
}
104+
105+
/**
106+
* Get all the constraints recursively.
107+
*/
108+
public List<RegisterResource.Constraint> getAllConstraintsRecursively() {
109+
ArtificialResource resource = resourceDefinition();
110+
ArrayList<RegisterResource.Constraint> constraints = new ArrayList<>(resource.constraints());
111+
112+
while (!(resource.innerResourceRef() instanceof RegisterTensor)) {
113+
resource = (ArtificialResource) resource.innerResourceRef();
114+
constraints.addAll(resource.constraints());
115+
}
116+
117+
return constraints;
118+
}
119+
120+
@Override
121+
public boolean canBeInstructionOperand() {
122+
var registerTensor = getBaseTensor();
123+
var constraints = getAllConstraintsRecursively();
124+
125+
// We have three cases:
126+
// (1): It's a register file and has no constant address -> ok
127+
// (2): It's a register file and has a constant address with constraint -> ok
128+
// (3): else -> not ok
129+
130+
// Case (2)
131+
if (registerTensor.isRegisterFile() && hasAddress() && address().isConstant()) {
132+
var cnst = ((ConstantNode) address()).constant.asVal().intValue();
133+
// Case (1)
134+
if (hasConstantAddress() && constraints.stream()
135+
.flatMap(x -> x.indices().stream()).anyMatch(addr -> addr.intValue() == cnst)) {
136+
return true;
137+
} else {
138+
return registerTensor.isRegisterFile() && hasAddress() && !address().isConstant();
139+
}
140+
} else {
141+
return registerTensor.isRegisterFile() && hasAddress();
142+
}
143+
}
87144
}

vadl/main/vadl/viam/graph/dependency/ReadRegTensorNode.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import vadl.viam.RegisterResource;
2727
import vadl.viam.RegisterTensor;
2828
import vadl.viam.graph.GraphNodeVisitor;
29+
import vadl.viam.graph.IsInstructionOperand;
2930
import vadl.viam.graph.NodeList;
3031
import vadl.viam.graph.ReadsRegisterTensor;
3132

@@ -45,7 +46,8 @@
4546
* (program) counter access. It is set by the
4647
* {@link vadl.viam.passes.staticCounterAccess.StaticCounterAccessResolvingPass}</p>
4748
*/
48-
public class ReadRegTensorNode extends ReadResourceNode implements ReadsRegisterTensor {
49+
public class ReadRegTensorNode extends ReadResourceNode implements ReadsRegisterTensor,
50+
IsInstructionOperand {
4951

5052
@DataValue
5153
protected RegisterTensor regTensor;
@@ -179,4 +181,9 @@ public RegisterTensor registerTensor() {
179181
public boolean hasRegisterFile() {
180182
return regTensor.isRegisterFile();
181183
}
184+
185+
@Override
186+
public boolean canBeInstructionOperand() {
187+
return hasRegisterFile() && !indices.isEmpty() && !hasConstantAddress();
188+
}
182189
}

0 commit comments

Comments
 (0)