diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 173118a7f..bd542cc4c 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -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. */ diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index ad8a3148c..8177d4fd4 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -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 diff --git a/test/err/bug1211-ice-move-refs-1.c b/test/val/bug1211-ice-move-refs-1.c similarity index 91% rename from test/err/bug1211-ice-move-refs-1.c rename to test/val/bug1211-ice-move-refs-1.c index f28d1fcbc..9be1696ee 100644 --- a/test/err/bug1211-ice-move-refs-1.c +++ b/test/val/bug1211-ice-move-refs-1.c @@ -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