1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-02 00:41:42 +00:00
kickc/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentSynthesis.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;
}
}