This is an archive of the discontinued LLVM Phabricator instance.

Introduce llc/ExecuteTestCommands pass
AbandonedPublic

Authored by MatzeB on Mar 1 2017, 6:44 PM.

Details

Summary

Putting this up for early review to get some of the bikeshedding done while I am finishing this up:

We sometimes want to test a specific codegen API rather than a whole pass.

This adds a special pass to llc that features a minimalistic scripting language to
call some predefined API functions so we can test the API with the usual lit+FileCheck tools.

The first implementation allows moving an instruction to a different
place and updating liveness information with the handleMove() API. This
obsoletes the previously existing unittest for this.

Diff Detail

Repository
rL LLVM

Event Timeline

MatzeB created this revision.Mar 1 2017, 6:44 PM
kristof.beyls added inline comments.
test/CodeGen/AMDGPU/handlemove.mir
2โ€“13

Without reading the source of the rest of this patch, I find it hard to guess exactly what this is testing.
Maybe before committing, also some documentation should be written on how to use this new feature?
I'm not sure where the best place would be for that documentation to live. Maybe http://llvm.org/docs/MIRLangRef.html?

MatzeB added inline comments.Mar 2 2017, 9:14 AM
test/CodeGen/AMDGPU/handlemove.mir
2โ€“13

This means: "In basic block 0, take the 2nd instruction and move it before the first instruction". This will use the LiveIntervalAnalysis::handleMove() function which updates live intervals after moving an instruction around in a basic block, which was notoriously buggy and at the same time it was hard to created directed tests for it.

Yes, this is far from a clear and obvious solution. In an ideal solution we would have a way to name instructions instead of refering to them by the numbers 2 and 1. So there is room for further improvements, but that is for another patch.

This patch is about replacing the unittest code written in C++ which today looks like this:

TEST(LiveIntervalTest, MoveUpDef) {
  // Value defined.
  liveIntervalTest(R"MIR(
    S_NOP 0
    S_NOP 0
    early-clobber %0 = IMPLICIT_DEF
    S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
    testHandleMove(MF, LIS, 2, 1);
  });
}

which is not clearer but instead has more C++ boilerplate noise (and no FileCheck available).

I will add more documentation in the next iterations of this patch.

kparzysz edited edge metadata.Mar 3 2017, 5:40 AM

This is really nice, but I have a few questions:

  • Will the testCommands be limited to functions that can take immediate values (i.e no functions taking pointers to MachineInstr or MachineBasicBlock)?
  • Are users expected to be adding more functions to ExecuteTestCommands.cpp as they create new tests?
test/CodeGen/AMDGPU/handlemove.mir
55

Is the " at the end a typo?

This is really nice, but I have a few questions:

  • Will the testCommands be limited to functions that can take immediate values (i.e no functions taking pointers to MachineInstr or MachineBasicBlock)?

In general I think it would be wrong to put too much effort into this language, if this becomes more than invocing a few functions then we should probably go back to writing unit tests.
That said there is already getMBB(number) and getMI(number) implemented in the current pass which allows you to pass an instr or block as the "this" parameter to the next function.

  • Are users expected to be adding more functions to ExecuteTestCommands.cpp as they create new tests?

Yes.

test/CodeGen/AMDGPU/handlemove.mir
55

Yes (a previous version was using a special instruction to carry the commands in a string, I forgot to remove this when I switched to the testCommands: version).

MatzeB updated this revision to Diff 90972.Mar 7 2017, 5:32 PM
  • Use bb()/insn() functions more consistenly to make the intention of the commands more clear.
  • Use C++ template magic to make adding new functions to the language easy.
  • Add some documentation.
  • Various fixes/improvements to the parser.
MatzeB marked 4 inline comments as done.Mar 7 2017, 5:34 PM
qcolombet edited edge metadata.Mar 10 2017, 11:19 AM

Hi Matthias,

