Thank you, looking forward to new Go version with regabi.
在2021年3月25日星期四 UTC+8 上午12:04:39<[email protected]> 写道:
> 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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/0bc8524d-fc09-46ad-8524-8cf9d5a0ff4fn%40googlegroups.com.