[llvm] r209007 - Fix most of PR10367.
Nick Lewycky
nicholas at mxc.ca
Sat May 17 22:42:42 PDT 2014
John McCall wrote:
> On May 17, 2014, at 9:03 PM, Nick Lewycky<nicholas at mxc.ca> wrote:
>> John McCall wrote:
>>> On May 17, 2014, at 7:54 PM, Rafael Espíndola<rafael.espindola at gmail.com> wrote:
>>>>> That argument works when it’s really just “allowing two different types”, but it breaks down the more features you add to aliases. If aliases support bitcasts (by just allowing the type to be different) and geps (by allowing an offset to be given) and address-space conversions (by allowing an explicit address space that doesn’t match the base)
>>>>
>>>> That is part of the type.
>>>>
>>>>> then it sure sounds to me like you’re just repeating a substantial subset of expressive capability of ConstantExpr, and there isn’t a huge benefit to this approach over just semantically restricting what can appear in an alias.
>>>>
>>>> It is not a substantial part. There is a *lot* more in ConstantExpr.
>>>> As an extreme example, ConstantExpr can trap.
>>>
>>> 1. That is clearly a mistake.
>>>
>>> 2. You are not going to be able to fix that mistake with a change in
>>> representation. In general, the operands of a trapping operation are
>>> otherwise legal constants, so the only way to intrinsically remove the
>>> potential for trapping constants is to remove all operations from
>>> ConstantExpr that could possibly trap on any operand. I don’t think
>>> you have community support for that, so assuming that that’s the
>>> inevitable direction is not actually reasonable.
>>>
>>> 3. There is a much more obvious solution: we should just document
>>> that ConstantExprs which are guaranteed to trap are illegal, and then
>>> check that in the verifier. Constant propagation should already be
>>> doing this analysis to decide that it can safely turn an instruction
>>> into a constant.
>>
>> Concisely, this saves almost no work over removing ConstantExpr entirely but falls quite a bit short of being a comprehensive fix. You have all the difficulty of dealing with autoupgrading code that uses ConstantExpr udiv&global1,&global2, but without going all the way to having a sane new design.
>
> 1. I don’t think we need to be able to autoupgrade code that we declare is ill-formed.
> 2. Your design is not sane.
>
>>>>> It also seems to me like the set of expressions supported by global aliases is only likely to increase. It’s a pretty natural way to express absolute symbols, for example.
>>>>>
>>>>> Note that there are already practical restrictions on the kinds of ConstantExpr that can appear in various places — e.g. you can make a ConstantExpr that divides the address of a global variable by 7, but there aren’t any relocations that support that, so…
>>>>
>>>> So it is better to have a more restrictive representation.
>>>
>>> I don’t see why. Your more restrictive representation is just going to
>>> arbitrarily limit the ability of frontends and backends to achieve things
>>> that your representation doesn’t realize are possible. LLVM IR should
>>> not be in the business of knowing what common object file formats
>>> are capable of expressing.
>>
>> On the contrary, that is precisely the plan (my plan). Why shouldn't it?
>>
>> Suppose we come up with a new set of constants that represent the set of relocations available in the targets we support. This would permit us to generate optimal code for those targets, instead of our current situation of producing worse code on Linux/ELF because Mach-O doesn't support certain kinds of relocation.
>
> Your proposal is seriously to introduce relocations as a first-class concept in LLVM IR?
Yes, filling the role ConstantExpr fills now.
What possible benefit does that have over just allowing them to be
expressed naturally as ConstantExprs and then ensuring that front-ends
and transforms are smart about not introducing ConstantExprs that the
tool chain / target can’t handle? You know, like they already do today,
both for relocations and for a thousand other features that might be
impossible on the target.
I need to pull this apart a bit to answer it. Why get rid of ConstantExpr?
ConstantExpr has the wrong design. The constants available in LLVM are
typed bags of bits, the address of a function, the address of a global
variable, an alias, and relocations. ConstantExpr is modeled as a mirror
of the set of LLVM instructions -- and which of those things in the
previous list correspond to instructions? Immediately we see problems
with the model, there are instructions aren't mirrored -- nobody has a
call ConstantExpr, or a load, or unreachable. Trapping is just another
problem on the list.
ConstantExpr's also try to offer other guarantees, like guarantees
they've been canonicalized. We can do this because unlike instructions
there's a centralized getter. The complexity of constantexpr
canonicalization gets worse as the matching instructions change. When
should 'add x, y' and 'add nsw x, y' be canonicalized to the same
constantexpr, when 'x' and 'y' aren't concrete yet? There are places in
LLVM which rely on the former fact that certain subsets of CEs will get
canonicalized, and which now miscompile because instructions got more
complex. For a personal pet peeve, GlobalOpt lost its war when
getelementptr grew the ability to have arbitrary precision integers for
its indices -- "gep x, i5 0" and "gep x, i6 0" as two different constant
expressions. (And how would we canonicalize those? When we don't have
DataLayout and thus don't know the pointer width? To i1 0 because that's
the smallest size that '0' fits in?) GlobalOpt used to work, an
implementation of symbolic execution that relies on ConstantExpr
(checked for simplicity) as keys to simulated memory. We don't see the
miscompiles with clang because these go away thanks to a second constant
folder that requires data layout to be present.
Speaking of which, how do we canonicalize ConstantExpr's? The creation
methods minimize their analysis by assuming canonical inputs, since you
can't possibly have gotten anything else. What happens when we have two
constant folders? Or better, what does it mean to take a Module that was
built without any DataLayout and then add a DataLayout to it? It turns
out that the answer is patchwork between people who think we need to
revisit all interior ConstantExpr's inside a ConstantExpr and people who
think we don't. Some places we do it, in others we don't. Which answer
you think is correct depends on how much weight you place on certain use
cases of llvm as a library vs other uses of llvm.
Now, how do we make sure we don't build invalid ConstantExpr's? Well
currently GlobalOpt just refuses to build anything too complicated for
the intersection of our supported platforms. As I alluded to in my last
email, there are cases globalopt drops on the floor that GCC (on Linux)
optimizes just fine, because globalopt doesn't know that ELF can do
things Mach-O can't. If we want to get these right, then we need to make
our constant expr creation depend on the target. So that's step one:
make target information a mandatory part of the Module, instead of optional.
I haven't settled on an API design (again, I wasn't yet ready to bring
the proposal to llvmdev) but the rough idea would be something that you
point at an instruction and it rolls it up into a new-form-constantexpr
if possible on the target at hand, or returns null if they need to stay
as instructions. This isn't an impressive API, it requires creating
instructions even if you know you don't need them, it doesn't help you
find the best covering set of constants for a given body of
instructions, but it serves as a starting point and proof of feasibility.
> It’s not as if using magic named relocations is going to actually guarantee anything, since different classes of “relocations” are legal in different situations and given different sets of operands, and since tool chains do evolve and improve, as do dynamic linkers.
What value is there in supporting arbitrarily complex or long constant
expressions?
Either way, we need to teach the compiler about a new relocation before
we can use it. You aren't gaining anything by deciding to lower CEs to
relocations in the backend. Even if your plan is to keep CEs in the IR
then feed that IR to a newer llvm whose backend knows about the new
relocations, the only difference is that the newer llvm would have to
try refolding relocations when loading older IR.
The guarantee we'll get is that constants in the IR should be treated as
having zero cost. Right now we treat ConstantExpr's as zero cost in loop
unrolling and in inlining heuristics, but then lower them to
instructions in the backend (if we're "lucky" and they show up as an
instruction operand instead of a globalvariable initializer, in which
case we report_fatal_error).
Yes I'm aware that not all constants have the same cost. A ConstantInt
may require multiple instructions to materialize on MIPS, etc.
> The problem you’ve identified is because LLVM IR is being unnecessarily paternalistic. The solution is not to introduce extra obstacles and be even more paternalistic.
The problem I've identified is that LLVM IR is too high level and is
providing an abstraction that doesn't match what actually happens at the
low level. Replacing the IR representation corrects the problem by
making the IR match reality.
Nick
More information about the llvm-commits
mailing list