I have mixed feeling on the approach. I like being able to use lite and the .mir format to specify the test, but one the other hand I feel like the ExecuteTestCommand pass will become big and hard to understand.
The unittests had this nice property that they are not mixed together (i.e., we shouldn't have a scheduler test with a coalescer test). Here every functionalities will end up in one place and that's what I don't like.

The bottom line is that I like the approach and I would be fine with this as a first step, but we need to think about a way to compartment the different functionalities (e.g., live range testing vs. scheduling testing).
Maybe we could have a high level class test command with bb and insn functionalities and specialization classes for LIS and so on?

Cheers,
-Quentin

tools/llc/ExecuteTestCommands.cpp
248

*instruction

chandlerc edited edge metadata.Mar 10 2017, 11:27 AM

Currently, I really don't understand why this is the right approach...

We sometimes want to test a specific codegen API rather than a whole pass.

This adds a special pass to llc that features a minimalistic scripting language to
call some predefined API functions so we can test the API with the usual lit+FileCheck tools.

If you're actually trying to test a specific API, why aren't unittests the correct approach?

If the problem is that the unittests are hard to write, why not write libraries to make authoring the unittest easier?

Essentially, I totally understand prefering FileCheck and friends when testing something that is reasonably cohesive as a *pass*. But I feel like the point at which a scripting language or other things are being used to handle the fact that we're actually testing a specific API, I would *much rather* just write a unittest against that API directly.

Hi Matthias,

I have mixed feeling on the approach. I like being able to use lite and the .mir format to specify the test, but one the other hand I feel like the ExecuteTestCommand pass will become big and hard to understand.
The unittests had this nice property that they are not mixed together (i.e., we shouldn't have a scheduler test with a coalescer test). Here every functionalities will end up in one place and that's what I don't like.

The bottom line is that I like the approach and I would be fine with this as a first step, but we need to think about a way to compartment the different functionalities (e.g., live range testing vs. scheduling testing).
Maybe we could have a high level class test command with bb and insn functionalities and specialization classes for LIS and so on?

Fair enough. I think in any case this should be split into some files to have the command implementations separate from the rest and then maybe split it into more files for separate sets of commands. I am less enthusiastic (though also not strictly against), about actually splitting this into separate classes so that we would end up with -run-pass=live-interval-tests or -run-pass=bundling-tests and actually restricting the set of commands available at the same time in a single test.

Currently, I really don't understand why this is the right approach...

We sometimes want to test a specific codegen API rather than a whole pass.

This adds a special pass to llc that features a minimalistic scripting language to
call some predefined API functions so we can test the API with the usual lit+FileCheck tools.

If you're actually trying to test a specific API, why aren't unittests the correct approach?

If the problem is that the unittests are hard to write, why not write libraries to make authoring the unittest easier?

I think there is something fundamental about how we write tests in llvm, and why we do not write pass tests in a unit testing framework because hey, who stops us implementing llc/opt as some functions in a unit testing library, they mostly have trivial APIs:

I think there is a beauty and we are hitting a sweet spot in the way most of our testing works in llvm: Forcing tests to be a simple human readable text file keeps things approachable and understandable. Understanding the basics of lit, llc, opt and FileCheck is easy. Having the full power of C++ in a unit test library on the other hand makes it way too easy do "fancy" stuff that is harder to understand because it happens to abstracted away in generic code and multiple layers of APIs that you have to learn and dig through when you actually have to understand a bug.

Essentially, I totally understand prefering FileCheck and friends when testing something that is reasonably cohesive as a *pass*. But I feel like the point at which a scripting language or other things are being used to handle the fact that we're actually testing a specific API, I would *much rather* just write a unittest against that API directly.

I don't see why wanting FileCheck for things that aren't a pass isn't valid. Admittedly I am not happy to introduce a scripting language here, but it seemed like the pragmatic choice compared with the alternative of building up a pile of small passes for every test or putting .mir code into C++ string literals in a unittest and still lacking all the llc mechanics for bringing up a target or having the nice matching capabilities of FileCheck.

MatzeB updated this revision to Diff 92568.Mar 21 2017, 4:28 PM

New version separating the implementation of the actual commands to TestCommands.cpp so it becomes clear that it is easy to introduce new ones or split it further into multiple files for multiple areas of the backend in the future.

Currently, I really don't understand why this is the right approach...

We sometimes want to test a specific codegen API rather than a whole pass.

This adds a special pass to llc that features a minimalistic scripting language to
call some predefined API functions so we can test the API with the usual lit+FileCheck tools.

If you're actually trying to test a specific API, why aren't unittests the correct approach?

If the problem is that the unittests are hard to write, why not write libraries to make authoring the unittest easier?

I think there is something fundamental about how we write tests in llvm, and why we do not write pass tests in a unit testing framework because hey, who stops us implementing llc/opt as some functions in a unit testing library, they mostly have trivial APIs:

I think there is a beauty and we are hitting a sweet spot in the way most of our testing works in llvm: Forcing tests to be a simple human readable text file keeps things approachable and understandable. Understanding the basics of lit, llc, opt and FileCheck is easy. Having the full power of C++ in a unit test library on the other hand makes it way too easy do "fancy" stuff that is harder to understand because it happens to abstracted away in generic code and multiple layers of APIs that you have to learn and dig through when you actually have to understand a bug.

I don't see how things being abstracted away in a scripting language and complex fake "pass" infrastructure is going to be substantially better here.

Essentially, I totally understand prefering FileCheck and friends when testing something that is reasonably cohesive as a *pass*. But I feel like the point at which a scripting language or other things are being used to handle the fact that we're actually testing a specific API, I would *much rather* just write a unittest against that API directly.

I don't see why wanting FileCheck for things that aren't a pass isn't valid.

I don't think that pass vs. non-pass is the right distinction.

I think FileCheck makes sense when there is already a fundamental reason to have textual output that is sufficiently stable to use in tests. This can be everything from having an IR to having a clear and precise printing structure for an analysis. Those have utility *outside* of tests and also are great tools for testing.

But when the thing we are testing, fundamentally, is *an API*, I think we should test it *by calling that API*.

Admittedly I am not happy to introduce a scripting language here, but it seemed like the pragmatic choice compared with the alternative of building up a pile of small passes for every test or putting .mir code into C++ string literals in a unittest and still lacking all the llc mechanics for bringing up a target or having the nice matching capabilities of FileCheck.

Again, I am very much in favor of having the facilities from 'llc' or 'FileCheck' in useful APIs for unittests to leverage. But I don't think we should be adding a scripting language in order to write tests in a language other than C++ to exercise C++ API calls. I think this is significantly more burdensome than just writing unittests.

Sorry for the slow response, I think we may just fundamentally disagree about the right approach here. Currently, the approach you are proposing would make the resulting tests nearly incomprehensible to me... On the other hand, I think unittests (which are "just" C++ code even if sometimes non-obvious C++ code) *when used well* are much more friendly to people outside of the particular area than a MIR-specific scripting framework.

Currently, I really don't understand why this is the right approach...

We sometimes want to test a specific codegen API rather than a whole pass.

This adds a special pass to llc that features a minimalistic scripting language to
call some predefined API functions so we can test the API with the usual lit+FileCheck tools.

If you're actually trying to test a specific API, why aren't unittests the correct approach?

If the problem is that the unittests are hard to write, why not write libraries to make authoring the unittest easier?

I think there is something fundamental about how we write tests in llvm, and why we do not write pass tests in a unit testing framework because hey, who stops us implementing llc/opt as some functions in a unit testing library, they mostly have trivial APIs:

I think there is a beauty and we are hitting a sweet spot in the way most of our testing works in llvm: Forcing tests to be a simple human readable text file keeps things approachable and understandable. Understanding the basics of lit, llc, opt and FileCheck is easy. Having the full power of C++ in a unit test library on the other hand makes it way too easy do "fancy" stuff that is harder to understand because it happens to abstracted away in generic code and multiple layers of APIs that you have to learn and dig through when you actually have to understand a bug.

I don't see how things being abstracted away in a scripting language and complex fake "pass" infrastructure is going to be substantially better here.

Essentially, I totally understand prefering FileCheck and friends when testing something that is reasonably cohesive as a *pass*. But I feel like the point at which a scripting language or other things are being used to handle the fact that we're actually testing a specific API, I would *much rather* just write a unittest against that API directly.

I don't see why wanting FileCheck for things that aren't a pass isn't valid.

I don't think that pass vs. non-pass is the right distinction.

I think FileCheck makes sense when there is already a fundamental reason to have textual output that is sufficiently stable to use in tests. This can be everything from having an IR to having a clear and precise printing structure for an analysis. Those have utility *outside* of tests and also are great tools for testing.

But when the thing we are testing, fundamentally, is *an API*, I think we should test it *by calling that API*.

Admittedly I am not happy to introduce a scripting language here, but it seemed like the pragmatic choice compared with the alternative of building up a pile of small passes for every test or putting .mir code into C++ string literals in a unittest and still lacking all the llc mechanics for bringing up a target or having the nice matching capabilities of FileCheck.

Again, I am very much in favor of having the facilities from 'llc' or 'FileCheck' in useful APIs for unittests to leverage. But I don't think we should be adding a scripting language in order to write tests in a language other than C++ to exercise C++ API calls. I think this is significantly more burdensome than just writing unittests.

Sorry for the slow response, I think we may just fundamentally disagree about the right approach here. Currently, the approach you are proposing would make the resulting tests nearly incomprehensible to me... On the other hand, I think unittests (which are "just" C++ code even if sometimes non-obvious C++ code) *when used well* are much more friendly to people outside of the particular area than a MIR-specific scripting framework.

FWIW, Chandler here put into words what my feeling is too, better than I could have done myself.

MatzeB abandoned this revision.Apr 14 2017, 6:44 PM

I'm still not convinced unittests are the best tool for the job when most of what you are doing is constructing a function, calling a simple API and then checking the resulting function.

Anyway seems this is getting no support, abandoning.