Hi guys,
After working for a long period of time on LLD, I think I found a few things that we should improve in the LLD design for both development ease and runtime performance. I would like to get feedback on this proposal. Thanks!
Problems with the current LLD architecture
The current LLD architecture has, in my opinion, two issues.
The atom model is not the best model for some architectures The atom model makes sense only for Mach-O, but it’s used everywhere. I guess that we originally expected that we would be able to model the linker’s behavior beautifully using the atom model because the atom model seemed like a superset of the section model. Although it *can*, it turned out that it’s not necessarily natural and efficient model for ELF or PE/COFF on which section-based linking is expected. On ELF or PE/COFF, sections are units of atomic data. We divide a section into smaller “atoms” and then restore the original data layout later to preserve section’s atomicity. That complicates the linker internals. Also it slows down the linker because of the overhead of creating and manipulating atoms. In addition to that, since section-based linking is expected on the architectures, some linker features are defined in terms of sections. An example is “select largest section” in PE/COFF. In the atom model, we don’t have a notion of sections at all, so we had to simulate such features using atoms in tricky ways.
One symbol resolution model doesn’t fit all
The symbol resolution semantics are not the same on three architectures (ELF, Mach-O and PE/COFF), but we only have only one "core" linker for the symbol resolution.
The core linker implements the Unix linker semantics; the linker visits a file at a time until all undefined symbols are resolved. For archive files having circular dependencies, you can group them to tell the linker to visit them more than once. This is not the only model to create a linker. It’s not the simplest nor fastest. It’s just that the Unix linker semantics is designed this way, and we all follow for compatibility.
For PE/COFF, the linker semantics are different. The order of files in the command line doesn’t matter. The linker scans all files first to create a map from symbols to files, and use the map to resolve all undefined symbols.
The PE/COFF semantics are currently simulated using the Unix linker semantics and groups. That made the linker inefficient because of the overhead to visit archive files again and again. Also it made the code bloated and awkward.
In short, we generalize too much, and we share code too much.
Proposal
Rafael and I have been discussing this change recently. It makes atoms
actually atomic, and also splits out symbols, which has been needed.
The main reason I like this over each target having its own model is
because it gives us a common textual representation to write tests
with.
As for symbol resolution. It seems the actual problem is name lookup,
not the core resolver semantics.
I'd rather not end up with basically 3 separate linkers in lld.
- Michael Spencer
_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
On Fri, May 1, 2015 at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:
> Caveat Why not define a section as an atom and keep using the atom model? If
> we do this, we would have to allow atoms to have more than one name. Each
> name would have an offset in the atom (to represent symbols whose offset
> from the section start is not zero). But still we need to copy section
> attributes to each atom. The resulting model no longer looks like the atom
> model, but a mix of the atom model and the section model, and that comes
> with the cost of both designs. I think it’s too complicated.
Rafael and I have been discussing this change recently. It makes atoms
actually atomic, and also splits out symbols, which has been needed.
The main reason I like this over each target having its own model is
because it gives us a common textual representation to write tests
with.
As for symbol resolution. It seems the actual problem is name lookup,
not the core resolver semantics.
I'd rather not end up with basically 3 separate linkers in lld.
It's pretty much the same. I read what you said as having a different
section representation for each target.
>
>> As for symbol resolution. It seems the actual problem is name lookup,
>> not the core resolver semantics.
>
>
> What's the difference between name lookup and the core resolver semantics?
>
Name lookup would be how it finds what symbols to consider for
resolving. The core resolver semantics is mostly
SymbolTable::addByName.
- Michael Spencer
>>
>> I'd rather not end up with basically 3 separate linkers in lld.
>
>
> I basically agree. However, if you take a look at the code of the PE/COFF
> port, you'll find something weird here and there.
_______________________________________________
On Fri, May 1, 2015 at 1:42 PM, Rui Ueyama <ru...@google.com> wrote:
> On Fri, May 1, 2015 at 1:32 PM, Michael Spencer <bigch...@gmail.com>
> wrote:
>>
>> On Fri, May 1, 2015 at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:
>> > Caveat Why not define a section as an atom and keep using the atom
>> > model? If
>> > we do this, we would have to allow atoms to have more than one name.
>> > Each
>> > name would have an offset in the atom (to represent symbols whose offset
>> > from the section start is not zero). But still we need to copy section
>> > attributes to each atom. The resulting model no longer looks like the
>> > atom
>> > model, but a mix of the atom model and the section model, and that comes
>> > with the cost of both designs. I think it’s too complicated.
>>
>> Rafael and I have been discussing this change recently. It makes atoms
>> actually atomic, and also splits out symbols, which has been needed.
>> The main reason I like this over each target having its own model is
>> because it gives us a common textual representation to write tests
>> with.
>
>
> If you allow multiple symbols in one atom, is the new definition of atom
> different from section? If so, in what way?
It's pretty much the same. I read what you said as having a different
section representation for each target.
>
>> As for symbol resolution. It seems the actual problem is name lookup,
>> not the core resolver semantics.
>
>
> What's the difference between name lookup and the core resolver semantics?
>
Name lookup would be how it finds what symbols to consider for
resolving. The core resolver semantics is mostly
SymbolTable::addByName.
I am on the airport waiting to go on vacations, but I must say I am extremely happy to see this happen!
I agree with the proposed direction and steps:
Implement section based linking for coff.
Use that for elf.
If it makes sense, use it for macho.
The atom model is not the best model for some architectures
One symbol resolution model doesn’t fit all
On May 1, 2015 9:42 PM, "Nick Kledzik" <kle...@apple.com> wrote:
>
>
> On May 1, 2015, at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:
>>
>> The atom model is not the best model for some architectures
>
>
> The atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom.
That is not the input to the linker and therefore irrelevant.
> The problem is the ELF/PECOFF file format. (Actually mach-o is also section based, but we have refrained from adding complex section-centric features to it, so mapping it to atoms is not too hard).
The objective is to build an elf and coff linker. The input has sections and splitting them is a total waste of time and extra design complexity.
> I’d rather see our effort put to moving ahead to an llvm based object file format (aka “native” format) which bypasses the impedance mismatch of going through ELF/COFF.
Absolutely not. We have to be able to handle elf and coff and do it well.
Also, gold shows that elf at least works extremely well. With function sections the compiler is in complete control of the size of the units the linker uses. With my recent work on MC the representation is also very efficient. I have no reason to believe coff is any different.
Cheers,
Rafael
On May 1, 2015, at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:The atom model is not the best model for some architecturesThe atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom.
The problem is the ELF/PECOFF file format. (Actually mach-o is also section based, but we have refrained from adding complex section-centric features to it, so mapping it to atoms is not too hard).I’d rather see our effort put to moving ahead to an llvm based object file format (aka “native” format) which bypasses the impedance mismatch of going through ELF/COFF.
One symbol resolution model doesn’t fit allYes, the Resolver was meant to call out to the LinkingContext object to direct it on how to link. Somehow that got morphed into “there should be a universal data model that when the Resolver process the input data, the right platform specific linking behavior falls out”.-Nick
On Fri, May 1, 2015 at 6:46 PM Nick Kledzik <kle...@apple.com> wrote:On May 1, 2015, at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:The atom model is not the best model for some architecturesThe atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom.I'm not sure how that's really relevant.On some architectures, the unit at which linking is defined to occur isn't a global object. A classic example of this are architectures that have a hard semantic reliance grouping two symbols together and linking either both or neither of them.The problem is the ELF/PECOFF file format. (Actually mach-o is also section based, but we have refrained from adding complex section-centric features to it, so mapping it to atoms is not too hard).I’d rather see our effort put to moving ahead to an llvm based object file format (aka “native” format) which bypasses the impedance mismatch of going through ELF/COFF.We still have to be able to (efficiently) link existing ELF and COFF objects though? While I'm actually pretty interested in some better object file format, I also want a better linker for the world we live in today...
There are projects (like FreeBSD) which need a new linker.
In the FreeBSD case it is mainly motivated by a licensing issue, but I
feel like this doesn't mean that the linker needs to be slower or
harder to hack on because we want to treat as first class citizen a
format that has been largely unmaintained in the last 6 months at
least and as second class citizen widespread formats like ELF. I'm
personally excited about the idea of a new format and I would like to
spend some time thinking about it, although I always try to be
pragmatic.
I will be happy to discuss this further in person.
--
Davide
"There are no solved problems; there are only problems that are more
or less solved" -- Henri Poincare
Why can't LLD be free to implement a resolving algorithm that performs better.
The PE/COFF method you describe seems more efficient that the existing
ELF method.
What is stopping LLD from using the PE/COFF method for ELF. It could
also do further optimizations such as caching the resolved symbols.
To me,the existing algorithms read as ELF == Full table scan, PE/COEF
== Indexed.
Also, could some of the symbol resolution be done at compile time?
E.g. If I include stdio.h, I know which link time library that is
associated with, so I can resolve those symbols at compile time.
Maybe we could store that information in the pre-compiled headers file
format, and subsequently in the .o files.
This would then leave far fewer symbols to resolve at link time.
Kind Regards
James
And now we're against the Atom model?
Also, could some of the symbol resolution be done at compile time?
E.g. If I include stdio.h, I know which link time library that is
associated with, so I can resolve those symbols at compile time.
Maybe we could store that information in the pre-compiled headers file
format, and subsequently in the .o files.
This would then leave far fewer symbols to resolve at link time.
It is not a secondary goal for me to create a linker that works very well with existing file formats. I'm trying to create a practical tool with clean codebase and with the LLVM library. So I can't agree with you that it's secondary.I don't also share the view that we are trying to solve the problem that's solved in '70s. How fast we can link a several hundred megabyte executable is, for example, a pretty modern problem that we have today.
Proposal
- Re-architect the linker based on the section model where it’s appropriate.
- Stop simulating different linker semantics using the Unix model. Instead, directly implement the native behavior.
It has been said in this thread before, but I fail to see how the atom
model is an actual improvement over the fine grained section model. It
seems to be artifically restricted for no good reasons.
> Lets stop thinking about lld as one linker, and instead think of it is
> two different ones. We’ll build a Camp B linker which is the best of
> breed section based linker. It will support linker scripts and do
> everything better than any existing section based linker. The first
> step of this is to do what Rui proposes and rip atoms out of the model.
This is another item that has been irritating me. While it is a very
laudable goal to not depend on linker scripts for the common case, not
having the functionality of fine grained output control is certainly a
problem. They are crucial for embedded developers and also at least
significant for anything near a system kernel.
> We will also build a no-holds-barred awesome atom based linker that
> takes advantage of everything it can from LLVM’s architecture to enable
> innovative new tools without worrying too much about backwards
> compatibility.
I'd say that a good justificatiton for way an atom based linker is/can
be better would be a good start...
Joerg
Sections come with a huge amount of bloat and overhead that atoms do not.
>> Lets stop thinking about lld as one linker, and instead think of it is
>> two different ones. We’ll build a Camp B linker which is the best of
>> breed section based linker. It will support linker scripts and do
>> everything better than any existing section based linker. The first
>> step of this is to do what Rui proposes and rip atoms out of the model.
>
> This is another item that has been irritating me. While it is a very
> laudable goal to not depend on linker scripts for the common case, not
> having the functionality of fine grained output control is certainly a
> problem. They are crucial for embedded developers and also at least
> significant for anything near a system kernel.
I’m not saying that the linker should eschew fine grained control, I’m saying it should dump linker scripts (and replace them with something better). Are you going to argue that linker scripts are great, or that they are what we would end up with if we weren’t driven by backwards compatibility goals?
-Chris
On May 4, 2015, at 1:16 PM, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:
> It has been said in this thread before, but I fail to see how the atom
> model is an actual improvement over the fine grained section model. It
> seems to be artifically restricted for no good reasons.
Sections come with a huge amount of bloat and overhead that atoms do not.
>> Lets stop thinking about lld as one linker, and instead think of it is
>> two different ones. We’ll build a Camp B linker which is the best of
>> breed section based linker. It will support linker scripts and do
>> everything better than any existing section based linker. The first
>> step of this is to do what Rui proposes and rip atoms out of the model.
>
> This is another item that has been irritating me. While it is a very
> laudable goal to not depend on linker scripts for the common case, not
> having the functionality of fine grained output control is certainly a
> problem. They are crucial for embedded developers and also at least
> significant for anything near a system kernel.
I’m not saying that the linker should eschew fine grained control, I’m saying it should dump linker scripts (and replace them with something better). Are you going to argue that linker scripts are great, or that they are what we would end up with if we weren’t driven by backwards compatibility goals?
I don't buy that as far as the internal representation is concerned.
On-disk format as Eric said is a somewhat different question.
> >> Lets stop thinking about lld as one linker, and instead think of it is
> >> two different ones. We’ll build a Camp B linker which is the best of
> >> breed section based linker. It will support linker scripts and do
> >> everything better than any existing section based linker. The first
> >> step of this is to do what Rui proposes and rip atoms out of the model.
> >
> > This is another item that has been irritating me. While it is a very
> > laudable goal to not depend on linker scripts for the common case, not
> > having the functionality of fine grained output control is certainly a
> > problem. They are crucial for embedded developers and also at least
> > significant for anything near a system kernel.
>
> I’m not saying that the linker should eschew fine grained control, I’m
> saying it should dump linker scripts (and replace them with something
> better). Are you going to argue that linker scripts are great, or that
> they are what we would end up with if we weren’t driven by backwards
> compatibility goals?
I haven't seen the better alternative yet. It is hard to reason about
vaporware. I'm not particulary attached to linker scripts, they are
certainly a horrible language. But the lack of support is certainly a
show stopper for those areas where the functionality is needed.
How is a linker supporting at least a major part of that functionality
going to be different from a linker that accepts linker scripts as
input?
Joerg
> Linker scripts are worse than everything - except for the alternatives that
> we know about. Any particular suggestions here?
I very much care about the functionality provided by linker scripts (for
embedded systems and kernel work), but I do agree that most current
script formats are hard to use, debug, reason about, etc... I have
often wondered whether embedding Python might be a better choice.
Take a look at how debuggers have migrated through the years. They too
used to have their own script format. Now most (all?) popular debuggers
do scripting through embedding an actual programming language. This
could be a better way forward for linkers as well -- embed Python in the
linker, define a Python API for linkable item placement, entry point,
symbol operations, etc..., and then you also have the rest of Python at
your fingertips.
Take a look at how debuggers have migrated through the years. They tooused to have their own script format. Now most (all?) popular debuggers
do scripting through embedding an actual programming language. This
could be a better way forward for linkers as well -- embed Python in the
linker, define a Python API for linkable item placement, entry point,
symbol operations, etc..., and then you also have the rest of Python at
your fingertips.I mostly care about specifying address where specific symbols will be placed and specifying the memory layout of the platform. I normally use __attribute__((section(""))) to place the symbols in their own sections and then use the linker script to place the sections at the required addresses. How would this be accomplished without linker scripts?
But that way you have to do layout by hand in C. Generally you won't
know the size of the preceding code or data so you won't know what
address to put things at at the granularity of a single C level
object/function. Better to say "put this in the ROM section" and set
the address of the ROM section once in a linker script and let the
linker do the layout.
But that way you have to do layout by hand in C. Generally you won'tOn Wed, May 6, 2015 at 6:22 AM, Chris Lattner <clat...@apple.com> wrote:
> On May 5, 2015, at 6:47 PM, Daniel Dilts <dilt...@gmail.com> wrote:
>
> Take a look at how debuggers have migrated through the years. They too
>>
>> used to have their own script format. Now most (all?) popular debuggers
>> do scripting through embedding an actual programming language. This
>> could be a better way forward for linkers as well -- embed Python in the
>> linker, define a Python API for linkable item placement, entry point,
>> symbol operations, etc..., and then you also have the rest of Python at
>> your fingertips.
>
>
> I mostly care about specifying address where specific symbols will be placed
> and specifying the memory layout of the platform. I normally use
> __attribute__((section(""))) to place the symbols in their own sections and
> then use the linker script to place the sections at the required addresses.
> How would this be accomplished without linker scripts?
>
>
> I’d prefer to use an "__attribute__((address(0x1234)))” myself. That way
> you can control platform specifics with #ifdefs.
know the size of the preceding code or data so you won't know what
address to put things at at the granularity of a single C level
object/function. Better to say "put this in the ROM section" and set
the address of the ROM section once in a linker script and let the
linker do the layout.
Why would you want to put an object at the address of some registers?
You would just want a cast would you not?
e.g. regs = (struct MyRegBlock *)0x1234
But that way you have to do layout by hand in C. Generally you won'tOn Wed, May 6, 2015 at 6:22 AM, Chris Lattner <clat...@apple.com> wrote:
> On May 5, 2015, at 6:47 PM, Daniel Dilts <dilt...@gmail.com> wrote:
>
> Take a look at how debuggers have migrated through the years. They too
>>
>> used to have their own script format. Now most (all?) popular debuggers
>> do scripting through embedding an actual programming language. This
>> could be a better way forward for linkers as well -- embed Python in the
>> linker, define a Python API for linkable item placement, entry point,
>> symbol operations, etc..., and then you also have the rest of Python at
>> your fingertips.
>
>
> I mostly care about specifying address where specific symbols will be placed
> and specifying the memory layout of the platform. I normally use
> __attribute__((section(""))) to place the symbols in their own sections and
> then use the linker script to place the sections at the required addresses.
> How would this be accomplished without linker scripts?
>
>
> I’d prefer to use an "__attribute__((address(0x1234)))” myself. That way
> you can control platform specifics with #ifdefs.
know the size of the preceding code or data so you won't know what
address to put things at at the granularity of a single C level
object/function. Better to say "put this in the ROM section" and set
the address of the ROM section once in a linker script and let the
linker do the layout.
While slightly off topic of LLD improvement,
volatile uint8_t __attribute__((address(0x1234))) foo;
is slightly nicer than
(*(volatile uint8_t *)0x1234)
because the latter ends up often being done in a header file as a macro, usually as something like:
#define foo (*(volatile uint8_t *)0x1234)
The former behaves with all the language scoping rules, so a foo within a function or as a parameter has the expected
behavior. The latter has all the downsides that macro usage can come with.
On topic – Sean – I think that is a great document on linker scripts and their usage and meaning.
Kevin Smith
The real use case is on platforms, like ARM, where control registers are mapped to a specific address range. Then it is useful to put an object that deals with the control registers at a specific address. __attribute__((address(0x1234))) can be replaced with a platform specific linker script. Which is better? I don't know, I haven't spent any time comparing them.
There is another important use of linker scripts, namely having a notion of separate load and virtual addresses. This is important for e.g. an initialized .data section which is stored in ROM, but needs to be copied to writable memory.
For more information on linker scripts, see my post "linker script findings": http://marc.info/?l=llvm-dev&m=135698146015498&w=2
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by the Linux Foundation
The atom model is optimized when you compile the code with -ffunction-sections and -fdata-sections.
Once targets start having -fno-unique-section-names as the default the atom model looks more promising.
Everyone likes to have the image size small, and making -ffunction-sections/-fdata-sections (or) -fno-unique-section-names the default make sense and the atom model design directly has a relation to it. In fact it simplifies the linker to not have extra data structures IMO.
On 5/7/2015 11:19 AM, Reid Kleckner wrote:
> On Thu, May 7, 2015 at 8:36 AM, Shankar Easwaran <shan...@codeaurora.org>
> wrote:
>
>> The atom model is optimized when you compile the code with
>> -ffunction-sections and -fdata-sections.
>>
> Not really, -ffunction-sections -fdata-sections gives a section-based
> linker the same flexibility as an atom-based linker.
>
> In the atom model, these extra sections simply serve as a crutch to ensure
> that object files can be safely atomized. If you commit to the atom model,
> you shouldn't even need these sections, you should only need a flag that
> says "this section is safe for atomization". On MachO, this is what
> .subsections_via_symbols is for.
We could have this flag on ELF too ? This will reducing code from third
party libraries ??
>
>> Once targets start having -fno-unique-section-names as the default the
>> atom model looks more promising.
>>
> Why does LLD care about -fno-unique-section-names? I thought this was just
> an object file size optimization.
Sorry I meant -ffunction-sections along with -fno-unique-section-names.
>
Hi Rui,Thank you for clarifying. This is very helpful.It’s unfortunate that you’re not seeing benefits from the increased semantic knowledge the atom based model can provide. I know you’ve explored the issue thoroughly, though, so I understand why you’re wanting to move a different direction for your platform.It’s reasonable to me to split the logic along atom based vs. section based in the LLD codebase. Ideally I’d love for that not to be so, but the practical results indicate that it is. I agree there is still worthwhile code sharing that can and should be done between the two. We’re talking about expanding the LLD project’s scope to include multiple linking models, not forking the project.It will be important to keep the layering here such that the linking model choice is orthogonal to the file format choice. It should be possible to construct both an atom based ELF linker and a section based Mach-O linker, for example, even though the default choice for both formats is the other way around. That way different platforms with different constraints, such as what Alex talked about earlier, can make the choice of model and the choice of representation independently.As a second step, I would very much like to see the native format brought back, if only for the atom-based model. Do you feel this is doable?
Nobody in this long thread appears to have yet explained why it's a bad idea to allow atomic fragments of code/data (whatever you want to call them: atoms, sections, who cares) to have more than one global symbol attached to them in LLD's internal representation.That seems like it'd provide the flexibility needed for ELF without hurting MachO. If that change'd allow you to avoid splitting the linker into two-codebases-in-one, isn't that preferable?
If you attach two ore more symbols along with offsets to a chunk of data, it would be a pretty similar to a section. That means that if you want to do something on the atom model, now you have to treat the atoms like sections.
On Mon, May 11, 2015 at 11:13 AM, Rui Ueyama <ru...@google.com> wrote:If you attach two ore more symbols along with offsets to a chunk of data, it would be a pretty similar to a section. That means that if you want to do something on the atom model, now you have to treat the atoms like sections.
What do you lose/pay by having to treat the atoms like sections?
On 26 May 2015, at 20:13, Rui Ueyama <ru...@google.com> wrote:
>
> I sent a patch to llvm-commits. You can see the code at http://reviews.llvm.org/D10036. Thanks!
Why does the link not actually go where the text of it would imply, and instead bounce via some random (malicious?) third party? Do you have some malware infecting your mail client?
It’s correct in the plain-text version of the mail. The HTML MIME part contains this:
<a href=3D"https://urldefense.proofpoint.com/v2/url?u=3Dhttp-3A__reviews=
.llvm.org_D10036&d=3DAwMFaQ&c=3D8hUWFZcy2Z-Za5rBPlktOQ&r=3DMfk2qtn1LTDThVkh=
6-oGglNfMADXfJdty4_bhmuhMHA&m=3D8dYF1obzqNfZvfOxlk7H-g8VUfu1ZyS0GdcCWRkWxCk=
&s=3DRu6670O4y8SpAwlp17gVmI7BLz3mIY7gs1Irvo9iDRw&e=3D">http://reviews.llvm.=
org/D10036</a>
Apparently they’re not malicious, but I find it somewhat unnerving when the URL that I click on turns out not to be the one that the mouseover text pops up. If you feel the need to insert a redirection link, I’d very much appreciate it if you would post the full link in the text version, as well as the href. If, on the other hand, you are unaware that your computer is doing this, then I would encourage you to work out what it is and that it is not malicious.
The archives only include the plain text version, not the HTML copy, so will not see this.
I think I found the problem:UIUC is apparently rewriting HTML links in emails to redirect through urldefense.proofpoint.com. This is visible in my version of Rui's email.
Thanks for the investigation. Can we turn this off? Having emails with text containing URLs that is not the same as the link target URL is a big red flag for phishing and this is an absolutely terrible idea. This would also explain why I’ve seen a number of LLVMdev emails show up in my spam folder - I hadn’t thought to check where their links were going.
Rui: sorry for assuming that it was your fault,
On 27 May 2015, at 17:56, Reid Kleckner <r...@google.com> wrote:
>
> I think I found the problem:
> https://opia.illinois.edu/content/targeted-attack-protection-tuning
>
> UIUC is apparently rewriting HTML links in emails to redirect through urldefense.proofpoint.com. This is visible in my version of Rui's email.
Thanks for the investigation. Can we turn this off? Having emails with text containing URLs that is not the same as the link target URL is a big red flag for phishing and this is an absolutely terrible idea. This would also explain why I’ve seen a number of LLVMdev emails show up in my spam folder - I hadn’t thought to check where their links were going.
Rui: sorry for assuming that it was your fault,
David
First, note that there is a nomenclature issue. A section in ELF/COFF
is closer to an atom in MachO than a MachO section IMHO.
A rose by any other name would smell as sweet, but sure as hell
creates a lot of confusion :-)
On 4 May 2015 at 18:05, Chris Lattner <clat...@apple.com> wrote:
> On May 4, 2015, at 1:16 PM, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:
>> It has been said in this thread before, but I fail to see how the atom
>> model is an actual improvement over the fine grained section model. It
>> seems to be artifically restricted for no good reasons.
>
> Sections come with a huge amount of bloat and overhead that atoms do not.
No, they don't. Not on ELF for sure.
On ELF a section is just a entry into a table marking a region in the
file. The "huge amount of bloat" that people associate with sections
is actually just the extra space for the ultra large section names
".text._ZFoo....". Create multiple sections with the same name (I
implemented that) and the bloat goes away.
As has been pointed before, a section in ELF is just a better version
of what is called an Atom in lld: It is a chunk of the file that the
linker can move, but it also supports multiple symbols, which is handy
for things like making the C1 and C2 constructors share the same
address or how MSVC implement vtables+rtti.
Atoms being a distinct entity from sections (i.e., having non atomic
sections) is a necessity for MachO because it has more restrictive
sections (as Kevin was kind enough to explain).
Another way of looking at it (for understanding, I wouldn't use the
nomenclature in code) is that with this proposal lld will still be
atom based, we will just be extending atoms to support multiple
symbols. The logic for splitting sections into atoms would become
* ELF/COFF: one atom per section.
* MachO: One atom per global symbol.
And so MachO ends up with atoms that have only one symbol, but that is
just a special case.
>> This is another item that has been irritating me. While it is a very
>> laudable goal to not depend on linker scripts for the common case, not
>> having the functionality of fine grained output control is certainly a
>> problem. They are crucial for embedded developers and also at least
>> significant for anything near a system kernel.
>
> I’m not saying that the linker should eschew fine grained control, I’m saying it should dump linker scripts (and replace them with something better). Are you going to argue that linker scripts are great, or that they are what we would end up with if we weren’t driven by backwards compatibility goals?
I agree that this is a distinct issue. Linker scripts are a backward
compatibility pain. Directly using sections for ELF/COFF is *better*
than what is currently being done in lld.
As for organization, I agree with Rui's suggestion of 2 linkers in
one. One is ELF/COFF and uses sections, one is MachO and uses atoms.
Even with the split there is still enough common code that I don't
think having two repositories would help.
I don't agree that there is value in keeping the current atom on top
ELF/COFF. It just adds cost to two formats whose sections are already
flexible atoms. It also prevents optimizations like not even reading
duplicated comdats.
Last but not least, on the idea of a new object format:
Everyone that has worked on linkers or assemblers has a list of things
they don't like about the format that was being used (I do for sure).
It is entirely possible that if we get our thoughts together we can
build a better format.
Having said that, an object file format has a tremendous cost. Just
look at the pain that is maintaining support for mips' interpretation
of r_info. We have to be sure there is a genuine advantage to it
before adding a new object format to the world. To know that I think
we need to push the current formats to see how far they go.
As an analogy, imagine if people working on BFD had decided that ELF
linking was too slow or missing features and had decided to create a
new format that fit BFD better. That would have been really
unfortunate, because as gold showed the problem was not ELF, it was
the organization of BFD, but now we would probably be stuck supporting
4 formats in llvm and lld.
Once we have a linker (and MC) that is as good as it gets for ELF/COFF
and MachO we well be in a good position for discussing a new format.
Cheers,
Rafael
And now we're against the Atom model?I'm quite new to the llvm community, and basically unfamiliar with LLD, so maybe I'm simply uninformed. If so, I will now proceed to demonstrate that to an entire list of people. :)I've read the doc on http://lld.llvm.org/design.html, but the list of features it says that you get with LLD/Atoms and don't get with the "old generation" of linkers that use "sections"...are all things that ELF linkers already do using sections, and do not require anything finer grained than sections. Sections in ELF objects can actually be as fine-grained as you want them to be -- just as an "Atom". Doc also says, "An atom is an indivisible chunk of code or data." -- which is also what a section is for ELF.AFAICT, atoms in LLD are simply a restricted form of ELF sections: restricted to having a single symbol associated with them. It doesn't appear that they're actually enabling any new features that no other linker can do.I'm not very familiar with Mach-O, but it sounds like, contrary to ELF, Mach-O files cannot be generated with one section per global object, but that Mach-O sections (at least as used by OSX) *are* expected to be subdivided/rearranged/etc, and are not atomic. Given that set of properties for the input file format, of course it makes sense that you'd want to subdivide Mach-O "sections" within the linker into smaller atomic pieces to work on them.But for ELF, the compiler can/will output separate sections for each function/global variable, and the contents of a section should never be mangled. It can also emit multiple symbols into a single section. That an ELF section *may* contain multiple functions/globals which need to stay together is not a problem with the file format -- it's an advantage -- an additional flexibility of representation.I gather the current model in LLD doesn't support an atomic unit with multiple symbols cleanly. And that that's the main issue that would be good to fix here.But, rather than talking about "eliminating the atom model" -- which seems to be contentious -- maybe it would be more peaceful to just say that the desired change is to "allow atoms to have multiple global symbols associated, and have more metadata"? It appears to me that it amounts to essentially the same thing, but may not be as contentious if described that way.If that change was made, you'd just need to know that LLD has slightly unique terminology; "ELF section" == "LLD Atom". (but "Mach-O section" turns into multiple "LLD Atom"s).Am I wrong?
James
I guess, looking back at Nick's comment:"The atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom."it seems that the primary issue on the ELF/COFF side is that currently the LLVM backends are taking a finer-grained atomicity that is present inside LLVM, and losing information by converting that to a coarser-grained atomicity that is the typical "section" in ELF/COFF.But doesn't -ffunction-sections -fdata-sections already fix this, basically?On the Mach-O side, the issue seems to be that Mach-O's notion of section carries more hard-coded meaning than e.g. ELF, so at the very least another layer of subdivision below what Mach-O calls "section" would be needed to preserve this information; currently symbols are used as a bit of a hack as this "sub-section" layer.
So the problem seems to be that the transport format between the compiler and linker varies by platform, and each one has a different way to represent things, some can't represent everything we want to do, apparently.
BUT it sounds like at least relocatable ELF semantics can, in principle, represent everything that we can imagine an "atom-based file format"/"native format" to want to represent. Just to play devil's advocate here, let's start out with the "native format" being relocatable ELF - on *all platforms*. Relocatable object files are just a transport format between compiler and linker, after all; who cares what we use? If the alternative is a completely new format, then bootstrapping from relocatable ELF is strictly less churn/tooling cost.People on the "atom side of the fence", what do you think? Is there anything that we cannot achieve by saying "native"="relocatable ELF"?
On May 1, 2015, at 12:31 PM, Rui Ueyama <ru...@google.com> wrote:Proposal
- Re-architect the linker based on the section model where it’s appropriate.
- Stop simulating different linker semantics using the Unix model. Instead, directly implement the native behavior.
Preface: I have never personally contributed code to LLD, so don’t take anything I’m about to say too seriously. This is not a mandate or anything, just an observation/idea.I think that there is an alternative solution to these exact same problems. What you’ve identified here is that there are two camps of people working on LLD, and they have conflicting goals:- Camp A: LLD is infrastructure for the next generation of awesome linking and toolchain features, it should take advantage of how compilers work to offer new features, performance, etc without deep concern for compatibility.- Camp B: LLD is a drop in replacement system linker (notably for COFF and ELF systems), which is best of breed and with no compromises w.r.t. that goal.I think the problem here is that these lead to natural and inescapable tensions, and Alex summarized how Camp B has been steering LLD away from what Camp A people want.
This isn’t bad in and of itself, because what Camp B wants is clearly and unarguably good for LLVM. However, it is also not sufficient, and while innovation in the linker space (e.g. a new “native” object file format generated directly from compiler structures) may or may not actually “work” or be “worth it”, we won’t know unless we try, and that won’t fulfill its promise if there are compromises to Camp B.So here’s my counterproposal: two different linkers.Lets stop thinking about lld as one linker, and instead think of it is two different ones. We’ll build a Camp B linker which is the best of breed section based linker. It will support linker scripts and do everything better than any existing section based linker. The first step of this is to do what Rui proposes and rip atoms out of the model.We will also build a no-holds-barred awesome atom based linker that takes advantage of everything it can from LLVM’s architecture to enable innovative new tools without worrying too much about backwards compatibility.These two linkers should share whatever code makes sense, but also shouldn’t try to share code that doesn’t make sense. The split between the semantic model of sections vs atoms seems like a very natural one to me.One question is: does it make sense for these to live in the same lld subproject, or be split into two different subprojects? I think the answer to that question is driven from whether there is shared code common between the two linkers that doesn’t make sense to sink down to the llvm subproject itself.What do you think?-Chris
On May 28, 2015, at 5:42 PM, Sean Silva <chiso...@gmail.com> wrote:I guess, looking back at Nick's comment:"The atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom."it seems that the primary issue on the ELF/COFF side is that currently the LLVM backends are taking a finer-grained atomicity that is present inside LLVM, and losing information by converting that to a coarser-grained atomicity that is the typical "section" in ELF/COFF.But doesn't -ffunction-sections -fdata-sections already fix this, basically?On the Mach-O side, the issue seems to be that Mach-O's notion of section carries more hard-coded meaning than e.g. ELF, so at the very least another layer of subdivision below what Mach-O calls "section" would be needed to preserve this information; currently symbols are used as a bit of a hack as this "sub-section" layer.I’m not sure what you mean here.So the problem seems to be that the transport format between the compiler and linker varies by platform, and each one has a different way to represent things, some can't represent everything we want to do, apparently.Yes!BUT it sounds like at least relocatable ELF semantics can, in principle, represent everything that we can imagine an "atom-based file format"/"native format" to want to represent. Just to play devil's advocate here, let's start out with the "native format" being relocatable ELF - on *all platforms*. Relocatable object files are just a transport format between compiler and linker, after all; who cares what we use? If the alternative is a completely new format, then bootstrapping from relocatable ELF is strictly less churn/tooling cost.People on the "atom side of the fence", what do you think? Is there anything that we cannot achieve by saying "native"="relocatable ELF"?1) Turns out .o files are written once but read many times by the linker. Therefore, the design goal of .o files should be that they are as fast to read/parse in the linker as possible. Slowing down the compiler to make a .o file that is faster for the linker to read is a good trade off. This is the motivation for the native format - not that it is a universal format.
2) I think the ELF camp still thinks that linkers are “dumb”. That they just collate .o files into executable files. The darwin linker does a lot of processing/optimizing the content (e.g. Objective-C optimizing, dead stripping, function/data re-ordering). This is why atom level granularity is needed.
For darwin, ELF based .o files is not interesting. It won’t be faster, and it will take a bunch of effort to figure out how to encode all the mach-o info into ELF.
On May 28, 2015, at 5:42 PM, Sean Silva <chiso...@gmail.com> wrote:I guess, looking back at Nick's comment:"The atom model is a good fit for the llvm compiler model for all architectures. There is a one-to-one mapping between llvm::GlobalObject (e.g. function or global variable) and lld:DefinedAtom."it seems that the primary issue on the ELF/COFF side is that currently the LLVM backends are taking a finer-grained atomicity that is present inside LLVM, and losing information by converting that to a coarser-grained atomicity that is the typical "section" in ELF/COFF.But doesn't -ffunction-sections -fdata-sections already fix this, basically?On the Mach-O side, the issue seems to be that Mach-O's notion of section carries more hard-coded meaning than e.g. ELF, so at the very least another layer of subdivision below what Mach-O calls "section" would be needed to preserve this information; currently symbols are used as a bit of a hack as this "sub-section" layer.I’m not sure what you mean here.So the problem seems to be that the transport format between the compiler and linker varies by platform, and each one has a different way to represent things, some can't represent everything we want to do, apparently.Yes!BUT it sounds like at least relocatable ELF semantics can, in principle, represent everything that we can imagine an "atom-based file format"/"native format" to want to represent. Just to play devil's advocate here, let's start out with the "native format" being relocatable ELF - on *all platforms*. Relocatable object files are just a transport format between compiler and linker, after all; who cares what we use? If the alternative is a completely new format, then bootstrapping from relocatable ELF is strictly less churn/tooling cost.People on the "atom side of the fence", what do you think? Is there anything that we cannot achieve by saying "native"="relocatable ELF"?1) Turns out .o files are written once but read many times by the linker. Therefore, the design goal of .o files should be that they are as fast to read/parse in the linker as possible. Slowing down the compiler to make a .o file that is faster for the linker to read is a good trade off. This is the motivation for the native format - not that it is a universal format.
2) I think the ELF camp still thinks that linkers are “dumb”. That they just collate .o files into executable files. The darwin linker does a lot of processing/optimizing the content (e.g. Objective-C optimizing, dead stripping, function/data re-ordering). This is why atom level granularity is needed.
For darwin, ELF based .o files is not interesting. It won’t be faster, and it will take a bunch of effort to figure out how to encode all the mach-o info into ELF. We’d rather wait for a new native format.
-Nick
If there is any linker in here that is dumb that is the old lld. It
reads parts of the file it doesn't need, it can't optimize exception
frames, --gc-section is not working, strings are not tail merged and
it takes about 2x the cpu time of gold even with gold doing all those
things.
Rui's new design finally given us the opportunity of making an useful
linker out of lld.
> 2) I think the ELF camp still thinks that linkers are “dumb”. That they
> just collate .o files into executable files. The darwin linker does a lot
> of processing/optimizing the content (e.g. Objective-C optimizing, dead
> stripping, function/data re-ordering). This is why atom level granularity
> is needed.
If there is any linker in here that is dumb that is the old lld.
I want to make it clear that I didn't (at least intend to) compromise flexibility or beauty of design with short-term performance gain. I was trying to do simple things in a simple way for both humans and computers, and I believe I did that fairly well. I'd even argue that the new design is cleaner and more expressive than before, because the "atom" model is in some part too detailed and restrictive on how to represent data and relations between symbols, particularly how to represent relocations. It also lacked capability of representing indivisible memory areas having multiple names.After I wrote up the first patch, I realized that the goal of the code is somewhat similar to what the atom model aims to achieve, with some differences. I assume that you have read the readme file for the new port. The differences are- An atom has only one name, but the new "chunk" can have one or more symbols referring that. But the actual difference is that chunks are agnostic of symbols referring them in the new design. I have separated actual data from symbols to get more flexibility. And that flexibility enabled me to achieve better performance by writing more abstract code which reads less data.- In the atom model, we have detailed information about relocations, including relocation target, offset, etc, for each atom. In the new design, we don't have them. Instead, we have just a set of symbols for each chunk that needs to be resolved to include that input chunk properly. This is more abstract and flexible than the existing design.- The atom model reads too much data from files prematurely to construct a complete graph, while the new design avoided that. This is partly an implementation's issue, but partly unavoidable, because we actually needed to build more complex data structure.- And this might be stemmed from the implementation and not from the model itself, but the thing is that it's hard to write code for the atom model because their data types have too much detailed relations with other types. For example, any atom in the model has to have a "file" that an atom was created from. This makes it hard to append linker-generated data to output which don't have a source file (we ended up having a notion of "virtual input file" that doesn't do anything meaningful itself.). Another example is that, if you want to create a symbol on-demand, you've got to create a "virtual archive" file that returns a "virtual file" containing one "virtual atom" when the archive file is asked for that symbol. In the new design, it can be expressed in one line of code instead of multiple class definitions and object juggling. Also, because relocations are explicitly represented as "references" in the atom model, we've got to create platform-specific relocation objects even for linker-generated data if it refers some other symbols, and let a platform-specific relocation function to consume that data to apply relocations. That's less abstracted than the new design, in which all classes but the actual data type needs to know about relocations are agnostic about how relocations are represented and how to actually apply them.
Besides them, I'd say from my experiences of working on the atom model, the new model's ability is not that different from the atom model. They are different, there are pros and cons, and I don't agree that the atom model is more flexible or conceptually better.
Practically, you're almost certainly right (in terms of development
costs to a temporary solution). But from a personal level I would much
prefer an ELF-based Darwin to the status quo. I think Darwin suffers
greatly from being based on MachO, which isn't something I could say
about Linux/ELF. There are far too many implicit contracts between
tools.
> We’d rather wait for a new native format.
Assuming a good one comes along.
Tim.
On Fri, May 29, 2015 at 1:14 AM, Rui Ueyama <ru...@google.com> wrote:I want to make it clear that I didn't (at least intend to) compromise flexibility or beauty of design with short-term performance gain. I was trying to do simple things in a simple way for both humans and computers, and I believe I did that fairly well. I'd even argue that the new design is cleaner and more expressive than before, because the "atom" model is in some part too detailed and restrictive on how to represent data and relations between symbols, particularly how to represent relocations. It also lacked capability of representing indivisible memory areas having multiple names.After I wrote up the first patch, I realized that the goal of the code is somewhat similar to what the atom model aims to achieve, with some differences. I assume that you have read the readme file for the new port. The differences are- An atom has only one name, but the new "chunk" can have one or more symbols referring that. But the actual difference is that chunks are agnostic of symbols referring them in the new design. I have separated actual data from symbols to get more flexibility. And that flexibility enabled me to achieve better performance by writing more abstract code which reads less data.- In the atom model, we have detailed information about relocations, including relocation target, offset, etc, for each atom. In the new design, we don't have them. Instead, we have just a set of symbols for each chunk that needs to be resolved to include that input chunk properly. This is more abstract and flexible than the existing design.- The atom model reads too much data from files prematurely to construct a complete graph, while the new design avoided that. This is partly an implementation's issue, but partly unavoidable, because we actually needed to build more complex data structure.- And this might be stemmed from the implementation and not from the model itself, but the thing is that it's hard to write code for the atom model because their data types have too much detailed relations with other types. For example, any atom in the model has to have a "file" that an atom was created from. This makes it hard to append linker-generated data to output which don't have a source file (we ended up having a notion of "virtual input file" that doesn't do anything meaningful itself.). Another example is that, if you want to create a symbol on-demand, you've got to create a "virtual archive" file that returns a "virtual file" containing one "virtual atom" when the archive file is asked for that symbol. In the new design, it can be expressed in one line of code instead of multiple class definitions and object juggling. Also, because relocations are explicitly represented as "references" in the atom model, we've got to create platform-specific relocation objects even for linker-generated data if it refers some other symbols, and let a platform-specific relocation function to consume that data to apply relocations. That's less abstracted than the new design, in which all classes but the actual data type needs to know about relocations are agnostic about how relocations are represented and how to actually apply them.These all sound like things that just indicate "we have some refactoring to do", just like Duncan did for debug metadata, or David is doing for the opaque pointer type, or how the Type system has been changed over the years, or how clang's template parsing is changed to be compatible with weird MSVC behavior. Is there something about the current situation with LLD that made you think that refactoring was hopeless and required a rewrite? If what we currently have doesn't fit our use cases, why not just fix it?
Besides them, I'd say from my experiences of working on the atom model, the new model's ability is not that different from the atom model. They are different, there are pros and cons, and I don't agree that the atom model is more flexible or conceptually better.I don't understand this focus on "the atom model". "the atom model" is not any particular thing. We can generalize the meaning of atom, we can make it more narrow, we can remove responsibilities from Atom, we can add responsibilities to Atom, we can do whatever is needed. As you yourself admit, the "new model" is not that different from "the atom model". Think of "the atom model" like SSA. LLVM IR is SSA; there is a very large amount of freedom to decide on the exact design within that scope. "the atom model" AFAICT just means that a core abstraction inside the linker is the notion of an indivisible chunk. Our current design might need to be changed, but starting from scratch only to arrive at the same basic idea but now having to effectively maintain two codebases doesn't seem worth it.
On May 29, 2015, at 10:08 PM, Rui Ueyama <ru...@google.com> wrote:
> Large part of the difficulties in development of the current LLD comes from over-generalizataion to share code between pretty much different file formats.
ISTM the problem that's been described isn't that code is shared between file formats, but that an internal representation was chosen based on what MachO required, which in the end turned out to be unsuitable for representing the greater generality allowed by the other two file formats.
You state that the goal is to use this new model for ELF too, assuming it proves itself worthy. Will ELF and COFF not share most of the core of the code at that point?
And once COFF and ELF are taken care of, do you not think the new model will be suitable for MachO, too? It seems to me that it ought to be able to handle MachO's needs just fine, with a suitable division of MachO sections into "Chunk"s.
Perhaps once everything is said and done, lld can once again have a single core model. I think that may be what Sean was trying to get at -- not that it's wrong to create a new model as a base, but that the expectation should be that new model will be _the_ model used in LLD, not a second model to be kept permanently alongside the current "single-symbol-per-atom" model, which has been shown insufficient.
On Fri, May 29, 2015 at 6:01 PM, Sean Silva <chiso...@gmail.com> wrote:On Fri, May 29, 2015 at 1:14 AM, Rui Ueyama <ru...@google.com> wrote:I want to make it clear that I didn't (at least intend to) compromise flexibility or beauty of design with short-term performance gain. I was trying to do simple things in a simple way for both humans and computers, and I believe I did that fairly well. I'd even argue that the new design is cleaner and more expressive than before, because the "atom" model is in some part too detailed and restrictive on how to represent data and relations between symbols, particularly how to represent relocations. It also lacked capability of representing indivisible memory areas having multiple names.After I wrote up the first patch, I realized that the goal of the code is somewhat similar to what the atom model aims to achieve, with some differences. I assume that you have read the readme file for the new port. The differences are- An atom has only one name, but the new "chunk" can have one or more symbols referring that. But the actual difference is that chunks are agnostic of symbols referring them in the new design. I have separated actual data from symbols to get more flexibility. And that flexibility enabled me to achieve better performance by writing more abstract code which reads less data.- In the atom model, we have detailed information about relocations, including relocation target, offset, etc, for each atom. In the new design, we don't have them. Instead, we have just a set of symbols for each chunk that needs to be resolved to include that input chunk properly. This is more abstract and flexible than the existing design.- The atom model reads too much data from files prematurely to construct a complete graph, while the new design avoided that. This is partly an implementation's issue, but partly unavoidable, because we actually needed to build more complex data structure.- And this might be stemmed from the implementation and not from the model itself, but the thing is that it's hard to write code for the atom model because their data types have too much detailed relations with other types. For example, any atom in the model has to have a "file" that an atom was created from. This makes it hard to append linker-generated data to output which don't have a source file (we ended up having a notion of "virtual input file" that doesn't do anything meaningful itself.). Another example is that, if you want to create a symbol on-demand, you've got to create a "virtual archive" file that returns a "virtual file" containing one "virtual atom" when the archive file is asked for that symbol. In the new design, it can be expressed in one line of code instead of multiple class definitions and object juggling. Also, because relocations are explicitly represented as "references" in the atom model, we've got to create platform-specific relocation objects even for linker-generated data if it refers some other symbols, and let a platform-specific relocation function to consume that data to apply relocations. That's less abstracted than the new design, in which all classes but the actual data type needs to know about relocations are agnostic about how relocations are represented and how to actually apply them.These all sound like things that just indicate "we have some refactoring to do", just like Duncan did for debug metadata, or David is doing for the opaque pointer type, or how the Type system has been changed over the years, or how clang's template parsing is changed to be compatible with weird MSVC behavior. Is there something about the current situation with LLD that made you think that refactoring was hopeless and required a rewrite? If what we currently have doesn't fit our use cases, why not just fix it?I don't think these points indicate a need of refactoring. Or the meaning of refactoring is too broad. "Atom has only one name" is a baked in assumption everywhere (like "in SSA variables are assigned only once"). "Atom has data" is another assumption. "Relocations are represented as graph edges" is yet another. Or "everything is represented using atoms and references". These design choices are made at the beginning, and they are everywhere. If you change them, you have to update virtually all code. Then what's the point of refactoring compared to creating a new foundation + move code on it? I found that the former is difficult to do.
I don’t have a specific recommendation on LLD since I know little about the details though I’ll voice general support of incremental refactor over anything else. Rewrites are seductive though I’ve seldom seen them come to successful conclusion.
On the flip side let’s not fall in to the trap of forcing an abstraction if it doesn’t fit. Abstract the algorithms that are common and don’t try to abstract things that are fundamentally different and are not interchangeable between formats.
Hopefully these vague comments are helpful in some way J
One of the standard reasons to prefer refactoring, even though it appears to take longer or be more difficult, is that it allows you to always keep all tests green. It is very easy for things to slip through the cracks and not promptly return to being green on a "from-scratch" version. This ultimately turns into bug reports later and the feature needs to be reimplemented; the apparent simplicity of the "from-scratch" version can disappear very rapidly.
In the refactoring approach you are forced to incorporate a holistic understanding of the necessary features into your simplification efforts, since the tests keep you from accidentally disregarding necessary features.
It is very easy to accidentally buy simplicity at the cost of losing features; if you eventually need the features back then the apparent simplicity is an illusion.
One of the standard reasons to prefer refactoring, even though it appears to take longer or be more difficult, is that it allows you to always keep all tests green. It is very easy for things to slip through the cracks and not promptly return to being green on a "from-scratch" version. This ultimately turns into bug reports later and the feature needs to be reimplemented; the apparent simplicity of the "from-scratch" version can disappear very rapidly.Hmm, why can't the from-scratch version use existing tests to make sure major features are not regressed?
Refactoring requires a good foundation. If the foundation is broken, rewriting is more preferred. There are many successful stories of complete rewrite.