mirror of
https://github.com/golang/go
synced 2024-11-26 07:27:59 -07:00
cmd/compile: test register ABI for method, interface, closure calls
This is enabled with a ridiculous magic name for method, or for last input type passed, that needs to be changed to something inutterable before actual release. Ridiculous method name: MagicMethodNameForTestingRegisterABI Ridiculous last (input) type name: MagicLastTypeNameForTestingRegisterABI RLTN is tested with strings.Contains, so you can have MagicLastTypeNameForTestingRegisterABI1 and MagicLastTypeNameForTestingRegisterABI2 if that is helpful Includes test test/abi/fibish2.go Updates #44816. Change-Id: I592a6edc71ca9bebdd1d00e24edee1ceebb3e43f Reviewed-on: https://go-review.googlesource.com/c/go/+/299410 Trust: David Chase <drchase@google.com> Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
cdd08e615a
commit
7240a18adb
@ -386,41 +386,34 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
|
|||||||
which := selector.AuxInt
|
which := selector.AuxInt
|
||||||
if which == aux.NResults() { // mem is after the results.
|
if which == aux.NResults() { // mem is after the results.
|
||||||
// rewrite v as a Copy of call -- the replacement call will produce a mem.
|
// rewrite v as a Copy of call -- the replacement call will produce a mem.
|
||||||
if call.Op == OpStaticLECall {
|
if leaf != selector {
|
||||||
if leaf != selector {
|
panic("Unexpected selector of memory")
|
||||||
panic("Unexpected selector of memory")
|
|
||||||
}
|
|
||||||
// StaticCall selector will address last element of Result.
|
|
||||||
// TODO do this for all the other call types eventually.
|
|
||||||
if aux.abiInfo == nil {
|
|
||||||
panic(badVal("aux.abiInfo nil for call", call))
|
|
||||||
}
|
|
||||||
if existing := x.memForCall[call.ID]; existing == nil {
|
|
||||||
selector.AuxInt = int64(aux.abiInfo.OutRegistersUsed())
|
|
||||||
x.memForCall[call.ID] = selector
|
|
||||||
} else {
|
|
||||||
selector.copyOf(existing)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
leaf.copyOf(call)
|
|
||||||
}
|
}
|
||||||
|
if aux.abiInfo == nil {
|
||||||
|
panic(badVal("aux.abiInfo nil for call", call))
|
||||||
|
}
|
||||||
|
if existing := x.memForCall[call.ID]; existing == nil {
|
||||||
|
selector.AuxInt = int64(aux.abiInfo.OutRegistersUsed())
|
||||||
|
x.memForCall[call.ID] = selector
|
||||||
|
} else {
|
||||||
|
selector.copyOf(existing)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
leafType := removeTrivialWrapperTypes(leaf.Type)
|
leafType := removeTrivialWrapperTypes(leaf.Type)
|
||||||
if x.canSSAType(leafType) {
|
if x.canSSAType(leafType) {
|
||||||
pt := types.NewPtr(leafType)
|
pt := types.NewPtr(leafType)
|
||||||
// Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
|
// Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
|
||||||
if call.Op == OpStaticLECall { // TODO this is temporary until all calls are register-able
|
// Create a "mem" for any loads that need to occur.
|
||||||
// Create a "mem" for any loads that need to occur.
|
if mem := x.memForCall[call.ID]; mem != nil {
|
||||||
if mem := x.memForCall[call.ID]; mem != nil {
|
if mem.Block != call.Block {
|
||||||
if mem.Block != call.Block {
|
panic(fmt.Errorf("selector and call need to be in same block, selector=%s; call=%s", selector.LongString(), call.LongString()))
|
||||||
panic(fmt.Errorf("selector and call need to be in same block, selector=%s; call=%s", selector.LongString(), call.LongString()))
|
|
||||||
}
|
|
||||||
call = mem
|
|
||||||
} else {
|
|
||||||
mem = call.Block.NewValue1I(call.Pos.WithNotStmt(), OpSelectN, types.TypeMem, int64(aux.abiInfo.OutRegistersUsed()), call)
|
|
||||||
x.memForCall[call.ID] = mem
|
|
||||||
call = mem
|
|
||||||
}
|
}
|
||||||
|
call = mem
|
||||||
|
} else {
|
||||||
|
mem = call.Block.NewValue1I(call.Pos.WithNotStmt(), OpSelectN, types.TypeMem, int64(aux.abiInfo.OutRegistersUsed()), call)
|
||||||
|
x.memForCall[call.ID] = mem
|
||||||
|
call = mem
|
||||||
}
|
}
|
||||||
outParam := aux.abiInfo.OutParam(int(which))
|
outParam := aux.abiInfo.OutParam(int(which))
|
||||||
if len(outParam.Registers) > 0 {
|
if len(outParam.Registers) > 0 {
|
||||||
@ -1350,14 +1343,15 @@ func expandCalls(f *Func) {
|
|||||||
case OpStaticLECall:
|
case OpStaticLECall:
|
||||||
v.Op = OpStaticCall
|
v.Op = OpStaticCall
|
||||||
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
|
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
|
||||||
// TODO need to insert all the register types.
|
|
||||||
v.Type = types.NewResults(append(rts, types.TypeMem))
|
v.Type = types.NewResults(append(rts, types.TypeMem))
|
||||||
case OpClosureLECall:
|
case OpClosureLECall:
|
||||||
v.Op = OpClosureCall
|
v.Op = OpClosureCall
|
||||||
v.Type = types.TypeMem
|
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
|
||||||
|
v.Type = types.NewResults(append(rts, types.TypeMem))
|
||||||
case OpInterLECall:
|
case OpInterLECall:
|
||||||
v.Op = OpInterCall
|
v.Op = OpInterCall
|
||||||
v.Type = types.TypeMem
|
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
|
||||||
|
v.Type = types.NewResults(append(rts, types.TypeMem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,10 +775,10 @@ func init() {
|
|||||||
faultOnNilArg0: true,
|
faultOnNilArg0: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific registers.
|
// With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific in and out registers.
|
||||||
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
|
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
|
||||||
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
|
{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
|
||||||
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
|
{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
|
||||||
|
|
||||||
// arg0 = destination pointer
|
// arg0 = destination pointer
|
||||||
// arg1 = source pointer
|
// arg1 = source pointer
|
||||||
|
@ -264,7 +264,7 @@ var genericOps = []opData{
|
|||||||
// ±0 → ±0 (sign preserved)
|
// ±0 → ±0 (sign preserved)
|
||||||
// x<0 → NaN
|
// x<0 → NaN
|
||||||
// NaN → NaN
|
// NaN → NaN
|
||||||
{name: "Sqrt", argLength: 1}, // √arg0 (floating point, double precision)
|
{name: "Sqrt", argLength: 1}, // √arg0 (floating point, double precision)
|
||||||
{name: "Sqrt32", argLength: 1}, // √arg0 (floating point, single precision)
|
{name: "Sqrt32", argLength: 1}, // √arg0 (floating point, single precision)
|
||||||
|
|
||||||
// Round to integer, float64 only.
|
// Round to integer, float64 only.
|
||||||
@ -396,9 +396,28 @@ var genericOps = []opData{
|
|||||||
// TODO(josharian): ClosureCall and InterCall should have Int32 aux
|
// TODO(josharian): ClosureCall and InterCall should have Int32 aux
|
||||||
// to match StaticCall's 32 bit arg size limit.
|
// to match StaticCall's 32 bit arg size limit.
|
||||||
// TODO(drchase,josharian): could the arg size limit be bundled into the rules for CallOff?
|
// TODO(drchase,josharian): could the arg size limit be bundled into the rules for CallOff?
|
||||||
{name: "ClosureCall", argLength: 3, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory.
|
|
||||||
{name: "StaticCall", argLength: -1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
|
// Before lowering, LECalls receive their fixed inputs (first), memory (last),
|
||||||
{name: "InterCall", argLength: 2, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory.
|
// and a variable number of input values in the middle.
|
||||||
|
// They produce a variable number of result values.
|
||||||
|
// These values are not necessarily "SSA-able"; they can be too large,
|
||||||
|
// but in that case inputs are loaded immediately before with OpDereference,
|
||||||
|
// and outputs are stored immediately with OpStore.
|
||||||
|
//
|
||||||
|
// After call expansion, Calls have the same fixed-middle-memory arrangement of inputs,
|
||||||
|
// with the difference that the "middle" is only the register-resident inputs,
|
||||||
|
// and the non-register inputs are instead stored at ABI-defined offsets from SP
|
||||||
|
// (and the stores thread through the memory that is ultimately an input to the call).
|
||||||
|
// Outputs follow a similar pattern; register-resident outputs are the leading elements
|
||||||
|
// of a Result-typed output, with memory last, and any memory-resident outputs have been
|
||||||
|
// stored to ABI-defined locations. Each non-memory input or output fits in a register.
|
||||||
|
//
|
||||||
|
// Subsequent architecture-specific lowering only changes the opcode.
|
||||||
|
|
||||||
|
{name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
|
||||||
|
{name: "StaticCall", argLength: -1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
|
||||||
|
{name: "InterCall", argLength: -1, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1..argN-1 are register inputs, argN=memory, auxint=arg size. Returns Result of register results, plus memory.
|
||||||
|
|
||||||
{name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
{name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
||||||
{name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
{name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
||||||
{name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
{name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Op encodes the specific operation that a Value performs.
|
// An Op encodes the specific operation that a Value performs.
|
||||||
@ -68,6 +69,27 @@ type regInfo struct {
|
|||||||
outputs []outputInfo
|
outputs []outputInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *regInfo) String() string {
|
||||||
|
s := ""
|
||||||
|
s += "INS:\n"
|
||||||
|
for _, i := range r.inputs {
|
||||||
|
mask := fmt.Sprintf("%64b", i.regs)
|
||||||
|
mask = strings.Replace(mask, "0", ".", -1)
|
||||||
|
s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
|
||||||
|
}
|
||||||
|
s += "OUTS:\n"
|
||||||
|
for _, i := range r.outputs {
|
||||||
|
mask := fmt.Sprintf("%64b", i.regs)
|
||||||
|
mask = strings.Replace(mask, "0", ".", -1)
|
||||||
|
s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
|
||||||
|
}
|
||||||
|
s += "CLOBBERS:\n"
|
||||||
|
mask := fmt.Sprintf("%64b", r.clobbers)
|
||||||
|
mask = strings.Replace(mask, "0", ".", -1)
|
||||||
|
s += fmt.Sprintf(" |%s|\n", mask)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
type auxType int8
|
type auxType int8
|
||||||
|
|
||||||
type Param struct {
|
type Param struct {
|
||||||
@ -116,20 +138,25 @@ func (a *AuxCall) Reg(i *regInfo, c *Config) *regInfo {
|
|||||||
a.reg = i
|
a.reg = i
|
||||||
return a.reg
|
return a.reg
|
||||||
}
|
}
|
||||||
a.reg.inputs = append(a.reg.inputs, i.inputs...)
|
|
||||||
|
k := len(i.inputs)
|
||||||
for _, p := range a.abiInfo.InParams() {
|
for _, p := range a.abiInfo.InParams() {
|
||||||
for _, r := range p.Registers {
|
for _, r := range p.Registers {
|
||||||
m := archRegForAbiReg(r, c)
|
m := archRegForAbiReg(r, c)
|
||||||
a.reg.inputs = append(a.reg.inputs, inputInfo{idx: len(a.reg.inputs), regs: (1 << m)})
|
a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
|
||||||
|
k++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.reg.outputs = append(a.reg.outputs, i.outputs...)
|
a.reg.inputs = append(a.reg.inputs, i.inputs...) // These are less constrained, thus should come last
|
||||||
|
k = len(i.outputs)
|
||||||
for _, p := range a.abiInfo.OutParams() {
|
for _, p := range a.abiInfo.OutParams() {
|
||||||
for _, r := range p.Registers {
|
for _, r := range p.Registers {
|
||||||
m := archRegForAbiReg(r, c)
|
m := archRegForAbiReg(r, c)
|
||||||
a.reg.outputs = append(a.reg.outputs, outputInfo{idx: len(a.reg.outputs), regs: (1 << m)})
|
a.reg.outputs = append(a.reg.outputs, outputInfo{idx: k, regs: (1 << m)})
|
||||||
|
k++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
a.reg.outputs = append(a.reg.outputs, i.outputs...)
|
||||||
a.reg.clobbers = i.clobbers
|
a.reg.clobbers = i.clobbers
|
||||||
return a.reg
|
return a.reg
|
||||||
}
|
}
|
||||||
@ -299,12 +326,20 @@ func StaticAuxCall(sym *obj.LSym, args []Param, results []Param, paramResultInfo
|
|||||||
|
|
||||||
// InterfaceAuxCall returns an AuxCall for an interface call.
|
// InterfaceAuxCall returns an AuxCall for an interface call.
|
||||||
func InterfaceAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
|
func InterfaceAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
|
||||||
return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo}
|
var reg *regInfo
|
||||||
|
if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
|
||||||
|
reg = ®Info{}
|
||||||
|
}
|
||||||
|
return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo, reg: reg}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClosureAuxCall returns an AuxCall for a closure call.
|
// ClosureAuxCall returns an AuxCall for a closure call.
|
||||||
func ClosureAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
|
func ClosureAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
|
||||||
return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo}
|
var reg *regInfo
|
||||||
|
if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
|
||||||
|
reg = ®Info{}
|
||||||
|
}
|
||||||
|
return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo, reg: reg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*AuxCall) CanBeAnSSAAux() {}
|
func (*AuxCall) CanBeAnSSAAux() {}
|
||||||
|
@ -13275,7 +13275,7 @@ var opcodeTable = [...]opInfo{
|
|||||||
{
|
{
|
||||||
name: "CALLclosure",
|
name: "CALLclosure",
|
||||||
auxType: auxCallOff,
|
auxType: auxCallOff,
|
||||||
argLen: 3,
|
argLen: -1,
|
||||||
clobberFlags: true,
|
clobberFlags: true,
|
||||||
call: true,
|
call: true,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
@ -13289,7 +13289,7 @@ var opcodeTable = [...]opInfo{
|
|||||||
{
|
{
|
||||||
name: "CALLinter",
|
name: "CALLinter",
|
||||||
auxType: auxCallOff,
|
auxType: auxCallOff,
|
||||||
argLen: 2,
|
argLen: -1,
|
||||||
clobberFlags: true,
|
clobberFlags: true,
|
||||||
call: true,
|
call: true,
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
@ -35596,7 +35596,7 @@ var opcodeTable = [...]opInfo{
|
|||||||
{
|
{
|
||||||
name: "ClosureCall",
|
name: "ClosureCall",
|
||||||
auxType: auxCallOff,
|
auxType: auxCallOff,
|
||||||
argLen: 3,
|
argLen: -1,
|
||||||
call: true,
|
call: true,
|
||||||
generic: true,
|
generic: true,
|
||||||
},
|
},
|
||||||
@ -35610,7 +35610,7 @@ var opcodeTable = [...]opInfo{
|
|||||||
{
|
{
|
||||||
name: "InterCall",
|
name: "InterCall",
|
||||||
auxType: auxCallOff,
|
auxType: auxCallOff,
|
||||||
argLen: 2,
|
argLen: -1,
|
||||||
call: true,
|
call: true,
|
||||||
generic: true,
|
generic: true,
|
||||||
},
|
},
|
||||||
|
@ -217,6 +217,10 @@ func AbiForFunc(fn *ir.Func) *abi.ABIConfig {
|
|||||||
return abiForFunc(fn, ssaConfig.ABI0, ssaConfig.ABI1).Copy() // No idea what races will result, be safe
|
return abiForFunc(fn, ssaConfig.ABI0, ssaConfig.ABI1).Copy() // No idea what races will result, be safe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (NLT 2021-04-15) This must be changed to a name that cannot match; it may be helpful to other register ABI work to keep the trigger-logic
|
||||||
|
const magicNameDotSuffix = ".MagicMethodNameForTestingRegisterABI"
|
||||||
|
const magicLastTypeName = "MagicLastTypeNameForTestingRegisterABI"
|
||||||
|
|
||||||
// abiForFunc implements ABI policy for a function, but does not return a copy of the ABI.
|
// abiForFunc implements ABI policy for a function, but does not return a copy of the ABI.
|
||||||
// Passing a nil function returns ABIInternal.
|
// Passing a nil function returns ABIInternal.
|
||||||
func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig {
|
func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig {
|
||||||
@ -224,16 +228,38 @@ func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig {
|
|||||||
if !regabiEnabledForAllCompilation() {
|
if !regabiEnabledForAllCompilation() {
|
||||||
a = abi0
|
a = abi0
|
||||||
}
|
}
|
||||||
if fn != nil && fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working
|
|
||||||
|
if fn != nil {
|
||||||
name := ir.FuncName(fn)
|
name := ir.FuncName(fn)
|
||||||
if strings.Contains(name, ".") {
|
magicName := strings.HasSuffix(name, magicNameDotSuffix)
|
||||||
base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
|
if fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working
|
||||||
|
if strings.Contains(name, ".") {
|
||||||
|
if !magicName {
|
||||||
|
base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = abi1
|
||||||
|
} else if magicName {
|
||||||
|
if base.FmtPos(fn.Pos()) == "<autogenerated>:1" {
|
||||||
|
// no way to put a pragma here, and it will error out in the real source code if they did not do it there.
|
||||||
|
a = abi1
|
||||||
|
} else {
|
||||||
|
base.ErrorfAt(fn.Pos(), "Methods with magic name %s (method %s) must also specify //go:registerparams", magicNameDotSuffix[1:], name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if regAbiForFuncType(fn.Type().FuncType()) {
|
||||||
|
// fmt.Printf("Saw magic last type name for function %s\n", name)
|
||||||
|
a = abi1
|
||||||
}
|
}
|
||||||
a = abi1
|
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func regAbiForFuncType(ft *types.Func) bool {
|
||||||
|
np := ft.Params.NumFields()
|
||||||
|
return np > 0 && strings.Contains(ft.Params.FieldType(np-1).String(), magicLastTypeName)
|
||||||
|
}
|
||||||
|
|
||||||
func regabiEnabledForAllCompilation() bool {
|
func regabiEnabledForAllCompilation() bool {
|
||||||
// TODO compiler does not yet change behavior for GOEXPERIMENT=regabi
|
// TODO compiler does not yet change behavior for GOEXPERIMENT=regabi
|
||||||
return false && objabi.Regabi_enabled != 0
|
return false && objabi.Regabi_enabled != 0
|
||||||
@ -4863,6 +4889,22 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
|||||||
var callArgs []*ssa.Value // For late-expansion, the args themselves (not stored, args to the call instead).
|
var callArgs []*ssa.Value // For late-expansion, the args themselves (not stored, args to the call instead).
|
||||||
inRegisters := false
|
inRegisters := false
|
||||||
|
|
||||||
|
var magicFnNameSym *types.Sym
|
||||||
|
if fn.Name() != nil {
|
||||||
|
magicFnNameSym = fn.Name().Sym()
|
||||||
|
ss := magicFnNameSym.Name
|
||||||
|
if strings.HasSuffix(ss, magicNameDotSuffix) {
|
||||||
|
inRegisters = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if magicFnNameSym == nil && n.Op() == ir.OCALLINTER {
|
||||||
|
magicFnNameSym = fn.(*ir.SelectorExpr).Sym()
|
||||||
|
ss := magicFnNameSym.Name
|
||||||
|
if strings.HasSuffix(ss, magicNameDotSuffix[1:]) {
|
||||||
|
inRegisters = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
case ir.OCALLFUNC:
|
case ir.OCALLFUNC:
|
||||||
if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
|
if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
|
||||||
@ -4871,7 +4913,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
|||||||
// TODO(register args) remove after register abi is working
|
// TODO(register args) remove after register abi is working
|
||||||
inRegistersImported := fn.Pragma()&ir.RegisterParams != 0
|
inRegistersImported := fn.Pragma()&ir.RegisterParams != 0
|
||||||
inRegistersSamePackage := fn.Func != nil && fn.Func.Pragma&ir.RegisterParams != 0
|
inRegistersSamePackage := fn.Func != nil && fn.Func.Pragma&ir.RegisterParams != 0
|
||||||
inRegisters = inRegistersImported || inRegistersSamePackage
|
inRegisters = inRegisters || inRegistersImported || inRegistersSamePackage
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
closure = s.expr(fn)
|
closure = s.expr(fn)
|
||||||
@ -4898,6 +4940,11 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
|||||||
types.CalcSize(fn.Type())
|
types.CalcSize(fn.Type())
|
||||||
stksize := fn.Type().ArgWidth() // includes receiver, args, and results
|
stksize := fn.Type().ArgWidth() // includes receiver, args, and results
|
||||||
|
|
||||||
|
if regAbiForFuncType(n.X.Type().FuncType()) {
|
||||||
|
// fmt.Printf("Saw magic last type in call %v\n", n)
|
||||||
|
inRegisters = true
|
||||||
|
}
|
||||||
|
|
||||||
callABI := s.f.ABI1
|
callABI := s.f.ABI1
|
||||||
if !inRegisters {
|
if !inRegisters {
|
||||||
callABI = s.f.ABI0
|
callABI = s.f.ABI0
|
||||||
@ -5047,11 +5094,11 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
|||||||
// critical that we not clobber any arguments already
|
// critical that we not clobber any arguments already
|
||||||
// stored onto the stack.
|
// stored onto the stack.
|
||||||
codeptr = s.rawLoad(types.Types[types.TUINTPTR], closure)
|
codeptr = s.rawLoad(types.Types[types.TUINTPTR], closure)
|
||||||
aux := ssa.ClosureAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
|
aux := ssa.ClosureAuxCall(ACArgs, ACResults, callABI.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
|
||||||
call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
|
call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
|
||||||
case codeptr != nil:
|
case codeptr != nil:
|
||||||
// Note that the "receiver" parameter is nil because the actual receiver is the first input parameter.
|
// Note that the "receiver" parameter is nil because the actual receiver is the first input parameter.
|
||||||
aux := ssa.InterfaceAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
|
aux := ssa.InterfaceAuxCall(ACArgs, ACResults, params)
|
||||||
call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
|
call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
|
||||||
case callee != nil:
|
case callee != nil:
|
||||||
aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults, params)
|
aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults, params)
|
||||||
|
40
test/abi/fibish2.go
Normal file
40
test/abi/fibish2.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
//go:build !wasm
|
||||||
|
// +build !wasm
|
||||||
|
|
||||||
|
// Copyright 2021 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 main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Test that register results are correctly returned (and passed)
|
||||||
|
|
||||||
|
type MagicLastTypeNameForTestingRegisterABI func(int,MagicLastTypeNameForTestingRegisterABI) int
|
||||||
|
|
||||||
|
//go:registerparams
|
||||||
|
//go:noinline
|
||||||
|
func minus(decrement int) MagicLastTypeNameForTestingRegisterABI {
|
||||||
|
return MagicLastTypeNameForTestingRegisterABI( func(x int, _ MagicLastTypeNameForTestingRegisterABI) int { return x-decrement} )
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func f(x int, sub1 MagicLastTypeNameForTestingRegisterABI) (int, int) {
|
||||||
|
|
||||||
|
if x < 3 {
|
||||||
|
return 0, x
|
||||||
|
}
|
||||||
|
|
||||||
|
a, b := f(sub1(sub1(x, sub1), sub1), sub1)
|
||||||
|
c, d := f(sub1(x, sub1), sub1)
|
||||||
|
return a + d, b + c
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
x := 40
|
||||||
|
a, b := f(x, minus(1))
|
||||||
|
fmt.Printf("f(%d)=%d,%d\n", x, a, b)
|
||||||
|
}
|
1
test/abi/fibish2.out
Normal file
1
test/abi/fibish2.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
f(40)=39088169,126491972
|
34
test/abi/fibish_closure.go
Normal file
34
test/abi/fibish_closure.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
//go:build !wasm
|
||||||
|
// +build !wasm
|
||||||
|
|
||||||
|
// Copyright 2021 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 main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Test that register results are correctly returned (and passed)
|
||||||
|
|
||||||
|
type MagicLastTypeNameForTestingRegisterABI func(int, MagicLastTypeNameForTestingRegisterABI) (int, int)
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func f(x int, unused MagicLastTypeNameForTestingRegisterABI) (int, int) {
|
||||||
|
|
||||||
|
if x < 3 {
|
||||||
|
return 0, x
|
||||||
|
}
|
||||||
|
|
||||||
|
a, b := f(x-2, unused)
|
||||||
|
c, d := f(x-1, unused)
|
||||||
|
return a + d, b + c
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
x := 40
|
||||||
|
a, b := f(x, f)
|
||||||
|
fmt.Printf("f(%d)=%d,%d\n", x, a, b)
|
||||||
|
}
|
1
test/abi/fibish_closure.out
Normal file
1
test/abi/fibish_closure.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
f(40)=39088169,126491972
|
51
test/abi/methods.go
Normal file
51
test/abi/methods.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
//go:build !wasm
|
||||||
|
// +build !wasm
|
||||||
|
|
||||||
|
// Copyright 2021 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type toobig struct {
|
||||||
|
a,b,c string
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:registerparams
|
||||||
|
//go:noinline
|
||||||
|
func (x *toobig) MagicMethodNameForTestingRegisterABI(y toobig, z toobig) toobig {
|
||||||
|
return toobig{x.a, y.b, z.c}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnInterface interface {
|
||||||
|
MagicMethodNameForTestingRegisterABI(y toobig, z toobig) toobig
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:registerparams
|
||||||
|
//go:noinline
|
||||||
|
func I(a,b,c string) toobig {
|
||||||
|
return toobig{a,b,c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnIid prevents the compiler from figuring out what the interface really is.
|
||||||
|
//go:noinline
|
||||||
|
func AnIid(x AnInterface) AnInterface {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmp toobig
|
||||||
|
func main() {
|
||||||
|
x := I("Ahoy", "1,", "2")
|
||||||
|
y := I("3", "there,", "4")
|
||||||
|
z := I("5", "6,", "Matey")
|
||||||
|
tmp = x.MagicMethodNameForTestingRegisterABI(y,z)
|
||||||
|
fmt.Println(tmp.a, tmp.b, tmp.c)
|
||||||
|
tmp = AnIid(&x).MagicMethodNameForTestingRegisterABI(y,z)
|
||||||
|
fmt.Println(tmp.a, tmp.b, tmp.c)
|
||||||
|
}
|
2
test/abi/methods.out
Normal file
2
test/abi/methods.out
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Ahoy there, Matey
|
||||||
|
Ahoy there, Matey
|
Loading…
Reference in New Issue
Block a user