1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-02 00:41:42 +00:00

Consolidate string constants per segment. If the same string is defined within 2 different segments, then this constant will be allocated in memory in the asm under each data segment.

This commit is contained in:
Sven Van de Velde 2023-10-26 20:39:22 +02:00
parent adb93aa3a0
commit 84d2715e34
2 changed files with 71 additions and 47 deletions

View File

@ -11,7 +11,11 @@ import dk.camelot64.kickc.model.values.*;
import java.util.*;
/**
* Compiler Pass finding and consolidating identical constant strings
* Compiler Pass finding and consolidating identical constant strings.
* The term "root" refers to the consolidated string that will actually result in a declaration
* in the assembler. The children will point to this declared root.
* The string consolidation will act for each data segment independently.
* So a program with multiple data segments will have multiple string consolidated roots.
*/
public class Pass2ConstantStringConsolidation extends Pass2SsaOptimization {
@ -58,69 +62,83 @@ public class Pass2ConstantStringConsolidation extends Pass2SsaOptimization {
*/
private boolean handleDuplicateConstantString(List<Variable> constantVars, ConstantString constString) {
boolean modified = false;
// Look for a constant in the root scope - or check if they are all in the same scope
Variable rootConstant = null;
boolean isCommonScope = true;
ScopeRef commonScope = null;
String segmentData = null;
Map<String, List<Variable>> segmentDataMap = new LinkedHashMap<>();
for(Variable constantVar : constantVars) {
ScopeRef constScope = constantVar.getScope().getRef();
segmentData = constantVar.getDataSegment();
if(constScope.equals(ScopeRef.ROOT)) {
rootConstant = constantVar;
break;
}
if(commonScope == null) {
commonScope = constScope;
} else {
if(!commonScope.equals(constScope)) {
// Found two different scopes
isCommonScope = false;
}
String segmentData = constantVar.getDataSegment();
if(segmentData!= null && !segmentDataMap.containsKey(segmentData)) {
segmentDataMap.put(segmentData, new LinkedList<Variable>());
}
List<Variable> constantVarsSegmentData = segmentDataMap.get(segmentData);
constantVarsSegmentData.add(constantVar);
}
for(String segmentData : segmentDataMap.keySet()) {
List<Variable> constantVarsSegmentData = segmentDataMap.get(segmentData);
// Look for a constant in the root scope - or check if they are all in the same scope
boolean modifiedSegment = false;
Variable rootConstant = null;
boolean isCommonScope = true;
ScopeRef commonScope = null;
for(Variable constantVar : constantVarsSegmentData) {
if (constantVar.getDataSegment().equals(segmentData)) {
ScopeRef constScope = constantVar.getScope().getRef();
if (constScope.equals(ScopeRef.ROOT)) {
rootConstant = constantVar;
break;
}
if (commonScope == null) {
commonScope = constScope;
} else {
if (!commonScope.equals(constScope)) {
// Found two different scopes
isCommonScope = false;
}
}
}
for(String keySegmentData : segmentDataMap.keySet()) {
List<Variable> constantVarsSegmentData = segmentDataMap.get(keySegmentData);
if (rootConstant == null) {
if (isCommonScope) {
// If all constants are in the same scope pick the first one as root
rootConstant = constantVarsSegmentData.get(0);
} else {
// Create a new root - and roll around again
ProgramScope rootScope = getProgramScope();
String localName = keySegmentData + getRootName(constantVars);
final long stringLength = constString.getStringLength();
final ConstantInteger arraySize = new ConstantInteger(stringLength, stringLength < 256 ? SymbolType.BYTE : SymbolType.WORD);
Variable newRootConstant = Variable.createConstant(localName, new SymbolTypePointer(SymbolType.BYTE, new ArraySpec(arraySize), false, false), rootScope, constString, segmentData);
rootScope.add(newRootConstant);
rootConstant = newRootConstant;
if (rootConstant == null) {
if (isCommonScope) {
// If all constants are in the same scope pick the first one as root
rootConstant = constantVarsSegmentData.get(0);
} else {
// Create a new root - and roll around again
ProgramScope rootScope = getProgramScope();
String localName = getRootName(constantVars, segmentData);
final long stringLength = constString.getStringLength();
final ConstantInteger arraySize = new ConstantInteger(stringLength, stringLength < 256 ? SymbolType.BYTE : SymbolType.WORD);
Variable newRootConstant = Variable.createConstant(localName, new SymbolTypePointer(SymbolType.BYTE, new ArraySpec(arraySize), false, false), rootScope, constString, segmentData);
rootScope.add(newRootConstant);
rootConstant = newRootConstant;
}
}
}
// Modify all other constants to be references to the root constant
for (Variable constantVar : constantVars) {
if (!constantVar.equals(rootConstant)) {
constantVar.setInitValue(new ConstantRef(rootConstant));
modified = true;
if(constantVar.getDataSegment().equals(segmentData)) {
if (!constantVar.equals(rootConstant)) {
constantVar.setInitValue(new ConstantRef(rootConstant));
modifiedSegment = true;
}
}
}
}
if(modified) {
getLog().append("Consolidated constant strings into " + rootConstant);
if(modifiedSegment) {
getLog().append("Consolidated constant strings into " + rootConstant);
}
modified |= modifiedSegment;
}
return modified;
}
private String getRootName(List<Variable> constantVars) {
private String getRootName(List<Variable> constantVars, String segmentData) {
String constName = null;
// Try all variables with non-intermediate names
for(Variable constantVar : constantVars) {
if(!constantVar.getRef().isIntermediate()) {
String candidateName = constantVar.getLocalName();
if(getProgramScope().getLocalSymbol(candidateName) == null) {
if(constName == null || constName.length() > candidateName.length()) {
constName = candidateName;
if(constantVar.getDataSegment().equals(segmentData)) {
if (!constantVar.getRef().isIntermediate()) {
String candidateName = constantVar.getLocalName();
if (getProgramScope().getLocalSymbol(candidateName) == null) {
if (constName == null || constName.length() > candidateName.length()) {
constName = candidateName;
}
}
}
}
@ -131,7 +149,7 @@ public class Pass2ConstantStringConsolidation extends Pass2SsaOptimization {
// Try string_nn until an unused name is found
int i = 0;
do {
String candidateName = "string_" + i;
String candidateName = "string_" + i + "_" + segmentData;
if(getProgramScope().getLocalSymbol(candidateName) == null) {
return candidateName;
}

View File

@ -4029,6 +4029,12 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("uninitialized.c");
}
@Test
public void testStringConstConsolidationRootSegments() throws IOException {
compileAndCompare("string-const-consolidation-root-segments.c");
}
@Test
public void testStringConstConsolidationNoRoot() throws IOException {
compileAndCompare("string-const-consolidation-noroot.c");