Commit 79561a84 authored by Austin Clements's avatar Austin Clements

runtime: simplify reflectcall write barriers

Currently reflectcall has a subtle dance with write barriers where the
assembly code copies the result values from the stack to the in-heap
argument frame without write barriers and then calls into the runtime
after the fact to invoke the necessary write barriers.

For the hybrid barrier (and for ROC), we need to switch to a
*pre*-write write barrier, which is very difficult to do with the
current setup. We could tie ourselves in knots of subtle reasoning
about why it's okay in this particular case to have a post-write write
barrier, but this commit instead takes a different approach. Rather
than making things more complex, this simplifies reflection calls so
that the argument copy is done in Go using normal bulk write barriers.

The one difficulty with this approach is that calling into Go requires
putting arguments on the stack, but the call* functions "donate" their
entire stack frame to the called function. We can get away with this
now because the copy avoids using the stack and has copied the results
out before we clobber the stack frame to call into the write barrier.
The solution in this CL is to call another function, passing arguments
in registers instead of on the stack, and let that other function
reserve more stack space and setup the arguments for the runtime.

This approach seemed to work out the best. I also tried making the
call* functions reserve 32 extra bytes of frame for the write barrier
arguments and adjust SP up by 32 bytes around the call. However, even
with the necessary changes to the assembler to correct the spdelta
table, the runtime was still having trouble with the frame layout (and
the changes to the assembler caused many other things that do strange
things with the SP to fail to assemble). The approach I took doesn't
require any funny business with the SP.

Updates #17503.

