@@ -21,9 +21,10 @@ import (
2121// the first return value as a load before the call, which makes o appear unchanged
2222// to the return value in our backend.
2323//
24- // This pass moves loads of local allocs used only for the final Return results
25- // to after any intervening calls that use the same alloc pointer, matching the
26- // behavior of the Go compiler for the stdlib cases we rely on (e.g. crypto/x509.ParseOID).
24+ // This pass moves loads of local allocs that feed a Return result and have no
25+ // intervening executable use before that Return to after any intervening calls
26+ // that use the same alloc pointer, matching the behavior of the Go compiler for
27+ // the stdlib cases we rely on (e.g. crypto/x509.ParseOID).
2728func fixSSAOrder (pkg * ssa.Package , files []* ast.File ) {
2829 if pkg == nil {
2930 return
@@ -186,7 +187,11 @@ func moveAssignDepsAfterRecv(b *ssa.BasicBlock, roots []ssa.Value, recv ssa.Valu
186187 if len (move ) == 0 {
187188 return false
188189 }
189- if moveWouldBreakSSA (b .Instrs , move , recvIdx ) {
190+ // Metadata uses must follow the definitions they describe rather than
191+ // blocking an otherwise safe source-order repair.
192+ moved := movedValuesForIndices (b .Instrs , move )
193+ includeDebugRefsForMovedValues (b .Instrs , move , moved , 0 , recvIdx )
194+ if moveWouldBreakSSA (b .Instrs , move , recvIdx , moved ) {
190195 return false
191196 }
192197 deps := make ([]ssa.Instruction , 0 , len (move ))
@@ -205,13 +210,47 @@ func moveAssignDepsAfterRecv(b *ssa.BasicBlock, roots []ssa.Value, recv ssa.Valu
205210 return true
206211}
207212
208- func moveWouldBreakSSA (instrs []ssa.Instruction , move map [int ]struct {}, recvIdx int ) bool {
213+ func movedValuesForIndices (instrs []ssa.Instruction , move map [int ]struct {}) map [ssa. Value ] struct {} {
209214 moved := make (map [ssa.Value ]struct {}, len (move ))
210215 for i := range move {
216+ if i < 0 || i >= len (instrs ) {
217+ continue
218+ }
211219 if v , ok := instrs [i ].(ssa.Value ); ok && v != nil {
212220 moved [v ] = struct {}{}
213221 }
214222 }
223+ return moved
224+ }
225+
226+ // includeDebugRefsForMovedValues adds metadata-only uses of moved values to
227+ // move. The moved value set is supplied by the caller so the same set can be
228+ // reused by the subsequent SSA safety check without rescanning move.
229+ func includeDebugRefsForMovedValues (instrs []ssa.Instruction , move map [int ]struct {}, moved map [ssa.Value ]struct {}, from , through int ) {
230+ if from < 0 {
231+ from = 0
232+ }
233+ if through > len (instrs ) {
234+ through = len (instrs )
235+ }
236+ for i := from ; i < through ; i ++ {
237+ if _ , moving := move [i ]; moving {
238+ continue
239+ }
240+ ref , ok := instrs [i ].(* ssa.DebugRef )
241+ if ! ok {
242+ continue
243+ }
244+ for v := range moved {
245+ if instrUsesValue (ref , v ) {
246+ move [i ] = struct {}{}
247+ break
248+ }
249+ }
250+ }
251+ }
252+
253+ func moveWouldBreakSSA (instrs []ssa.Instruction , move map [int ]struct {}, recvIdx int , moved map [ssa.Value ]struct {}) bool {
215254 for i := 0 ; i <= recvIdx && i < len (instrs ); i ++ {
216255 if _ , moving := move [i ]; moving {
217256 continue
@@ -302,11 +341,20 @@ func fixSSAOrderBlock(b *ssa.BasicBlock) {
302341 continue
303342 }
304343
305- // If the loaded value is used by any instruction between its current
306- // position and the return (excluding return itself), moving it may place
307- // its definition after one of those uses and break SSA form.
344+ // DebugRefs are metadata-only and move with the value they describe. Any
345+ // executable use before Return still makes reordering unsafe.
346+ movingIndices := map [int ]struct {}{loadIdx : {}}
347+ moved := movedValuesForIndices (b .Instrs , movingIndices )
348+ includeDebugRefsForMovedValues (b .Instrs , movingIndices , moved , loadIdx + 1 , retIdx )
349+ moving := make (map [ssa.Instruction ]struct {}, len (movingIndices ))
350+ for i := range movingIndices {
351+ moving [b.Instrs [i ]] = struct {}{}
352+ }
308353 usedBeforeReturn := false
309354 for i := loadIdx + 1 ; i < retIdx ; i ++ {
355+ if _ , moving := movingIndices [i ]; moving {
356+ continue
357+ }
310358 if instrUsesValue (b .Instrs [i ], u ) {
311359 usedBeforeReturn = true
312360 break
@@ -316,9 +364,7 @@ func fixSSAOrderBlock(b *ssa.BasicBlock) {
316364 continue
317365 }
318366
319- // Move the load right after the last call (but before Return).
320- b .Instrs = moveInstr (b .Instrs , loadIdx , lastCallIdx + 1 )
321- // Adjust retIdx for subsequent moves in this block.
367+ b .Instrs = moveInstrsAfter (b .Instrs , moving , b .Instrs [lastCallIdx ])
322368 retIdx = indexOfInstr (b .Instrs , ret )
323369 }
324370}
@@ -391,41 +437,34 @@ func valueDependsOn(v, target ssa.Value, seen map[ssa.Value]struct{}) bool {
391437 return false
392438}
393439
394- // moveInstr moves instrs[from] to position to (like inserting before to),
395- // preserving relative order of other elements.
396- func moveInstr (instrs []ssa.Instruction , from , to int ) []ssa.Instruction {
397- if from < 0 || from >= len (instrs ) {
398- return instrs
399- }
400- if to < 0 {
401- to = 0
402- }
403- if to > len (instrs ) {
404- to = len (instrs )
405- }
406- if from == to || from + 1 == to {
440+ // moveInstrsAfter moves selected instructions as a stable group immediately
441+ // after anchor. The anchor must not be in moving; callers use an instruction
442+ // that remains in the block. It returns instrs unchanged when moving is empty,
443+ // or anchor is nil or absent.
444+ func moveInstrsAfter (instrs []ssa.Instruction , moving map [ssa.Instruction ]struct {}, anchor ssa.Instruction ) []ssa.Instruction {
445+ if len (moving ) == 0 || anchor == nil {
407446 return instrs
408447 }
409-
410- ins := instrs [from ]
411- // Remove.
412- copy (instrs [from :], instrs [from + 1 :])
413- instrs = instrs [:len (instrs )- 1 ]
414-
415- // Recompute insertion index after removal.
416- if to > from {
417- to --
418- }
419- if to < 0 {
420- to = 0
448+ if _ , ok := moving [anchor ]; ok {
449+ panic ("moveInstrsAfter: anchor is in moving set" )
421450 }
422- if to > len (instrs ) {
423- to = len (instrs )
451+ moved := make ([]ssa.Instruction , 0 , len (moving ))
452+ remaining := make ([]ssa.Instruction , 0 , len (instrs ))
453+ for _ , instr := range instrs {
454+ if _ , ok := moving [instr ]; ok {
455+ moved = append (moved , instr )
456+ continue
457+ }
458+ remaining = append (remaining , instr )
459+ }
460+ for i , instr := range remaining {
461+ if instr == anchor {
462+ ret := make ([]ssa.Instruction , 0 , len (instrs ))
463+ ret = append (ret , remaining [:i + 1 ]... )
464+ ret = append (ret , moved ... )
465+ ret = append (ret , remaining [i + 1 :]... )
466+ return ret
467+ }
424468 }
425-
426- // Insert.
427- instrs = append (instrs , nil )
428- copy (instrs [to + 1 :], instrs [to :])
429- instrs [to ] = ins
430469 return instrs
431470}
0 commit comments