Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missed reduction: inlining template functions #89

Open
mgehre-amd opened this issue Oct 20, 2022 · 8 comments
Open

Missed reduction: inlining template functions #89

mgehre-amd opened this issue Oct 20, 2022 · 8 comments

Comments

@mgehre-amd
Copy link

mgehre-amd commented Oct 20, 2022

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.

@marxin
Copy link
Owner

marxin commented Oct 20, 2022

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?

@mgehre-amd
Copy link
Author

mgehre-amd commented Oct 20, 2022

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.

@strimo378
Copy link
Contributor

Hi,

there are two things that are not correctly working here:

  1. There exists a simple inlining transformation. I have no experience with that but it seems to be not working for your test case.
  2. 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

@mgehre-amd
Copy link
Author

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.

@strimo378
Copy link
Contributor

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...

@marxin
Copy link
Owner

marxin commented Oct 20, 2022

Please take a look at clang_delta --verbose-transformations which describes all transformations related to templates.

@marxin
Copy link
Owner

marxin commented Oct 20, 2022

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?

@mgehre-amd
Copy link
Author

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'

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

No branches or pull requests

3 participants