Skip to content

Comments

Reiterate layout step until section addresses converge#688

Open
parth-07 wants to merge 1 commit intoqualcomm:mainfrom
parth-07:SectionAddressConvergence
Open

Reiterate layout step until section addresses converge#688
parth-07 wants to merge 1 commit intoqualcomm:mainfrom
parth-07:SectionAddressConvergence

Conversation

@parth-07
Copy link
Contributor

@parth-07 parth-07 commented Jan 5, 2026

This commit modifies the layout step to reiterate until the section addresses converge. The reiteration has an upper limit of 4. On reaching this limit, a warning is emitted and the layout reiteration is stopped.

Please note that this reiteration limit is for layout iterations before relaxations take place. After a layout relaxation pass, the reiteration count is reset to 0.

Resolves #687

@parth-07 parth-07 force-pushed the SectionAddressConvergence branch 5 times, most recently from 81a1a0b to 7b61c10 Compare January 7, 2026 09:58
@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 7b61c10 to 2d6821f Compare January 8, 2026 12:30
@partaror partaror force-pushed the SectionAddressConvergence branch from 2d6821f to 93a16f2 Compare January 9, 2026 10:30
Copy link
Contributor

@quic-seaswara quic-seaswara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you exposing maxPreRelaxationPasses to the function ?

I feel createProgramHeaders is becoming overly iterative.

The logic can be contained in GNULDBackend.

@parth-07
Copy link
Contributor Author

Why are you exposing maxPreRelaxationPasses to the function ?

