mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-28 01:29:44 +00:00
Added fragment system handling of CR / CRLF. #490
This commit is contained in:
parent
b8682bd770
commit
b5854c1fa7
@ -1,4 +1,4 @@
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268 16e7ed6a89
|
||||
//FRAGMENT vbuz1=vbuc1
|
||||
lda #{c1}
|
||||
sta {z1}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268 16e7ed6a89
|
||||
//FRAGMENT vduz1=vduc1
|
||||
lda #<{c1}
|
||||
sta {z1}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268 16e7ed6a89
|
||||
//FRAGMENT vbuz1=vbuc1
|
||||
lda #{c1}
|
||||
sta {z1}
|
||||
|
1520
src/main/fragment/cache/fragment-cache-mos6502x.asm
vendored
1520
src/main/fragment/cache/fragment-cache-mos6502x.asm
vendored
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268
|
||||
//KICKC FRAGMENT CACHE 16e7ed5268 16e7ed6a89
|
||||
//FRAGMENT vbuz1=_deref_pbuc1
|
||||
lda {c1}
|
||||
sta {z1}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package dk.camelot64.kickc.fragment;
|
||||
|
||||
import dk.camelot64.kickc.model.InternalError;
|
||||
import dk.camelot64.kickc.model.TargetCpu;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
@ -12,59 +15,144 @@ import java.nio.file.Path;
|
||||
*/
|
||||
public class AsmFragmentSystemHash {
|
||||
|
||||
/** Hash of the fragment source files. */
|
||||
private long hash;
|
||||
/** Hash of the fragment source files on any system where a newline is LF. */
|
||||
private Long hashLF;
|
||||
|
||||
/** Hash of the fragment source files on any system where a newline is CRLF. */
|
||||
private Long hashCRLF;
|
||||
|
||||
/** Last modified date for the fragment source files. */
|
||||
private long lastModified;
|
||||
|
||||
|
||||
AsmFragmentSystemHash(long hash, long lastModified) {
|
||||
public AsmFragmentSystemHash(Long hashLF, Long hashCRLF, long lastModified) {
|
||||
this.hashCRLF = hashCRLF;
|
||||
this.hashLF = hashLF;
|
||||
this.lastModified = lastModified;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run through the fragment folder and calculate the hash/modify time
|
||||
*
|
||||
* @param baseFragmentFolder The fragment folder
|
||||
* @param allSystems Should hash code be calculated for all systems (both LF and CRLF newlines).
|
||||
* If false hash is only calculated for the current system, which is fast.
|
||||
* If true calculate for all systems, which requires reading through all files to count newlines - so it is a lot slower.
|
||||
* @return The fragment system hash
|
||||
*/
|
||||
public static AsmFragmentSystemHash calculate(Path baseFragmentFolder) {
|
||||
long hash = 0;
|
||||
public static AsmFragmentSystemHash calculate(Path baseFragmentFolder, boolean allSystems) {
|
||||
Long hashLF = 0L;
|
||||
Long hashCRLF = 0L;
|
||||
long lastModified = 0;
|
||||
final TargetCpu.Feature[] cpuFeatures = TargetCpu.Feature.values();
|
||||
for(TargetCpu.Feature cpuFeature : cpuFeatures) {
|
||||
hash += cpuFeature.getName().hashCode();
|
||||
hashCRLF += cpuFeature.getName().hashCode();
|
||||
hashLF += cpuFeature.getName().hashCode();
|
||||
final Path cpuFeatureFolder = baseFragmentFolder.resolve(cpuFeature.getName());
|
||||
final File cpuFeatureFolderFile = cpuFeatureFolder.toFile();
|
||||
if(cpuFeatureFolderFile.exists() && cpuFeatureFolderFile.isDirectory()) {
|
||||
final File[] files = cpuFeatureFolderFile.listFiles((dir, name) -> name.endsWith(".asm"));
|
||||
if(files != null)
|
||||
for(File file : files) {
|
||||
hash += file.length();
|
||||
hash += file.getName().hashCode();
|
||||
long lengthLF;
|
||||
long lengthCRLF;
|
||||
if(allSystems) {
|
||||
// Calculate length for all systems by reading the file
|
||||
int fileNewlines = getFileNewlineCount(file);
|
||||
if(isSystemLF()) {
|
||||
lengthLF = file.length();
|
||||
lengthCRLF = file.length() + fileNewlines;
|
||||
} else {
|
||||
lengthLF = file.length() - fileNewlines;
|
||||
lengthCRLF = file.length();
|
||||
}
|
||||
} else {
|
||||
// Only find length for current system - set other to zero
|
||||
if(isSystemLF()) {
|
||||
lengthLF = file.length();
|
||||
lengthCRLF = 0;
|
||||
} else {
|
||||
lengthLF = 0;
|
||||
lengthCRLF = file.length();
|
||||
}
|
||||
}
|
||||
hashCRLF += lengthCRLF;
|
||||
hashLF += lengthLF;
|
||||
hashCRLF += file.getName().hashCode();
|
||||
hashLF += file.getName().hashCode();
|
||||
if(file.lastModified() > lastModified)
|
||||
lastModified = file.lastModified();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also hash in all synthesis rules
|
||||
for(AsmFragmentTemplateSynthesisRule synthesisRule : AsmFragmentTemplateSynthesisRule.getSynthesisRules()) {
|
||||
hash += synthesisRule.hashCode();
|
||||
hashCRLF += synthesisRule.hashCode();
|
||||
hashLF += synthesisRule.hashCode();
|
||||
}
|
||||
return new AsmFragmentSystemHash(hash, lastModified);
|
||||
if(!allSystems) {
|
||||
// If not all systems - null out other system hash since it is wrong
|
||||
if(isSystemLF())
|
||||
hashCRLF = null;
|
||||
else
|
||||
hashLF = null;
|
||||
}
|
||||
return new AsmFragmentSystemHash(hashLF, hashCRLF, lastModified);
|
||||
}
|
||||
|
||||
public long getHash() {
|
||||
return hash;
|
||||
/**
|
||||
* Get the number of newlines in a file
|
||||
* @param file The file to examine
|
||||
* @return The number of newline characters
|
||||
*/
|
||||
private static int getFileNewlineCount(File file) {
|
||||
int fileNewlines = 0;
|
||||
final byte[] fileBytes;
|
||||
try {
|
||||
fileBytes = Files.readAllBytes(file.toPath());
|
||||
} catch(IOException e) {
|
||||
throw new InternalError("Error reading ASM fragment file "+file.getAbsolutePath(),e);
|
||||
}
|
||||
for(int i = 0; i < fileBytes.length; i++) {
|
||||
byte fileByte = fileBytes[i];
|
||||
if(fileByte=='\n') fileNewlines++;
|
||||
}
|
||||
return fileNewlines;
|
||||
}
|
||||
|
||||
public String getHashString() {
|
||||
return Long.toHexString(hash);
|
||||
/**
|
||||
* Does the current system use LF as newline
|
||||
*
|
||||
* @return true if the line separator is LF
|
||||
*/
|
||||
static boolean isSystemLF() {
|
||||
return System.lineSeparator().length() == 1;
|
||||
}
|
||||
|
||||
public String getHashStringLF() {
|
||||
return hashLF==null?null:Long.toHexString(hashLF);
|
||||
}
|
||||
|
||||
public String getHashStringCRLF() {
|
||||
return hashCRLF==null?null:Long.toHexString(hashCRLF);
|
||||
}
|
||||
|
||||
/** Determines if this hash mathes the passed hash values.
|
||||
* Only examines the value relevant for the current system
|
||||
* @param hashLF Hash of the fragment source files on any system where a newline is LF
|
||||
* @param hashCRLF Hash of the fragment source files on any system where a newline is CRLF
|
||||
* @return true if the hash matches
|
||||
*/
|
||||
public boolean matches(String hashLF, String hashCRLF) {
|
||||
if(isSystemLF()) {
|
||||
return getHashStringLF().equals(hashLF);
|
||||
} else {
|
||||
return getHashStringCRLF().equals(hashCRLF);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package dk.camelot64.kickc.fragment;
|
||||
|
||||
import dk.camelot64.kickc.CompileLog;
|
||||
import dk.camelot64.kickc.model.InternalError;
|
||||
import dk.camelot64.kickc.model.TargetCpu;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
@ -32,7 +33,12 @@ public class AsmFragmentTemplateCache {
|
||||
/** Detects any modification of the cache. */
|
||||
private boolean modified;
|
||||
|
||||
AsmFragmentTemplateCache(Path cacheFolder, TargetCpu cpu, AsmFragmentSystemHash fragmentSystemHash, Map<String, AsmFragmentTemplate> cache) {
|
||||
/**
|
||||
* Creates a cache
|
||||
*
|
||||
* @param cpu The target CPU
|
||||
*/
|
||||
private AsmFragmentTemplateCache(Path cacheFolder, TargetCpu cpu, AsmFragmentSystemHash fragmentSystemHash, Map<String, AsmFragmentTemplate> cache) {
|
||||
this.cacheFolder = cacheFolder;
|
||||
this.cpu = cpu;
|
||||
this.fragmentSystemHash = fragmentSystemHash;
|
||||
@ -84,8 +90,18 @@ public class AsmFragmentTemplateCache {
|
||||
* @param cpu The CPU to make a cache for
|
||||
* @return The new empty cache
|
||||
*/
|
||||
public static AsmFragmentTemplateCache memory(TargetCpu cpu) {
|
||||
return new AsmFragmentTemplateCache(null, cpu, new AsmFragmentSystemHash(0, 0), new LinkedHashMap<>());
|
||||
public static AsmFragmentTemplateCache memoryCache(TargetCpu cpu) {
|
||||
return new AsmFragmentTemplateCache(null, cpu, new AsmFragmentSystemHash(0L, 0L, 0), new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a disk fragment cache
|
||||
*
|
||||
* @param cpu The CPU to make a cache for
|
||||
* @return The new empty cache
|
||||
*/
|
||||
public static AsmFragmentTemplateCache diskCache(Path cacheFolder, TargetCpu cpu, AsmFragmentSystemHash fragmentSystemHash, Map<String, AsmFragmentTemplate> cache) {
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,37 +112,41 @@ public class AsmFragmentTemplateCache {
|
||||
* @return The map with all best fragments from the cache file. null if the cache file is not found.
|
||||
*/
|
||||
public static AsmFragmentTemplateCache load(TargetCpu cpu, Path baseFragmentFolder, CompileLog log) {
|
||||
final AsmFragmentSystemHash fragmentSystemHash = AsmFragmentSystemHash.calculate(baseFragmentFolder);
|
||||
final AsmFragmentSystemHash fragmentSystemHash = AsmFragmentSystemHash.calculate(baseFragmentFolder, false);
|
||||
Path cacheFolder = baseFragmentFolder.resolve(CACHE_FOLDER_NAME);
|
||||
final Date before = new Date();
|
||||
if(!cacheFolder.toFile().exists()) {
|
||||
if(log.isVerboseFragmentLog())
|
||||
log.append("Creating fragment cache folder " + cacheFolder.toAbsolutePath().toString());
|
||||
cacheFolder.toFile().mkdir();
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
return diskCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
}
|
||||
try {
|
||||
File cacheFile = cacheFolder.resolve(getCacheFileName(cpu)).toFile();
|
||||
if(!cacheFile.exists()) {
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
return diskCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
}
|
||||
if(cacheFile.lastModified() < fragmentSystemHash.getLastModified()) {
|
||||
if(log.isVerboseFragmentLog())
|
||||
log.append("Deleting outdated fragment cache file " + cacheFile.getAbsolutePath());
|
||||
cacheFile.delete();
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
return diskCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
}
|
||||
LinkedHashMap<String, AsmFragmentTemplate> cache = new LinkedHashMap<>();
|
||||
BufferedReader fragmentCacheReader = new BufferedReader(new FileReader(cacheFile));
|
||||
String hashLine = fragmentCacheReader.readLine();
|
||||
final String hash = hashLine.substring(HASH_HEADER.length());
|
||||
final String hashPayload = hashLine.substring(HASH_HEADER.length());
|
||||
final String[] hashes = hashPayload.split(" ");
|
||||
final String hashLF = hashes[0];
|
||||
final String hashCRLF = (hashes.length > 1) ? hashes[1] : null;
|
||||
// Compare cache file hash with fragment system hash
|
||||
if(!hash.equals(fragmentSystemHash.getHashString())) {
|
||||
if(!fragmentSystemHash.matches(hashLF, hashCRLF)) {
|
||||
// Cache file hash does not match fragment system hash
|
||||
if(log.isVerboseFragmentLog())
|
||||
log.append("Deleting hash mismatch fragment cache file " + cacheFile.getAbsolutePath());
|
||||
cacheFile.delete();
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
// And make a new cache to hold the fragments
|
||||
return diskCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>());
|
||||
}
|
||||
// Read the first "real" line
|
||||
String cacheLine = fragmentCacheReader.readLine();
|
||||
@ -154,7 +174,7 @@ public class AsmFragmentTemplateCache {
|
||||
final long millis = after.getTime() - before.getTime();
|
||||
if(log.isVerboseFragmentLog())
|
||||
log.append("Loaded cached fragments " + cache.size() + " from " + cacheFile.getPath() + " in " + millis + "ms");
|
||||
return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, cache);
|
||||
return diskCache(cacheFolder, cpu, fragmentSystemHash, cache);
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException("Error loading fragment cache file " + cacheFolder, e);
|
||||
} catch(StringIndexOutOfBoundsException e) {
|
||||
@ -178,16 +198,20 @@ public class AsmFragmentTemplateCache {
|
||||
*
|
||||
* @param log The compile log
|
||||
*/
|
||||
public void save(CompileLog log) {
|
||||
public void save(Path baseFragmentFolder, CompileLog log) {
|
||||
if(!modified)
|
||||
return;
|
||||
if(this.cacheFolder == null)
|
||||
return;
|
||||
Date before = new Date();
|
||||
// Calculate a new hash which handles all systems
|
||||
final AsmFragmentSystemHash newFragmentSystemHash = AsmFragmentSystemHash.calculate(baseFragmentFolder, true);
|
||||
File cacheFile = this.cacheFolder.resolve(getCacheFileName(cpu)).toFile();
|
||||
try {
|
||||
PrintStream fragmentFilePrint = new PrintStream(cacheFile);
|
||||
fragmentFilePrint.println(HASH_HEADER + fragmentSystemHash.getHashString());
|
||||
if(newFragmentSystemHash.getHashStringLF() == null || newFragmentSystemHash.getHashStringCRLF() == null)
|
||||
throw new InternalError("Error saving ASM fragment cache file. Not calculated for all systems!");
|
||||
fragmentFilePrint.println(HASH_HEADER + newFragmentSystemHash.getHashStringLF() + " " + newFragmentSystemHash.getHashStringCRLF());
|
||||
for(String signature : this.cache.keySet()) {
|
||||
AsmFragmentTemplate fragmentTemplate = this.cache.get(signature);
|
||||
fragmentFilePrint.println(FRAGMENT_HEADER + signature);
|
||||
|
@ -34,7 +34,7 @@ public class AsmFragmentTemplateSynthesizer {
|
||||
if(useFragmentCache)
|
||||
this.fragmentCache = AsmFragmentTemplateCache.load(targetCpu, baseFragmentFolder, log);
|
||||
else
|
||||
this.fragmentCache = AsmFragmentTemplateCache.memory(targetCpu);
|
||||
this.fragmentCache = AsmFragmentTemplateCache.memoryCache(targetCpu);
|
||||
}
|
||||
|
||||
/** The folder containing fragment files. */
|
||||
@ -58,7 +58,7 @@ public class AsmFragmentTemplateSynthesizer {
|
||||
/** Finalize the fragment template synthesizer. */
|
||||
void finalize(CompileLog log) {
|
||||
if(fragmentCache != null)
|
||||
fragmentCache.save(log);
|
||||
fragmentCache.save(baseFragmentFolder, log);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user