Skip to content

Commit d74a1f9

Browse files
committed
decoder: extend CLI with new decoder options
- extend options with memory penalty and instruction statistics path - generator fixes: pattern splitting and decision function generation
1 parent f336f3d commit d74a1f9

15 files changed

Lines changed: 409 additions & 49 deletions

vadl-cli/main/vadl/cli/BaseCommand.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,34 @@ private DecoderOptions getDecoderOptions() {
450450
result.setOptsToSkip(skipOpts.toArray(new DecoderOptions.OptionToSkip[0]));
451451
}
452452

453+
var statisticOpts = decoderOptions.stream()
454+
.filter(DecoderStatistics.class::isInstance)
455+
.map(DecoderStatistics.class::cast)
456+
.map(DecoderStatistics::stats)
457+
.toList();
458+
459+
if (statisticOpts.size() > 1) {
460+
throw new IllegalArgumentException("Multiple statistics configuration are not allowed.");
461+
}
462+
463+
if (statisticOpts.size() == 1) {
464+
result.setStatistics(statisticOpts.getFirst().getAbsolutePath());
465+
}
466+
467+
var penaltyOpts = decoderOptions.stream()
468+
.filter(DecoderPenaltyFactor.class::isInstance)
469+
.map(DecoderPenaltyFactor.class::cast)
470+
.map(DecoderPenaltyFactor::penalty)
471+
.toList();
472+
473+
if (penaltyOpts.size() > 1) {
474+
throw new IllegalArgumentException("Multiple penalty configuration are not allowed.");
475+
}
476+
477+
if (penaltyOpts.size() == 1) {
478+
result.setMemoryPenalty(penaltyOpts.getFirst());
479+
}
480+
453481
return result;
454482
}
455483
}

vadl-cli/main/vadl/cli/Helpers.java

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

1717
package vadl.cli;
1818

19+
import java.io.File;
1920
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.Comparator;
@@ -24,6 +25,7 @@
2425
import java.util.Stack;
2526
import java.util.stream.Collectors;
2627
import javax.annotation.Nonnull;
28+
import org.apache.commons.lang3.StringUtils;
2729
import org.apache.commons.lang3.stream.Streams;
2830
import picocli.CommandLine;
2931
import vadl.OpenVadlProperties;
@@ -117,11 +119,20 @@ record DecoderStrategy(DecoderOptions.Generator generator) implements DecoderOpt
117119
record DecoderSkipOption(DecoderOptions.OptionToSkip option) implements DecoderOpt {
118120
}
119121

122+
record DecoderPenaltyFactor(Double penalty) implements DecoderOpt {
123+
}
124+
125+
record DecoderStatistics(File stats) implements DecoderOpt {
126+
}
127+
120128
class DecoderOptsConverter implements Iterable<String>, CommandLine.ITypeConverter<DecoderOpt> {
121129

122130
static final String KEY_STRATEGY = "strategy";
123131
static final String KEY_SKIP = "skip";
124132

133+
static final String KEY_STATS = "statistics";
134+
static final String KEY_PENALTY_FACTOR = "penalty";
135+
125136
@Override
126137
public DecoderOpt convert(String value) throws Exception {
127138
final String[] fragments = value.split("=", -1);
@@ -156,6 +167,26 @@ public DecoderOpt convert(String value) throws Exception {
156167
.map(DecoderOptions.OptionToSkip::getSelector).toList()));
157168
}
158169

170+
if (KEY_STATS.equals(fragments[0].trim())) {
171+
var statFile = new File(fragments[1].trim());
172+
if (!statFile.exists()) {
173+
throw new CommandLine.TypeConversionException(
174+
"Unable to parse decoder option '%s'. Stats file does not exist".formatted(
175+
statFile.getAbsolutePath())
176+
);
177+
}
178+
return new DecoderStatistics(statFile);
179+
}
180+
181+
if (KEY_PENALTY_FACTOR.equals(fragments[0].trim())) {
182+
var val = fragments[1].trim();
183+
if (!StringUtils.isNumeric(val)) {
184+
throw new CommandLine.TypeConversionException(
185+
"Unable to parse decoder option '%s'. Penalty factor is not numeric".formatted(val));
186+
}
187+
return new DecoderPenaltyFactor(Double.parseDouble(val));
188+
}
189+
159190
throw new CommandLine.TypeConversionException(
160191
"Illegal decoder option '%s'. Available options are: %s".formatted(value,
161192
List.of(KEY_SKIP, KEY_STRATEGY)));
@@ -172,6 +203,10 @@ public static List<String> getOptions() {
172203
options.add(
173204
"%n%s=%s (%s)".formatted(KEY_SKIP, skipOption.getSelector(), skipOption.getDesc()));
174205
}
206+
options.add("%n%s=%f (Penalty factor for occurrence aware decoder generator)".formatted(
207+
KEY_PENALTY_FACTOR, 1.0));
208+
options.add(
209+
"%n%s=%s (Instruction occurrence statistics)".formatted(KEY_STATS, "/insn-stats.json"));
175210
return options;
176211
}
177212

