1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-22 06:29:23 +00:00

separated synthesis from fragment tempaltes.

This commit is contained in:
jespergravgaard 2021-12-28 20:39:51 +01:00
parent 36936efe80
commit 24049a12c1
10 changed files with 738 additions and 660 deletions

View File

@ -1,10 +1,10 @@
package dk.camelot64.kickc;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateUsages;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentSynthesisResult;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateUsages;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.symbols.Procedure;
@ -275,8 +275,8 @@ public class KickC implements Callable<Integer> {
try {
final AsmFragmentTemplateMasterSynthesizer masterSynthesizer = compiler.getAsmFragmentMasterSynthesizer();
final AsmFragmentTemplateSynthesizer cpuSynthesizer = masterSynthesizer.getSynthesizer(program.getTargetCpu());
Collection<AsmFragmentTemplate> fragmentTemplates = cpuSynthesizer.getBestTemplates(fragment, compiler.getLog());
for(AsmFragmentTemplate fragmentTemplate : fragmentTemplates) {
Collection<AsmFragmentSynthesisResult> fragmentTemplates = cpuSynthesizer.getBestTemplates(fragment, compiler.getLog());
for(AsmFragmentSynthesisResult fragmentTemplate : fragmentTemplates) {
AsmFragmentTemplateUsages.logTemplate(compiler.getLog(), fragmentTemplate, "");
}
if(fragmentTemplates.size() == 0) {
@ -359,7 +359,7 @@ public class KickC implements Callable<Integer> {
}
StringBuilder CFileNames = new StringBuilder();
cFiles.stream().forEach(path -> CFileNames.append(path.toString()).append(" "));
cFiles.forEach(path -> CFileNames.append(path.toString()).append(" "));
Map<String, String> effectiveDefines = new LinkedHashMap<>();
effectiveDefines.put("__KICKC__", "1");

View File

@ -2,7 +2,6 @@ package dk.camelot64.kickc.fragment;
import dk.camelot64.cpufamily6502.CpuClobber;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesisRule;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.TargetCpu;
@ -19,6 +18,7 @@ import dk.camelot64.kickc.parser.AsmParser;
import dk.camelot64.kickc.parser.KickCParser;
import java.util.LinkedHashMap;
import java.util.Objects;
/**
* An ASM fragment template usable for generating KickAssembler code for different bindings.
@ -26,43 +26,24 @@ import java.util.LinkedHashMap;
*/
public class AsmFragmentTemplate {
/** true if the fragment was loaded from disk. */
private boolean file;
/** true if the fragment was loaded from the disk cache. */
private boolean cache;
/** The fragment template signature name. */
private final String signature;
/** The fragment template body */
private String body;
/** The synthesis that created the fragment. null if the fragment template was loaded. */
private AsmFragmentTemplateSynthesisRule synthesis;
/** The sub fragment template that the synthesis modified to create this. null if the fragment template was loaded. */
private AsmFragmentTemplate subFragment;
/** The target CPU. */
private final TargetCpu targetCpu;
/** The parsed ASM lines. Initially null. Will be non-null, is the template is ever used to generate ASM code. */
/** The fragment template body */
private String body;
/** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */
private KickCParser.AsmLinesContext bodyAsm;
/** The ASM clobber of the fragment. */
/** The ASM clobber of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/
private AsmFragmentClobber clobber;
/** The cycles consumed by the ASM of the fragment. */
/** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/
private Double cycles;
public AsmFragmentTemplate(String signature, String body, TargetCpu targetCpu, boolean cache) {
public AsmFragmentTemplate(String signature, String body, TargetCpu targetCpu) {
this.signature = signature;
this.body = body;
this.targetCpu = targetCpu;
this.file = true;
this.cache = cache;
}
public AsmFragmentTemplate(String signature, String body, AsmFragmentTemplateSynthesisRule synthesis, AsmFragmentTemplate subFragment) {
this.signature = signature;
this.body = body;
this.synthesis = synthesis;
this.subFragment = subFragment;
this.targetCpu = subFragment.targetCpu;
this.file = false;
this.cache = false;
}
/**
@ -173,7 +154,7 @@ public class AsmFragmentTemplate {
this.cycles = asm.getCycles();
}
String getSignature() {
public String getSignature() {
return signature;
}
@ -206,43 +187,16 @@ public class AsmFragmentTemplate {
return cycles;
}
public boolean isFile() {
return file;
}
public boolean isCache() {
return cache;
}
AsmFragmentTemplateSynthesisRule getSynthesis() {
return synthesis;
}
AsmFragmentTemplate getSubFragment() {
return subFragment;
}
public String getName() {
StringBuilder name = new StringBuilder();
name.append(signature);
if(synthesis != null) {
name.append(" < ");
name.append(subFragment.getName());
}
return name.toString();
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AsmFragmentTemplate that = (AsmFragmentTemplate) o;
return getName().equals(that.getName());
return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body);
}
@Override
public int hashCode() {
return getName().hashCode();
return Objects.hash(signature, targetCpu, body);
}
}

View File

@ -48,7 +48,7 @@ public class AsmFragmentTemplateCache {
}
/** Special singleton representing that the fragment can not be synthesized or loaded. */
public static AsmFragmentTemplate NO_SYNTHESIS = new AsmFragmentTemplate("NO_SYNTHESIS", "NO_SYNTHESIS", null, false);
public static AsmFragmentTemplate NO_SYNTHESIS = new AsmFragmentTemplate("NO_SYNTHESIS", "NO_SYNTHESIS", null);
/** Prefix for the fragment hash file header. */
public static final String HASH_HEADER = "//KICKC FRAGMENT CACHE ";
@ -189,7 +189,7 @@ public class AsmFragmentTemplateCache {
cache.put(signature, NO_SYNTHESIS);
} else {
CharStream fragmentCharStream = CharStreams.fromString(bodyString);
AsmFragmentTemplate template = new AsmFragmentTemplate(signature, AsmFragmentTemplateSynthesizer.fixNewlines(fragmentCharStream.toString()), targetCpu, true);
AsmFragmentTemplate template = new AsmFragmentTemplate(signature, AsmFragmentTemplateSynthesizer.fixNewlines(fragmentCharStream.toString()), targetCpu);
cache.put(signature, template);
}
}

View File

@ -1,9 +1,7 @@
package dk.camelot64.kickc.fragment;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesisRule;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesisRuleManager;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.fragment.synthesis.*;
import java.io.File;
import java.util.*;
@ -12,22 +10,23 @@ import java.util.*;
public class AsmFragmentTemplateUsages {
/** Usage Statistics for fragment templates. */
private static Map<AsmFragmentTemplate, Integer> fragmentTemplateUsage = new HashMap<>();
private static Map<AsmFragmentSynthesisResult, Integer> fragmentTemplateUsage = new HashMap<>();
/**
* Count one usage of ASM fragment templates - directly or through synthesis
*
* @param fragmentTemplate The template to increment usage of
*/
public static void incUsage(AsmFragmentTemplate fragmentTemplate) {
public static void incUsage(AsmFragmentSynthesisResult fragmentTemplate) {
Integer usage = fragmentTemplateUsage.get(fragmentTemplate);
if(usage == null) {
usage = 0;
}
fragmentTemplateUsage.put(fragmentTemplate, usage + 1);
AsmFragmentTemplate subFragment = fragmentTemplate.getSubFragment();
if(subFragment != null) {
incUsage(subFragment);
for (AsmFragmentSynthesisResult subFragment : fragmentTemplate.getSubFragments()) {
if (subFragment != null) {
incUsage(subFragment);
}
}
}
@ -38,7 +37,7 @@ public class AsmFragmentTemplateUsages {
*/
public static void logUsages(AsmFragmentTemplateSynthesizer synthesizer, CompileLog log, boolean logRedundantFiles, boolean logUnusedFiles, boolean logUnusedRules, boolean logFileDetails, boolean logAllDetails, boolean logDetailsBody) {
Map<String, AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis> synthesisGraph =
Map<String, AsmFragmentSynthesis> synthesisGraph =
synthesizer.getSynthesisGraph();
ArrayList<String> signatures = new ArrayList<>(synthesisGraph.keySet());
Collections.sort(signatures);
@ -51,29 +50,21 @@ public class AsmFragmentTemplateUsages {
String fileName = file.getName();
String signature = fileName.substring(0, fileName.length() - 4);
// Synthesize the fragment - and check if the synthesis is as good as the file body
Collection<AsmFragmentTemplate> templates = synthesizer.getBestTemplates(signature, log);
Collection<AsmFragmentSynthesisResult> templates = synthesizer.getBestTemplates(signature, log);
boolean isFile = false;
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
isFile |= template.isFile();
}
if(!isFile) {
StringBuilder templateNames = new StringBuilder();
boolean first = true;
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
templateNames.append(template.getName());
if(first) {
first = false;
} else {
templateNames.append(" / ");
}
// Check if the synthesis uses a file marked as redundant
AsmFragmentTemplate sourceFileTemplate = template;
while(!sourceFileTemplate.isFile()) {
sourceFileTemplate = sourceFileTemplate.getSubFragment();
}
if(redundantSignatures.contains(sourceFileTemplate.getSignature())) {
throw new RuntimeException("Problem in redundancy analysis! " + sourceFileTemplate.getSignature() + ".asm seems redundant but is needed for synthesis of " + signature);
}
}
log.append("rm " + signature + ".asm #synthesized better ASM by " + templateNames);
redundantSignatures.add(signature);
@ -86,13 +77,13 @@ public class AsmFragmentTemplateUsages {
for(File file : files) {
String fileName = file.getName();
String signature = fileName.substring(0, fileName.length() - 4);
AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis asmFragmentSynthesis = synthesisGraph.get(signature);
Collection<AsmFragmentTemplate> templates = asmFragmentSynthesis.getBestTemplates();
AsmFragmentSynthesis asmFragmentSynthesis = synthesisGraph.get(signature);
Collection<AsmFragmentSynthesisResult> templates = asmFragmentSynthesis.getBestTemplates();
if(templates != null && templates.size() > 0) {
// The template has been loaded / synthesized - is the usage count zero?
boolean allZero = true;
Integer fileUsage = null;
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
Integer usage = fragmentTemplateUsage.get(template);
if(usage == null) usage = 0;
if(usage > 0) {
@ -118,16 +109,16 @@ public class AsmFragmentTemplateUsages {
if(logUnusedRules) {
log.append("\nUNUSED ASM FRAGMENT SYNTHESIS RULE ANALYSIS (if found consider removing them)");
final Collection<AsmFragmentTemplateSynthesisRule> allRules = AsmFragmentTemplateSynthesisRuleManager.getAllSynthesisRules();
for(String signature : signatures) {
Collection<AsmFragmentTemplate> templates =
synthesizer.getBestTemplates(signature, log);
for(AsmFragmentTemplate template : templates) {
while(template.getSynthesis()!=null) {
allRules.remove(template.getSynthesis());
template = template.getSubFragment();
}
LinkedList<AsmFragmentSynthesisResult> templates = new LinkedList<>(synthesizer.getBestTemplates(signature, log));
while(!templates.isEmpty()) {
final AsmFragmentSynthesisResult template = templates.pop();
allRules.remove(template.getSynthesis());
templates.addAll(template.getSubFragments());
}
}
for(AsmFragmentTemplateSynthesisRule rule : allRules) {
log.append("Synthesis Rule Unused: - "+rule.toString());
}
@ -136,10 +127,10 @@ public class AsmFragmentTemplateUsages {
if(logFileDetails) {
log.append("\nDETAILED ASM FILE USAGES");
// Find all file templates
List<AsmFragmentTemplate> fileTemplates = new ArrayList<>();
List<AsmFragmentSynthesisResult> fileTemplates = new ArrayList<>();
for(String signature : signatures) {
Collection<AsmFragmentTemplate> templates = synthesisGraph.get(signature).getBestTemplates();
for(AsmFragmentTemplate template : templates) {
Collection<AsmFragmentSynthesisResult> templates = synthesisGraph.get(signature).getBestTemplates();
for(AsmFragmentSynthesisResult template : templates) {
if(template.isFile()) {
fileTemplates.add(template);
}
@ -151,9 +142,9 @@ public class AsmFragmentTemplateUsages {
if(logAllDetails) {
log.append("\nDETAILED ASM FRAGMENT USAGES");
List<AsmFragmentTemplate> allTemplates = new ArrayList<>();
List<AsmFragmentSynthesisResult> allTemplates = new ArrayList<>();
for(String signature : signatures) {
Collection<AsmFragmentTemplate> templates = synthesisGraph.get(signature).getBestTemplates();
Collection<AsmFragmentSynthesisResult> templates = synthesisGraph.get(signature).getBestTemplates();
allTemplates.addAll(templates);
}
logTemplatesByUsage(synthesizer, log, allTemplates, logDetailsBody);
@ -162,7 +153,7 @@ public class AsmFragmentTemplateUsages {
}
private static void logTemplatesByUsage(AsmFragmentTemplateSynthesizer synthesizer, CompileLog log, List<AsmFragmentTemplate> templates, boolean logBody) {
private static void logTemplatesByUsage(AsmFragmentTemplateSynthesizer synthesizer, CompileLog log, List<AsmFragmentSynthesisResult> templates, boolean logBody) {
// Sort by usage
Collections.sort(templates, (o1, o2) -> {
Integer u1 = fragmentTemplateUsage.get(o1);
@ -173,14 +164,14 @@ public class AsmFragmentTemplateUsages {
}
);
// Output
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
Integer usage = fragmentTemplateUsage.get(template);
if(usage == null) usage = 0;
AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis synthesis = synthesizer.getOrCreateSynthesis(template.getSignature(), log);
Collection<AsmFragmentTemplate> bestTemplates = synthesis.getBestTemplates();
AsmFragmentSynthesis synthesis = synthesizer.getOrCreateSynthesis(template.getSignature(), log);
Collection<AsmFragmentSynthesisResult> bestTemplates = synthesis.getBestTemplates();
log.append(String.format("%8d", usage) + " " + template.getSignature()+" - templates: " + bestTemplates.size());
if(logBody) {
for(AsmFragmentTemplate bestTemplate : bestTemplates) {
for(AsmFragmentSynthesisResult bestTemplate : bestTemplates) {
logTemplate(log, bestTemplate, " ");
}
@ -188,7 +179,7 @@ public class AsmFragmentTemplateUsages {
}
}
public static void logTemplate(CompileLog log, AsmFragmentTemplate template, String indent) {
public static void logTemplate(CompileLog log, AsmFragmentSynthesisResult template, String indent) {
String prefix;
if(template.isCache()) {
prefix = "cached ";

View File

@ -0,0 +1,169 @@
package dk.camelot64.kickc.fragment.synthesis;
import dk.camelot64.kickc.fragment.AsmFragmentClobber;
import java.util.*;
/**
* The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules.
* The synthesis caches the best fragments for each clobber profile (loaded or synthesized).
* A node in the synthesis graph.
*/
public class AsmFragmentSynthesis {
/**
* The signature of the fragment template being synthesized.
*/
private final String signature;
/**
* The best template loaded/synthesized so far for each clobber profile
*/
private final Map<AsmFragmentClobber, AsmFragmentSynthesisResult> bestTemplates;
/**
* Options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph.
*/
private final Set<AsmFragmentSynthesisOption> synthesisOptions;
/**
* Options for synthesizing the other templates from this template using a specific synthesis rule. Backward edges in the synthesis graph.
*/
private final Set<AsmFragmentSynthesisOption> parentOptions;
/**
* The templates loaded from a file. Empty if no file exists for the signature.
*/
private final List<AsmFragmentSynthesisResult> fileTemplates;
/**
* Create a new synthesis
*
* @param signature The signature of the fragment template to load/synthesize
*/
AsmFragmentSynthesis(String signature) {
this.signature = signature;
this.bestTemplates = new LinkedHashMap<>();
this.synthesisOptions = new LinkedHashSet<>();
this.parentOptions = new LinkedHashSet<>();
this.fileTemplates = new ArrayList<>();
}
/**
* Add a synthesis option to the template synthesis.
* The synthesis options can be used for synthesizing the template from sub-fragments using a specific synthesis rule.
*
* @param synthesisOption The option to add
*/
void addSynthesisOption(AsmFragmentSynthesisOption synthesisOption) {
this.synthesisOptions.add(synthesisOption);
}
/**
* Get the options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph.
*
* @return The options
*/
Collection<AsmFragmentSynthesisOption> getSynthesisOptions() {
return synthesisOptions;
}
/**
* Add a parent. The parent is an option for synthesizing another fragment template from this one using a specific synthesis rule.
*
* @param synthesisOption Thew parent option to add
*/
void addParentOption(AsmFragmentSynthesisOption synthesisOption) {
this.parentOptions.add(synthesisOption);
}
void addFileTemplate(AsmFragmentSynthesisResult fileTemplate) {
this.fileTemplates.add(fileTemplate);
}
List<AsmFragmentSynthesisResult> getFileTemplates() {
return fileTemplates;
}
/**
* Handle a candidate for best template.
* If the candidate is better than the current best for its clobber profile update the best template
*
* @param candidate The template candidate to examine
* @return true if the best template was updated
*/
boolean bestTemplateCandidate(AsmFragmentSynthesisResult candidate) {
AsmFragmentClobber candidateClobber = candidate.getClobber();
double candidateCycles = candidate.getCycles();
// Check if any current best templates are better
Set<AsmFragmentClobber> bestClobbers = new LinkedHashSet<>(bestTemplates.keySet());
for (AsmFragmentClobber bestClobber : bestClobbers) {
AsmFragmentSynthesisResult bestTemplate = bestTemplates.get(bestClobber);
double bestCycles = bestTemplate.getCycles();
if (bestClobber.isTrueSubset(candidateClobber) && bestCycles <= candidateCycles) {
// A better template already found - don't update
return false;
}
if (bestClobber.isSubset(candidateClobber) && bestCycles < candidateCycles) {
// A better template already found - don't update
return false;
}
if (bestClobber.equals(candidateClobber) && bestCycles == candidateCycles && bestTemplate.getBody().compareTo(candidate.getBody()) <= 0) {
// A better template already found - don't update
return false;
}
}
// The candidate is better than some of the current best!
// Remove any current templates that are worse
for (AsmFragmentClobber bestClobber : bestClobbers) {
AsmFragmentSynthesisResult bestTemplate = bestTemplates.get(bestClobber);
double bestCycles = bestTemplate.getCycles();
if (candidateClobber.isTrueSubset(bestClobber) && candidateCycles <= bestCycles) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
if (candidateClobber.isSubset(bestClobber) && candidateCycles < bestCycles) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
if (candidateClobber.equals(bestClobber) && candidateCycles == bestCycles && candidate.getBody().compareTo(bestTemplate.getBody()) <= 0) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
}
// Update the current best
bestTemplates.put(candidateClobber, candidate);
return true;
}
/**
* Get the parent options.
* These are options for synthesizing other templates
* from this template using a specific synthesis rule. Backward edges in the synthesis graph.
*
* @return The parent options.
*/
Set<AsmFragmentSynthesisOption> getParentOptions() {
return parentOptions;
}
/**
* Get the best fragment templates of the synthesis.
* Multiple templates are returned if templates with different clobber profiles exist.
*
* @return The best templates of the synthesis.
*/
public Collection<AsmFragmentSynthesisResult> getBestTemplates() {
return bestTemplates.values();
}
public String getSignature() {
return signature;
}
}

View File

@ -0,0 +1,64 @@
package dk.camelot64.kickc.fragment.synthesis;
import java.util.List;
import java.util.Objects;
/**
* An option for synthesizing a fragment template from a sub-template using a specific synthesis rule. An edge in the synthesis graph.
*/
public class AsmFragmentSynthesisOption {
/**
* The signature of the fragment template being synthesized. The from-node in the graph.
*/
private final String signature;
/**
* The signatures of the sub-fragment templates to synthesize from. The to-nodes in the graph.
*/
private final List<String> subSignatures;
/**
* The synthesis rule capable of synthesizing this template from the sub-fragments.
*/
private final AsmFragmentTemplateSynthesisRule rule;
/**
* Create a synthesis option
*
* @param signature the signature of the fragment template being synthesized.
* @param rule The synthesis rule capable of synthesizing this template from the sub-fragment.
*/
AsmFragmentSynthesisOption(String signature, AsmFragmentTemplateSynthesisRule rule) {
this.signature = signature;
this.rule = rule;
this.subSignatures = rule.getSubSignatures(signature);
}
public String getSignature() {
return signature;
}
List<String> getSubSignatures() {
return subSignatures;
}
AsmFragmentTemplateSynthesisRule getRule() {
return rule;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AsmFragmentSynthesisOption that = (AsmFragmentSynthesisOption) o;
return Objects.equals(signature, that.signature) &&
Objects.equals(subSignatures, that.subSignatures) &&
Objects.equals(rule, that.rule);
}
@Override
public int hashCode() {
return Objects.hash(signature, subSignatures, rule);
}
}

View File

@ -0,0 +1,98 @@
package dk.camelot64.kickc.fragment.synthesis;
import dk.camelot64.kickc.fragment.AsmFragmentClobber;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
import java.util.List;
/**
* A successfully synthesized or loaded fragment.
*/
public class AsmFragmentSynthesisResult {
/**
* The ASM fragment template.
*/
final AsmFragmentTemplate fragment;
/**
* true if the fragment was loaded from disk.
*/
private boolean file;
/**
* true if the fragment was loaded from the disk cache.
*/
private boolean cache;
/**
* The synthesis that created the fragment. null if the fragment template was loaded.
*/
private AsmFragmentTemplateSynthesisRule synthesis;
/**
* The sub fragment template that the synthesis used to create this.
*/
private List<AsmFragmentSynthesisResult> subFragments;
public AsmFragmentSynthesisResult(AsmFragmentTemplate fragment, boolean file, boolean cache, AsmFragmentTemplateSynthesisRule synthesis, List<AsmFragmentSynthesisResult> subFragments) {
this.fragment = fragment;
this.file = file;
this.cache = cache;
this.synthesis = synthesis;
this.subFragments = subFragments;
}
public boolean isFile() {
return file;
}
public boolean isCache() {
return cache;
}
public AsmFragmentTemplateSynthesisRule getSynthesis() {
return synthesis;
}
public AsmFragmentTemplate getFragment() {
return fragment;
}
public List<AsmFragmentSynthesisResult> getSubFragments() {
return subFragments;
}
public String getName() {
StringBuilder name = new StringBuilder();
name.append(fragment.getSignature());
if (synthesis != null) {
name.append(" < ");
if (subFragments.size() == 1) {
name.append(subFragments.get(0).getName());
} else {
for (AsmFragmentSynthesisResult subFragment : subFragments) {
name.append("(");
name.append(subFragment.getName());
name.append(")");
}
}
}
return name.toString();
}
public AsmFragmentClobber getClobber() {
return fragment.getClobber();
}
public double getCycles() {
return fragment.getCycles();
}
public String getBody() {
return fragment.getBody();
}
public String getSignature() {
return fragment.getSignature();
}
}

View File

@ -97,7 +97,7 @@ class AsmFragmentTemplateSynthesisRuleRegex implements AsmFragmentTemplateSynthe
subSignature = subSignature.replace(bound, bindMappings.get(bound));
}
}
return Arrays.asList(subSignature);
return Collections.singletonList(subSignature);
} else {
return null;
}
@ -148,7 +148,7 @@ class AsmFragmentTemplateSynthesisRuleRegex implements AsmFragmentTemplateSynthe
if(newFragment.length() > 0 && newFragment.charAt(newFragment.length() - 1) == '\n') {
newFragment = new StringBuilder(newFragment.substring(0, newFragment.length() - 1));
}
return new AsmFragmentTemplate(signature, newFragment.toString(), this, subTemplate);
return new AsmFragmentTemplate(signature, newFragment.toString(), subTemplate.getTargetCpu());
}
static String regexpRewriteSignature(String signature, String match, String replace) {

View File

@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
/**
* Provides fragment templates from their signature.
@ -27,593 +28,394 @@ import java.util.*;
public class AsmFragmentTemplateSynthesizer {
/** Create synthesizer. */
public AsmFragmentTemplateSynthesizer(TargetCpu targetCpu, Path baseFragmentFolder, boolean useFragmentCache, CompileLog log) {
this.baseFragmentFolder = baseFragmentFolder;
this.targetCpu = targetCpu;
this.synthesisGraph = new LinkedHashMap<>();
this.bestTemplateUpdate = new ArrayDeque<>();
if(useFragmentCache)
this.fragmentCache = AsmFragmentTemplateCache.load(targetCpu, baseFragmentFolder, log);
else
this.fragmentCache = AsmFragmentTemplateCache.memoryCache(targetCpu);
}
public AsmFragmentTemplateSynthesizer(TargetCpu targetCpu, Path baseFragmentFolder, boolean useFragmentCache, CompileLog log) {
this.baseFragmentFolder = baseFragmentFolder;
this.targetCpu = targetCpu;
this.synthesisGraph = new LinkedHashMap<>();
this.bestTemplateUpdate = new ArrayDeque<>();
if (useFragmentCache)
this.fragmentCache = AsmFragmentTemplateCache.load(targetCpu, baseFragmentFolder, log);
else
this.fragmentCache = AsmFragmentTemplateCache.memoryCache(targetCpu);
}
/** The folder containing fragment files. */
private final Path baseFragmentFolder;
private final Path baseFragmentFolder;
/** The Target CPU - used for obtaining CPU-specific fragment files. */
private final TargetCpu targetCpu;
private final TargetCpu targetCpu;
/** Cache for the best fragment templates. Maps signature to the best fragment template for the signature. */
private final AsmFragmentTemplateCache fragmentCache;
private final AsmFragmentTemplateCache fragmentCache;
/**
* Contains the synthesis for each fragment template signature.
* The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules.
* The synthesis caches the best fragments for each clobber profile (loaded or synthesized).
* This map is effectively a full-graph where the nodes are synthesis for signatures and edges are the
* synthesis rules capable of synthesizing one fragment temple from another.
*/
private final Map<String, AsmFragmentSynthesis> synthesisGraph;
/**
* Contains the synthesis for each fragment template signature.
* The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules.
* The synthesis caches the best fragments for each clobber profile (loaded or synthesized).
* This map is effectively a full-graph where the nodes are synthesis for signatures and edges are the
* synthesis rules capable of synthesizing one fragment temple from another.
*/
private final Map<String, AsmFragmentSynthesis> synthesisGraph;
/** Finalize the fragment template synthesizer. */
void finalize(CompileLog log) {
if(fragmentCache != null)
fragmentCache.save(baseFragmentFolder, log);
}
void finalize(CompileLog log) {
if (fragmentCache != null)
fragmentCache.save(baseFragmentFolder, log);
}
/**
* Get information about the size of the synthesizer data structures
*
* @return the size
*/
public int getSize() {
return synthesisGraph.size();
}
/**
* Get information about the size of the synthesizer data structures
*
* @return the size
*/
public int getSize() {
return synthesisGraph.size();
}
public AsmFragmentInstance getFragmentInstance(AsmFragmentInstanceSpec instanceSpec, CompileLog log) {
String signature = instanceSpec.getSignature();
AsmFragmentTemplate fragmentTemplate = getFragmentTemplate(signature, log);
// Return the resulting fragment instance
return new AsmFragmentInstance(
instanceSpec.getProgram(),
signature,
instanceSpec.getCodeScope(),
fragmentTemplate,
instanceSpec.getBindings());
}
public AsmFragmentInstance getFragmentInstance(AsmFragmentInstanceSpec instanceSpec, CompileLog log) {
String signature = instanceSpec.getSignature();
AsmFragmentTemplate fragmentTemplate = getFragmentTemplate(signature, log);
// Return the resulting fragment instance
return new AsmFragmentInstance(
instanceSpec.getProgram(),
signature,
instanceSpec.getCodeScope(),
fragmentTemplate,
instanceSpec.getBindings());
}
/**
* Get the best fragment templates for a signature
*
* @param signature The signature
* @param log The log
* @return The best templates (with different clobber profiles) for the signature
*/
private AsmFragmentTemplate getFragmentTemplate(String signature, CompileLog log) {
// Attempt to find in memory/disk cache
AsmFragmentTemplate bestTemplate = fragmentCache.get(signature);
if(bestTemplate == AsmFragmentTemplateCache.NO_SYNTHESIS) {
if(log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature);
}
throw new UnknownFragmentException(signature);
}
if(bestTemplate == null) {
// Attempt to synthesize or load in main fragment folder
Collection<AsmFragmentTemplate> candidates = getBestTemplates(signature, log);
if(candidates.size() == 0) {
if(log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature);
/**
* Get the best fragment templates for a signature
*
* @param signature The signature
* @param log The log
* @return The best templates (with different clobber profiles) for the signature
*/
private AsmFragmentTemplate getFragmentTemplate(String signature, CompileLog log) {
// Attempt to find in memory/disk cache
AsmFragmentTemplate bestTemplate = fragmentCache.get(signature);
if (bestTemplate == AsmFragmentTemplateCache.NO_SYNTHESIS) {
if (log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature);
}
throw new UnknownFragmentException(signature);
}
if (bestTemplate != null) {
AsmFragmentTemplateUsages.incUsage(new AsmFragmentSynthesisResult(bestTemplate, false, true, null, new ArrayList<>()));
return bestTemplate;
}
AsmFragmentSynthesisResult bestResult = null;
// Attempt to synthesize or load in main fragment folder
Collection<AsmFragmentSynthesisResult> candidates = getBestTemplates(signature, log);
if (candidates.size() == 0) {
if (log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature);
}
fragmentCache.put(signature, AsmFragmentTemplateCache.NO_SYNTHESIS);
throw new UnknownFragmentException(signature);
}
double minScore = Double.MAX_VALUE;
for(AsmFragmentTemplate candidateTemplate : candidates) {
}
double minScore = Double.MAX_VALUE;
for (AsmFragmentSynthesisResult candidateTemplate : candidates) {
double score = candidateTemplate.getCycles();
if(candidateTemplate.getClobber().isClobberZ()) score += 0.25;
if(candidateTemplate.getClobber().isClobberA()) score += 0.5;
if(candidateTemplate.getClobber().isClobberY()) score += 1.0;
if(candidateTemplate.getClobber().isClobberX()) score += 1.5;
if(score < minScore) {
minScore = score;
bestTemplate = candidateTemplate;
if (candidateTemplate.getClobber().isClobberZ()) score += 0.25;
if (candidateTemplate.getClobber().isClobberA()) score += 0.5;
if (candidateTemplate.getClobber().isClobberY()) score += 1.0;
if (candidateTemplate.getClobber().isClobberX()) score += 1.5;
if (score < minScore) {
minScore = score;
bestResult = candidateTemplate;
}
}
if(log.isVerboseFragmentLog()) {
log.append("Found best fragment " + bestTemplate.getName() + " score: " + minScore);
}
fragmentCache.put(signature, bestTemplate);
}
// Count usages
AsmFragmentTemplateUsages.incUsage(bestTemplate);
return bestTemplate;
}
}
if (log.isVerboseFragmentLog()) {
log.append("Found best fragment " + bestResult.getName() + " score: " + minScore);
}
fragmentCache.put(signature, bestResult.getFragment());
AsmFragmentTemplateUsages.incUsage(bestResult);
return bestResult.getFragment();
}
/**
* Get the best fragment templates for a signature.
* The templates are either loaded or synthesized from other templates.
*
* @param signature The signature of the fragment template to get
* @param log The compile log
* @return The best templates for the passed signature
*/
public Collection<AsmFragmentTemplate> getBestTemplates(String signature, CompileLog log) {
getOrCreateSynthesis(signature, log);
updateBestTemplates(log);
AsmFragmentSynthesis synthesis = getSynthesis(signature);
if(synthesis == null) {
throw new RuntimeException("Synthesis Graph Error! synthesis not found in graph " + signature);
}
return synthesis.getBestTemplates();
}
/**
* Get the best fragment templates for a signature.
* The templates are either loaded or synthesized from other templates.
*
* @param signature The signature of the fragment template to get
* @param log The compile log
* @return The best templates for the passed signature
*/
public Collection<AsmFragmentSynthesisResult> getBestTemplates(String signature, CompileLog log) {
getOrCreateSynthesis(signature, log);
updateBestTemplates(log);
AsmFragmentSynthesis synthesis = getSynthesis(signature);
if (synthesis == null) {
throw new RuntimeException("Synthesis Graph Error! synthesis not found in graph " + signature);
}
return synthesis.getBestTemplates();
}
/**
* The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules.
* The synthesis caches the best fragments for each clobber profile (loaded or synthesized).
* A node in the synthesis graph.
*/
public static class AsmFragmentSynthesis {
/**
* Get the entire synthesis graph. Called by the usage statistics.
*
* @return The entire synthesis graph
*/
public Map<String, AsmFragmentSynthesis> getSynthesisGraph() {
return synthesisGraph;
}
/** The signature of the fragment template being synthesized. */
private final String signature;
/**
* Get the synthesis used to synthesize a fragment template.
* If the synthesis does not exist null is returned.
*
* @param signature The signature of the template to synthesize
* @return The synthesis used to synthesize the fragment template.
* null if the synthesis graph does not contain the synthesis.
*/
private AsmFragmentSynthesis getSynthesis(String signature) {
return synthesisGraph.get(signature);
}
/** The best template loaded/synthesized so far for each clobber profile */
private final Map<AsmFragmentClobber, AsmFragmentTemplate> bestTemplates;
/** Options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph. */
private final Set<AsmFragmentSynthesisOption> synthesisOptions;
/** Options for synthesizing the other templates from this template using a specific synthesis rule. Backward edges in the synthesis graph. */
private final Set<AsmFragmentSynthesisOption> parentOptions;
/** The templates loaded from a file. Empty if no file exists for the signature. */
private final List<AsmFragmentTemplate> fileTemplates;
/**
* Create a new synthesis
*
* @param signature The signature of the fragment template to load/synthesize
*/
AsmFragmentSynthesis(String signature) {
this.signature = signature;
this.bestTemplates = new LinkedHashMap<>();
this.synthesisOptions = new LinkedHashSet<>();
this.parentOptions = new LinkedHashSet<>();
this.fileTemplates = new ArrayList<>();
}
/**
* Add a synthesis option to the template synthesis.
* The synthesis options can be used for synthesizing the template from sub-fragments using a specific synthesis rule.
*
* @param synthesisOption The option to add
*/
void addSynthesisOption(AsmFragmentSynthesisOption synthesisOption) {
this.synthesisOptions.add(synthesisOption);
}
/**
* Get the options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph.
*
* @return The options
*/
Collection<AsmFragmentSynthesisOption> getSynthesisOptions() {
return synthesisOptions;
}
/**
* Add a parent. The parent is an option for synthesizing another fragment template from this one using a specific synthesis rule.
*
* @param synthesisOption Thew parent option to add
*/
void addParentOption(AsmFragmentSynthesisOption synthesisOption) {
this.parentOptions.add(synthesisOption);
}
void addFileTemplate(AsmFragmentTemplate fileTemplate) {
this.fileTemplates.add(fileTemplate);
}
List<AsmFragmentTemplate> getFileTemplates() {
return fileTemplates;
}
/**
* Handle a candidate for best template.
* If the candidate is better than the current best for its clobber profile update the best template
*
* @param candidate The template candidate to examine
* @return true if the best template was updated
*/
boolean bestTemplateCandidate(AsmFragmentTemplate candidate) {
AsmFragmentClobber candidateClobber = candidate.getClobber();
double candidateCycles = candidate.getCycles();
// Check if any current best templates are better
Set<AsmFragmentClobber> bestClobbers = new LinkedHashSet<>(bestTemplates.keySet());
for(AsmFragmentClobber bestClobber : bestClobbers) {
AsmFragmentTemplate bestTemplate = bestTemplates.get(bestClobber);
double bestCycles = bestTemplate.getCycles();
if(bestClobber.isTrueSubset(candidateClobber) && bestCycles <= candidateCycles) {
// A better template already found - don't update
return false;
/**
* Get (or create) the synthesis used to synthesize a fragment template.
* If the synthesis does not already exist in the synthesis graph it is created - along with any sub-synthesis recursively usable for creating it.
* If any synthesis are created they are added to the {@link #bestTemplateUpdate} queue.
*
* @param signature The signature of the template to synthesize
* @param log The compile log
* @return The synthesis that is used to load/synthesize the best template
*/
public AsmFragmentSynthesis getOrCreateSynthesis(String signature, CompileLog log) {
AsmFragmentSynthesis synthesis = getSynthesis(signature);
if (synthesis != null) {
return synthesis;
}
// Synthesis not found - create it
if (log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature);
}
synthesis = new AsmFragmentSynthesis(signature);
synthesisGraph.put(signature, synthesis);
queueUpdateBestTemplate(synthesis);
// Load the template from file - if it exists
List<AsmFragmentSynthesisResult> fileTemplates = loadFragmentTemplates(signature);
for (AsmFragmentSynthesisResult fileTemplate : fileTemplates) {
synthesis.addFileTemplate(fileTemplate);
if (log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature + " - Successfully loaded " + signature + ".asm");
}
if(bestClobber.isSubset(candidateClobber) && bestCycles < candidateCycles) {
// A better template already found - don't update
return false;
}
// Populate with synthesis options
for (AsmFragmentTemplateSynthesisRule rule : AsmFragmentTemplateSynthesisRuleManager.getSynthesisRules(targetCpu)) {
if (rule.matches(signature)) {
AsmFragmentSynthesisOption synthesisOption = new AsmFragmentSynthesisOption(signature, rule);
synthesis.addSynthesisOption(synthesisOption);
if (log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature + " - sub-option " + String.join(",", synthesisOption.getSubSignatures()));
}
}
if(bestClobber.equals(candidateClobber) && bestCycles == candidateCycles && bestTemplate.getBody().compareTo(candidate.getBody()) <= 0) {
// A better template already found - don't update
return false;
}
// Ensure that all sub-synthesis exist (recursively) - and set their parent options
for (AsmFragmentSynthesisOption synthesisOption : synthesis.getSynthesisOptions()) {
for (String subSignature : synthesisOption.getSubSignatures()) {
AsmFragmentSynthesis subSynthesis = getOrCreateSynthesis(subSignature, log);
subSynthesis.addParentOption(synthesisOption);
}
}
// The candidate is better than some of the current best!
// Remove any current templates that are worse
for(AsmFragmentClobber bestClobber : bestClobbers) {
AsmFragmentTemplate bestTemplate = bestTemplates.get(bestClobber);
double bestCycles = bestTemplate.getCycles();
if(candidateClobber.isTrueSubset(bestClobber) && candidateCycles <= bestCycles) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
if(candidateClobber.isSubset(bestClobber) && candidateCycles < bestCycles) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
if(candidateClobber.equals(bestClobber) && candidateCycles == bestCycles && candidate.getBody().compareTo(bestTemplate.getBody()) < 0) {
// The candidate is better - remove the current template
bestTemplates.remove(bestClobber);
}
}
// Update the current best
bestTemplates.put(candidateClobber, candidate);
return true;
}
/**
* Get the parent options.
* These are options for synthesizing other templates
* from this template using a specific synthesis rule. Backward edges in the synthesis graph.
*
* @return The parent options.
*/
Set<AsmFragmentSynthesisOption> getParentOptions() {
return parentOptions;
}
/**
* Get the best fragment templates of the synthesis.
* Multiple templates are returned if templates with different clobber profiles exist.
*
* @return The best templates of the synthesis.
*/
public Collection<AsmFragmentTemplate> getBestTemplates() {
return bestTemplates.values();
}
public String getSignature() {
return signature;
}
}
/** An option for synthesizing a fragment template from a sub-template using a specific synthesis rule. An edge in the synthesis graph. */
public static class AsmFragmentSynthesisOption {
/** The signature of the fragment template being synthesized. The from-node in the graph. */
private final String signature;
/** The signatures of the sub-fragment templates to synthesize from. The to-nodes in the graph. */
private final List<String> subSignatures;
/** The synthesis rule capable of synthesizing this template from the sub-fragments. */
private final AsmFragmentTemplateSynthesisRule rule;
/**
* Create a synthesis option
*
* @param signature he signature of the fragment template being synthesized.
* @param rule The synthesis rule capable of synthesizing this template from the sub-fragment.
*/
AsmFragmentSynthesisOption(String signature, AsmFragmentTemplateSynthesisRule rule) {
this.signature = signature;
this.rule = rule;
this.subSignatures = rule.getSubSignatures(signature);
}
public String getSignature() {
return signature;
}
List<String> getSubSignatures() {
return subSignatures;
}
AsmFragmentTemplateSynthesisRule getRule() {
return rule;
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
AsmFragmentSynthesisOption that = (AsmFragmentSynthesisOption) o;
return Objects.equals(signature, that.signature) &&
Objects.equals(subSignatures, that.subSignatures) &&
Objects.equals(rule, that.rule);
}
@Override
public int hashCode() {
return Objects.hash(signature, subSignatures, rule);
}
}
/**
* Get the entire synthesis graph. Called by the usage statistics.
*
* @return The entire synthesis graph
*/
public Map<String, AsmFragmentSynthesis> getSynthesisGraph() {
return synthesisGraph;
}
/**
* Get the synthesis used to synthesize a fragment template.
* If the synthesis does not exist null is returned.
*
* @param signature The signature of the template to synthesize
* @return The synthesis used to synthesize the fragment template.
* null if the synthesis graph does not contain the synthesis.
*/
private AsmFragmentSynthesis getSynthesis(String signature) {
return synthesisGraph.get(signature);
}
/**
* Get (or create) the synthesis used to synthesize a fragment template.
* If the synthesis does not already exist in the synthesis graph it is created - along with any sub-synthesis recursively usable for creating it.
* If any synthesis are created they are added to the {@link #bestTemplateUpdate} queue.
*
* @param signature The signature of the template to synthesize
* @param log The compile log
* @return The synthesis that is used to load/synthesize the best template
*/
public AsmFragmentSynthesis getOrCreateSynthesis(String signature, CompileLog log) {
AsmFragmentSynthesis synthesis = getSynthesis(signature);
if(synthesis != null) {
return synthesis;
}
// Synthesis not found - create it
if(log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature);
}
synthesis = new AsmFragmentSynthesis(signature);
synthesisGraph.put(signature, synthesis);
queueUpdateBestTemplate(synthesis);
// Load the template from file - if it exists
List<AsmFragmentTemplate> fileTemplates = loadFragmentTemplates(signature);
for(AsmFragmentTemplate fileTemplate : fileTemplates) {
synthesis.addFileTemplate(fileTemplate);
if(log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature + " - Successfully loaded " + signature + ".asm");
}
}
// Populate with synthesis options
for(AsmFragmentTemplateSynthesisRule rule : AsmFragmentTemplateSynthesisRuleManager.getSynthesisRules(targetCpu)) {
if(rule.matches(signature)) {
AsmFragmentSynthesisOption synthesisOption = new AsmFragmentSynthesisOption(signature, rule);
synthesis.addSynthesisOption(synthesisOption);
if(log.isVerboseFragmentLog()) {
log.append("New fragment synthesis " + signature + " - sub-option " + String.join(",", synthesisOption.getSubSignatures()));
}
}
}
// Ensure that all sub-synthesis exist (recursively) - and set their parent options
for(AsmFragmentSynthesisOption synthesisOption : synthesis.getSynthesisOptions()) {
for (String subSignature : synthesisOption.getSubSignatures()) {
AsmFragmentSynthesis subSynthesis = getOrCreateSynthesis(subSignature, log);
subSynthesis.addParentOption(synthesisOption);
}
}
return synthesis;
}
}
return synthesis;
}
/** Work queue with synthesis that need to be recalculated to find the best templates. */
private final Deque<AsmFragmentSynthesis> bestTemplateUpdate;
private final Deque<AsmFragmentSynthesis> bestTemplateUpdate;
/**
* Queue an update of the best templates for a synthesis to the work queue
*
* @param synthesis The synthesis to add to the work queue
*/
private void queueUpdateBestTemplate(AsmFragmentSynthesis synthesis) {
if(!bestTemplateUpdate.contains(synthesis)) {
bestTemplateUpdate.push(synthesis);
}
}
/**
* Queue an update of the best templates for a synthesis to the work queue
*
* @param synthesis The synthesis to add to the work queue
*/
private void queueUpdateBestTemplate(AsmFragmentSynthesis synthesis) {
if (!bestTemplateUpdate.contains(synthesis)) {
bestTemplateUpdate.push(synthesis);
}
}
/**
* Find the best fragment template for all synthesis in the work queue {@link #bestTemplateUpdate} queue.
* During the update more items can be added to the work queue.
* Returns when the work queue is empty.
*/
private void updateBestTemplates(CompileLog log) {
while(!bestTemplateUpdate.isEmpty()) {
AsmFragmentSynthesis synthesis = bestTemplateUpdate.pop();
boolean modified = false;
// Check if the file template is best in class
List<AsmFragmentTemplate> fileTemplates = synthesis.getFileTemplates();
for(AsmFragmentTemplate fileTemplate : fileTemplates) {
modified |= synthesis.bestTemplateCandidate(fileTemplate);
}
Collection<AsmFragmentSynthesisOption> synthesisOptions = synthesis.getSynthesisOptions();
for(AsmFragmentSynthesisOption synthesisOption : synthesisOptions) {
// for each sub-signature find the best templates
List<Collection<AsmFragmentTemplate>> subSignatureTemplates = new ArrayList<>();
for (String subSignature : synthesisOption.getSubSignatures()) {
AsmFragmentSynthesis subSynthesis = getSynthesis(subSignature);
if (subSynthesis == null) {
throw new RuntimeException("Synthesis Graph Error! Sub-synthesis not found in graph " + subSignature);
}
Collection<AsmFragmentTemplate> subTemplates = subSynthesis.getBestTemplates();
subSignatureTemplates.add(subTemplates);
/**
* Find the best fragment template for all synthesis in the work queue {@link #bestTemplateUpdate} queue.
* During the update more items can be added to the work queue.
* Returns when the work queue is empty.
*/
private void updateBestTemplates(CompileLog log) {
while (!bestTemplateUpdate.isEmpty()) {
AsmFragmentSynthesis synthesis = bestTemplateUpdate.pop();
boolean modified = false;
// Check if the file template is best in class
List<AsmFragmentSynthesisResult> fileTemplates = synthesis.getFileTemplates();
for (AsmFragmentSynthesisResult fileTemplate : fileTemplates) {
modified |= synthesis.bestTemplateCandidate(fileTemplate);
}
AsmFragmentTemplateSynthesisRule rule = synthesisOption.getRule();
// Create all combinations of the best sub-templates
List<List<AsmFragmentTemplate>> combinations = combinations(subSignatureTemplates);
for (List<AsmFragmentTemplate> subTemplateCombination : combinations) {
AsmFragmentTemplate synthesized = rule.synthesize(synthesis.getSignature(), subTemplateCombination);
if (synthesized != null) {
if (log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - Successfully synthesized from " + String.join(",", synthesisOption.subSignatures));
}
modified |= synthesis.bestTemplateCandidate(synthesized);
} else {
if (log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - Sub clobber prevents synthesis from " + String.join(",", synthesisOption.subSignatures));
}
}
Collection<AsmFragmentSynthesisOption> synthesisOptions = synthesis.getSynthesisOptions();
for (AsmFragmentSynthesisOption synthesisOption : synthesisOptions) {
// for each sub-signature find the best templates
List<Collection<AsmFragmentSynthesisResult>> subSignatureTemplates = new ArrayList<>();
for (String subSignature : synthesisOption.getSubSignatures()) {
AsmFragmentSynthesis subSynthesis = getSynthesis(subSignature);
if (subSynthesis == null) {
throw new RuntimeException("Synthesis Graph Error! Sub-synthesis not found in graph " + subSignature);
}
Collection<AsmFragmentSynthesisResult> subTemplates = subSynthesis.getBestTemplates();
subSignatureTemplates.add(subTemplates);
}
AsmFragmentTemplateSynthesisRule rule = synthesisOption.getRule();
// Create all combinations of the best sub-templates
List<List<AsmFragmentSynthesisResult>> combinations = combinations(subSignatureTemplates);
for (List<AsmFragmentSynthesisResult> subFragmentCombination : combinations) {
final List<AsmFragmentTemplate> subFragments = subFragmentCombination.stream().map(AsmFragmentSynthesisResult::getFragment).collect(Collectors.toList());
final AsmFragmentTemplate synthesize = rule.synthesize(synthesis.getSignature(), subFragments);
if (synthesize != null) {
if (log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - Successfully synthesized from " + String.join(",", synthesisOption.getSubSignatures()));
}
AsmFragmentSynthesisResult synthesisResult = new AsmFragmentSynthesisResult(synthesize, false, false, rule, subFragmentCombination);
modified |= synthesis.bestTemplateCandidate(synthesisResult);
} else {
if (log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - Sub clobber prevents synthesis from " + String.join(",", synthesisOption.getSubSignatures()));
}
}
}
}
}
if(modified) {
// The synthesis was modified - update all parents later
for(AsmFragmentSynthesisOption parentOption : synthesis.getParentOptions()) {
String parentSignature = parentOption.getSignature();
AsmFragmentSynthesis parentSynthesis = getSynthesis(parentSignature);
if(parentSynthesis == null) {
throw new RuntimeException("Synthesis Graph Error! Parent synthesis not found in graph " + parentSignature);
}
queueUpdateBestTemplate(parentSynthesis);
if(log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - New best, scheduling parent " + parentSignature);
}
if (modified) {
// The synthesis was modified - update all parents later
for (AsmFragmentSynthesisOption parentOption : synthesis.getParentOptions()) {
String parentSignature = parentOption.getSignature();
AsmFragmentSynthesis parentSynthesis = getSynthesis(parentSignature);
if (parentSynthesis == null) {
throw new RuntimeException("Synthesis Graph Error! Parent synthesis not found in graph " + parentSignature);
}
queueUpdateBestTemplate(parentSynthesis);
if (log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - New best, scheduling parent " + parentSignature);
}
}
}
}
if(synthesis.getBestTemplates().isEmpty() && log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - No file or synthesis results!");
}
}
}
/**
* Generate all possible combinations of a number of options.
* @param options A list of the options for each position.
* @param <T> The top of an option
* @return A list with all combinations of the passed options on each position.
*/
private static <T> List<List<T>> combinations(List<Collection<T>> options) {
if(options.isEmpty()) {
// the empty list has one single combination
final ArrayList<List<T>> combinations = new ArrayList<>();
combinations.add(new ArrayList<>());
return combinations;
} else {
// Calculate recursively
final List<Collection<T>> tail = options.subList(1, options.size());
List<List<T>> tailCombinations = combinations(tail);
final ArrayList<List<T>> combinations = new ArrayList<>();
final Collection<T> head = options.get(0);
for (T headOption : head) {
for (List<T> tailCombination : tailCombinations) {
final ArrayList<T> combination = new ArrayList<>();
combination.add(headOption);
combination.addAll(tailCombination);
combinations.add(combination);
if (synthesis.getBestTemplates().isEmpty() && log.isVerboseFragmentLog()) {
log.append("Fragment synthesis " + synthesis.getSignature() + " - No file or synthesis results!");
}
}
return combinations;
}
}
}
}
/**
* Attempt to load a fragment template from disk. Also searches relevant fragment sub-folders specified by CPU and other options.
*
* @param signature The signature
* @return The fragment template from a file. null if the template is not found as a file.
*/
private List<AsmFragmentTemplate> loadFragmentTemplates(String signature) {
ArrayList<AsmFragmentTemplate> fileTemplates = new ArrayList<>();
List<TargetCpu.Feature> cpuFeatures = targetCpu.getFeatures();
for(TargetCpu.Feature cpuFeature : cpuFeatures) {
AsmFragmentTemplate fileFragment = loadFragmentTemplate(signature, baseFragmentFolder.resolve(cpuFeature.getName()));
if(fileFragment != null)
fileTemplates.add(fileFragment);
}
return fileTemplates;
}
/**
* Generate all possible combinations of a number of options.
*
* @param options A list of the options for each position.
* @param <T> The top of an option
* @return A list with all combinations of the passed options on each position.
*/
private static <T> List<List<T>> combinations(List<Collection<T>> options) {
if (options.isEmpty()) {
// the empty list has one single combination
final ArrayList<List<T>> combinations = new ArrayList<>();
combinations.add(new ArrayList<>());
return combinations;
} else {
// Calculate recursively
final List<Collection<T>> tail = options.subList(1, options.size());
List<List<T>> tailCombinations = combinations(tail);
final ArrayList<List<T>> combinations = new ArrayList<>();
final Collection<T> head = options.get(0);
for (T headOption : head) {
for (List<T> tailCombination : tailCombinations) {
final ArrayList<T> combination = new ArrayList<>();
combination.add(headOption);
combination.addAll(tailCombination);
combinations.add(combination);
}
}
return combinations;
}
}
/**
* Attempt to load a fragment template from a folder on disk
*
* @param signature The signature to search for
* @param fragmentFolder The folder to look in
* @return any fragment with the gicen signature found in the folder. null if not found.
*/
private AsmFragmentTemplate loadFragmentTemplate(String signature, Path fragmentFolder) {
try {
File fragmentFile = fragmentFolder.resolve(signature + ".asm").toFile();
if(!fragmentFile.exists()) {
return null;
}
InputStream fragmentStream = new FileInputStream(fragmentFile);
String body;
if(fragmentStream.available() == 0) {
body = "";
} else {
CharStream fragmentCharStream = CharStreams.fromStream(fragmentStream);
body = fixNewlines(fragmentCharStream.toString());
}
return new AsmFragmentTemplate(signature, body, targetCpu, false);
} catch(IOException e) {
throw new RuntimeException("Error loading fragment file " + signature, e);
} catch(StringIndexOutOfBoundsException e) {
throw new RuntimeException("Problem reading fragment file " + signature, e);
}
}
/**
* Attempt to load a fragment template from disk. Also searches relevant fragment sub-folders specified by CPU and other options.
*
* @param signature The signature
* @return The fragment template from a file. null if the template is not found as a file.
*/
private List<AsmFragmentSynthesisResult> loadFragmentTemplates(String signature) {
ArrayList<AsmFragmentSynthesisResult> fileTemplates = new ArrayList<>();
List<TargetCpu.Feature> cpuFeatures = targetCpu.getFeatures();
for (TargetCpu.Feature cpuFeature : cpuFeatures) {
AsmFragmentSynthesisResult fileFragment = loadFragmentTemplate(signature, baseFragmentFolder.resolve(cpuFeature.getName()));
if (fileFragment != null)
fileTemplates.add(fileFragment);
}
return fileTemplates;
}
/**
* Fix all newlines in the body.
* - Removes all '\r'
* - Removes all trailing newlines
*
* @param body The body
* @return The body with fixed newlines
*/
public static String fixNewlines(String body) {
body = body.replace("\r", "");
while(body.length() > 0 && body.charAt(body.length() - 1) == '\n') {
body = body.substring(0, body.length() - 1);
}
return body;
}
/**
* Attempt to load a fragment template from a folder on disk
*
* @param signature The signature to search for
* @param fragmentFolder The folder to look in
* @return any fragment with the given signature found in the folder. null if not found.
*/
private AsmFragmentSynthesisResult loadFragmentTemplate(String signature, Path fragmentFolder) {
try {
File fragmentFile = fragmentFolder.resolve(signature + ".asm").toFile();
if (!fragmentFile.exists()) {
return null;
}
InputStream fragmentStream = new FileInputStream(fragmentFile);
String body;
if (fragmentStream.available() == 0) {
body = "";
} else {
CharStream fragmentCharStream = CharStreams.fromStream(fragmentStream);
body = fixNewlines(fragmentCharStream.toString());
}
final AsmFragmentTemplate fragment = new AsmFragmentTemplate(signature, body, targetCpu);
return new AsmFragmentSynthesisResult(fragment, true, false, null, new ArrayList<>());
} catch (IOException e) {
throw new RuntimeException("Error loading fragment file " + signature, e);
} catch (StringIndexOutOfBoundsException e) {
throw new RuntimeException("Problem reading fragment file " + signature, e);
}
}
public File[] allFragmentFiles() {
return baseFragmentFolder.toFile().listFiles((dir, name) -> name.endsWith(".asm"));
/**
* Fix all newlines in the body.
* - Removes all '\r'
* - Removes all trailing newlines
*
* @param body The body
* @return The body with fixed newlines
*/
public static String fixNewlines(String body) {
body = body.replace("\r", "");
while (body.length() > 0 && body.charAt(body.length() - 1) == '\n') {
body = body.substring(0, body.length() - 1);
}
return body;
}
}
public File[] allFragmentFiles() {
return baseFragmentFolder.toFile().listFiles((dir, name) -> name.endsWith(".asm"));
public static class UnknownFragmentException extends RuntimeException {
}
private final String signature;
public static class UnknownFragmentException extends RuntimeException {
public UnknownFragmentException(String signature) {
super("Fragment not found " + signature);
this.signature = signature;
}
private final String signature;
public String getFragmentSignature() {
return signature;
}
public UnknownFragmentException(String signature) {
super("Fragment not found " + signature);
this.signature = signature;
}
}
public String getFragmentSignature() {
return signature;
}
}
}

View File

@ -1,9 +1,9 @@
package dk.camelot64.kickc.test;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateUsages;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentSynthesisResult;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.model.TargetCpu;
import dk.camelot64.kickc.model.operators.Operators;
import org.junit.jupiter.api.AfterAll;
@ -196,11 +196,11 @@ public class TestFragments {
asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(TargetCpu.MOS6502X, new File("src/main/fragment/").toPath(), false, new CompileLog());
log.setSysOut(true);
//log.setVerboseFragmentLog(true);
List<AsmFragmentTemplate> templates =
List<AsmFragmentSynthesisResult> templates =
new ArrayList<>(asmFragmentTemplateSynthesizer.getBestTemplates(signature, log));
if(templates.size() > 0) {
log.append("");
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
AsmFragmentTemplateUsages.logTemplate(log, template, "");
}
log.append("");
@ -227,14 +227,14 @@ public class TestFragments {
String signature = sigs.get(testIdx);
List<AsmFragmentTemplate> templates =
List<AsmFragmentSynthesisResult> templates =
new ArrayList<>(asmFragmentTemplateSynthesizer.getBestTemplates(signature, log));
Collections.sort(templates, Comparator.comparing(AsmFragmentTemplate::getClobber));
Collections.sort(templates, Comparator.comparing(AsmFragmentSynthesisResult::getClobber));
if(templates.size() == 0) {
log.append("CANNOT SYNTHESIZE " + signature);
}
for(AsmFragmentTemplate template : templates) {
for(AsmFragmentSynthesisResult template : templates) {
String prefix = "";
if(template.isCache()) {
prefix = "cached ";