mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-06-02 00:41:42 +00:00
201 lines
7.1 KiB
Java
201 lines
7.1 KiB
Java
package dk.camelot64.kickc.fragment.synthesis;
|
|
|
|
import dk.camelot64.kickc.fragment.AsmFragmentClobber;
|
|
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
|
|
import org.antlr.v4.runtime.ParserRuleContext;
|
|
import org.antlr.v4.runtime.tree.ParseTree;
|
|
|
|
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 AsmFragmentSignature 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 List<AsmFragmentSynthesisOption> synthesisOptions;
|
|
|
|
/**
|
|
* Options for synthesizing the other templates from this template using a specific synthesis rule. Backward edges in the synthesis graph.
|
|
*/
|
|
private final List<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(AsmFragmentSignature signature) {
|
|
this.signature = signature;
|
|
this.bestTemplates = new LinkedHashMap<>();
|
|
this.synthesisOptions = new ArrayList<>();
|
|
this.parentOptions = new ArrayList<>();
|
|
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) {
|
|
|
|
// Check if any current best templates are better
|
|
Set<AsmFragmentClobber> bestClobbers = new LinkedHashSet<>(bestTemplates.keySet());
|
|
for(AsmFragmentClobber bestClobber : bestClobbers) {
|
|
AsmFragmentSynthesisResult bestTemplate = bestTemplates.get(bestClobber);
|
|
if(isBetter(bestTemplate, candidate)) return false;
|
|
}
|
|
|
|
// The candidate is better than some current best!
|
|
|
|
// Remove any current templates that are worse
|
|
for(AsmFragmentClobber bestClobber : bestClobbers) {
|
|
AsmFragmentSynthesisResult bestTemplate = bestTemplates.get(bestClobber);
|
|
double bestCycles = bestTemplate.getCycles();
|
|
if(isBetter(candidate, bestTemplate)) {
|
|
// The candidate is better - remove the current template
|
|
bestTemplates.remove(bestClobber);
|
|
}
|
|
}
|
|
// Update the current best
|
|
bestTemplates.put(candidate.getClobber(), candidate);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Determines if a candidate ASM fragment template is better than another template.
|
|
* <p>
|
|
* The candidate is better if either
|
|
* <ul>
|
|
* <li>The clobber of the candidate is a true subset of the other and the number of cycles spent is smaller</li>
|
|
* <li>The clobber of the candidate is the same as the other and the number of cycles spent is smaller</li>
|
|
* <li>The clobber and number of cycles spent is the same but the body is shorter</li>
|
|
* </ul>
|
|
*
|
|
* @param candidate The candidate template
|
|
* @param other The other template
|
|
* @return true if the candidate is better
|
|
*/
|
|
private boolean isBetter(AsmFragmentSynthesisResult candidate, AsmFragmentSynthesisResult other) {
|
|
final AsmFragmentClobber candidateClobber = candidate.getClobber();
|
|
final double candidateCycles = candidate.getCycles();
|
|
final double otherCycles = other.getCycles();
|
|
final AsmFragmentClobber otherClobber = other.getClobber();
|
|
if(candidateClobber.isTrueSubset(otherClobber) && candidateCycles <= otherCycles) {
|
|
// The candidate template is better
|
|
return true;
|
|
}
|
|
if(candidateClobber.isSubset(otherClobber) && candidateCycles < otherCycles) {
|
|
// The candidate template is better
|
|
return false;
|
|
}
|
|
if(candidateClobber.equals(otherClobber) && candidateCycles == otherCycles && asmSize(candidate.fragment.getBodyAsm()) < asmSize(other.getFragment().getBodyAsm())) {
|
|
// The candidate template is better
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Find the size of some ASM
|
|
*
|
|
* @param asm The ASM
|
|
* @return The size of the ASM represented as the number of "nodes" in the ASM body
|
|
*/
|
|
private static int asmSize(ParseTree asm) {
|
|
int size = 1;
|
|
if(asm instanceof ParserRuleContext) {
|
|
ParserRuleContext parent = (ParserRuleContext) asm;
|
|
if(parent.children != null) {
|
|
for(ParseTree child : parent.children) {
|
|
size += asmSize(child);
|
|
}
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
List<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 AsmFragmentSignature getSignature() {
|
|
return signature;
|
|
}
|
|
}
|