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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/c8e31728-bad5-4462-83ce-1ed202b20b94n%40googlegroups.com.