-
Notifications
You must be signed in to change notification settings - Fork 414
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
[GSoC] LLVM IR dumping #6135
[GSoC] LLVM IR dumping #6135
Conversation
I think it would be better, in this use case, if you could print to stdout. Also the LLVM docs say ->dump is for debugging. Is there a 'print' method of some sort you could use instead? |
compiler/codegen/symbol.cpp
Outdated
char llvmFuncDumpName[FUNC_NAME_MAX+1] = ""; | ||
int llvmFuncOptDump = 0; | ||
|
||
void llvmFunctionDump(int optLevel, const std::string &name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function has the wrong arguments. I think it should accept a Chapel Function* and an llvm::Function*.
Then, compare llvmFuncDumpName against chapelFunction->name and chapelFunction->cname.
compiler/codegen/symbol.cpp
Outdated
@@ -43,7 +43,6 @@ | |||
|
|||
// LLVM debugging support | |||
#include "llvmDebug.h" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put this space back, since it doesn't have to do with your intended change set.
compiler/codegen/symbol.cpp
Outdated
@@ -75,6 +102,7 @@ void Symbol::codegenDef() { | |||
|
|||
void Symbol::codegenPrototype() { } | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's take this space away.
compiler/codegen/symbol.cpp
Outdated
@@ -1261,6 +1289,9 @@ void FnSymbol::codegenDef() { | |||
body->codegen(); | |||
flushStatements(); | |||
|
|||
#ifdef HAVE_LLVM | |||
#endif | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take out this ifdef doing nothing?
compiler/codegen/symbol.cpp
Outdated
|
||
#ifdef HAVE_LLVM | ||
void llvmFunctionDump(int optLevel, llvm::Function *llvmFunc, FnSymbol *chapelFunc) { | ||
static llvm::Function *func = NULL; //Store function once we've found it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really so that you can make the call in makeBinaryLLVM work nicely, right? Maybe put a comment to that effect?
compiler/include/symbol.h
Outdated
extern int llvmFuncOptDump; | ||
|
||
#ifdef HAVE_LLVM | ||
void llvmFunctionDump(int optLevel, llvm::Function *llvmFunc = NULL, FnSymbol *chapelFunc = NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put a comment here saying what this function does. Something like
Prints out LLVM IR for the function selected by llvmFuncDumpName at the optimization level llvmFuncOptDump. If llvmFunc and chapelFunc are NULL, this function assumes it has been called previously with non-NULL arguments for these functions.
But I find this behavior a little bit odd and wonder if it would be better to just make llvmFunctionDump's static variable in to a global. Is that what you had before? Sorry if I'm leading you in a circle..
compiler/main/driver.cpp
Outdated
@@ -704,6 +704,8 @@ static ArgumentDescription arg_desc[] = { | |||
{"", ' ', NULL, "LLVM Code Generation Options", NULL, NULL, NULL, NULL}, | |||
{"llvm", ' ', NULL, "[Don't] use the LLVM code generator", "N", &llvmCodegen, "CHPL_LLVM_CODEGEN", NULL}, | |||
{"llvm-wide-opt", ' ', NULL, "Enable [disable] LLVM wide pointer optimizations", "N", &fLLVMWideOpt, "CHPL_LLVM_WIDE_OPTS", NULL}, | |||
{"llvm-fdump", ' ', "<name>", "Dump LLVM Intermediate Representation of given function to stdout", "S256", llvmFuncDumpName, "CHPL_LLVM_FDUMP", NULL}, | |||
{"llvm-fdump-opt", ' ', "<opt>", "Specifies from which LLVM transformation phase to print function: 0 - before any transformation, 1 - basic cleaning, 2 - full optimization", "I", &llvmFuncOptDump, "CHPL_LLVM_OPT_FDUMP", NULL}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be more reasonable for llvm-fdump-opt to take in a string, so that if we add/change optimization levels we don't have to renumber & change tests. Also I might prefer different flag names.
I might like
--print-llvm-ir
instead of llvm-fdump
and I'm not sure what to call llvm-fdump-opt
. Maybe print-llvm-ir-after
and then it can take start
simplify
and optimize
, for example?
(I think we'll want to ask some others about that part so make sure to put the user interface for this feature in your PR message. Generally the user interface will receive more scrutiny than the change itself).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this needs attention of other developers, I'm not going to focus on that part. I'll just write tests and change them later accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but could you summarize the usage of the new feature in an email to chapel-developers and/or in the PR message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I'll write all down in PR message and then send e-mail pointing to this PR. I'm not sure which is preferred one (e-mail or PR), but I suspect most of devs don't look that often into PR section. Whenever I'm doing these tasks I make quiet assumption that you inform the rest of developers.
compiler/util/clangUtil.cpp
Outdated
@@ -1867,6 +1867,7 @@ void makeBinaryLLVM(void) { | |||
output.keep(); | |||
output.os().flush(); | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this new space.
I've run into small Issues with figuring out simple IR testcase where non-optimized version would differ from simplify optimization. As far as I understood code there is some dataLayout pass which is architecture dependent, and -O2 pass on a function (yes! the one from clang), more details in proc test()
{
forall i in 1..100
{
writeln(i);
}
} Shows some differences, but they are very minor and generated IR takes ~400 lines of unreadable and difficult to understand code, so I decided not to use it for testing this feature. |
compiler/codegen/symbol.cpp
Outdated
@@ -1204,6 +1222,9 @@ void FnSymbol::codegenDef() { | |||
#ifdef HAVE_LLVM | |||
func = getFunctionLLVM(cname); | |||
|
|||
if(strcmp(llvmFuncDumpName, name) == 0) | |||
llvmFuncDumpCName = strdup(cname); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the way the Chapel compiler manages strings, you don't need to call strdup here. Just make it point to cname.
compiler/codegen/symbol.cpp
Outdated
@@ -1281,11 +1302,17 @@ void FnSymbol::codegenDef() { | |||
INT_FATAL("LLVM function verification failed"); | |||
} | |||
} | |||
|
|||
if(llvmFuncOptDump == 0 && strcmp(llvmFuncDumpName, name) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice if 0 meant no dumping, to save some strcmparing in the common case
Thanks for the improvements! What we need to do next:
About simplifying the tests even more, in this case I'm not too worried if the output differs for different optimization levels. Let's test it with as trivial functions as are possible. |
I prefer the flags: --print-llvm-ir --print-llvm-ir-after where is: That's just my own favour. |
Personally, I'd probably choose the names --llvm-print-* rather than --print-llvm-* just to put the llvm-specific aspect front-and-center, but I don't feel strongly about this if you disagree. I also agree that the mnemonic flag settings rather than integers seem more attractive. |
OK, so if we combine all of the thoughts, we end up with:
|
Well I the only thing that comes to my mind would be change And when it comes to testing I've came up with simple idea, which would make tests a bit more reasonable. When we dump LLVM IR representation I guess it would be good to add comment in printed LLVM IR saying from which optimization stage the printed IR comes from. Like:
Then in tests we can check if we printed that line. It doesn't of course ensure that printed IR is actually from stage, but provides extra info to the user and can be good starting point for correctness tests. |
I like the strategy to improve the tests. Re the naming, it would be fine with me to have --llvm-print-ir-stage. I think you have enough information to adjust this PR. |
compiler/main/driver.cpp
Outdated
@@ -704,8 +709,8 @@ static ArgumentDescription arg_desc[] = { | |||
{"", ' ', NULL, "LLVM Code Generation Options", NULL, NULL, NULL, NULL}, | |||
{"llvm", ' ', NULL, "[Don't] use the LLVM code generator", "N", &llvmCodegen, "CHPL_LLVM_CODEGEN", NULL}, | |||
{"llvm-wide-opt", ' ', NULL, "Enable [disable] LLVM wide pointer optimizations", "N", &fLLVMWideOpt, "CHPL_LLVM_WIDE_OPTS", NULL}, | |||
{"llvm-fdump", ' ', "<name>", "Dump LLVM Intermediate Representation of given function to stdout", "S256", llvmFuncDumpName, "CHPL_LLVM_FDUMP", NULL}, | |||
{"llvm-fdump-opt", ' ', "<opt>", "Specifies from which LLVM transformation phase to print function: 0 - before any transformation, 1 - basic cleaning, 2 - full optimization", "I", &llvmFuncOptDump, "CHPL_LLVM_OPT_FDUMP", NULL}, | |||
{"llvm-print-ir", ' ', "<name>", "Dump LLVM Intermediate Representation of given function to stdout", "S256", llvmPrintIrName, "CHPL_LLVM_FDUMP", NULL}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update enviro var names too e.g. "CHPL_LLVM_FDUMP"
compiler/codegen/symbol.cpp
Outdated
@@ -1222,8 +1238,8 @@ void FnSymbol::codegenDef() { | |||
#ifdef HAVE_LLVM | |||
func = getFunctionLLVM(cname); | |||
|
|||
if(strcmp(llvmFuncDumpName, name) == 0) | |||
llvmFuncDumpCName = strdup(cname); | |||
if(strcmp(llvmPrintIrName, name) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be if (llvmPrintIrNameStageNum !=0 && strcmp(...) == 0 ) ?
compiler/codegen/symbol.cpp
Outdated
{LLVM_FULL_STAGE_NAME, LLVM_FULL_STAGE_NUM} | ||
}; | ||
|
||
std::map<int, std::string> llvmStageRevMap = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::map seems overkill for these 3-element debugging lists. I'd personally probably just use an array for int -> string and then linear search for the other way... but there's not anything wrong exactly with using std::map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to agree with you that map is a bit of overkill.
In this case it was just easier for me to assign (value)->(key) using std::map notation. Normally I'd like to have std::array or std::vector equivalent looking like this:
std::vector llvmStageRevMap =
{
[LLVM_NONE_STAGE_NUM] = LLVM_NONE_STAGE_NAME,
[LLVM_BASIC_STAGE_NUM] = LLVM_BASIC_STAGE_NAME,
[LLVM_FULL_STAGE_NUM] = LLVM_FULL_STAGE_NAME
};
But C++ doesn't support such assignment to vector.
I consider to even remove map, and simply use bunch of 'if, else' in places where I actually use this: llvmFunctionDump, and verifyStageAndsetStageNum, but this is just my programming style to use map for this kind of task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can just be
char* llvmStageRevMap[] = {
"",
LLVM_NONE_STAGE_NAME,
LLVM_BASIC_STAGE_NAME,
LLVM_FULL_STAGE_NAME }
Anybody adding stages will need to modify it anyway
@@ -0,0 +1,5 @@ | |||
proc mainTest() | |||
{ | |||
writeln("Hello World!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it just be an empty proc?
mainTest_chpl_2blk_body_: ; preds = %entry | ||
%0 = load %string, %string* @_str_literal_1924, !tbaa !0 | ||
store %string %0, %string* %local__str_literal_1924_chpl, !tbaa !0 | ||
call void @writeln_chpl2(%string* %local__str_literal_1924_chpl, i64 3, i32 51) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeln_chpl2 won't be stable as the compiler changes.
Can you put a PREDIFF that just greps out everything except for the "LLVM IR representation" comment?
… can be in any order
compiler/codegen/symbol.cpp
Outdated
@@ -1204,6 +1245,9 @@ void FnSymbol::codegenDef() { | |||
#ifdef HAVE_LLVM | |||
func = getFunctionLLVM(cname); | |||
|
|||
if(strcmp(llvmPrintIrName, name) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be totally clear & to minimize the function call, can this be
if (llvmPrintIrStageNum != llvmStageNum::NOPRINT && strcmp(llvmPrintIrName, name) == 0)
?
compiler/include/symbol.h
Outdated
extern const char *llvmStageName[llvmStageNum::LAST]; | ||
|
||
const char *stageNameFromStageNum(int stageNum); | ||
int stageNumFromStageName(const char* stageName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these functions be prefixed with 'llvm' e.g. 'llvmStageNameFromStageNum' ?
compiler/main/driver.cpp
Outdated
static void verifyStageAndSetStageNum(const ArgumentDescription* desc, const char* arg_unused) | ||
{ | ||
int stageNum = stageNumFromStageName(llvmPrintIrStage); | ||
if(!stageNum) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everywhere else, stageNum is only ever one of the enum values. So shouldn't this say
if (stageNum == llvmStageNum::NOPRINT)
?
compiler/include/symbol.h
Outdated
extern int llvmPrintIrStageNum; | ||
|
||
namespace llvmStageNum { | ||
enum { NOPRINT = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider making this a typedef enum
and then using that type instead of int for llvmPrintIrStageNum.
man/chpl.rst
Outdated
**--llvm-print-ir-stage <stage>** | ||
Picks stage from which to print LLVM IR of function defined in | ||
**--llvm-print-ir**. | ||
Chapel compiler runs many different optimization passes each of which |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a The -> "The chapel compiler runs..."
man/chpl.rst
Outdated
Picks stage from which to print LLVM IR of function defined in | ||
**--llvm-print-ir**. | ||
Chapel compiler runs many different optimization passes each of which | ||
can change IR of function. This option allows to pick IR of function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"can change IR of function" -> "can change the IR of functions"
"allows to pick IR" -> "allows one to pick the IR"
I ran full local testing and got these failures:
|
Passed full local testing. I needed to make some space changes to chpl.rst in order to get |
Follow-on to PR chapel-lang#6135.
Adjust spacing in chpl.rst to be valid rst Follow-on to PR #6135 so that `make docs` completes without error.
@@ -704,6 +713,8 @@ static ArgumentDescription arg_desc[] = { | |||
{"", ' ', NULL, "LLVM Code Generation Options", NULL, NULL, NULL, NULL}, | |||
{"llvm", ' ', NULL, "[Don't] use the LLVM code generator", "N", &llvmCodegen, "CHPL_LLVM_CODEGEN", NULL}, | |||
{"llvm-wide-opt", ' ', NULL, "Enable [disable] LLVM wide pointer optimizations", "N", &fLLVMWideOpt, "CHPL_LLVM_WIDE_OPTS", NULL}, | |||
{"llvm-print-ir", ' ', "<name>", "Dump LLVM Intermediate Representation of given function to stdout", "S256", llvmPrintIrName, "CHPL_LLVM_PRINT_IR", NULL}, | |||
{"llvm-print-ir-stage", ' ', "<stage>", "Specifies from which LLVM optimization stage to print function: none, basic, full", "S256", llvmPrintIrStage, "CHPL_LLVM_PRINT_IR_STAGE", &verifyStageAndSetStageNum}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coodie and @mppf: In putting together the release CHANGES file, I only noticed these flags for the first time. These strike me as developer, rather than user, flags. Is there an argument for keeping them in user space rather than pushing them down into developer space? (the arguments for pushing them down being to keep the --help output and man page slightly shorter). Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The argument for having them in user space is "Users might want to see the LLVM IR output" but I think it'd be reasonable to call such users "developers", whatever that means. I'll make a PR to move it down.
This PR adds convenience functions for dumping LLVM IR of given chapel function.
Need for such functions occurred when I started to work on adding LLVM vectorization tests for chapel test suite. They might also be useful in further work on LLVM.
Even very tiny chapel source files generate very big LLVM IR, when working with LLVM we usually need to look only at a piece of code. It seems reasonable to abstract our code into a function and then dump IR of function. There is another problem of chapel internal types, because they are in global space and thus cannot be enclosed in function, but I see it as another problem.
Compiler runs many transformations on LLVM IR, and sometimes we'd like to know if some optimizations occured or not (for example, was this loop vectorized?). At this moment there are 3 possibly useful places where we'd like to dump part of our code:
There might be a possibility and a need to add more granularity to 3rd point, like after which pass should we exactly dump a function.