We need to use maxPreRelaxationPasses in createProgramHdrsandcreateScriptProgramHdrs` function to determine
the number of times the layout needs to be computed when it is not getting converged.

I feel createProgramHeaders is becoming overly iterative.
The logic can be contained in GNULDBackend.

To push the logic in GNULDBackend, we will need to refactor createProgramHdrs and createScriptProgramHdrs so that
we can run the inner loop of these functions directly from GNULDBackend. However, in our offline discussions, you told not
to refactor those functions as of now as that will increase the testing requirement.

@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 93a16f2 to 42473f8 Compare January 14, 2026 05:47
@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 42473f8 to 485a38d Compare January 14, 2026 06:24
while (out != outEnd) {
bool useSetLMA = false;
int preRelaxPassIter = 0;
do {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a massive loop that requires indenting everything and makes the diff really large. I wonder if this can be avoided somehow? Can we isolate the single-pass into a new function and then have a small loop in relax() or somewhere else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we isolate the single-pass into a new function and then have a small loop in relax() or somewhere else?

That will also result in a huge diff, same as it is right now.

I wonder if this can be avoided somehow?

We can keep the indentation of the original loop as before, but still enclose it within do .. while(), however, then the indentation would be incorrect and reading the code in future might be misleading in the future due to incorrect indentation.

Copy link
Contributor

@quic-areg quic-areg Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking to first refactor the single-pass in a separate patch, and then this PR just becomes adding a loop

If refactoring is not an option, can the loop be a while() loop instead of a do...while? Since the function is massive it's not obvious what condition we are looping over until we find the end of the loop many lines down.

Regardless of whatever decision is taken here I think the two .hpp files could benefit from refactoring somewhere down the line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the design so that now we don't have large number of changes in this PR.

Regardless of whatever decision is taken here I think the two .hpp files could benefit from refactoring somewhere down the line.

Totally agree on this.

GNUVerDefFragment *GNUVerDefFrag = nullptr;
std::unordered_map<const ResolveInfo *, uint16_t> OutputVersionIDs;
#endif
OutputSectionEntry * changedOutputSection = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layout iterations only continue when section addresses change, but what about symbol-only forward references?

cat > 1.c << \!
int foo() {return 0;}
!

cat > cycle.t << \!
SECTIONS {
  . = 0x1000;
  .text : {*(.text*)}
  a = b + 4;
  b = a + 4;
}
!

cat > sym.t << \!
PHDRS {TEXT PT_LOAD;}
SECTIONS {
  . = 0x1000;
  .text : {*(.text*)} : TEXT
  a = b + 4;
  b = c + 4;
  c = 0x2000;
}
!

clang -c 1.c

ld.eld 1.o -T sym.t # a,b,c = 0x8,    0x2004, 0x2000
ld.lld 1.o -T sym.t # a,b,c = 0x2008, 0x2004, 0x2000

ld.lld 1.o -T cycle.t # error: assignment to symbol a does not converge
ld.eld 1.o -T cycle.t # a = 0x14; b=0x18

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layout iterations only continue when section addresses change, but what about symbol-only forward references?

I plan to handle symbol address convergence in the next patch. The subsequent patches will add support for symbol address convergence, improved diagnostics related to layout iterations and convergence, and some heuristics to reduce the number of iterations wherever possible.

@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 485a38d to 27b20eb Compare January 14, 2026 17:31
@parth-07 parth-07 requested a review from quic-areg January 14, 2026 17:32
};

bool linkerScriptHasMemoryCommand = m_Module.getScript().hasMemoryCommand();
auto reset_state = [&](OutputSectionEntry *OS = nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we reuse the segment table across passes; segments from a transient layout can stick around:

In the first pass we create a new PT_LOAD for .bar due to the forward reference that remains even after v is fixed:

cat > 1.c << \!
[[gnu::section(".foo")]] int foo = 1;
[[gnu::section(".bar")]] int bar = 2;
!

cat > fwd.t << \!
SECTIONS {
  . = u;
  .foo : {*(.foo)}
  . = v;
  .bar : {*(.bar)}
  u = 0x1000;
  v = u + 0x10;
}
!

cat > nofwd.t << \!
SECTIONS {
  . = 0x1000;
  .foo : {*(.foo)}
  . = 0x1010;
  .bar : {*(.bar)}
}
!

clang -c 1.c
ld.eld 1.o -T fwd.t -o fwd     # two PT_LOADs with .foo and .bar separate.
ld.eld 1.o -T nofwd.t -o nofwd # single PT_LOAD with .foo and .bar together.

ld.lld 1.o -T fwd.t -o fwd
ld.lld 1.o -T fwd.t -o nofwd

llvm-readelf -l fwd nofwd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we reuse the segment table across passes; segments from a transient layout can stick around:

Thank you for mentioning this. This can indeed create some issues.

ld.eld 1.o -T fwd.t -o fwd # two PT_LOADs with .foo and .bar separate.
ld.eld 1.o -T nofwd.t -o nofwd # single PT_LOAD with .foo and .bar together.

The behavior here is the same as before this patch.

I will change the patch functionality so that the segments from a transient layout do not cause issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ld.eld 1.o -T fwd.t -o fwd # two PT_LOADs with .foo and .bar separate.
ld.eld 1.o -T nofwd.t -o nofwd # single PT_LOAD with .foo and .bar together.

With the latest patchset, we have single PT_LOAD segment in both the cases. Thank you for pointing this out.

#endif
OutputSectionEntry * changedOutputSection = nullptr;
const int maxPreRelaxationPasses = 4;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like lld's max passes is 5. Is there any reason we picked a different number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLD's has two different kind of max passes, one is 30 and the other is 5. LLD uses the combination of these two during the layout. eld layout mechanism is different from LLD. 4 is not fixed here, and can be changed if a greater / smaller value seems better in the future. I believe that 4 is appropriate here as a good upper-bound, especially after adding some of the heuristics to converge faster that are planned as a follow-up after this patch. And if the layout converges in the 2nd iteration, then the remaining iterations will be skipped.

@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 27b20eb to c94344c Compare January 28, 2026 11:10
@partaror partaror requested a review from quic-areg January 28, 2026 11:12
@partaror partaror force-pushed the SectionAddressConvergence branch from c94344c to 72783d2 Compare January 30, 2026 16:22
@parth-07 parth-07 force-pushed the SectionAddressConvergence branch from 72783d2 to e641fed Compare February 3, 2026 07:40
This commit modifies the layout step to reiterate until the section
addresses converge. The reiteration has an upper limit of 4. On
reaching this limit, a note is emitted and the layout reiteration
is stopped. It is not warning as of now because it can cause link
failure when --fatal-warnings is used. This note will later be converted
to a warning once the feature is complete.

Please note that this reiteration limit is for layout
iterations before relaxations take place. After a layout relaxation
pass, the reiteration count is reset to 0.

Resolves qualcomm#687

Signed-off-by: Parth Arora <partaror@qti.qualcomm.com>
@partaror partaror force-pushed the SectionAddressConvergence branch from e641fed to 7da71b2 Compare February 3, 2026 15:52
Copy link
Contributor

@quic-areg quic-areg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM aside from some comments

// -------------------- Finalize Layout callback ----------------
virtual bool finalizeLayout() { return true; }

// -------------------- Layout convergence snapshot -------------------
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a tangent, I am generally not a fan of this kind of comment.

// -------------------- Layout convergence snapshot -------------------
// -------------------- Finalize Layout callback ----------------

The length of the ---- portions are always inconsistent. The comment would look a lot better as

// Layout convergence

Or in some cases, omitted entirely.

const OutputSectionEntry *isConverged(const LayoutSnapshot &Prev,
const LayoutSnapshot &cur) const;

// --- Exclude symbol support
Copy link
Contributor

@quic-areg quic-areg Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tangent: This is an example where the comment is better omitted. It just repeats the function name, which is already self-describing.

uint64_t lma = 0;
};

struct LayoutSnapshot {
Copy link
Contributor

@quic-areg quic-areg Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be

using LayoutSnapShot = llvm::DenseMap...

?

Copy link
Contributor

@quic-seaswara quic-seaswara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally like the direction we are going with this.

};

struct LayoutSnapshot {
llvm::DenseMap<const OutputSectionEntry *, SectionAddrs> outSections;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this a vector of all the output sections ?

Since we know the order in this case, it is much easier to compare and return ?

struct { 
   OutputSectionEntry *OS;
   uint64_t vma;
   uint64_t lma;
   operator ==(OSE &right) {
   ... 
   }
} OSE;
std::vector<OSE> OutputSections;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect layout when there is a forward reference in a dot assignment

3 participants