vadl/main/vadl/configuration/DecoderOptions.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ public class DecoderOptions {
2828
* The possible VADL decode tree generator strategies to choose from.
2929
*/
3030
public enum Generator {
31+
/**
32+
* For now only supported by the RTL backend target, since HW is the only target which would
33+
* now require sequential lookup. Supports only regular encodings.
34+
*/
35+
TABLE("Lookup table based decoder", "table"),
3136
REGULAR("Regular decoder generator", "regular"),
3237
IRREGULAR("Irregular decoder generator, default", "irregular"),
33-
RTL_TABLE("RTL table based decoder", "rtl-table"),
34-
OCC("Irregular decoder generator, using occurrence probabilities", "occ");
38+
OCCURRENCE_AWARE("Occurrence aware generator", "occ-aware");
3539

3640
private final String selector;
3741
private final String desc;
@@ -122,9 +126,18 @@ public static OptionToSkip fromSelector(String selector) {
122126
private OptionToSkip[] optsToSkip;
123127
private Generator generator;
124128

129+
/**
130+
* Options for occurrence aware decoder generator.
131+
*/
132+
private double memoryPenalty;
133+
134+
@Nullable
135+
private String statistics;
136+
125137
public DecoderOptions() {
126138
optsToSkip = new OptionToSkip[0];
127139
generator = Generator.IRREGULAR;
140+
memoryPenalty = 1.0;
128141
}
129142

130143
public OptionToSkip[] getOptsToSkip() {
@@ -143,6 +156,23 @@ public void setGenerator(Generator generator) {
143156
this.generator = generator;
144157
}
145158

159+
public Double getMemoryPenalty() {
160+
return memoryPenalty;
161+
}
162+
163+
public void setMemoryPenalty(Double memoryPenalty) {
164+
this.memoryPenalty = memoryPenalty;
165+
}
166+
167+
@Nullable
168+
public String getStatistics() {
169+
return statistics;
170+
}
171+
172+
public void setStatistics(@Nullable String statistics) {
173+
this.statistics = statistics;
174+
}
175+
146176
public DecoderOptions withGenerator(Generator generator) {
147177
setGenerator(generator);
148178
return this;
@@ -153,6 +183,16 @@ public DecoderOptions withOptsToSkip(OptionToSkip... optsToSkip) {
153183
return this;
154184
}
155185

186+
public DecoderOptions withMemoryPenalty(Double memoryPenalty) {
187+
setMemoryPenalty(memoryPenalty);
188+
return this;
189+
}
190+
191+
public DecoderOptions withStatistics(@Nullable String statistics) {
192+
setStatistics(statistics);
193+
return this;
194+
}
195+
156196
@Override
157197
public String toString() {
158198
return "DecoderOptions{"

vadl/main/vadl/dump/infoEnrichers/VdtEnricherCollection.java

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
import vadl.dump.InfoEnricher;
2323
import vadl.dump.InfoUtils;
2424
import vadl.dump.entities.VdtEntity;
25+
import vadl.pass.PassResults;
26+
import vadl.vdt.impl.irregular.model.DecodeEntry;
27+
import vadl.vdt.passes.VdtConstraintSynthesisPass;
28+
import vadl.vdt.passes.VdtInputPreparationPass;
2529
import vadl.vdt.target.common.DecisionTreeStatsCalculator;
2630
import vadl.vdt.target.dump.DotGraphGenerator;
2731
import vadl.vdt.target.dump.InsnDecisionTableGenerator;
@@ -108,21 +112,36 @@ public class VdtEnricherCollection {
108112
entity.addInfo(info);
109113
});
110114

115+
@SuppressWarnings("unchecked")
111116
public static InfoEnricher VDT_STATS_EXPANDABLE =
112117
InfoEnricher.forType(VdtEntity.class, (entity, passResults) -> {
113118

114119
var stats = DecisionTreeStatsCalculator.statistics(entity.tree());
115120

116121
final var statsTable = new ArrayList<List<String>>();
117122

118-
statsTable.add(List.of("Property", "Number of Nodes", "Number of Leaves (Instructions)",
119-
"Minimum Depth",
120-
"Maximal Depth", "Average Depth", "Longest instruction width"));
121-
statsTable.add(List.of("Value", String.valueOf(stats.getNumberOfNodes()),
122-
String.valueOf(stats.getNumberOfLeafNodes()), String.valueOf(stats.getMinDepth()),
123-
String.valueOf(stats.getMaxDepth()),
124-
String.valueOf(Math.round(stats.getAvgDepth() * 100) / 100.0),
125-
stats.getMaxInstructionWidth() + " bit"));
123+
final List<String> categories = new ArrayList<>(
124+
List.of("Property", "Number of Nodes", "Number of Instructions",
125+
"Number of Leaves", "Minimum Depth", "Maximal Depth", "Average Depth"));
126+
if (stats.getOccurrenceProbability() > 0.0) {
127+
categories.add("Weighted Average Depth");
128+
}
129+
categories.add("Longest instruction width");
130+
statsTable.add(categories);
131+
132+
final List<String> values = new ArrayList<>(
133+
List.of("Value", String.valueOf(stats.getNumberOfNodes()),
134+
String.valueOf(getInsnCount(passResults)),
135+
String.valueOf(stats.getNumberOfLeafNodes()), String.valueOf(stats.getMinDepth()),
136+
String.valueOf(stats.getMaxDepth()),
137+
String.valueOf(Math.round(stats.getAvgDepth() * 100) / 100.0))
138+
);
139+
if (stats.getOccurrenceProbability() > 0.0) {
140+
values.add(String.valueOf(Math.round(stats.getWeightedAvgDepth() * 100) / 100.0));
141+
}
142+
values.add(stats.getMaxInstructionWidth() + " bit");
143+
144+
statsTable.add(values);
126145

127146
var info = InfoUtils.createTableExpandable("Statistics", statsTable);
128147
entity.addInfo(info);
@@ -133,13 +152,33 @@ public class VdtEnricherCollection {
133152

134153
var stats = DecisionTreeStatsCalculator.statistics(entity.tree());
135154

136-
entity.addInfo(Info.Tag.of("Instructions", String.valueOf(stats.getNumberOfLeafNodes())));
155+
entity.addInfo(Info.Tag.of("Instructions", String.valueOf(getInsnCount(passResults))));
137156
entity.addInfo(Info.Tag.of("Nodes", String.valueOf(stats.getNumberOfNodes())));
157+
entity.addInfo(Info.Tag.of("Leaves", String.valueOf(stats.getNumberOfLeafNodes())));
138158
entity.addInfo(Info.Tag.of("Max Depth", String.valueOf(stats.getMaxDepth())));
139159
entity.addInfo(Info.Tag.of("Avg Depth",
140160
String.valueOf(Math.round(stats.getAvgDepth() * 100) / 100.0)));
161+
162+
if (stats.getOccurrenceProbability() > 0.0) {
163+
entity.addInfo(Info.Tag.of("Weighted Avg Depth",
164+
String.valueOf(Math.round(stats.getWeightedAvgDepth() * 100) / 100.0)));
165+
}
141166
});
142167

168+
@SuppressWarnings("unchecked")
169+
private static int getInsnCount(PassResults passResults) {
170+
final List<DecodeEntry> entries;
171+
if (passResults.hasRunPassOnce(VdtConstraintSynthesisPass.class)) {
172+
entries =
173+
(List<DecodeEntry>) passResults.lastNullableResultOf(
174+
VdtConstraintSynthesisPass.class);
175+
} else {
176+
entries =
177+
(List<DecodeEntry>) passResults.lastNullableResultOf(VdtInputPreparationPass.class);
178+
}
179+
return entries != null ? entries.size() : 0;
180+
}
181+
143182
public static List<InfoEnricher> all = List.of(
144183
VDT_STATS_TAGS,
145184
VDT_STATS_EXPANDABLE,

vadl/main/vadl/pass/PassOrders.java

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

1717
package vadl.pass;
1818

19-
import static vadl.configuration.DecoderOptions.Generator.RTL_TABLE;
19+
import static vadl.configuration.DecoderOptions.Generator.TABLE;
2020
import static vadl.configuration.DecoderOptions.OptionToSkip.OPT_CONSTRAINT_SYNTHESIS;
2121
import static vadl.configuration.DecoderOptions.OptionToSkip.OPT_DECODER_VERIFICATION;
2222
import static vadl.configuration.DecoderOptions.OptionToSkip.OPT_ENCODING_VERIFICATION;
@@ -699,8 +699,8 @@ public static PassOrder rtl(RtlConfiguration config) throws IOException {
699699
"mia-inline",
700700
"MiA after inlining instruction behavior");
701701

702-
if (config.getDecoderOptions().getGenerator() != RTL_TABLE) {
703-
// Prepares and constructs the VDT, which is not used by the rtl-table strategy
702+
if (config.getDecoderOptions().getGenerator() != TABLE) {
703+
// Prepares and constructs the VDT, which is not used by the 'table' strategy
704704
addDecodePasses(order, config);
705705
}
706706

vadl/main/vadl/vdt/impl/irregular/IrregularDecodeTreeGenerator.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ protected Node generateInternal(DecodeEntries decodeEntries) {
100100
return makeNode(decodeEntries);
101101
}
102102

103+
if (decodeEntries.isEmpty()) {
104+
throw new IllegalStateException("Entry set must not be empty");
105+
}
106+
103107
// It's possible to have multiple decode entries pointing to the same instruction
104108
var entry = combineEntries(decodeEntries);
105109

@@ -230,18 +234,31 @@ protected SingleSplitEntrySet split(DecodeEntries decodeEntries, BitPattern patt
230234

231235
for (DecodeEntry e : decodeEntries.entries()) {
232236

233-
var matchesThen = compatible(e.pattern(), pattern) && e.exclusionConditions().stream()
234-
.noneMatch(c -> contain(pattern, c.matching())
235-
&& c.unmatching().stream().noneMatch(p -> compatible(pattern, p)));
236-
var matchesElse = !contain(e.pattern(), pattern);
237-
238-
if (matchesThen ^ matchesElse) {
237+
// See if there is an encoding that matches the base pattern and is not excluded by the
238+
// exclusion conditions
239+
var mThen = compatible(e.pattern(), pattern) && e.exclusionConditions().stream()
240+
.allMatch(ex -> {
241+
var combined = combinePatterns(e.pattern(), pattern);
242+
return !contain(combined, ex.matching()) || ex.unmatching().stream()
243+
.anyMatch(u -> compatible(combined, u));
244+
});
245+
246+
// See if there is an encoding that either does not match the base pattern or is excluded by
247+
// the exclusion conditions (and not re-included by the unmatching patterns)
248+
var mElse = !compatible(e.pattern(), pattern) || e.exclusionConditions().stream()
249+
.anyMatch(ex -> {
250+
var combined = combinePatterns(e.pattern(), pattern);
251+
return compatible(combined, ex.matching()) && ex.unmatching().stream()
252+
.noneMatch(u -> compatible(combined, u));
253+
});
254+
255+
if (mThen ^ mElse) {
239256
// The entry occurs only on one side of the branch, we can keep the current probability.
240-
(matchesThen ? matchingEntries : otherEntries).add(e);
257+
(mThen ? matchingEntries : otherEntries).add(e);
241258
continue;
242259
}
243260

244-
if (!matchesThen) {
261+
if (!mThen) {
245262
// Must not happen
246263
throw toConstructionDiagnostic(decodeEntries);
247264
}

vadl/main/vadl/vdt/impl/irregular/OccurrenceAwareDecodeTreeGenerator.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,13 @@ private Optional<Pair<Double, MultiSplitEntrySet>> bestMultiDecision(
148148
.filter(Objects::nonNull)
149149
.min(Comparator.comparing(Pair::left)).orElse(null);
150150

151+
if (base == null) {
152+
return Optional.empty();
153+
}
154+
151155
final var checked = decodeEntries.checkedBits().toMaskVector();
152156

153-
Pair<Double, MultiSplitEntrySet> prev = Objects.requireNonNull(base);
157+
Pair<Double, MultiSplitEntrySet> prev = base;
154158
Pair<Double, MultiSplitEntrySet> next = prev;
155159
do {
156160

@@ -224,7 +228,7 @@ private Optional<BitPattern> findBestPattern(final BitPattern base,
224228

225229
} while (nextCost < prevCost);
226230

227-
return Optional.of(prev);
231+
return prev == base ? Optional.empty() : Optional.of(prev);
228232
}
229233

230234
/**

vadl/main/vadl/vdt/impl/irregular/model/DecodeEntries.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ public boolean hasMultiple() {
3333
return entries.stream().map(DecodeEntry::source).collect(Collectors.toSet()).size() > 1;
3434
}
3535

36+
public boolean isEmpty() {
37+
return entries.isEmpty();
38+
}
39+
3640
}

vadl/main/vadl/vdt/passes/VdtInputPreparationPass.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public Object execute(PassResults passResults, Specification viam) throws IOExce
104104
final var pattern = toFixedBitPattern(i, bo);
105105
final var exclusions = getExclusionsFromConstraints(bo, i);
106106

107-
// TODO: Add the option to supply occurrence probabilities. For now we assume uniform
107+
// TODO: Process & parse the occurrence probabilities. For now we'll assume uniform
108108
// distribution.
109109
final double probability = 1.0 / isa.ownInstructions().size();
110110

0 commit comments

Comments
 (0)