How to traverse all the local variables that declared in the current routine?

Qing Zhao QING.ZHAO@ORACLE.COM
Wed Nov 25 17:41:10 GMT 2020



> On Nov 25, 2020, at 3:11 AM, Richard Biener <richard.guenther@gmail.com> wrote:
>> 
>> 
>> Hi,
>> 
>> Does gcc provide an iterator to traverse all the local variables that are declared in the current routine?
>> 
>> If not, what’s the best way to traverse the local variables?
>> 
>> 
>> Depends on what for.  There's the source level view you get by walking
>> BLOCK_VARS of the
>> scope tree, theres cfun->local_variables (FOR_EACH_LOCAL_DECL) and
>> there's SSA names
>> (FOR_EACH_SSA_NAME).
>> 
>> 
>> I am planing to add a new phase immediately after “pass_late_warn_uninitialized” to initialize all auto-variables that are
>> not explicitly initialized in the declaration, the basic idea is following:
>> 
>> ** The proposal:
>> 
>> A. add a new GCC option: (same name and meaning as CLANG)
>> -ftrivial-auto-var-init=[pattern|zero], similar pattern init as CLANG;
>> 
>> B. add a new attribute for variable:
>> __attribute((uninitialized)
>> the marked variable is uninitialized intentionaly for performance purpose.
>> 
>> C. The implementation needs to keep the current static warning on uninitialized
>> variables untouched in order to avoid "forking the language".
>> 
>> 
>> ** The implementation:
>> 
>> There are two major requirements for the implementation:
>> 
>> 1. all auto-variables that do not have an explicit initializer should be initialized to
>> zero by this option.  (Same behavior as CLANG)
>> 
>> 2. keep the current static warning on uninitialized variables untouched.
>> 
>> In order to satisfy 1, we should check whether an auto-variable has initializer
>> or not;
>> In order to satisfy 2, we should add this new transformation after
>> "pass_late_warn_uninitialized".
>> 
>> So, we should be able to check whether an auto-variable has initializer or not after “pass_late_warn_uninitialized”,
>> If Not, then insert an initialization for it.
>> 
>> For this purpose, I guess that “FOR_EACH_LOCAL_DECL” might be better?
>> 
>> 
>> Yes, but do you want to catch variables promoted to register as well
>> or just variables
>> on the stack?
>> 
>> 
>> I think both as long as they are source-level auto-variables. Then which one is better?
>> 
>> 
>> Another issue is, in order to check whether an auto-variable has initializer, I plan to add a new bit in “decl_common” as:
>> /* In a VAR_DECL, this is DECL_IS_INITIALIZED.  */
>> unsigned decl_is_initialized :1;
>> 
>> /* IN VAR_DECL, set when the decl is initialized at the declaration.  */
>> #define DECL_IS_INITIALIZED(NODE) \
>> (DECL_COMMON_CHECK (NODE)->decl_common.decl_is_initialized)
>> 
>> set this bit when setting DECL_INITIAL for the variables in FE. then keep it
>> even though DECL_INITIAL might be NULLed.
>> 
>> 
>> For locals it would be more reliable to set this flag during gimplification.
>> 
>> 
>> You mean I can set the flag “DECL_IS_INITIALIZED (decl)”  inside the routine “gimpley_decl_expr” (gimplify.c) as following:
>> 
>>  if (VAR_P (decl) && !DECL_EXTERNAL (decl))
>>    {
>>      tree init = DECL_INITIAL (decl);
>> ...
>>      if (init && init != error_mark_node)
>>        {
>>          if (!TREE_STATIC (decl))
>>    {
>>      DECL_IS_INITIALIZED(decl) = 1;
>>    }
>> 
>> Is this enough for all Frontends? Are there other places that I need to maintain this bit?
>> 
>> 
>> 
>> Do you have any comment and suggestions?
>> 
>> 
>> As said above - do you want to cover registers as well as locals?
>> 
>> 
>> All the locals from the source-code point of view should be covered.   (From my study so far,  looks like that Clang adds that phase in FE).
>> If GCC adds this phase in FE, then the following design requirement
>> 
>> C. The implementation needs to keep the current static warning on uninitialized
>> variables untouched in order to avoid "forking the language”.
>> 
>> cannot be satisfied.  Since gcc’s uninitialized variables analysis is applied quite late.
>> 
>> So, we have to add this new phase after “pass_late_warn_uninitialized”.
>> 
>> I'd do
>> the actual zeroing during RTL expansion instead since otherwise you
>> have to figure youself whether a local is actually used (see expand_stack_vars)
>> 
>> 
>> Adding  this new transformation during RTL expansion is okay.  I will check on this in more details to see how to add it to RTL expansion phase.
>> 
>> 
>> Note that optimization will already made have use of "uninitialized" state
>> of locals so depending on what the actual goal is here "late" may be too late.
>> 
>> 
>> This is a really good point…
>> 
>> In order to avoid optimization  to use the “uninitialized” state of locals, we should add the zeroing phase as early as possible (adding it in FE might be best
>> for this issue). However, if we have to met the following requirement:
> 
> So is optimization supposed to pick up zero or is it supposed to act
> as if the initializer
> is unknown?

Good question!

Theoretically,  the new option -ftrivial-auto-var-init=zero is supposed to add zero initialization to auto-variables 
that are not explicitly initialized in order to avoid the possible undefined behavior. 

So, I think that with the new option specified, compiler optimization should pick up zero initialization. 
Therefore, ideally, zero initializations should  be inserted before optimizations. 

However, this will conflict with the requirement “ keep the current static warning on uninitialized
variables untouched in order to avoid "forking the language”."

>> C. The implementation needs to keep the current static warning on uninitialized
>> variables untouched in order to avoid "forking the language”.
>> 
>> We have to move the new phase after all the uninitialized analysis is done in order to avoid “forking the language”.
>> 
>> So, this is a problem that is not easy to resolve.
> 
> Indeed, those are conflicting goals.

Yes, this is the most difficult part for this task. 
Not sure how CLANG resolved this issue?

> 
>> Do you have suggestion on this?
> 
> No, not any easy ones.  Doing more of the uninit analysis early (there
> is already an early
> uninit pass) which would mean doing IPA analysis turing GCC into more
> of a static analysis
> tool.  Theres the analyzer now, not sure if that can employ an early
> LTO phase for example.

You mean to enhance “pass_early_warn_uninitialized” or “pass_analyzer” to catch
more uninitialized cases, then add the new “zero initialization” after these passes?

However, both “pass_early_warn_uninitialized” and “pass_analyzer” still utilize
some early ipa optimizations. These early optimizations still act as the initializers are unknown. 

So, looks like the conflicting cannot be completely resolved. 


Another thought, If we still add the initializations at “pass_expand” as you suggested in the previous email, 
GCC will be split into two parts, the earlier part before “pass_expand” all act without the zero initialization
And report the uninitialized warnings based on this. 
The later part after “pass_expand” will pick up zero initializations. All the RTL optimizations will be applied
on the program with all new zero initializations. 
Will such approach have any potential big issue?

Qing

> 
> Richard.
> 
>> Qing
>> 
>> 
>> Richard.
>> 
>> 
>> Thanks a lot for the help.
>> 
>> Qing
>> 
>> Richard.
>> 
>> 
>> Thanks.
>> 
>> Qing



More information about the Gcc-patches mailing list