diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs
index 13c9e5f..7c6a1e8 100644
--- a/SourceGen/AsmGen/AsmMerlin32.cs
+++ b/SourceGen/AsmGen/AsmMerlin32.cs
@@ -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;
diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs
index 13b7ccc..14567d6 100644
--- a/SourceGen/AsmGen/AsmTass64.cs
+++ b/SourceGen/AsmGen/AsmTass64.cs
@@ -160,6 +160,7 @@ namespace SourceGen.AsmGen {
Project = project;
Quirks = new AssemblerQuirks();
+ Quirks.HasRedefinableSymbols = true;
Quirks.StackIntOperandIsImmediate = true;
mWorkDirectory = workDirectory;
diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs
index 6d321a9..1fc9801 100644
--- a/SourceGen/AsmGen/GenCommon.cs
+++ b/SourceGen/AsmGen/GenCommon.cs
@@ -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);
diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs
index d366523..3736333 100644
--- a/SourceGen/AsmGen/IGenerator.cs
+++ b/SourceGen/AsmGen/IGenerator.cs
@@ -186,26 +186,21 @@ namespace SourceGen.AsmGen {
/// Enumeration of quirky or buggy behavior that GenCommon needs to handle.
///
public class AssemblerQuirks {
- ///
- /// Are the arguments to MVN/MVP reversed?
- ///
- public bool BlockMoveArgsReversed { get; set; }
-
///
/// Are 8-bit constant args to MVN/MVP output without a leading '#'?
///
public bool BlockMoveArgsNoHash { get; set; }
///
- /// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
+ /// Are the arguments to MVN/MVP reversed?
///
- public bool StackIntOperandIsImmediate { get; set; }
+ public bool BlockMoveArgsReversed { get; set; }
///
- /// 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?
///
- public bool TracksSepRepNotEmu { get; set; }
+ public bool HasRedefinableSymbols { get; set; }
///
/// Is the assembler unable to generate relative branches that wrap around banks?
@@ -213,6 +208,11 @@ namespace SourceGen.AsmGen {
///
public bool NoPcRelBankWrap { get; set; }
+ ///
+ /// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
+ ///
+ public bool StackIntOperandIsImmediate { get; set; }
+
///
/// Is the assembler implemented as a single pass? (e.g. cc65)
///
@@ -223,5 +223,11 @@ namespace SourceGen.AsmGen {
/// and not corrected when the actual width is determined?
///
public bool SinglePassNoLabelCorrection { get; set; }
+
+ ///
+ /// Does the assembler configure assembler widths based on SEP/REP, but doesn't
+ /// track the emulation bit?
+ ///
+ public bool TracksSepRepNotEmu { get; set; }
}
}
\ No newline at end of file
diff --git a/SourceGen/DefSymbol.cs b/SourceGen/DefSymbol.cs
index c07f300..c4be472 100644
--- a/SourceGen/DefSymbol.cs
+++ b/SourceGen/DefSymbol.cs
@@ -134,6 +134,17 @@ namespace SourceGen {
Tag = string.Empty;
}
+ ///
+ /// Constructs a DefSymbol from an existing DefSymbol, with a different label. Use
+ /// this to change the label while keeping everything else the same.
+ ///
+ /// Source DefSymbol.
+ /// Label to use.
+ public DefSymbol(DefSymbol defSym, string label)
+ : this(label, defSym.Value, defSym.SymbolSource, defSym.SymbolType,
+ defSym.DataDescriptor.FormatSubType, defSym.Comment, defSym.Tag,
+ defSym.DataDescriptor.Length) { }
+
///
/// Determines whether a symbol overlaps with a region. Useful for variables.
///
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 1090f45..0732e2c 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -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.
///
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 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);
}
}
}
diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs
index 9c112f1..9001a40 100644
--- a/SourceGen/LineListGen.cs
+++ b/SourceGen/LineListGen.cs
@@ -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;
}
diff --git a/SourceGen/LocalVariableLookup.cs b/SourceGen/LocalVariableLookup.cs
index 5272e4e..f629092 100644
--- a/SourceGen/LocalVariableLookup.cs
+++ b/SourceGen/LocalVariableLookup.cs
@@ -34,6 +34,42 @@ namespace SourceGen {
///
private SymbolTable mSymbolTable;
+ ///
+ /// Label uniquification helper.
+ ///
+ 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;
+ }
+
+ ///
+ /// Updates the Label to be unique. Call this when a symbol is defined or
+ /// re-defined.
+ ///
+ /// Symbol table, for uniqueness check.
+ 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 mUniqueLabels;
+
///
/// Reference to project, so we can query the Anattrib array to identify "hidden" tables.
///
@@ -73,6 +109,9 @@ namespace SourceGen {
mProject = project;
mCurrentTable = new LocalVariableTable();
+ if (mSymbolTable != null) {
+ mUniqueLabels = new Dictionary();
+ }
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 {
/// Symbol, or null if no match found.
public DefSymbol GetSymbol(int offset, int operandValue, Symbol.Type type) {
AdvanceToOffset(offset);
-
return mCurrentTable.GetByValueRange(operandValue, 1, type);
}
///
- /// 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.
///
/// Offset of start of instruction.
/// Reference to symbol.
@@ -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;
}
///
@@ -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.
///
- /// Target offset.
- private void AdvanceToOffset(int offset) {
+ /// Target offset.
+ 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.
diff --git a/SourceGen/LocalVariableTable.cs b/SourceGen/LocalVariableTable.cs
index 168eb1c..dbec864 100644
--- a/SourceGen/LocalVariableTable.cs
+++ b/SourceGen/LocalVariableTable.cs
@@ -133,15 +133,25 @@ namespace SourceGen {
}
}
+ ///
+ /// Clears the tables.
+ ///
public void Clear() {
mVarByLabel.Clear();
mVarByValue.Clear();
}
+ ///
+ /// Returns the symbol that matches the label, or null if not found.
+ ///
public DefSymbol GetByLabel(string label) {
- return mVarByLabel[label];
+ mVarByLabel.TryGetValue(label, out DefSymbol defSym);
+ return defSym;
}
+ ///
+ /// Removes the symbol with the matching label.
+ ///
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]);
}
diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs
index 613fe7a..61329c9 100644
--- a/SourceGen/PseudoOp.cs
+++ b/SourceGen/PseudoOp.cs
@@ -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) {
diff --git a/SourceGen/SymbolTable.cs b/SourceGen/SymbolTable.cs
index 29d3759..6ab4c45 100644
--- a/SourceGen/SymbolTable.cs
+++ b/SourceGen/SymbolTable.cs
@@ -203,6 +203,21 @@ namespace SourceGen {
return mSymbols.TryGetValue(key, out sym);
}
+ ///
+ /// Gets the value associated with the key, unless it's a variable.
+ ///
+ /// Label to look up.
+ /// Symbol, or null if not found, or found but it's a variable.
+ /// True if the key is present, false otherwise.
+ 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;
+ }
+
///
/// Removes the specified symbol.
///
diff --git a/SourceGen/WeakSymbolRef.cs b/SourceGen/WeakSymbolRef.cs
index a221dbc..edf789f 100644
--- a/SourceGen/WeakSymbolRef.cs
+++ b/SourceGen/WeakSymbolRef.cs
@@ -117,7 +117,7 @@ namespace SourceGen {
}
public override string ToString() {
- return "WeakSym: " + Label + ":" + ValuePart;
+ return "WeakSym: " + (IsVariable ? "var " : "") + Label + ":" + ValuePart;
}
}
}