Thank you Keith for clarification. It's really of great help.

Ge

在2021年3月24日星期三 UTC+8 上午7:45:13<Keith Randall> 写道:

> On Tuesday, March 23, 2021 at 9:11:13 AM UTC-7 Ge wrote:
>
>>
>> Hi,
>> Recently I encountered a problem which seems to be related to SSA 
>> optimization 
>> and feels hard to figure out what some SSA operation means.
>>
>> Code:
>> case1:
>> func main() {
>>     var x int
>>     go func() {
>>         for {   
>>             x++          //no matter "-N" compile flags is specified or 
>> not, 'x++' will be optimized
>>         }
>>     }()
>>     println(x)
>> }
>>
>> case2:
>> func main() {
>>     var x int
>>     go func() {
>>         for {   
>>             x++          
>>             dummy()   // when empty function 'dummy' is added to this 
>> infinite loop, ''x++' stays last
>>         }
>>     }()
>>     println(x)
>> }
>>
>> //go:noinline
>> func dummy() {
>> }
>>
>> I tried 'GOSSAFUNC=main.func1 go tool compile case2.go' and found the key 
>> point is
>> deadcode phase in SSA. Here is CFG before 'early deadcode' phase:
>>
>> ``` *ssaoptx.go*
>> 5  go func() {
>> 6      for {
>> 7          x++
>> 8          dummy()
>> 9      }
>> 10 }()
>> ```
>>
>> ``` *early copyelim*
>>
>>    - b1:
>>    - 
>>       - v1 (?) = InitMem <mem>
>>       - v2 (?) = SP <uintptr>
>>       - v3 (?) = SB <uintptr>
>>       - v4 (?) = LocalAddr <**int> {&x} v2 v1
>>       - v5 (5) = Arg <*int> {&x} (&x[*int])
>>       - v9 (?) = Const64 <int> [1]
>>    - Plain → b2 (*+6*)
>>
>>
>>    - b2: ← b1 b4
>>    - 
>>       - v14 (7) = Phi <mem> v1 v12
>>       - v15 (7) = Copy <*int> v5 (&x[*int])
>>    - Plain → b3 (7)
>>
>>
>>    - b3: ← b2
>>    - 
>>       - v6 (7) = Copy <*int> v5 (&x[*int])
>>       - v7 (7) = Copy <mem> v14
>>       - v8 (*+7*) = Load <int> v5 v14
>>       - v10 (7) = Add64 <int> v8 v9
>>       - v11 (7) = Store <mem> {int} v5 v10 v14
>>       - v12 (*+8*) = StaticCall <mem> {"".dummy} v11
>>    - Plain → b4 (8)
>>
>>
>>    - b4: ← b3
>>    - Plain → b2 (7)
>>
>>
>>    - b5:
>>    - 
>>       - v13 (10) = Unknown <mem>
>>    - Ret v13
>>
>> ```
>> deadcode phase will traverse all blocks and find out the reachable blocks 
>> (In above example is b1,b2,b3,b4, while b5 is isolated block), Second it 
>> will
>> find out live values based on reachable blocks and eliminate dead values.
>>
>> The call of dummy function makes v8,v10,v11 all live so 'x++' isn't 
>> optimized.
>> I have read ssa/README.md but still had some questions.
>>
>> 1. The role of InitMem.
>>      It seems that every function starts with it, are some initialize 
>> work like 
>>      stack space allocation and named return values initialization done 
>> by it?
>>
>>
> Not really. Stack space and any zeroing required are done when generating 
> the preamble. They are not represented in SSA.
> InitMem is just the initial state of memory on entry to the function. It 
> does not generate any actual code.
>  
>
>> 2.  The meaning of 'v14 (7) = Phi <mem> v1 v12'.
>>       It looks like v14 = Φ(v1, v12), but I don't know why InitMem and 
>> dummy function
>>       call will affect here.
>>
>> 3.  The meaning of of  StaticCall's argument .
>>       Some ssa operations are easy to understand,  for example,  
>>       'v8 (*+7*) = Load <int> v5 v14' means v8<int>=Load(v5) and v14 is 
>> the memory state            which implies this load operation must happens 
>> after v14 is determined.
>>
>>       That's all I know from README.md, but about other operations like 
>> StaticCall
>>        I can't get enough information. Here is the relevant souce In 
>> genericOps.go:
>>        ```
>>       
>>  {name: "StaticCall", argLength: 1, aux: "CallOff", call: true},     
>>       
>>  // call function aux.(*obj.LSym), arg0=memory.  auxint=arg size.  Returns 
>> memory.
>>       ```
>>       For 'v12 (*+8*) = StaticCall <mem> {"".dummy} v11' the only 
>> argument is v11 but
>>       obviously v11 seems not the address of dummy function.
>>
>>
> The address of the target of the call is not stored in a separate SSA 
> value - it is encoded directly in the StaticCall Value (in the Aux field).
> Other types of calls (the indirect ones whose target must be computed at 
> runtime, like InterCall) do take a target as an SSA value.
>  
>
>> 4.  As threre are other incomprehensible ssa operations except InitMem, 
>> Phi, ... ,
>>       Is there any documents which can help understanding?
>>      
>>
>
> In general these all have to do with the concept of the "memory" type. 
> Values in SSA can have such a type, which means "the entire state of 
> memory". Function calls, for example, take a memory state as an argument 
> (as well as any explicit arguments) and return a new memory state. Same for 
> stores. Loads take a memory state as input.
>
> Phi operations are described here: 
> https://en.wikipedia.org/wiki/Static_single_assignment_form
> Phis of memory mean the merge of two memory states.
>  
>
>> 'Thanks for you time.
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/c8e31728-bad5-4462-83ce-1ed202b20b94n%40googlegroups.com.

Reply via email to