On Tue, Jul 14, 2020 at 11:23 PM Masoud Gholami <ghol...@zib.de> wrote:
>
> Hi,
>
> I am writing a plugin that  uses the PLUGIN_PRAGMAS event to register a 
> custom pragma that is expected to be before a function call as follows:
>
> int main() {
>
>         char *filename = “path/to/file”;
>         #pragma inject_before_call
>         File *f = fopen(filename, …);           // marked fopen (by the 
> pragma)
>         …
>         fclose(f);
>         char *filename2 = “path/to/file2”;
>         File *f2 = fopen(filename2, …);         // non-marked fopen
>         …
>                 fclose(f2);
>         return 0;
>
> }
>
> In fact, I am using the inject_before_call pragma to mark some fopen calls in 
> the code (in this example, the first  fopen call is marked). Then, for each 
> marked fopen call, some extra expressions/statements/declarations are 
> injected into the code before calling the marked function. For example, the 
> above main function would be transformed as follows:
>
> int main() {
>
>         char *filename = “/path/to/file”;
>         File *tmp_f = fopen(“/path/to/another/file”, “w+");
>         fclose(tmp_f);
>         File *f = fopen(filename, …);
>         …
>         fclose(f);
>         char *filename2 = “path/to/file2”;      // codes not injected for the 
> non-marked fopen
>         File *f2 = fopen(filename2, …);
>         …
>                 fclose(f2);
>         return 0;
>
> }
>
> Here, because of the inject_before_call pragma, the grey code is injected 
> into the main function before calling the marked fopen. It simply opens a new 
> file (“/path/to/another/file”) and closes it.
> The thing about the injected code is that it should be inserted only if a 
> fopen call is marked by a inject_before_call pragma. And if after the 
> inject_before_call pragma no fopen calls are made, the user gets an error 
> (the pragma should be only inserted before a fopen call).
>
> I implemented this in 3 steps as follows:
>
> 1. detection of the marked fopen calls: I created a pragma_handler which 
> remembers the location_t of all inject_before_call pragmas. Then using a pass 
> (before ssa), I look for the statements/expressions that are in the next line 
> of each remembered location. If it’s a fopen call, it is considered as a 
> marked call and the code should be inserted before the fopen call. If it’s 
> something other than a fopen call, an error will be generated. However, I’m 
> not aware if there are any better ways to detect the marked calls.
>
> Here is the simplified pass to find the marked fopen calls (generating errors 
> not covered):
>
> unsigned int execute(function *func) {
> basic_block bb;
> FOR_EACH_BB_FN (bb, func) {
>         for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); 
> gsi_next (&gsi)) {
>                 gimple *stmt = gsi_stmt (gsi);
>                 if (gimple_is_fopen(stmt)) {
>                         if (marked_fopen(stmt)) {
>                                 handle_marked_fopen(stmt);
>                         }
>                 }
>         }
> }
> }
>
> 2. create the GIMPLE representation of the code to be injected: after finding 
> the marked fopen calls, I construct some declaration and expressions as 
> follows:
>
> // create the strings “/path/to/another/file" and “w+"
> tree another_path = build_string (20, “/path/to/another/file");
> fix_string_type (another_path);
> tree mode = build_string (3, “w+\0");
> fix_string_type (mode);
>
> // create a call to the fopen function with the created strings
> tree fopen_decl = lookup_qualified_name (global_namespace, 
> get_identifier("fopen"), 0, true, false);
> gimple *new_open_call = gimple_build_call(fopen_decl, 2, another_path, mode);
>
> // create the tmp_f declaration
> f_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(“tmp_f"), 
> fileptr_type_node);
> pushdecl (f_decl);
> rest_of_decl_compilation (f_decl, 0, 0);

That's the wrong interface for GIMPLE code.  Is f_decl supposed to be
a global variable
or a function local one?  For the latter simply use

 f_decl = create_tmp_var (fileptr_type_node, "tmp_f");

> // set the lhs of the fopen call to be f_decl
> gimple_call_set_lhs(new_open_call, f_decl)
>
> // create a call to the fclose function with the tmp_f variable
> tree fclose_decl = lookup_qualified_name (global_namespace, 
> get_identifier("fclose"), 0, true, false);

Likewise lookup_qualified_name is a frontend specific function, since
there's no builtin declaration
for fclose you'll have to build one yourself.

> gimple *new_close_call = gimple_build_call(fclose_decl, 1, f_decl);
>
>
> 3. add the created GIMPLE trees to the code (basic-blocks):
>
> basic_block bb = gimple_bb(stmt);
> for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next 
> (&gsi)) {         gimple *st = gsi_stmt (gsi);
>         if (st == stmt) {  // the marked fopen call
>                 gsi_insert_before(&gsi, new_open_call, GSI_NEW_STMT);
>                 gsi_insert_after(&gsi, new_open_call, GSI_NEW_STMT);
>                 break;
>         }
> }
>
> This is how I implemented the plugin. However, after compiling a sample code 
> (like the main function above), I get segmentation fault. By defining another 
> pass  to print the statements of the code and by executing this pass after 
> the previous pass (that injects the code), I see correct results (i.e., the 
> injected code is correctly generated and inserted into the right location). 
> But when I debug the sample code, I see that only the last injected statement 
> (fclose) is executed with NULL in the f_decl variable which causes the 
> segmentation fault. I searched everywhere, read all the documentations I 
> could find, and digged into the gcc code for other pragmas (i.e. omp 
> parallel, etc.). But still I have no success in doing this correctly. Could 
> you please point me where the problem is?
>
> Thanks,
> M. Gholami
>
>

Reply via email to