src/internal/runtime/gc/scan/scan_amd64.s 123 lines View on github.com → Search inside
1// Copyright 2025 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45#include "go_asm.h"6#include "textflag.h"78// Test-only.9TEXT ·ExpandAVX512(SB), NOSPLIT, $0-2410	MOVQ sizeClass+0(FP), CX11	MOVQ packed+8(FP), AX1213	// Call the expander for this size class14	LEAQ ·gcExpandersAVX512(SB), BX15	MOVQ (BX)(CX*8), DX // Move to register first so -spectre works16	CALL DX1718	MOVQ unpacked+16(FP), DI // Expanded output bitmap pointer19	VMOVDQU64 Z1, 0(DI)20	VMOVDQU64 Z2, 64(DI)21	VZEROUPPER22	RET2324TEXT ·scanSpanPackedAVX512(SB), NOSPLIT, $256-4425	// Z1+Z2 = Expand the grey object mask into a grey word mask26	MOVQ objMarks+16(FP), AX27	MOVQ sizeClass+24(FP), CX28	LEAQ ·gcExpandersAVX512(SB), BX29	MOVQ (BX)(CX*8), DX // Move to register first so -spectre works30	CALL DX3132	// Z3+Z4 = Load the pointer mask33	MOVQ ptrMask+32(FP), AX34	VMOVDQU64 0(AX), Z335	VMOVDQU64 64(AX), Z43637	// Z1+Z2 = Combine the grey word mask with the pointer mask to get the scan mask38	VPANDQ Z1, Z3, Z139	VPANDQ Z2, Z4, Z24041	// Now each bit of Z1+Z2 represents one word of the span.42	// Thus, each byte covers 64 bytes of memory, which is also how43	// much we can fix in a Z register.44	//45	// We do a load/compress for each 64 byte frame.46	//47	// Z3+Z4 [128]uint8 = Number of memory words to scan in each 64 byte frame48	VPOPCNTB Z1, Z3 // Requires BITALG49	VPOPCNTB Z2, Z45051	// Store the scan mask and word counts at 0(SP) and 128(SP).52	//53	// TODO: Is it better to read directly from the registers?54	VMOVDQU64 Z1, 0(SP)55	VMOVDQU64 Z2, 64(SP)56	VMOVDQU64 Z3, 128(SP)57	VMOVDQU64 Z4, 192(SP)5859	// SI = Current address in span60	MOVQ mem+0(FP), SI61	// DI = Scan buffer base62	MOVQ bufp+8(FP), DI63	// DX = Index in scan buffer, (DI)(DX*8) = Current position in scan buffer64	MOVQ $0, DX6566	// AX = address in scan mask, 128(AX) = address in popcount67	LEAQ 0(SP), AX6869	// Loop over the 64 byte frames in this span.70	// BX = 1 past the end of the scan mask71	LEAQ 128(SP), BX7273	// Align loop to a cache line so that performance is less sensitive74	// to how this function ends up laid out in memory. This is a hot75	// function in the GC, and this is a tight loop. We don't want76	// performance to waver wildly due to unrelated changes.77	PCALIGN $6478loop:79	// CX = Fetch the mask of words to load from this frame.80	MOVBQZX 0(AX), CX81	// Skip empty frames.82	TESTQ CX, CX83	JZ skip8485	// Load the 64 byte frame.86	KMOVB CX, K187	VMOVDQA64 0(SI), Z18889	// Collect just the pointers from the greyed objects into the scan buffer,90	// i.e., copy the word indices in the mask from Z1 into contiguous memory.91	//92	// N.B. VPCOMPRESSQ supports a memory destination. Unfortunately, on93	// AMD Genoa / Zen 4, using VPCOMPRESSQ with a memory destination94	// imposes a severe performance penalty of around an order of magnitude95	// compared to a register destination.96	//97	// This workaround is unfortunate on other microarchitectures, where a98	// memory destination is slightly faster than adding an additional move99	// instruction, but no where near an order of magnitude. It would be100	// nice to have a Genoa-only variant here.101	//102	// AMD Turin / Zen 5 fixes this issue.103	//104	// See105	// https://lemire.me/blog/2025/02/14/avx-512-gotcha-avoid-compressing-words-to-memory-with-amd-zen-4-processors/.106	VPCOMPRESSQ Z1, K1, Z2107	VMOVDQU64 Z2, (DI)(DX*8)108109	// Advance the scan buffer position by the number of pointers.110	MOVBQZX 128(AX), CX111	ADDQ CX, DX112113skip:114	ADDQ $64, SI115	ADDQ $1, AX116	CMPQ AX, BX117	JB loop118119end:120	MOVL DX, count+40(FP)121	VZEROUPPER122	RET

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.