CS_MergeLabels: Keep labels referenced by data

Partial fix for ICE in #1211.  This may fix enough to allow #1049 to be
fixed.

When merging labels, keep the first label with a ref that has no JumpTo;
this is a data segment label, used by computed gotos.

The real fix is to track and rewrite labels in data, but this is more
involved.
This commit is contained in:
Jesse Rosenstock 2020-08-24 22:24:00 +02:00 committed by Oliver Schmidt
parent 037a806036
commit 81550ca1ee
3 changed files with 47 additions and 3 deletions

View File

@ -180,6 +180,16 @@ INLINE CodeLabel* CE_GetLabel (CodeEntry* E, unsigned Index)
# define CE_GetLabel(E, Index) CollAt (&(E)->Labels, (Index))
#endif
#if defined(HAVE_INLINE)
INLINE void CE_ReplaceLabel (CodeEntry* E, CodeLabel* L, unsigned Index)
/* Replace the code label at the specified index with L */
{
return CollReplace (&E->Labels, L, Index);
}
#else
# define CE_ReplaceLabel(E, L, Index) CollReplace (&(E)->Labels, (L), (Index))
#endif
void CE_MoveLabel (CodeLabel* L, CodeEntry* E);
/* Move the code label L from it's former owner to the code entry E. */

View File

@ -183,6 +183,37 @@ static void CS_RemoveLabelFromHash (CodeSeg* S, CodeLabel* L)
static CodeLabel* PickRefLab (CodeEntry* E)
/* Pick a reference label and move it to index 0 in E. */
{
unsigned I;
unsigned LabelCount = CE_GetLabelCount (E);
CHECK (LabelCount > 0);
/* Use either the first one as reference label, or a label with a Ref that has no JumpTo.
** This is a hack to partially work around #1211. Refs with no JumpTo are labels used
** in data segments. (They are not tracked.) If a data segment is the only reference,
** the label will be pruned away, but the data reference will remain, causing linking to fail.
*/
CodeLabel* L0 = CE_GetLabel (E, 0);
for (I = 1; I < LabelCount; ++I) {
unsigned J;
CodeLabel* L = CE_GetLabel (E, I);
unsigned RefCount = CL_GetRefCount (L);
for (J = 0; J < RefCount; ++J) {
CodeEntry* EJ = CL_GetRef (L, J);
if (EJ->JumpTo == NULL) {
/* Move it to the beginning since it's simpler to handle the removal this way. */
CE_ReplaceLabel (E, L, 0);
CE_ReplaceLabel (E, L0, I);
return L;
}
}
}
return L0;
}
/*****************************************************************************/
/* Functions for parsing instructions */
/*****************************************************************************/
@ -935,8 +966,10 @@ void CS_MergeLabels (CodeSeg* S)
continue;
}
/* We have at least one label. Use the first one as reference label. */
RefLab = CE_GetLabel (E, 0);
/* Pick a label to keep, all references will be moved to this one, and the other labels
** will be deleted. PickRefLab moves this to index 0.
*/
RefLab = PickRefLab (E);
/* Walk through the remaining labels and change references to these
** labels to a reference to the one and only label. Delete the labels

View File

@ -21,7 +21,8 @@
/*
Test of indirect goto with label merge ICE.
https://github.com/cc65/cc65/issues/1211
This should compile and should be moved to tests/val/ when the bug is fixed.
This test case works because CS_MergeLabels has a hack to keep the "unreferenced" label
(i.e. the one referenced in a data segment).
*/
#include <stdio.h>