From: John S. <leg...@gm...> - 2008-12-25 05:17:02
|
Hi: For my analysis, I would like to easily find the variables which get assigned the return value of functions. For example, I would like to be able to get the variable "ptr" in the following code: int* ptr = foo(); // assuming foo() returns something of type int* By default, CIL will create a temporary variable and do the following: int* tmp = foo(); ptr = tmp; On the CIL website, it says that in order to stop this, I have to set Cabs2cil.doCollapseCallCast to true. However, I've set it to true in the file cil/src/frontc/cabs2cil.ml (and have verified that its true when I print out its value in my analysis code), but CIL still creates and uses a temporary variable. Can anyone help me? Cheers, legit |
From: John S. <leg...@gm...> - 2009-01-12 23:29:19
|
Hi: So, is there anyone out there who could help me with solving this problem (outside of the workaround which was suggested)? Cheers, legit |
From: Hills, M. A <mh...@cs...> - 2009-01-13 19:48:43
|
Ok, I've figured out what's going on, but since I didn't write the code originally I'm not sure of all the motivations for why things work as they do. This hopefully will work for you though. If you don't care as much about the why's, feel free to skip to the end. The problem appears to be with a combination of how CIL handles C declarations that include initializers and the collapseCallCast functionality, which doesn't quite apply to what you are trying to do. CIL will always turn something like this: int x = 5; into something like this: int x; x = 5; In the case of the function call that you are making in your sample code, it automatically breaks this: int *p = foo(); into int *p; p = tmp; However, it doesn't just do this, it also inserts a temporary: int *p; int *tmp; tmp = foo(); p = tmp; This is because of the way that CIL is converting the expression from a CABS expression (the format that it initially uses when it parses the program) into a CIL expression, which takes place in the file you mentioned earlier, cabs2cil.ml. If you search that file for "addCall", you will see the code a few lines down below this function for the default pattern match against !pwhat, creating a temporary (you will see a comment that says "Remember that this variable has been created for this specific call. We will use this in collapseCallCast."). So, it added the temporary, but if it gets rid of it in collapseCallCast it should still be fine. However, it doesn't. Here is the original code: let collapseCallCast = function Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | i1,i2 -> None The problem is that collapseCallCast really does just collapse unneeded casts - you can see this in the third line, where the match against Set must include a cast in the right-hand side expression, i.e., something like x = (int *)y. The code that is being generated in this case you are encountering doesn't include a cast, though, so this doesn't match, and the assignment to the temporary is left in place. I tried changing this function to the following, though, which seems to work: let collapseCallCast = function Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, Lval(Var vi', NoOffset), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig vi'.vtype) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | i1,i2 -> None Here, I've just added a new case, specifically for when the first instruction is a Call that assigns into a variable and the second instruction is a Set from one variable to another. I keep a similar check in place, which you can see in the second Util.equals line, where I make sure that the types of both sides match. If I run this on the example that you provided, it does work, eliminating the assignment into tmp. You should be able to use it by just finding function collapseCallCast and replacing it with my version. Hopefully this all works and all makes sense. Feel free to contact me if you have any questions. Mark Hills Formal Systems Lab, UIUC From: John Smith [mailto:leg...@gm...] Sent: Monday, January 12, 2009 5:28 PM To: CIL-users Mailing List Subject: [CIL users] Trouble Enabling Cabs2cil.doCollapseCallCast Hi: So, is there anyone out there who could help me with solving this problem (outside of the workaround which was suggested)? Cheers, legit |
From: John S. <leg...@gm...> - 2009-01-25 02:22:06
|
Hi Mark: Sorry about the late reply - I've been a bit busy. WOW!! :D It worked and you've been a big help. Many, many thanks for taking the time to not only find the fix, but explain it in such detail. Yeah, I'm curious too as to why they originally designed it this way; it seems a bit confusing. If the original coder is reading this, maybe he/she can provide some insight. Thanks again, legit 2009/1/13 Hills, Mark A <mh...@cs...> > Ok, I've figured out what's going on, but since I didn't write the code > originally I'm not sure of all the motivations for why things work as they > do. This hopefully will work for you though. If you don't care as much about > the why's, feel free to skip to the end. > > > > The problem appears to be with a combination of how CIL handles C > declarations that include initializers and the collapseCallCast > functionality, which doesn't quite apply to what you are trying to do. CIL > will always turn something like this: > > > > int x = 5; > > > > into something like this: > > > > int x; > > x = 5; > > > > In the case of the function call that you are making in your sample code, > it automatically breaks this: > > > > int *p = foo(); > > > > into > > > > int *p; > > p = tmp; > > > > However, it doesn't just do this, it also inserts a temporary: > > > > int *p; > > int *tmp; > > tmp = foo(); > > p = tmp; > > > > This is because of the way that CIL is converting the expression from a > CABS expression (the format that it initially uses when it parses the > program) into a CIL expression, which takes place in the file you mentioned > earlier, cabs2cil.ml. If you search that file for "addCall", you will see > the code a few lines down below this function for the default pattern match > against !pwhat, creating a temporary (you will see a comment that says > "Remember that this variable has been created for this specific call. We > will use this in collapseCallCast."). > > > > So, it added the temporary, but if it gets rid of it in collapseCallCast it > should still be fine. However, it doesn't. Here is the original code: > > > > let collapseCallCast = function > > Call(Some(Var vi, NoOffset), f, args, l), > > Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) > > when (not vi.vglob && > > String.length vi.vname >= 3 && > > (* Watch out for the possibility that we have an implied cast > in > > * the call *) > > (let tcallres = > > match unrollType (typeOf f) with > > TFun (rt, _, _, _) -> rt > > | _ -> E.s (E.bug "Function call to a non-function") > > in > > Util.equals (typeSig tcallres) (typeSig vi.vtype) && > > Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && > > IH.mem callTempVars vi.vid && > > vi' == vi) > > -> Some [Call(Some destlv, f, args, l)] > > | i1,i2 -> None > > > > The problem is that collapseCallCast really does just collapse unneeded > casts – you can see this in the third line, where the match against Set must > include a cast in the right-hand side expression, i.e., something like x = > (int *)y. The code that is being generated in this case you are encountering > doesn't include a cast, though, so this doesn't match, and the assignment to > the temporary is left in place. I tried changing this function to the > following, though, which seems to work: > > > > let collapseCallCast = function > > Call(Some(Var vi, NoOffset), f, args, l), > > Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) > > when (not vi.vglob && > > String.length vi.vname >= 3 && > > (* Watch out for the possibility that we have an implied cast > in > > * the call *) > > (let tcallres = > > match unrollType (typeOf f) with > > TFun (rt, _, _, _) -> rt > > | _ -> E.s (E.bug "Function call to a non-function") > > in > > Util.equals (typeSig tcallres) (typeSig vi.vtype) && > > Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && > > IH.mem callTempVars vi.vid && > > vi' == vi) > > -> Some [Call(Some destlv, f, args, l)] > > | Call(Some(Var vi, NoOffset), f, args, l), > > Set(destlv, Lval(Var vi', NoOffset), _) > > when (not vi.vglob && > > String.length vi.vname >= 3 && > > (* Watch out for the possibility that we have an implied cast > in > > * the call *) > > (let tcallres = > > match unrollType (typeOf f) with > > TFun (rt, _, _, _) -> rt > > | _ -> E.s (E.bug "Function call to a non-function") > > in > > Util.equals (typeSig tcallres) (typeSig vi.vtype) && > > Util.equals (typeSig vi'.vtype) (typeSig (typeOfLval destlv))) > && > > IH.mem callTempVars vi.vid && > > vi' == vi) > > -> Some [Call(Some destlv, f, args, l)] > > | i1,i2 -> None > > > > Here, I've just added a new case, specifically for when the first > instruction is a Call that assigns into a variable and the second > instruction is a Set from one variable to another. I keep a similar check in > place, which you can see in the second Util.equals line, where I make sure > that the types of both sides match. If I run this on the example that you > provided, it does work, eliminating the assignment into tmp. You should be > able to use it by just finding function collapseCallCast and replacing it > with my version. > > > > Hopefully this all works and all makes sense. Feel free to contact me if > you have any questions. > > > > Mark Hills > > Formal Systems Lab, UIUC > > > > > > *From:* John Smith [mailto:leg...@gm...] > *Sent:* Monday, January 12, 2009 5:28 PM > *To:* CIL-users Mailing List > *Subject:* [CIL users] Trouble Enabling Cabs2cil.doCollapseCallCast > > > > Hi: > > So, is there anyone out there who could help me with solving this problem > (outside of the workaround which was suggested)? > > Cheers, > legit > |
From: Hills, M. A <mh...@cs...> - 2009-01-25 02:35:02
|
Just a quick correction to my original email, this: int *p; p = tmp; should be: int *p; p = foo(); since, at this point, tmp hasn't been created yet. Best regards, Mark ________________________________ From: John Smith [leg...@gm...] Sent: Saturday, January 24, 2009 8:22 PM To: CIL-users Mailing List Subject: Re: [CIL users] Trouble Enabling Cabs2cil.doCollapseCallCast Hi Mark: Sorry about the late reply - I've been a bit busy. WOW!! :D It worked and you've been a big help. Many, many thanks for taking the time to not only find the fix, but explain it in such detail. Yeah, I'm curious too as to why they originally designed it this way; it seems a bit confusing. If the original coder is reading this, maybe he/she can provide some insight. Thanks again, legit 2009/1/13 Hills, Mark A <mh...@cs...<mailto:mh...@cs...>> Ok, I've figured out what's going on, but since I didn't write the code originally I'm not sure of all the motivations for why things work as they do. This hopefully will work for you though. If you don't care as much about the why's, feel free to skip to the end. The problem appears to be with a combination of how CIL handles C declarations that include initializers and the collapseCallCast functionality, which doesn't quite apply to what you are trying to do. CIL will always turn something like this: int x = 5; into something like this: int x; x = 5; In the case of the function call that you are making in your sample code, it automatically breaks this: int *p = foo(); into int *p; p = tmp; However, it doesn't just do this, it also inserts a temporary: int *p; int *tmp; tmp = foo(); p = tmp; This is because of the way that CIL is converting the expression from a CABS expression (the format that it initially uses when it parses the program) into a CIL expression, which takes place in the file you mentioned earlier, cabs2cil.ml<http://cabs2cil.ml>. If you search that file for "addCall", you will see the code a few lines down below this function for the default pattern match against !pwhat, creating a temporary (you will see a comment that says "Remember that this variable has been created for this specific call. We will use this in collapseCallCast."). So, it added the temporary, but if it gets rid of it in collapseCallCast it should still be fine. However, it doesn't. Here is the original code: let collapseCallCast = function Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | i1,i2 -> None The problem is that collapseCallCast really does just collapse unneeded casts – you can see this in the third line, where the match against Set must include a cast in the right-hand side expression, i.e., something like x = (int *)y. The code that is being generated in this case you are encountering doesn't include a cast, though, so this doesn't match, and the assignment to the temporary is left in place. I tried changing this function to the following, though, which seems to work: let collapseCallCast = function Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, CastE (newt, Lval(Var vi', NoOffset)), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig newt) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | Call(Some(Var vi, NoOffset), f, args, l), Set(destlv, Lval(Var vi', NoOffset), _) when (not vi.vglob && String.length vi.vname >= 3 && (* Watch out for the possibility that we have an implied cast in * the call *) (let tcallres = match unrollType (typeOf f) with TFun (rt, _, _, _) -> rt | _ -> E.s (E.bug "Function call to a non-function") in Util.equals (typeSig tcallres) (typeSig vi.vtype) && Util.equals (typeSig vi'.vtype) (typeSig (typeOfLval destlv))) && IH.mem callTempVars vi.vid && vi' == vi) -> Some [Call(Some destlv, f, args, l)] | i1,i2 -> None Here, I've just added a new case, specifically for when the first instruction is a Call that assigns into a variable and the second instruction is a Set from one variable to another. I keep a similar check in place, which you can see in the second Util.equals line, where I make sure that the types of both sides match. If I run this on the example that you provided, it does work, eliminating the assignment into tmp. You should be able to use it by just finding function collapseCallCast and replacing it with my version. Hopefully this all works and all makes sense. Feel free to contact me if you have any questions. Mark Hills Formal Systems Lab, UIUC From: John Smith [mailto:leg...@gm...<mailto:leg...@gm...>] Sent: Monday, January 12, 2009 5:28 PM To: CIL-users Mailing List Subject: [CIL users] Trouble Enabling Cabs2cil.doCollapseCallCast Hi: So, is there anyone out there who could help me with solving this problem (outside of the workaround which was suggested)? Cheers, legit |
From: Gabriel K. <ke...@pp...> - 2008-12-25 11:19:37
|
On Thu, Dec 25, 2008 at 12:16:57AM -0500, John Smith wrote: > On the CIL website, it says that in order to stop this, I have to set > Cabs2cil.doCollapseCallCast to true. However, I've set it to true in the > file cil/src/frontc/cabs2cil.ml (and have verified that its true when I > print out its value in my analysis code), but CIL still creates and uses a > temporary variable. Can anyone help me? CIL introduces temporary variables in some cases, in order to get a simpler code (remember that CIL is a subset of C), but a I don't understand why it would do so in your example (apart from the casting trick that you disabled). Could you provide a minimal C file showing the problem, please? Regards, -- Gabriel Kerneis |
From: John S. <leg...@gm...> - 2008-12-25 16:39:56
|
Hi: The following code should exhibit the problem: #include <stdio.h> #include <stdlib.h> int a,b,c; int returnConst() { return 5; } int* foo() { return NULL; } int main (void) { a = returnConst(); int* p = foo(); return 0; } I am using CIL 1.3.6 on Ubuntu 8.10, and run it with the following: cilly --save-temps=[some_dir] --noInsertImplicitCast [filename] -o [executable_name] I've also tried a version where foo returns an integer pointer that was locally declared, but I'm seeing the same problem. Just to be clear, I'm looking at the *.cil.c file that gets generated to see how CIL transforms my code. Cheers, legit |
From: Gabriel K. <ke...@pp...> - 2008-12-27 09:13:42
|
On Thu, Dec 25, 2008 at 11:39:50AM -0500, John Smith wrote: > int main (void) > { > a = returnConst(); > int* p = foo(); > > return 0; > } This is what I expected. I don't know if this is a bug or feature, but anyway, a workaround is to declare the pointer at the top of the main function. Hope this helps, -- Gabriel Kerneis |
From: John S. <leg...@gm...> - 2008-12-28 04:53:47
|
Hi: Thanks, the workaround does work :) However, the problem is that I'm writing a CIL analysis for other people's code. It appears that any time I define and initialize a variable with the return value of a function, it introduces a temporary variable and splits in into 2 statements (as shown in my original post). I've tried this for pointers and for non-pointers. This behaviour contradicts the documentation for Cabs2cil.doCollapseCallCast, which states: "Without this option, CIL converts "T* x = malloc(n);" into "void* tmp = malloc(n); T* x = (T*)tmp;". If you don't need all casts to be made explicit, you can set Cabs2cil.doCollapseCallCast to true so that CIL won't insert a temporary and you can more easily determine the allocation type from calls to malloc" How can I fix this bug (please know that I'm not familiar with the CIL internals, so I would be extremely hesitant to modify it)? Cheers, legit 2008/12/27 Gabriel Kerneis <ke...@pp...> > On Thu, Dec 25, 2008 at 11:39:50AM -0500, John Smith wrote: > > int main (void) > > { > > a = returnConst(); > > int* p = foo(); > > > > return 0; > > } > > This is what I expected. I don't know if this is a bug or feature, but > anyway, > a workaround is to declare the pointer at the top of the main function. > > Hope this helps, > -- > Gabriel Kerneis > |