From b1b67a36ace635744cd261ee6f3441d1044c66b3 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 22 Feb 2013 15:23:57 -0500 Subject: [PATCH] reflect: stop using run-time code generation Step 3 of http://golang.org/s/go11func. Fixes #3736. Fixes #3738. Fixes #4081. R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/7393050 --- src/pkg/reflect/asm_386.s | 15 ++--- src/pkg/reflect/asm_amd64.s | 15 ++--- src/pkg/reflect/asm_arm.s | 10 +-- src/pkg/reflect/makefunc.go | 122 +++++------------------------------- src/pkg/reflect/value.go | 5 +- 5 files changed, 32 insertions(+), 135 deletions(-) diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s index 30ff3413ac..27d3fa21d5 100644 --- a/src/pkg/reflect/asm_386.s +++ b/src/pkg/reflect/asm_386.s @@ -2,17 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// makeFuncStub is jumped to by the code generated by MakeFunc. -// The code sets AX = type, BX = fn, CX = frame before the jump. +// makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in value.go // for more details. -TEXT ·makeFuncStub(SB),7,$12 - MOVL AX, 0(SP) - MOVL BX, 4(SP) - MOVL CX, 8(SP) +TEXT ·makeFuncStub(SB),7,$8 + MOVL DX, 0(SP) + LEAL arg+0(FP), CX + MOVL CX, 4(SP) CALL ·callReflect(SB) RET - -// unused -TEXT ·cacheflush(SB),7,$0 - RET diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s index ce33e78c24..d51d982a9d 100644 --- a/src/pkg/reflect/asm_amd64.s +++ b/src/pkg/reflect/asm_amd64.s @@ -2,17 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// makeFuncStub is jumped to by the code generated by MakeFunc. -// The code sets AX = type, BX = fn, CX = frame before the jump. +// makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in value.go // for more details. -TEXT ·makeFuncStub(SB),7,$24 - MOVQ AX, 0(SP) - MOVQ BX, 8(SP) - MOVQ CX, 16(SP) +TEXT ·makeFuncStub(SB),7,$16 + MOVQ DX, 0(SP) + LEAQ arg+0(FP), CX + MOVQ CX, 8(SP) CALL ·callReflect(SB) RET - -// unused -TEXT ·cacheflush(SB),7,$0 - RET diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s index 3f1814cf06..db487f8a56 100644 --- a/src/pkg/reflect/asm_arm.s +++ b/src/pkg/reflect/asm_arm.s @@ -3,15 +3,11 @@ // license that can be found in the LICENSE file. // makeFuncStub is jumped to by the code generated by MakeFunc. -// The code sets R0 = type, R1 = fn, R2 = frame before the jump. // See the comment on the declaration of makeFuncStub in value.go // for more details. -TEXT ·makeFuncStub(SB),7,$12 - MOVW R0, 4(R13) +TEXT ·makeFuncStub(SB),7,$8 + MOVW R7, 4(R13) + MOVW $arg+0(FP), R1 MOVW R1, 8(R13) - MOVW R2, 12(R13) BL ·callReflect(SB) RET - -TEXT ·cacheflush(SB),7,$-4 - B runtime·cacheflush(SB) diff --git a/src/pkg/reflect/makefunc.go b/src/pkg/reflect/makefunc.go index e85a1f3b0e..024f938f1d 100644 --- a/src/pkg/reflect/makefunc.go +++ b/src/pkg/reflect/makefunc.go @@ -7,23 +7,15 @@ package reflect import ( - "runtime" "unsafe" ) // makeFuncImpl is the closure value implementing the function // returned by MakeFunc. type makeFuncImpl struct { - codeptr unsafe.Pointer - - // References visible to the garbage collector. - // The code array below contains the same references - // embedded in the machine code. - typ *rtype - fn func([]Value) []Value - - // code is the actual machine code invoked for the closure. - code [40]byte + code uintptr + typ *funcType + fn func([]Value) []Value } // MakeFunc returns a new function of the given Type @@ -53,107 +45,23 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { panic("reflect: call of MakeFunc with non-Func type") } - // Gather type pointer and function pointers - // for use in hand-assembled closure. t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) - // Create function impl. - // We don't need to save a pointer to makeFuncStub, because it is in - // the text segment and cannot be garbage collected. - impl := &makeFuncImpl{ - typ: t, - fn: fn, - } - impl.codeptr = unsafe.Pointer(&impl.code[0]) - - tptr := unsafe.Pointer(t) - fptr := *(*unsafe.Pointer)(unsafe.Pointer(&fn)) - tmp := makeFuncStub - stub := **(**unsafe.Pointer)(unsafe.Pointer(&tmp)) - - // Create code. Copy template and fill in pointer values. - switch runtime.GOARCH { - default: - panic("reflect.MakeFunc: unexpected GOARCH: " + runtime.GOARCH) + // indirect Go func value (dummy) to obtain + // actual code address. (A Go func is a pointer + // to a C function pointer. http://golang.org/s/go11func.) + dummy := makeFuncStub + code := **(**uintptr)(unsafe.Pointer(&dummy)) - case "amd64": - copy(impl.code[:], amd64CallStub) - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[2])) = tptr - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[12])) = fptr - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[22])) = stub - - case "386": - copy(impl.code[:], _386CallStub) - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[1])) = tptr - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[6])) = fptr - *(*unsafe.Pointer)(unsafe.Pointer(&impl.code[11])) = stub - - case "arm": - code := (*[10]uintptr)(unsafe.Pointer(&impl.code[0])) - copy(code[:], armCallStub) - code[len(armCallStub)] = uintptr(tptr) - code[len(armCallStub)+1] = uintptr(fptr) - code[len(armCallStub)+2] = uintptr(stub) - - cacheflush(&impl.code[0], &impl.code[len(impl.code)-1]) - } + impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn} return Value{t, unsafe.Pointer(impl), flag(Func) << flagKindShift} } -func cacheflush(start, end *byte) - -// makeFuncStub is an assembly function used by the code generated -// and returned from MakeFunc. The code returned from makeFunc -// does, schematically, -// -// MOV $typ, R0 -// MOV $fn, R1 -// MOV $0(FP), R2 -// JMP makeFuncStub -// -// That is, it copies the type and function pointer passed to MakeFunc -// into the first two machine registers and then copies the argument frame -// pointer into the third. Then it jumps to makeFuncStub, which calls callReflect -// with those arguments. Using a jmp to makeFuncStub instead of making the -// call directly keeps the allocated code simpler but, perhaps more -// importantly, also keeps the allocated PCs off the call stack. -// Nothing ever returns to the allocated code. +// makeFuncStub is an assembly function that is the code half of +// the function returned from MakeFunc. It expects a *callReflectFunc +// as its context register, and its job is to invoke callReflect(ctxt, frame) +// where ctxt is the context register and frame is a pointer to the first +// word in the passed-in argument frame. func makeFuncStub() - -// amd64CallStub is the MakeFunc code template for amd64 machines. -var amd64CallStub = []byte{ - // MOVQ $constant, AX - 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // MOVQ $constant, BX - 0x48, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // MOVQ $constant, DX - 0x48, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // LEAQ 8(SP), CX (argument frame) - 0x48, 0x8d, 0x4c, 0x24, 0x08, - // JMP *DX - 0xff, 0xe2, -} - -// _386CallStub is the MakeFunc code template for 386 machines. -var _386CallStub = []byte{ - // MOVL $constant, AX - 0xb8, 0x00, 0x00, 0x00, 0x00, - // MOVL $constant, BX - 0xbb, 0x00, 0x00, 0x00, 0x00, - // MOVL $constant, DX - 0xba, 0x00, 0x00, 0x00, 0x00, - // LEAL 4(SP), CX (argument frame) - 0x8d, 0x4c, 0x24, 0x04, - // JMP *DX - 0xff, 0xe2, -} - -// armCallStub is the MakeFunc code template for arm machines. -var armCallStub = []uintptr{ - 0xe59f000c, // MOVW 0x14(PC), R0 - 0xe59f100c, // MOVW 0x14(PC), R1 - 0xe28d2004, // MOVW $4(SP), R2 - 0xe59ff008, // MOVW 0x10(PC), PC - 0xeafffffe, // B 0(PC), just in case -} diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 11659751d1..c87812c469 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -555,7 +555,10 @@ func (v Value) call(method string, in []Value) []Value { // frame into a call using Values. // It is in this file so that it can be next to the call method above. // The remainder of the MakeFunc implementation is in makefunc.go. -func callReflect(ftyp *funcType, f func([]Value) []Value, frame unsafe.Pointer) { +func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) { + ftyp := ctxt.typ + f := ctxt.fn + // Copy argument frame into Values. ptr := frame off := uintptr(0) -- 2.48.1