amd64.mx raw

   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: [][]byte{
  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, []byte){
  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 []byte) {
  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 []byte) 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) []byte {
  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) []byte {
 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