mirror of
https://github.com/fadden/6502bench.git
synced 2025-04-08 23:38:15 +00:00
Add local variable uniquification
For ACME and cc65, enable uniqification. This works with my basic tests, but there are a lot of potential edge cases.
This commit is contained in:
parent
6a2532588b
commit
02c79db749
@ -134,8 +134,9 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
Project = project;
|
||||
Quirks = new AssemblerQuirks();
|
||||
Quirks.TracksSepRepNotEmu = true;
|
||||
Quirks.HasRedefinableSymbols = true;
|
||||
Quirks.NoPcRelBankWrap = true;
|
||||
Quirks.TracksSepRepNotEmu = true;
|
||||
|
||||
mWorkDirectory = workDirectory;
|
||||
mFileNameBase = fileNameBase;
|
||||
|
@ -160,6 +160,7 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
Project = project;
|
||||
Quirks = new AssemblerQuirks();
|
||||
Quirks.HasRedefinableSymbols = true;
|
||||
Quirks.StackIntOperandIsImmediate = true;
|
||||
|
||||
mWorkDirectory = workDirectory;
|
||||
|
@ -39,9 +39,8 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false);
|
||||
|
||||
// TODO: switch uniqueness based on quirk
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables,
|
||||
proj.SymbolTable, proj);
|
||||
gen.Quirks.HasRedefinableSymbols ? null : proj.SymbolTable, proj);
|
||||
|
||||
GenerateHeader(gen, sw);
|
||||
|
||||
|
@ -186,26 +186,21 @@ namespace SourceGen.AsmGen {
|
||||
/// Enumeration of quirky or buggy behavior that GenCommon needs to handle.
|
||||
/// </summary>
|
||||
public class AssemblerQuirks {
|
||||
/// <summary>
|
||||
/// Are the arguments to MVN/MVP reversed?
|
||||
/// </summary>
|
||||
public bool BlockMoveArgsReversed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Are 8-bit constant args to MVN/MVP output without a leading '#'?
|
||||
/// </summary>
|
||||
public bool BlockMoveArgsNoHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
|
||||
/// Are the arguments to MVN/MVP reversed?
|
||||
/// </summary>
|
||||
public bool StackIntOperandIsImmediate { get; set; }
|
||||
public bool BlockMoveArgsReversed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the assembler configure assembler widths based on SEP/REP, but doesn't
|
||||
/// track the emulation bit?
|
||||
/// Does the assembler support a type of label whose value can be redefined to
|
||||
/// act as a local variable?
|
||||
/// </summary>
|
||||
public bool TracksSepRepNotEmu { get; set; }
|
||||
public bool HasRedefinableSymbols { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the assembler unable to generate relative branches that wrap around banks?
|
||||
@ -213,6 +208,11 @@ namespace SourceGen.AsmGen {
|
||||
/// </summary>
|
||||
public bool NoPcRelBankWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
|
||||
/// </summary>
|
||||
public bool StackIntOperandIsImmediate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the assembler implemented as a single pass? (e.g. cc65)
|
||||
/// </summary>
|
||||
@ -223,5 +223,11 @@ namespace SourceGen.AsmGen {
|
||||
/// and not corrected when the actual width is determined?
|
||||
/// </summary>
|
||||
public bool SinglePassNoLabelCorrection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the assembler configure assembler widths based on SEP/REP, but doesn't
|
||||
/// track the emulation bit?
|
||||
/// </summary>
|
||||
public bool TracksSepRepNotEmu { get; set; }
|
||||
}
|
||||
}
|
@ -134,6 +134,17 @@ namespace SourceGen {
|
||||
Tag = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a DefSymbol from an existing DefSymbol, with a different label. Use
|
||||
/// this to change the label while keeping everything else the same.
|
||||
/// </summary>
|
||||
/// <param name="defSym">Source DefSymbol.</param>
|
||||
/// <param name="label">Label to use.</param>
|
||||
public DefSymbol(DefSymbol defSym, string label)
|
||||
: this(label, defSym.Value, defSym.SymbolSource, defSym.SymbolType,
|
||||
defSym.DataDescriptor.FormatSubType, defSym.Comment, defSym.Tag,
|
||||
defSym.DataDescriptor.Length) { }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a symbol overlaps with a region. Useful for variables.
|
||||
/// </summary>
|
||||
|
@ -908,25 +908,39 @@ namespace SourceGen {
|
||||
/// variables to take precedence.
|
||||
///
|
||||
/// This also adds all symbols in non-hidden variable tables to the main SymbolTable,
|
||||
/// for the benefit of uniqueness checks.
|
||||
/// for the benefit of future uniqueness checks.
|
||||
/// </summary>
|
||||
private void GenerateVariableRefs() {
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, SymbolTable, this);
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, null, this);
|
||||
|
||||
for (int offset = 0; offset < FileData.Length; ) {
|
||||
// All entries also get added to the main SymbolTable. This is a little
|
||||
// wonky because the symbol might already exist with a different value.
|
||||
// So long as the previous thing was also a variable, it doesn't matter.
|
||||
// Was a table defined at this offset?
|
||||
List<DefSymbol> vars = lvLookup.GetVariablesDefinedAtOffset(offset);
|
||||
if (vars != null) {
|
||||
// All entries also get added to the main SymbolTable. This is a little
|
||||
// wonky because the symbol might already exist with a different value.
|
||||
// So long as the previous thing was also a variable, it doesn't matter.
|
||||
foreach (DefSymbol defSym in vars) {
|
||||
if (!SymbolTable.TryGetValue(defSym.Label, out Symbol sym)) {
|
||||
// Symbol not yet in symbol table. Add it.
|
||||
//
|
||||
// NOTE: if you try to run the main app with uniqification enabled,
|
||||
// this will cause the various uniquified forms of local variables
|
||||
// to end up in the main symbol table. This can clashes with user
|
||||
// labels that would not occur otherwise.
|
||||
SymbolTable[defSym.Label] = defSym;
|
||||
} else if (!sym.IsVariable) {
|
||||
// Shouldn't happen, and will cause trouble if we're not
|
||||
// uniquifying names.
|
||||
Debug.Assert(false,
|
||||
"Found non-variable with var name in symbol table: " + sym);
|
||||
// Somehow we have a variable and a non-variable with the same
|
||||
// name. Platform/project symbols haven't been processed yet, so
|
||||
// this must be a clash with a user label. This will likely cause
|
||||
// assembly source gen to fail later on. It's possible to do this
|
||||
// by "hiding" a table and then adding a user label, so we can't just
|
||||
// fix it at project load time. The full fix is to permanently
|
||||
// rename the dup in the LvTable and reset the LvLookup here, but I
|
||||
// hate trashing user data.
|
||||
Debug.WriteLine("Found non-variable with var name in symbol table: "
|
||||
+ sym);
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,8 +442,7 @@ namespace SourceGen {
|
||||
mFormattedLineCache = new FormattedOperandCache();
|
||||
mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS,
|
||||
false);
|
||||
// TODO: remove SymbolTable -- don't need unique
|
||||
mLvLookup = new LocalVariableLookup(mProject.LvTables, mProject.SymbolTable, mProject);
|
||||
mLvLookup = new LocalVariableLookup(mProject.LvTables, null, mProject);
|
||||
|
||||
mDisplayList.ListGen = this;
|
||||
}
|
||||
|
@ -34,6 +34,42 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
private SymbolTable mSymbolTable;
|
||||
|
||||
/// <summary>
|
||||
/// Label uniquification helper.
|
||||
/// </summary>
|
||||
private class UniqueLabel {
|
||||
public string BaseLabel { get; private set; }
|
||||
public string Label { get; private set; }
|
||||
private int Counter { get; set; }
|
||||
|
||||
public UniqueLabel(string baseLabel) {
|
||||
Label = BaseLabel = baseLabel;
|
||||
Counter = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Label to be unique. Call this when a symbol is defined or
|
||||
/// re-defined.
|
||||
/// </summary>
|
||||
/// <param name="symbolTable">Symbol table, for uniqueness check.</param>
|
||||
public void MakeUnique(SymbolTable symbolTable) {
|
||||
// The main symbol table might have user-supplied labels like "ptr_2", so we
|
||||
// need to keep testing against that. However, it should not be possible for
|
||||
// us to clash with other uniquified variables. So we don't need to check
|
||||
// for clashes in the UniqueLabel list.
|
||||
//
|
||||
// It *is* possible to clash with other variable base names, so we can't
|
||||
// exclude variables from our SymbolTable lookup.
|
||||
string testLabel;
|
||||
do {
|
||||
Counter++;
|
||||
testLabel = BaseLabel + "_" + Counter;
|
||||
} while (symbolTable.TryGetValue(testLabel, out Symbol unused1));
|
||||
Label = testLabel;
|
||||
}
|
||||
}
|
||||
private Dictionary<string, UniqueLabel> mUniqueLabels;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to project, so we can query the Anattrib array to identify "hidden" tables.
|
||||
/// </summary>
|
||||
@ -73,6 +109,9 @@ namespace SourceGen {
|
||||
mProject = project;
|
||||
|
||||
mCurrentTable = new LocalVariableTable();
|
||||
if (mSymbolTable != null) {
|
||||
mUniqueLabels = new Dictionary<string, UniqueLabel>();
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -80,6 +119,7 @@ namespace SourceGen {
|
||||
mRecentOffset = -1;
|
||||
mRecentSymbols = null;
|
||||
mCurrentTable.Clear();
|
||||
mUniqueLabels?.Clear();
|
||||
if (mLvTables.Count == 0) {
|
||||
mNextLvtIndex = -1;
|
||||
mNextLvtOffset = mProject.FileDataLength;
|
||||
@ -99,12 +139,13 @@ namespace SourceGen {
|
||||
/// <returns>Symbol, or null if no match found.</returns>
|
||||
public DefSymbol GetSymbol(int offset, int operandValue, Symbol.Type type) {
|
||||
AdvanceToOffset(offset);
|
||||
|
||||
return mCurrentTable.GetByValueRange(operandValue, 1, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the symbol associated with a symbol reference.
|
||||
/// Gets the symbol associated with a symbol reference. If uniquification is enabled,
|
||||
/// the unique-label map for the specified offset will be used to transform the
|
||||
/// symbol reference.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of start of instruction.</param>
|
||||
/// <param name="symRef">Reference to symbol.</param>
|
||||
@ -112,7 +153,19 @@ namespace SourceGen {
|
||||
public DefSymbol GetSymbol(int offset, WeakSymbolRef symRef) {
|
||||
AdvanceToOffset(offset);
|
||||
|
||||
return mCurrentTable.GetByLabel(symRef.Label);
|
||||
// The symRef uses the non-uniqified symbol, so we need to get the unique value at
|
||||
// the current offset.
|
||||
string label = symRef.Label;
|
||||
if (mUniqueLabels != null && mUniqueLabels.TryGetValue(label, out UniqueLabel ulab)) {
|
||||
label = ulab.Label;
|
||||
}
|
||||
DefSymbol defSym = mCurrentTable.GetByLabel(label);
|
||||
|
||||
// In theory this is okay, but in practice the only things asking for symbols are
|
||||
// entirely convinced that the symbol exists here. So this is probably a bug.
|
||||
Debug.Assert(defSym != null);
|
||||
|
||||
return defSym;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -138,7 +191,6 @@ namespace SourceGen {
|
||||
if (mRecentOffset == offset) {
|
||||
return mRecentSymbols;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -150,16 +202,16 @@ namespace SourceGen {
|
||||
/// do an incremental update. If the offset moves backward, we have to reset and walk
|
||||
/// forward again.
|
||||
/// </remarks>
|
||||
/// <param name="offset">Target offset.</param>
|
||||
private void AdvanceToOffset(int offset) {
|
||||
/// <param name="targetOffset">Target offset.</param>
|
||||
private void AdvanceToOffset(int targetOffset) {
|
||||
if (mNextLvtIndex < 0) {
|
||||
return;
|
||||
}
|
||||
if (offset < mRecentOffset) {
|
||||
if (targetOffset < mRecentOffset) {
|
||||
// We went backwards.
|
||||
Reset();
|
||||
}
|
||||
while (mNextLvtOffset <= offset) {
|
||||
while (mNextLvtOffset <= targetOffset) {
|
||||
if (!mProject.GetAnattrib(mNextLvtOffset).IsStart) {
|
||||
// Hidden table, ignore it.
|
||||
Debug.WriteLine("Ignoring LvTable at +" + mNextLvtOffset.ToString("x6"));
|
||||
@ -175,15 +227,25 @@ namespace SourceGen {
|
||||
mRecentOffset = mNextLvtOffset;
|
||||
|
||||
// Merge the new entries into the work table. This automatically
|
||||
// discards entries that clash.
|
||||
// discards entries that clash by name or value.
|
||||
for (int i = 0; i < lvt.Count; i++) {
|
||||
// TODO: uniquify
|
||||
mCurrentTable.AddOrReplace(lvt[i]);
|
||||
DefSymbol defSym = lvt[i];
|
||||
if (mSymbolTable != null) {
|
||||
if (mUniqueLabels.TryGetValue(defSym.Label, out UniqueLabel ulab)) {
|
||||
// We've seen this label before; generate a unique version.
|
||||
ulab.MakeUnique(mSymbolTable);
|
||||
defSym = new DefSymbol(defSym, ulab.Label);
|
||||
} else {
|
||||
// Haven't seen this before. Add it to the unique-labels table.
|
||||
mUniqueLabels.Add(defSym.Label, new UniqueLabel(defSym.Label));
|
||||
}
|
||||
}
|
||||
mCurrentTable.AddOrReplace(defSym);
|
||||
|
||||
mRecentSymbols.Add(lvt[i]);
|
||||
mRecentSymbols.Add(defSym);
|
||||
}
|
||||
|
||||
mCurrentTable.DebugDump();
|
||||
mCurrentTable.DebugDump(mNextLvtOffset);
|
||||
}
|
||||
|
||||
// Update state to look for next table.
|
||||
|
@ -133,15 +133,25 @@ namespace SourceGen {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the tables.
|
||||
/// </summary>
|
||||
public void Clear() {
|
||||
mVarByLabel.Clear();
|
||||
mVarByValue.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the symbol that matches the label, or null if not found.
|
||||
/// </summary>
|
||||
public DefSymbol GetByLabel(string label) {
|
||||
return mVarByLabel[label];
|
||||
mVarByLabel.TryGetValue(label, out DefSymbol defSym);
|
||||
return defSym;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the symbol with the matching label.
|
||||
/// </summary>
|
||||
public void RemoveByLabel(string label) {
|
||||
if (mVarByLabel.TryGetValue(label, out DefSymbol defSym)) {
|
||||
mVarByLabel.Remove(defSym.Label);
|
||||
@ -260,9 +270,9 @@ namespace SourceGen {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public void DebugDump() {
|
||||
Debug.WriteLine("LocalVariableTable count=" + Count + " clear-previous=" +
|
||||
ClearPrevious);
|
||||
public void DebugDump(int offset) {
|
||||
Debug.WriteLine("LocalVariableTable +" + offset.ToString("x6") + " count=" +
|
||||
Count + " clear-previous=" + ClearPrevious);
|
||||
for (int i = 0; i < Count; i++) {
|
||||
Debug.WriteLine(" " + i + ": " + this[i]);
|
||||
}
|
||||
|
@ -599,8 +599,8 @@ namespace SourceGen {
|
||||
Debug.Assert(false);
|
||||
return formatter.FormatHexValue(operandValue, hexMinLen);
|
||||
}
|
||||
} else if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym) &&
|
||||
!sym.IsVariable) {
|
||||
} else if (symbolTable.TryGetNonVariableValue(dfd.SymbolRef.Label,
|
||||
out Symbol sym)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
switch (formatter.ExpressionMode) {
|
||||
|
@ -203,6 +203,21 @@ namespace SourceGen {
|
||||
return mSymbols.TryGetValue(key, out sym);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value associated with the key, unless it's a variable.
|
||||
/// </summary>
|
||||
/// <param name="key">Label to look up.</param>
|
||||
/// <param name="sym">Symbol, or null if not found, or found but it's a variable.</param>
|
||||
/// <returns>True if the key is present, false otherwise.</returns>
|
||||
public bool TryGetNonVariableValue(string key, out Symbol sym) {
|
||||
bool found = mSymbols.TryGetValue(key, out sym);
|
||||
if (found && sym.IsVariable) {
|
||||
sym = null;
|
||||
found = false;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified symbol.
|
||||
/// </summary>
|
||||
|
@ -117,7 +117,7 @@ namespace SourceGen {
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "WeakSym: " + Label + ":" + ValuePart;
|
||||
return "WeakSym: " + (IsVariable ? "var " : "") + Label + ":" + ValuePart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user