cvise
cvise copied to clipboard
Missed reduction: inlining template functions
Hi!
Since I discovered cvise, I love it. Thank you very much for making this helpful tool!
I saw the following reduced code coming out of cvise 2.6.0 (9d01a9c),
template < int = 4 > void a() {
[Code that creates the crash]
}
template < int = 8 > void c() { a<>; }
template < int = 0 > void d() { c<>; }
void e() { d<>; }
but I can manually reduce it further to
template < int = 4 > void a() {
[Code that creates the crash]
}
void e() { a<>; }
How can we teach cvise to do those reductions? I'm happy to get my hands dirty (I have experience with clang), but I would need some pointers where and how to implement this transformation to get started.
Since I discovered cvise, I love it. Thank you very much for making this helpful tool!
Thank you very much!
To be honest, I'm not C++ expect, can you please describe what does the syntax:
template < int = 4 > void a() {
[Code that creates the crash]
}
template < int = 8 > void c() { a<>; }
template < int = 0 > void d() { c<>; }
void e() { d<>; }
?
Maybe @strimo378 can chime in?
Essentially, clang crashes as long as the template function a
is instantiated. Usually, you would call they function, i.e. write a<>();
, (which is equivalent to a();
), but here some pass of cvise figured out that it's enough to mention the name of the function to force the crash. That's why we see a<>;
(i.e. getting the functions address and then doing nothing with it).
Now a
is only instantiated when the template function c
is instantiated, which in turn only happens when d
is instantiated,
which finally is instantiated in e
.
I guess there is an "inlining" pass in cvise that would resolve code like
void a() {
[Code that creates the crash when a() is called]
}
void c() { a(); }
void d() { c(); }
void main() { d(); }
into
void a() {
[Code that creates the crash when a() is called]
}
void main() { a(); }
? If you could point me at it, maybe I could get some inspiration from there.
Hi,
there are two things that are not correctly working here:
- There exists a simple inlining transformation. I have no experience with that but it seems to be not working for your test case.
- The templates can be removed from functions c and d. I already recognized that there is a transformation missing or a current one not working properly. I have the topic on my todo list...
If you want to get your hands dirty, I suggest that you take a look at the inlining transformation and see if you can fix it.
The transformation is part of the clang_delta utility and you can find it within SimpleInliner.cpp. You can manually run clang_delta for your test case using the following command line:
clang_delta --counter=1 --transformation=simple-inliner testcase.cpp
I think the inlining transformation is actually fine here. I manually removed as many templates as I could while preserving the crash and got to
template < int = 4 > void a() {
[Code that creates the crash]
}
void c() { a<>; }
void d() { c; }
void e() { d; }
When I run cvise on it, it gives
template < int = 4 > void a() {
[Code that creates the crash]
}
void c() { a<>; }
which is the smallest reproducer.
I guess I should then look at the pass that is supposed to turn the function templates into normal functions and remove the <>
from their references.
Yes, but why is the inlining transformation not trying to inline templated functions? That would also work for you. Maybe there is a simple test that could be removed.
I am not sure if there exists a transformation for removing templates from functions. I think it is currently implicitly covered by the token remover pass. There exists a transformation to remove templates from classes (class-template-to-class) but there exists also passes to remove single template parameters...
Please take a look at clang_delta --verbose-transformations
which describes all transformations related to templates.
Essentially, clang crashes as long as the template function
a
is instantiated.
Can you please provide an interestingness test I can use? Does it happen for stock clang as well?
No, unfortunately the crash only happens with our internal version. But you can reproduce the same cvise behavior with
template <int N = 4> void a() { static_assert(N != 4); }
template <int = 8> void c() { a<>; }
template <int = 0> void d() { c<>; }
void e() { d<>; }
and
#!/bin/sh
clang++ test.cpp -Wfatal-errors 2>&1 | grep 'error: static_assert failed'