aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/expand_calls.go
blob: 34cff51c00095a4bcc84ef0d0e5879734ecf38ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// 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)
			}
		}
	}
}