cvise icon indicating copy to clipboard operation
cvise copied to clipboard

Missed reduction: inlining template functions

Open mgehre-amd opened this issue 2 years ago • 8 comments

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.

mgehre-amd avatar Oct 20 '22 08:10 mgehre-amd

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?

marxin avatar Oct 20 '22 12:10 marxin

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.

mgehre-amd avatar Oct 20 '22 15:10 mgehre-amd

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

strimo378 avatar Oct 20 '22 15:10 strimo378

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.

mgehre-amd avatar Oct 20 '22 15:10 mgehre-amd

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

strimo378 avatar Oct 20 '22 15:10 strimo378

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

marxin avatar Oct 20 '22 15:10 marxin

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?

marxin avatar Oct 20 '22 15:10 marxin

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'

mgehre-amd avatar Oct 20 '22 16:10 mgehre-amd