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.