Source file src/math/big/internal/asmgen/amd64.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package asmgen
     6  
     7  var ArchAMD64 = &Arch{
     8  	Name:      "amd64",
     9  	WordBits:  64,
    10  	WordBytes: 8,
    11  
    12  	regs: []string{
    13  		"BX", "SI", "DI",
    14  		"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
    15  		"AX", "DX", "CX", // last to leave available for hinted allocation
    16  	},
    17  	op3:              x86Op3,
    18  	hint:             x86Hint,
    19  	memOK:            true,
    20  	subCarryIsBorrow: true,
    21  
    22  	// Note: Not setting memIndex, because code generally runs faster
    23  	// if we avoid the use of scaled-index memory references,
    24  	// particularly in ADX instructions.
    25  
    26  	options: map[Option]func(*Asm, string){
    27  		OptionAltCarry: amd64JmpADX,
    28  	},
    29  
    30  	mov:      "MOVQ",
    31  	adds:     "ADDQ",
    32  	adcs:     "ADCQ",
    33  	subs:     "SUBQ",
    34  	sbcs:     "SBBQ",
    35  	lsh:      "SHLQ",
    36  	lshd:     "SHLQ",
    37  	rsh:      "SHRQ",
    38  	rshd:     "SHRQ",
    39  	and:      "ANDQ",
    40  	or:       "ORQ",
    41  	xor:      "XORQ",
    42  	neg:      "NEGQ",
    43  	lea:      "LEAQ",
    44  	addF:     amd64Add,
    45  	mulWideF: x86MulWide,
    46  
    47  	addWords: "LEAQ (%[2]s)(%[1]s*8), %[3]s",
    48  
    49  	jmpZero:       "TESTQ %[1]s, %[1]s; JZ %[2]s",
    50  	jmpNonZero:    "TESTQ %[1]s, %[1]s; JNZ %[2]s",
    51  	loopBottom:    "SUBQ $1, %[1]s; JNZ %[2]s",
    52  	loopBottomNeg: "ADDQ $1, %[1]s; JNZ %[2]s",
    53  }
    54  
    55  func amd64JmpADX(a *Asm, label string) {
    56  	a.Printf("\tCMPB ·hasADX(SB), $0; JNZ %s\n", label)
    57  }
    58  
    59  func amd64Add(a *Asm, src1, src2 Reg, dst Reg, carry Carry) bool {
    60  	if a.Enabled(OptionAltCarry) {
    61  		// If OptionAltCarry is enabled, the generator is emitting ADD instructions
    62  		// both with and without the AltCarry flag set; the AltCarry flag means to
    63  		// use ADOX. Otherwise we have to use ADCX.
    64  		// Using regular ADD/ADC would smash both carry flags,
    65  		// so we reject anything we can't handled with ADCX/ADOX.
    66  		if carry&UseCarry != 0 && carry&(SetCarry|SmashCarry) != 0 {
    67  			if carry&AltCarry != 0 {
    68  				a.op3("ADOXQ", src1, src2, dst)
    69  			} else {
    70  				a.op3("ADCXQ", src1, src2, dst)
    71  			}
    72  			return true
    73  		}
    74  		if carry&(SetCarry|UseCarry) == SetCarry && a.IsZero(src1) && src2 == dst {
    75  			// Clearing carry flag. Caller will add EOL comment.
    76  			a.Printf("\tTESTQ AX, AX\n")
    77  			return true
    78  		}
    79  		if carry != KeepCarry {
    80  			a.Fatalf("unsupported carry")
    81  		}
    82  	}
    83  	return false
    84  }
    85  
    86  // The x86-prefixed functions are shared with Arch386 in 386.go.
    87  
    88  func x86Op3(name string) bool {
    89  	// As far as a.op3 is concerned, there are no 3-op instructions.
    90  	// (We print instructions like MULX ourselves.)
    91  	return false
    92  }
    93  
    94  func x86Hint(a *Asm, h Hint) string {
    95  	switch h {
    96  	case HintShiftCount:
    97  		return "CX"
    98  	case HintMulSrc:
    99  		if a.Enabled(OptionAltCarry) { // using MULX
   100  			return "DX"
   101  		}
   102  		return "AX"
   103  	case HintMulHi:
   104  		if a.Enabled(OptionAltCarry) { // using MULX
   105  			return ""
   106  		}
   107  		return "DX"
   108  	}
   109  	return ""
   110  }
   111  
   112  func x86Suffix(a *Asm) string {
   113  	// Note: Not using a.Arch == Arch386 to avoid init cycle.
   114  	if a.Arch.Name == "386" {
   115  		return "L"
   116  	}
   117  	return "Q"
   118  }
   119  
   120  func x86MulWide(a *Asm, src1, src2, dstlo, dsthi Reg) {
   121  	if a.Enabled(OptionAltCarry) {
   122  		// Using ADCX/ADOX; use MULX to avoid clearing carry flag.
   123  		if src1.name != "DX" {
   124  			if src2.name != "DX" {
   125  				a.Fatalf("mul src1 or src2 must be DX")
   126  			}
   127  			src2 = src1
   128  		}
   129  		a.Printf("\tMULXQ %s, %s, %s\n", src2, dstlo, dsthi)
   130  		return
   131  	}
   132  
   133  	if src1.name != "AX" {
   134  		if src2.name != "AX" {
   135  			a.Fatalf("mulwide src1 or src2 must be AX")
   136  		}
   137  		src2 = src1
   138  	}
   139  	if dstlo.name != "AX" {
   140  		a.Fatalf("mulwide dstlo must be AX")
   141  	}
   142  	if dsthi.name != "DX" {
   143  		a.Fatalf("mulwide dsthi must be DX")
   144  	}
   145  	a.Printf("\tMUL%s %s\n", x86Suffix(a), src2)
   146  }
   147  

View as plain text