mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-21 14:30:21 +00:00
separated synthesis from fragment tempaltes.
This commit is contained in:
parent
36936efe80
commit
24049a12c1
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 ";
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 ";
|
||||
|
Loading…
Reference in New Issue
Block a user