Reiterate layout step until section addresses converge#688
Reiterate layout step until section addresses converge#688parth-07 wants to merge 1 commit intoqualcomm:mainfrom
Conversation
81a1a0b to
7b61c10
Compare
7b61c10 to
2d6821f
Compare
2d6821f to
93a16f2
Compare
quic-seaswara
left a comment
There was a problem hiding this comment.
Why are you exposing maxPreRelaxationPasses to the function ?
I feel createProgramHeaders is becoming overly iterative.
The logic can be contained in GNULDBackend.
We need to use
To push the logic in GNULDBackend, we will need to refactor |
93a16f2 to
42473f8
Compare
42473f8 to
485a38d
Compare
| while (out != outEnd) { | ||
| bool useSetLMA = false; | ||
| int preRelaxPassIter = 0; | ||
| do { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
include/eld/Target/GNULDBackend.h
Outdated
| GNUVerDefFragment *GNUVerDefFrag = nullptr; | ||
| std::unordered_map<const ResolveInfo *, uint16_t> OutputVersionIDs; | ||
| #endif | ||
| OutputSectionEntry * changedOutputSection = nullptr; |
There was a problem hiding this comment.
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=0x18There was a problem hiding this comment.
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.
485a38d to
27b20eb
Compare
| }; | ||
|
|
||
| bool linkerScriptHasMemoryCommand = m_Module.getScript().hasMemoryCommand(); | ||
| auto reset_state = [&](OutputSectionEntry *OS = nullptr) { |
There was a problem hiding this comment.
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 nofwdThere was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; | ||
| }; |
There was a problem hiding this comment.
It looks like lld's max passes is 5. Is there any reason we picked a different number?
There was a problem hiding this comment.
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.
27b20eb to
c94344c
Compare
c94344c to
72783d2
Compare
72783d2 to
e641fed
Compare
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>
e641fed to
7da71b2
Compare
quic-areg
left a comment
There was a problem hiding this comment.
LGTM aside from some comments
| // -------------------- Finalize Layout callback ---------------- | ||
| virtual bool finalizeLayout() { return true; } | ||
|
|
||
| // -------------------- Layout convergence snapshot ------------------- |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
can this be
using LayoutSnapShot = llvm::DenseMap...?
quic-seaswara
left a comment
There was a problem hiding this comment.
I generally like the direction we are going with this.
| }; | ||
|
|
||
| struct LayoutSnapshot { | ||
| llvm::DenseMap<const OutputSectionEntry *, SectionAddrs> outSections; |
There was a problem hiding this comment.
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;
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