aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/expand_calls.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/ssa/expand_calls.go')
-rw-r--r--src/cmd/compile/internal/ssa/expand_calls.go81
1 files changed, 65 insertions, 16 deletions
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index e1c657d4a4..af994d4b5b 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -24,6 +24,10 @@ type offsetKey struct {
pt *types.Type
}
+func isBlockMultiValueExit(b *Block) bool {
+ return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
+}
+
// expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
// that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into
// more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are
@@ -194,7 +198,8 @@ func expandCalls(f *Func) {
}
break
}
- if leaf.Op == OpIData {
+ switch leaf.Op {
+ case OpIData, OpStructSelect, OpArraySelect:
leafType = removeTrivialWrapperTypes(leaf.Type)
}
aux := selector.Aux
@@ -624,6 +629,24 @@ func expandCalls(f *Func) {
return x
}
+ rewriteDereference := func(b *Block, base, a, mem *Value, offset, size int64, typ *types.Type, pos src.XPos) *Value {
+ source := a.Args[0]
+ dst := offsetFrom(base, offset, source.Type)
+ if a.Uses == 1 && a.Block == b {
+ a.reset(OpMove)
+ a.Pos = pos
+ a.Type = types.TypeMem
+ a.Aux = typ
+ a.AuxInt = size
+ a.SetArgs3(dst, source, mem)
+ mem = a
+ } else {
+ mem = b.NewValue3A(pos, OpMove, types.TypeMem, typ, dst, source, mem)
+ mem.AuxInt = size
+ }
+ return mem
+ }
+
// rewriteArgs removes all the Args from a call and converts the call args into appropriate
// stores (or later, register movement). Extra args for interface and closure calls are ignored,
// but removed.
@@ -631,7 +654,7 @@ func expandCalls(f *Func) {
// Thread the stores on the memory arg
aux := v.Aux.(*AuxCall)
pos := v.Pos.WithNotStmt()
- m0 := v.Args[len(v.Args)-1]
+ m0 := v.MemoryArg()
mem := m0
for i, a := range v.Args {
if i < firstArg {
@@ -647,20 +670,7 @@ func expandCalls(f *Func) {
}
// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
// TODO this will be more complicated with registers in the picture.
- source := a.Args[0]
- dst := f.ConstOffPtrSP(source.Type, aux.OffsetOfArg(auxI), sp)
- if a.Uses == 1 && a.Block == v.Block {
- a.reset(OpMove)
- a.Pos = pos
- a.Type = types.TypeMem
- a.Aux = aux.TypeOfArg(auxI)
- a.AuxInt = aux.SizeOfArg(auxI)
- a.SetArgs3(dst, source, mem)
- mem = a
- } else {
- mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, source, mem)
- mem.AuxInt = aux.SizeOfArg(auxI)
- }
+ mem = rewriteDereference(v.Block, sp, a, mem, aux.OffsetOfArg(auxI), aux.SizeOfArg(auxI), aux.TypeOfArg(auxI), pos)
} else {
if debug {
fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
@@ -692,6 +702,45 @@ func expandCalls(f *Func) {
v.SetArgs2(code, mem)
}
}
+ if isBlockMultiValueExit(b) {
+ // Very similar to code in rewriteArgs, but results instead of args.
+ v := b.Controls[0]
+ m0 := v.MemoryArg()
+ mem := m0
+ aux := f.OwnAux
+ pos := v.Pos.WithNotStmt()
+ for j, a := range v.Args {
+ i := int64(j)
+ if a == m0 {
+ break
+ }
+ auxType := aux.TypeOfResult(i)
+ auxBase := b.NewValue2A(v.Pos, OpLocalAddr, types.NewPtr(auxType), aux.results[i].Name, sp, mem)
+ auxOffset := int64(0)
+ auxSize := aux.SizeOfResult(i)
+ if a.Op == OpDereference {
+ // Avoid a self-move, and if one is detected try to remove the already-inserted VarDef for the assignment that won't happen.
+ if dAddr, dMem := a.Args[0], a.Args[1]; dAddr.Op == OpLocalAddr && dAddr.Args[0].Op == OpSP &&
+ dAddr.Args[1] == dMem && dAddr.Aux == aux.results[i].Name {
+ if dMem.Op == OpVarDef && dMem.Aux == dAddr.Aux {
+ dMem.copyOf(dMem.MemoryArg()) // elide the VarDef
+ }
+ continue
+ }
+ mem = rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos)
+ } else {
+ if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr {
+ addr := a.Args[0]
+ if addr.MemoryArg() == a.MemoryArg() && addr.Aux == aux.results[i].Name {
+ continue
+ }
+ }
+ mem = storeArgOrLoad(v.Pos, b, auxBase, a, mem, aux.TypeOfResult(i), auxOffset)
+ }
+ }
+ b.SetControl(mem)
+ v.reset(OpInvalid) // otherwise it can have a mem operand which will fail check(), even though it is dead.
+ }
}
for i, name := range f.Names {