| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssa |
| |
| import "cmd/compile/internal/types" |
| |
| // 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 also rewritten into |
| // more appropriate forms. |
| func expandCalls(f *Func) { |
| canSSAType := f.fe.CanSSA |
| sp, _ := f.spSb() |
| // Calls that need lowering have some number of inputs, including a memory input, |
| // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. |
| |
| // With the current ABI those inputs need to be converted into stores to memory, |
| // rethreading the call's memory input to the first, and the new call now receiving the last. |
| |
| // With the current ABI, the outputs need to be converted to loads, which will all use the call's |
| // memory output as their input. |
| |
| // Step 1: find all references to calls as values and rewrite those. |
| for _, b := range f.Blocks { |
| for _, v := range b.Values { |
| switch v.Op { |
| case OpSelectN: |
| call := v.Args[0] |
| aux := call.Aux.(*AuxCall) |
| which := v.AuxInt |
| t := v.Type |
| if which == aux.NResults() { // mem is after the results. |
| // rewrite v as a Copy of call -- the replacement call will produce a mem. |
| v.copyOf(call) |
| } else { |
| pt := types.NewPtr(t) |
| if canSSAType(t) { |
| off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) |
| v.reset(OpLoad) |
| v.SetArgs2(off, call) |
| } else { |
| panic("Should not have non-SSA-able OpSelectN") |
| } |
| } |
| v.Type = t // not right for the mem operand yet, but will be when call is rewritten. |
| |
| case OpSelectNAddr: |
| call := v.Args[0] |
| which := v.AuxInt |
| aux := call.Aux.(*AuxCall) |
| pt := v.Type |
| off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp) |
| v.copyOf(off) |
| } |
| } |
| } |
| |
| // Step 2: rewrite the calls |
| for _, b := range f.Blocks { |
| for _, v := range b.Values { |
| switch v.Op { |
| case OpStaticLECall: |
| // Thread the stores on the memory arg |
| m0 := v.Args[len(v.Args)-1] |
| mem := m0 |
| pos := v.Pos.WithNotStmt() |
| aux := v.Aux.(*AuxCall) |
| auxInt := v.AuxInt |
| for i, a := range v.Args { |
| if a == m0 { |
| break |
| } |
| if a.Op == OpDereference { |
| // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move |
| src := a.Args[0] |
| dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(int64(i)), sp) |
| a.reset(OpMove) |
| a.Pos = pos |
| a.Type = types.TypeMem |
| a.Aux = aux.TypeOfArg(int64(i)) |
| a.AuxInt = aux.SizeOfArg(int64(i)) |
| a.SetArgs3(dst, src, mem) |
| mem = a |
| } else { |
| // Add a new store. |
| t := aux.TypeOfArg(int64(i)) |
| dst := f.ConstOffPtrSP(types.NewPtr(t), aux.OffsetOfArg(int64(i)), sp) |
| mem = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem) |
| } |
| } |
| v.reset(OpStaticCall) |
| v.Type = types.TypeMem |
| v.Aux = aux |
| v.AuxInt = auxInt |
| v.SetArgs1(mem) |
| } |
| } |
| } |
| } |