Working on ASM fragment template interface and implementation. Currently synthesizing even simple fragments uses up all memory.

This commit is contained in:
jespergravgaard 2022-04-02 08:27:34 +02:00
parent 7234060c41
commit 03150c83c3
10 changed files with 190 additions and 171 deletions

View File

@ -23,15 +23,7 @@ public interface AsmFragmentTemplate {
* @return The target CPU
*/
TargetCpu getTargetCpu();
/**
* Get the ASM body as a string.
* The ASM body contains parameter placeholders where bindings will be used to place actual values.
*
* @return The ASM body
*/
String getBody();
/**
* Get the ASM body as parsed ASM.
* The ASM body contains parameter placeholders where bindings will be used to place actual values.

View File

@ -47,7 +47,7 @@ public class AsmFragmentTemplateCache {
/** Special singleton representing that the fragment can not be synthesized or loaded. */
public static AsmFragmentTemplate NO_SYNTHESIS =
new AsmFragmentTemplateImpl(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), "NO_SYNTHESIS", null, null, null, null);
new AsmFragmentTemplateImpl(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), null, null, null, null);
/** Prefix for the fragment hash file header. */
public static final String HASH_HEADER = "//KICKC FRAGMENT CACHE ";
@ -184,7 +184,7 @@ public class AsmFragmentTemplateCache {
private static void addFragment(LinkedHashMap<AsmFragmentSignature, AsmFragmentTemplate> cache, String signatureText, StringBuilder body, TargetCpu targetCpu) {
AsmFragmentSignature signature = AsmFragmentSignature.parse(signatureText);
final String bodyString = body.toString();
if(bodyString.startsWith(NO_SYNTHESIS.getBody())) {
if(bodyString.startsWith("NO_SYNTHESIS")) {
cache.put(signature, NO_SYNTHESIS);
} else {
AsmFragmentTemplate template = AsmFragmentTemplateParser.parse(signature, AsmFragmentTemplateParser.fixNewlines(bodyString), targetCpu);
@ -215,10 +215,12 @@ public class AsmFragmentTemplateCache {
AsmFragmentTemplate fragmentTemplate = this.cache.get(signature);
fragmentFilePrint.println(FRAGMENT_HEADER + signature);
if(fragmentTemplate == NO_SYNTHESIS) {
fragmentFilePrint.println(NO_SYNTHESIS.getBody());
fragmentFilePrint.println("NO_SYNTHESIS");
} else {
if(fragmentTemplate.getBody() != null)
fragmentFilePrint.println(fragmentTemplate.getBody());
if(fragmentTemplate.getBodyAsm() != null) {
final String bodyString = AsmTemplateBodyPrinter.print(fragmentTemplate.getBodyAsm());
fragmentFilePrint.println(bodyString);
}
}
}
fragmentFilePrint.close();

View File

@ -16,8 +16,6 @@ public class AsmFragmentTemplateImpl implements AsmFragmentTemplate {
private final AsmFragmentSignature signature;
/** The target CPU. */
private final TargetCpu targetCpu;
/** The fragment template body */
private final String body;
/** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */
private final KickCParser.AsmLinesContext bodyAsm;
@ -26,9 +24,8 @@ public class AsmFragmentTemplateImpl implements AsmFragmentTemplate {
/** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code. */
private final Double cycles;
AsmFragmentTemplateImpl(AsmFragmentSignature signature, String body, TargetCpu targetCpu, KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) {
AsmFragmentTemplateImpl(AsmFragmentSignature signature, TargetCpu targetCpu, KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) {
this.signature = signature;
this.body = body;
this.targetCpu = targetCpu;
this.bodyAsm = bodyAsm;
this.clobber = clobber;
@ -39,10 +36,6 @@ public class AsmFragmentTemplateImpl implements AsmFragmentTemplate {
return signature;
}
public String getBody() {
return body;
}
@Override
public KickCParser.AsmLinesContext getBodyAsm() {
return bodyAsm;
@ -65,11 +58,11 @@ public class AsmFragmentTemplateImpl implements AsmFragmentTemplate {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AsmFragmentTemplateImpl that = (AsmFragmentTemplateImpl) o;
return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body);
return signature.equals(that.signature) && targetCpu == that.targetCpu && Objects.equals(bodyAsm, that.bodyAsm);
}
@Override
public int hashCode() {
return Objects.hash(signature, targetCpu, body);
return Objects.hash(signature, targetCpu, bodyAsm);
}
}

View File

@ -37,7 +37,7 @@ public class AsmFragmentTemplateParser {
*/
public static AsmFragmentTemplate parse(AsmFragmentSignature signature, String body, TargetCpu targetCpu) {
final ParsedAsmBody parsedAsmBody = parseAsm(signature, targetCpu, body);
final AsmFragmentTemplateImpl asmFragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, parsedAsmBody.bodyAsm, parsedAsmBody.clobber, parsedAsmBody.cycles);
final AsmFragmentTemplateImpl asmFragmentTemplate = new AsmFragmentTemplateImpl(signature, targetCpu, parsedAsmBody.bodyAsm, parsedAsmBody.clobber, parsedAsmBody.cycles);
// assertBodyPrint(signature, body, parsedAsmBody.bodyAsm);
return asmFragmentTemplate;
}
@ -90,7 +90,7 @@ public class AsmFragmentTemplateParser {
* @return The ASM fragment template
*/
public static AsmFragmentTemplate inline(AsmFragmentSignature signature, KickCParser.AsmLinesContext asmBody, TargetCpu targetCpu) {
return new AsmFragmentTemplateImpl(signature, null, targetCpu, asmBody, null, null);
return new AsmFragmentTemplateImpl(signature, targetCpu, asmBody, null, null);
}
/**
@ -207,7 +207,7 @@ public class AsmFragmentTemplateParser {
if (signatureName.contains("c6")) bindings.put("c6", new ConstantInteger(360L));
if (signatureName.contains("la1")) bindings.put("la1", new Label("@1", scope, true));
if (signatureName.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true));
final AsmFragmentTemplateImpl fragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, bodyAsm, null, null);
final AsmFragmentTemplateImpl fragmentTemplate = new AsmFragmentTemplateImpl(signature, targetCpu, bodyAsm, null, null);
AsmFragmentInstance fragmentInstance =
new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, fragmentTemplate, bindings);
AsmProgram asm = new AsmProgram(targetCpu);

View File

@ -11,7 +11,7 @@ import java.util.List;
*/
public class AsmTemplateBodyPrinter extends KickCParserBaseVisitor {
private StringBuffer out;
private final StringBuffer out;
private AsmTemplateBodyPrinter(StringBuffer out) {
this.out = out;
@ -25,7 +25,7 @@ public class AsmTemplateBodyPrinter extends KickCParserBaseVisitor {
}
private void printTags(List<TerminalNode> tags) {
tags.stream().forEach(tag -> out.append(" ").append(tag));
tags.forEach(tag -> out.append(" ").append(tag));
}
@Override

View File

@ -2,6 +2,8 @@ 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.*;
@ -12,159 +14,187 @@ import java.util.*;
*/
public class AsmFragmentSynthesis {
/**
* The signature of the fragment template being synthesized.
*/
private final AsmFragmentSignature signature;
/**
* 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;
/**
* 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 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;
/**
* 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;
/**
* 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<>();
}
/**
* 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);
}
/**
* 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;
}
/**
* 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);
}
/**
* 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);
}
void addFileTemplate(AsmFragmentSynthesisResult fileTemplate) {
this.fileTemplates.add(fileTemplate);
}
List<AsmFragmentSynthesisResult> getFileTemplates() {
return fileTemplates;
}
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();
/**
* 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);
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;
}
// 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!
// 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(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;
}
// Remove any current templates that are worse
for (AsmFragmentClobber bestClobber : bestClobbers) {
AsmFragmentSynthesisResult bestTemplate = bestTemplates.get(bestClobber);
double bestCycles = bestTemplate.getCycles();
/**
* 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;
}
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);
}
/**
* 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;
}
}
// 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.
*/
List<AsmFragmentSynthesisOption> getParentOptions() {
return parentOptions;
}
/**
* 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();
}
/**
* 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;
}
public AsmFragmentSignature getSignature() {
return signature;
}
}

View File

@ -3,6 +3,7 @@ package dk.camelot64.kickc.fragment.synthesis;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.fragment.AsmFragmentClobber;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
import dk.camelot64.kickc.fragment.AsmTemplateBodyPrinter;
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
import java.util.List;
@ -91,7 +92,7 @@ public class AsmFragmentSynthesisResult {
prefix = "synthesized ";
}
log.append(indent + prefix + this.getSynthesisDescription() + " - clobber:" + fragment.getClobber().toString() + " cycles:" + fragment.getCycles());
log.append(indent + " " + fragment.getBody().replace("\n", "\n" + indent + " "));
log.append(indent + " " + getBodyString().replace("\n", "\n" + indent + " "));
}
public AsmFragmentClobber getClobber() {
@ -102,8 +103,8 @@ public class AsmFragmentSynthesisResult {
return fragment.getCycles();
}
public String getBody() {
return fragment.getBody();
public String getBodyString() {
return AsmTemplateBodyPrinter.print(fragment.getBodyAsm());
}
public AsmFragmentSignature getSignature() {

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.fragment.synthesis;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateParser;
import dk.camelot64.kickc.fragment.AsmTemplateBodyPrinter;
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
import java.util.*;
@ -127,7 +128,7 @@ class AsmFragmentTemplateSynthesisRuleRegex implements AsmFragmentTemplateSynthe
if(asmPrefix != null) {
newFragment.append(asmPrefix).append("\n");
}
String subFragment = subTemplate.getBody();
String subFragment = AsmTemplateBodyPrinter.print(subTemplate.getBodyAsm());
if(bindMappings != null) {
if(mapSignature) {
// When mapping the signature we do the reverse replacement in the ASM

View File

@ -251,7 +251,7 @@ public class TestFragments {
}
log.append(prefix + template.getSynthesisDescription() + " - clobber:" + template.getClobber().toString() + " cycles:" + template.getCycles());
log.append(" " + template.getBody().replace("\n", "\n "));
log.append(" " + template.getBodyString().replace("\n", "\n "));
}
}
// Print some information about the testing to stdout

View File

@ -3131,7 +3131,7 @@ public class TestProgramsFast extends TestPrograms {
@Test
public void testSimpleLoop() throws IOException {
compileAndCompare("simple-loop.c");
compileAndCompare("simple-loop.c", log());
}
@Test