Change-Id: Ie2bb0084b24d6cff38b5afb218b9e0534ad2119e
Reviewed-on: https://go-review.googlesource.com/31655
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
parent 1c3ab3d4
...@@ -447,7 +447,7 @@ func (v Value) call(op string, in []Value) []Value { ...@@ -447,7 +447,7 @@ func (v Value) call(op string, in []Value) []Value {
// because the Values returned by this function contain pointers to the args object, // because the Values returned by this function contain pointers to the args object,
// and will thus keep the args object alive indefinitely. // and will thus keep the args object alive indefinitely.
memclr(args, retOffset) memclr(args, retOffset)
// Copy return values out of args. // Wrap Values around return values in args.
ret = make([]Value, nout) ret = make([]Value, nout)
off = retOffset off = retOffset
for i := 0; i < nout; i++ { for i := 0; i < nout; i++ {
......
...@@ -477,6 +477,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -477,6 +477,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
CALL AX; \ CALL AX; \
/* copy return values back */ \ /* copy return values back */ \
MOVL argtype+0(FP), DX; \
MOVL argptr+8(FP), DI; \ MOVL argptr+8(FP), DI; \
MOVL argsize+12(FP), CX; \ MOVL argsize+12(FP), CX; \
MOVL retoffset+16(FP), BX; \ MOVL retoffset+16(FP), BX; \
...@@ -484,17 +485,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -484,17 +485,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
ADDL BX, DI; \ ADDL BX, DI; \
ADDL BX, SI; \ ADDL BX, SI; \
SUBL BX, CX; \ SUBL BX, CX; \
REP;MOVSB; \ CALL callRet<>(SB); \
/* execute write barrier updates */ \ RET
MOVL argtype+0(FP), DX; \
MOVL argptr+8(FP), DI; \ // callRet copies return values back at the end of call*. This is a
MOVL argsize+12(FP), CX; \ // separate function so it can allocate stack space for the arguments
MOVL retoffset+16(FP), BX; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVL DX, 0(SP); \ // arguments in registers.
MOVL DI, 4(SP); \ TEXT callRet<>(SB), NOSPLIT, $16-0
MOVL CX, 8(SP); \ MOVL DX, 0(SP)
MOVL BX, 12(SP); \ MOVL DI, 4(SP)
CALL runtime·callwritebarrier(SB); \ MOVL SI, 8(SP)
MOVL CX, 12(SP)
CALL runtime·reflectcallmove(SB)
RET RET
CALLFNcall16, 16) CALLFNcall16, 16)
......
...@@ -416,8 +416,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0 ...@@ -416,8 +416,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
TEXT ·reflectcall(SB), NOSPLIT, $0-32 TEXT ·reflectcall(SB), NOSPLIT, $0-32
MOVLQZX argsize+24(FP), CX MOVLQZX argsize+24(FP), CX
// NOTE(rsc): No call16, because CALLFN needs four words
// of argument space to invoke callwritebarrier.
DISPATCH(runtime·call32, 32) DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64) DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128) DISPATCH(runtime·call128, 128)
...@@ -460,24 +458,28 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \ ...@@ -460,24 +458,28 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
CALL (DX); \ CALL (DX); \
/* copy return values back */ \ /* copy return values back */ \
MOVQ argtype+0(FP), DX; \
MOVQ argptr+16(FP), DI; \ MOVQ argptr+16(FP), DI; \
MOVLQZX argsize+24(FP), CX; \ MOVLQZX argsize+24(FP), CX; \
MOVLQZX retoffset+28(FP), BX; \ MOVLQZX retoffset+28(FP), BX; \
MOVQ SP, SI; \ MOVQ SP, SI; \
ADDQ BX, DI; \ ADDQ BX, DI; \
ADDQ BX, SI; \ ADDQ BX, SI; \
SUBQ BX, CX; \ SUBQ BX, CX; \
REP;MOVSB; \ CALL callRet<>(SB); \
/* execute write barrier updates */ \ RET
MOVQ argtype+0(FP), DX; \
MOVQ argptr+16(FP), DI; \ // callRet copies return values back at the end of call*. This is a
MOVLQZX argsize+24(FP), CX; \ // separate function so it can allocate stack space for the arguments
MOVLQZX retoffset+28(FP), BX; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVQ DX, 0(SP); \ // arguments in registers.
MOVQ DI, 8(SP); \ TEXT callRet<>(SB), NOSPLIT, $32-0
MOVQ CX, 16(SP); \ NO_LOCAL_POINTERS
MOVQ BX, 24(SP); \ MOVQ DX, 0(SP)
CALL runtime·callwritebarrier(SB); \ MOVQ DI, 8(SP)
MOVQ SI, 16(SP)
MOVQ CX, 24(SP)
CALL runtime·reflectcallmove(SB)
RET RET
CALLFNcall32, 32) CALLFNcall32, 32)
......
...@@ -371,6 +371,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -371,6 +371,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
MOVL (DX), AX; \ MOVL (DX), AX; \
CALL AX; \ CALL AX; \
/* copy return values back */ \ /* copy return values back */ \
MOVL argtype+0(FP), DX; \
MOVL argptr+8(FP), DI; \ MOVL argptr+8(FP), DI; \
MOVL argsize+12(FP), CX; \ MOVL argsize+12(FP), CX; \
MOVL retoffset+16(FP), BX; \ MOVL retoffset+16(FP), BX; \
...@@ -378,17 +379,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -378,17 +379,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
ADDL BX, DI; \ ADDL BX, DI; \
ADDL BX, SI; \ ADDL BX, SI; \
SUBL BX, CX; \ SUBL BX, CX; \
REP;MOVSB; \ CALL callRet<>(SB); \
/* execute write barrier updates */ \ RET
MOVL argtype+0(FP), DX; \
MOVL argptr+8(FP), DI; \ // callRet copies return values back at the end of call*. This is a
MOVL argsize+12(FP), CX; \ // separate function so it can allocate stack space for the arguments
MOVL retoffset+16(FP), BX; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVL DX, 0(SP); \ // arguments in registers.
MOVL DI, 4(SP); \ TEXT callRet<>(SB), NOSPLIT, $16-0
MOVL CX, 8(SP); \ MOVL DX, 0(SP)
MOVL BX, 12(SP); \ MOVL DI, 4(SP)
CALL runtime·callwritebarrier(SB); \ MOVL SI, 8(SP)
MOVL CX, 12(SP)
CALL runtime·reflectcallmove(SB)
RET RET
CALLFNcall16, 16) CALLFNcall16, 16)
......
...@@ -406,6 +406,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -406,6 +406,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
BL (R0); \ BL (R0); \
/* copy return values back */ \ /* copy return values back */ \
MOVW argtype+0(FP), R4; \
MOVW argptr+8(FP), R0; \ MOVW argptr+8(FP), R0; \
MOVW argsize+12(FP), R2; \ MOVW argsize+12(FP), R2; \
MOVW retoffset+16(FP), R3; \ MOVW retoffset+16(FP), R3; \
...@@ -413,24 +414,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \ ...@@ -413,24 +414,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
ADD R3, R1; \ ADD R3, R1; \
ADD R3, R0; \ ADD R3, R0; \
SUB R3, R2; \ SUB R3, R2; \
loop: \ BL callRet<>(SB); \
CMP $0, R2; \ RET
B.EQ end; \
MOVBU.P 1(R1), R5; \ // callRet copies return values back at the end of call*. This is a
MOVBU.P R5, 1(R0); \ // separate function so it can allocate stack space for the arguments
SUB $1, R2, R2; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
B loop; \ // arguments in registers.
end: \ TEXT callRet<>(SB), NOSPLIT, $16-0
/* execute write barrier updates */ \ MOVW R4, 4(R13)
MOVW argtype+0(FP), R1; \ MOVW R0, 8(R13)
MOVW argptr+8(FP), R0; \ MOVW R1, 12(R13)
MOVW argsize+12(FP), R2; \ MOVW R2, 16(R13)
MOVW retoffset+16(FP), R3; \ BL runtime·reflectcallmove(SB)
MOVW R1, 4(R13); \
MOVW R0, 8(R13); \
MOVW R2, 12(R13); \
MOVW R3, 16(R13); \
BL runtime·callwritebarrier(SB); \
RET RET
CALLFNcall16, 16) CALLFNcall16, 16)
......
...@@ -335,8 +335,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0 ...@@ -335,8 +335,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
TEXT ·reflectcall(SB), NOSPLIT, $-8-32 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
MOVWU argsize+24(FP), R16 MOVWU argsize+24(FP), R16
// NOTE(rsc): No call16, because CALLFN needs four words
// of argument space to invoke callwritebarrier.
DISPATCH(runtime·call32, 32) DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64) DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128) DISPATCH(runtime·call128, 128)
...@@ -387,33 +385,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ ...@@ -387,33 +385,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
BL (R0); \ BL (R0); \
/* copy return values back */ \ /* copy return values back */ \
MOVD argtype+0(FP), R7; \
MOVD arg+16(FP), R3; \ MOVD arg+16(FP), R3; \
MOVWU n+24(FP), R4; \ MOVWU n+24(FP), R4; \
MOVWU retoffset+28(FP), R6; \ MOVWU retoffset+28(FP), R6; \
MOVD RSP, R5; \ ADD $8, RSP, R5; \
ADD R6, R5; \ ADD R6, R5; \
ADD R6, R3; \ ADD R6, R3; \
SUB R6, R4; \ SUB R6, R4; \
ADD $(8-1), R5; \ BL callRet<>(SB); \
SUB $1, R3; \ RET
ADD R5, R4; \
loop: \ // callRet copies return values back at the end of call*. This is a
CMP R5, R4; \ // separate function so it can allocate stack space for the arguments
BEQ end; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVBU.W 1(R5), R6; \ // arguments in registers.
MOVBU.W R6, 1(R3); \ TEXT callRet<>(SB), NOSPLIT, $40-0
B loop; \ MOVD R7, 8(RSP)
end: \ MOVD R3, 16(RSP)
/* execute write barrier updates */ \ MOVD R5, 24(RSP)
MOVD argtype+0(FP), R7; \ MOVD R4, 32(RSP)
MOVD arg+16(FP), R3; \ BL runtime·reflectcallmove(SB)
MOVWU n+24(FP), R4; \
MOVWU retoffset+28(FP), R6; \
MOVD R7, 8(RSP); \
MOVD R3, 16(RSP); \
MOVD R4, 24(RSP); \
MOVD R6, 32(RSP); \
BL runtime·callwritebarrier(SB); \
RET RET
// These have 8 added to make the overall frame size a multiple of 16, // These have 8 added to make the overall frame size a multiple of 16,
......
...@@ -309,8 +309,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0 ...@@ -309,8 +309,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
TEXT ·reflectcall(SB), NOSPLIT, $-8-32 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
MOVWU argsize+24(FP), R1 MOVWU argsize+24(FP), R1
// NOTE(rsc): No call16, because CALLFN needs four words
// of argument space to invoke callwritebarrier.
DISPATCH(runtime·call32, 32) DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64) DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128) DISPATCH(runtime·call128, 128)
...@@ -361,33 +359,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ ...@@ -361,33 +359,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
JAL (R4); \ JAL (R4); \
/* copy return values back */ \ /* copy return values back */ \
MOVV argtype+0(FP), R5; \
MOVV arg+16(FP), R1; \ MOVV arg+16(FP), R1; \
MOVWU n+24(FP), R2; \ MOVWU n+24(FP), R2; \
MOVWU retoffset+28(FP), R4; \ MOVWU retoffset+28(FP), R4; \
MOVV R29, R3; \ ADDV $8, R29, R3; \
ADDV R4, R3; \ ADDV R4, R3; \
ADDV R4, R1; \ ADDV R4, R1; \
SUBVU R4, R2; \ SUBVU R4, R2; \
ADDV $8, R3; \ JAL callRet<>(SB); \
ADDV R3, R2; \ RET
loop: \
BEQ R3, R2, end; \ // callRet copies return values back at the end of call*. This is a
MOVBU (R3), R4; \ // separate function so it can allocate stack space for the arguments
ADDV $1, R3; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVBU R4, (R1); \ // arguments in registers.
ADDV $1, R1; \ TEXT callRet<>(SB), NOSPLIT, $32-0
JMP loop; \ MOVV R5, 8(R29)
end: \ MOVV R1, 16(R29)
/* execute write barrier updates */ \ MOVV R3, 24(R29)
MOVV argtype+0(FP), R5; \ MOVV R2, 32(R29)
MOVV arg+16(FP), R1; \ JAL runtime·reflectcallmove(SB)
MOVWU n+24(FP), R2; \
MOVWU retoffset+28(FP), R4; \
MOVV R5, 8(R29); \
MOVV R1, 16(R29); \
MOVV R2, 24(R29); \
MOVV R4, 32(R29); \
JAL runtime·callwritebarrier(SB); \
RET RET
CALLFNcall16, 16) CALLFNcall16, 16)
......
...@@ -363,8 +363,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0 ...@@ -363,8 +363,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32 TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
MOVWZ argsize+24(FP), R3 MOVWZ argsize+24(FP), R3
// NOTE(rsc): No call16, because CALLFN needs four words
// of argument space to invoke callwritebarrier.
DISPATCH(runtime·call32, 32) DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64) DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128) DISPATCH(runtime·call128, 128)
...@@ -418,33 +416,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ ...@@ -418,33 +416,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
BL (CTR); \ BL (CTR); \
MOVD 24(R1), R2; \ MOVD 24(R1), R2; \
/* copy return values back */ \ /* copy return values back */ \
MOVD argtype+0(FP), R7; \
MOVD arg+16(FP), R3; \ MOVD arg+16(FP), R3; \
MOVWZ n+24(FP), R4; \ MOVWZ n+24(FP), R4; \
MOVWZ retoffset+28(FP), R6; \ MOVWZ retoffset+28(FP), R6; \
MOVD R1, R5; \ ADD $FIXED_FRAME, R1, R5; \
ADD R6, R5; \ ADD R6, R5; \
ADD R6, R3; \ ADD R6, R3; \
SUB R6, R4; \ SUB R6, R4; \
ADD $(FIXED_FRAME-1), R5; \ BL callRet<>(SB); \
SUB $1, R3; \ RET
ADD R5, R4; \
loop: \ // callRet copies return values back at the end of call*. This is a
CMP R5, R4; \ // separate function so it can allocate stack space for the arguments
BEQ end; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVBZU 1(R5), R6; \ // arguments in registers.
MOVBZU R6, 1(R3); \ TEXT callRet<>(SB), NOSPLIT, $32-0
BR loop; \ MOVD R7, FIXED_FRAME+0(R1)
end: \ MOVD R3, FIXED_FRAME+8(R1)
/* execute write barrier updates */ \ MOVD R5, FIXED_FRAME+16(R1)
MOVD argtype+0(FP), R7; \ MOVD R4, FIXED_FRAME+24(R1)
MOVD arg+16(FP), R3; \ BL runtime·reflectcallmove(SB)
MOVWZ n+24(FP), R4; \
MOVWZ retoffset+28(FP), R6; \
MOVD R7, FIXED_FRAME+0(R1); \
MOVD R3, FIXED_FRAME+8(R1); \
MOVD R4, FIXED_FRAME+16(R1); \
MOVD R6, FIXED_FRAME+24(R1); \
BL runtime·callwritebarrier(SB); \
RET RET
CALLFNcall32, 32) CALLFNcall32, 32)
......
...@@ -320,8 +320,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0 ...@@ -320,8 +320,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
TEXT ·reflectcall(SB), NOSPLIT, $-8-32 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
MOVWZ argsize+24(FP), R3 MOVWZ argsize+24(FP), R3
// NOTE(rsc): No call16, because CALLFN needs four words
// of argument space to invoke callwritebarrier.
DISPATCH(runtime·call32, 32) DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64) DISPATCH(runtime·call64, 64)
DISPATCH(runtime·call128, 128) DISPATCH(runtime·call128, 128)
...@@ -377,6 +375,7 @@ callFunction: \ ...@@ -377,6 +375,7 @@ callFunction: \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
BL (R8); \ BL (R8); \
/* copy return values back */ \ /* copy return values back */ \
MOVD argtype+0(FP), R7; \
MOVD arg+16(FP), R6; \ MOVD arg+16(FP), R6; \
MOVWZ n+24(FP), R5; \ MOVWZ n+24(FP), R5; \
MOVD $stack-MAXSIZE(SP), R4; \ MOVD $stack-MAXSIZE(SP), R4; \
...@@ -384,27 +383,19 @@ callFunction: \ ...@@ -384,27 +383,19 @@ callFunction: \
ADD R1, R4; \ ADD R1, R4; \
ADD R1, R6; \ ADD R1, R6; \
SUB R1, R5; \ SUB R1, R5; \
loopRets: /* copy 256 bytes at a time */ \ BL callRet<>(SB); \
CMP R5, $256; \ RET
BLT tailRets; \
SUB $256, R5; \ // callRet copies return values back at the end of call*. This is a
MVC $256, 0(R4), 0(R6); \ // separate function so it can allocate stack space for the arguments
MOVD $256(R4), R4; \ // to reflectcallmove. It does not follow the Go ABI; it expects its
MOVD $256(R6), R6; \ // arguments in registers.
BR loopRets; \ TEXT callRet<>(SB), NOSPLIT, $32-0
tailRets: /* copy remaining bytes */ \ MOVD R7, 8(R15)
CMP R5, $0; \ MOVD R6, 16(R15)
BEQ writeBarrierUpdates; \ MOVD R4, 24(R15)
SUB $1, R5; \ MOVD R5, 32(R15)
EXRL $callfnMVC<>(SB), R5; \ BL runtime·reflectcallmove(SB)
writeBarrierUpdates: \
/* execute write barrier updates */ \
MOVD argtype+0(FP), R1; \
MOVD arg+16(FP), R2; \
MOVWZ n+24(FP), R3; \
MOVWZ retoffset+28(FP), R4; \
STMG R1, R4, stack-MAXSIZE(SP); \
BL runtime·callwritebarrier(SB); \
RET RET
CALLFNcall32, 32) CALLFNcall32, 32)
......
...@@ -215,19 +215,21 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size ...@@ -215,19 +215,21 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size
heapBitsBulkBarrier(uintptr(dst), size&^(sys.PtrSize-1)) heapBitsBulkBarrier(uintptr(dst), size&^(sys.PtrSize-1))
} }
// callwritebarrier is invoked at the end of reflectcall, to execute // reflectcallmove is invoked by reflectcall to copy the return values
// write barrier operations to record the fact that a call's return // out of the stack and into the heap, invoking the necessary write
// values have just been copied to frame, starting at retoffset // barriers. dst, src, and size describe the return value area to
// and continuing to framesize. The entire frame (not just the return // copy. typ describes the entire frame (not just the return values).
// values) is described by typ. Because the copy has already // typ may be nil, which indicates write barriers are not needed.
// happened, we call writebarrierptr_nostore, and this is nosplit so //
// the copy and write barrier appear atomic to GC. // It must be nosplit and must only call nosplit functions because the
// stack map of reflectcall is wrong.
//
//go:nosplit //go:nosplit
func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) { func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr) {
if !writeBarrier.needed || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize { memmove(dst, src, size)
return if writeBarrier.needed && typ != nil && typ.kind&kindNoPointers == 0 && size >= sys.PtrSize {
heapBitsBulkBarrier(uintptr(dst), size)
} }
heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
} }
//go:nosplit //go:nosplit
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment