Thank you, looking forward to new Go version with regabi.

在2021年3月25日星期四 UTC+8 上午12:04:39<drc...@google.com> 写道:

> Also, be aware that work that we really hope lands in 1.17 will tinker 
> with all the call operations.
>
> The goal is to switch to an ABI that passes parameters to/from calls in 
> registers, and the way
> that ends up expressed in SSA is that first (and we do this part in 1.16) 
> the parameters to the 
> call will appear as inputs, and the results will be obtained with 
> OpSelectN.  The call will still
> receive and return a memory value, as last input and last element of 
> result.
>
> This is then lowered to a machine-agnostic level where some parameters are 
> stored in memory,
> the others in registers -- where the registers appear as inputs/outputs of 
> the call -- and then
> further lowered (simple OpCode rewrite) to machine-specific call 
> instructions.  The registers themselves
> are bound in the register allocator, by the "trick" of telling it that 
> Calls have only a single appropriate
> register for each of their inputs/outputs.
>
> This might look a little odd the first time you see it.
>
> And, also, the order in which the registers inputs/outputs are encoded is 
> not fixed; for compiler
> efficiency purposes, we *might* reorder them so that all the integer 
> registers come first, etc.
> (This would allow a lot more sharing of register masks.)
>
> This full change is likely only for amd64 in 1.17, then for other 
> architectures once we figure out
> the exact recipe.  It touches many parts of the compiler and runtime.
>
> On Wednesday, March 24, 2021 at 4:01:36 AM UTC-4 Ge wrote:
>
>> 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/0bc8524d-fc09-46ad-8524-8cf9d5a0ff4fn%40googlegroups.com.

Reply via email to