[PATCH] [OPENMP] Do not emit references to original variables in 'private' clause.

Bataev, Alexey a.bataev at hotmail.com
Mon May 18 20:49:35 PDT 2015


John, we need all this stuff because of asynchronous nature of tasks.
In other OpenMP directives we can directly allocate private copies and 
use them instead of the original variables in the outlined OpenMP function:
void outlined(...) {
%g = alloca i32
store i32 0, %g
}

But for tasks we cannot allocate private copies on stack in the outlined 
function. It happens because there are actually 2 kind of tasks - tied 
tasks and untied tasks. Untied tasks may have many entry/exit points and 
because of that we cant simply allocate private copies in the outlined 
function.
Also we can't allocate memory for these privates in a calling function 
because tasks are asynchronous and the parent function may be terminated 
earlier than the task itself.
Instead we're adding private copies at the end of kmp_task_t structure 
and the memory for them is allocated by the runtime. Also we're 
generating a function with the destructors calls for each private field 
(if it is required) and provide a pointer to this function to runtime:
1. struct kmp_task_t_with_privates {
   struct kmp_task_t {
     void *shareds; /**< pointer to block of pointers to shared vars   */
     kmp_routine_entry_t routine;            /**< pointer to outlined 
function for tasks */
     kmp_int32           part_id;            /**< part id for the untied 
task (defines current entry point) */
     kmp_routine_entry_t destructors;        /* pointer to function to 
invoke deconstructors of private C++ objects */
   } task_data;
   struct .kmp_private. { // private copies of all private/firstprivate 
variables
     int a;
     S s;
   } privates;
};

2. Our outlined function for task has the next profile
void oulined(int ThreadID, int PartId, void *shareds) {
...
}
This function with this profile is automatically generated from 
CapturedStmt construct.

3. Then we have to create a task using
void *new_task = __kmpc_task_allocate(<loc>, i32 ThreadId, flags, size_t 
sizeof(kmp_task_t_with_privates), size_t sizeof(shareds), 
kmp_routine_entry_t outlined);

libcall and it returns a pointer to created kmp_task_t_with_privates 
structure.

4. We have to initialize our private copies:
(kmp_task_t_with_privates*)new_task->privates.a = original_a; // if 'a' 
is a firstprivate;
DefaultConstructor_for_S_class 
(&(kmp_task_t_with_privates*)new_task->privates.s); // if 's' is a 
private and because of that it must be initialized with the default 
constructor.

5. We have to generate a function for destructors calls:
void destructors(kmp_task_t_with_privates *task) {
~Destructor_for_S_class(&task->privates.s);
}
...
(kmp_task_t_with_privates*)new_task->task_data.destructors = 
(kmp_routine_entry_t)&destructors;

6. You're saying that capture field is not initialized. It is not so. 
We're using it to provide a reference to private copy to outlined task 
function:
(shareds_type*)((kmp_task_t_with_privates*)new_task->task_data.shareds)->ref_a 
= &(kmp_task_t_with_privates*)new_task->privates.a;
(shareds_type*)((kmp_task_t_with_privates*)new_task->task_data.shareds)->ref_s 
= &(kmp_task_t_with_privates*)new_task->privates.s;

So we have to initialize these references manually rather than 
automatically as for other constructs.
Shareds is the structure of captures generated by the CapturedStmt 
construct.

7. We're creating a task by calling
__kmpc_omp_task(<loc>, i32 ThreadId, new_task);

This is how tasks are implemented in libiomp5 runtime library.


19.05.2015 1:56, John McCall пишет:
> Can you expand a bit on what the task directive needs the capture field for if it's never initialized?  This feels like a very significant change, and I'd be a lot happier if the field could be dropped entirely.
>
> At the very least, CapturedStmt need to document this.
>
>
> ================
> Comment at: lib/CodeGen/CGStmt.cpp:2120
> @@ +2119,3 @@
> +    // Do not emit initialization for OpenMP private variables.
> +    if (CurField->hasCapturedVLAType() || *I) {
> +      LValue LV = EmitLValueForFieldInitialization(SlotLV, *CurField);
> ----------------
> The more officially-approved way to do this would be to use "continue".  Not a big deal, though.
>
> http://reviews.llvm.org/D9550
>
> EMAIL PREFERENCES
>    http://reviews.llvm.org/settings/panel/emailpreferences/
>
>
>
>


-- 
Best regards,
Alexey Bataev
=============
Software Engineer
Intel Compiler Team






More information about the cfe-commits mailing list