// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package asmgen

import (
	
	
	
	
	
	
)

// Note: Exported fields and methods are expected to be used
// by function generators (like the ones in add.go and so on).
// Unexported fields and methods should not be.

// An Asm is an assembly file being written.
type Asm struct {
	Arch     *Arch           // architecture
	out      bytes.Buffer    // output buffer
	regavail uint64          // bitmap of available registers
	enabled  map[Option]bool // enabled optional CPU features
}

// NewAsm returns a new Asm preparing assembly
// for the given architecture to be written to file.
func ( *Arch) *Asm {
	 := &Asm{Arch: , enabled: make(map[Option]bool)}
	 := ""
	if .Build != "" {
		 = " && (" + .Build + ")"
	}
	.Printf(asmHeader, )
	return 
}

// Note: Using Copyright 2025, not the current year, to avoid test failures
// on January 1 and spurious diffs when regenerating assembly.
// The generator was written in 2025; that's good enough.
// (As a matter of policy the Go project does not update copyright
// notices every year, since copyright terms are so long anyway.)

var asmHeader = `// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Code generated by 'go generate' (with ./internal/asmgen). DO NOT EDIT.

//go:build !math_big_pure_go%s

#include "textflag.h"
`

// Fatalf reports a fatal error by panicking.
// Panicking is appropriate because there is a bug in the generator,
// and panicking will show the exact source lines leading to that bug.
func ( *Asm) ( string,  ...any) {
	 := .out.String()
	 := strings.LastIndex(, "\nTEXT")
	 = [+1:]
	panic("[" + .Arch.Name + "] asmgen internal error: " + fmt.Sprintf(, ...) + "\n" + )
}

// hint returns the register name for the given hint.
func ( *Asm) ( Hint) string {
	if  == HintCarry && .Arch.regCarry != "" {
		return .Arch.regCarry
	}
	if  == HintAltCarry && .Arch.regAltCarry != "" {
		return .Arch.regAltCarry
	}
	if  == HintNone || .Arch.hint == nil {
		return ""
	}
	return .Arch.hint(, )
}

// ZR returns the zero register (the specific register guaranteed to hold the integer 0),
// or else the zero Reg (Reg{}, which has r.Valid() == false).
func ( *Asm) () Reg {
	return Reg{.Arch.reg0}
}

// tmp returns the temporary register, or else the zero Reg.
// The temporary register is one available for use implementing logical instructions
// that compile into multiple actual instructions on a given system.
// The assembler sometimes uses it for that purpose, as do we.
// Of course, if we are using it, we'd better not emit an instruction that
// will cause the assembler to smash it while we want it to be holding
// a live value. In general it is the architecture implementation's responsibility
// not to suggest the use of any such pseudo-instructions in situations
// where they would cause problems.
func ( *Asm) () Reg {
	return Reg{.Arch.regTmp}
}

// Carry returns the carry register, or else the zero Reg.
func ( *Asm) () Reg {
	return Reg{.Arch.regCarry}
}

// AltCarry returns the secondary carry register, or else the zero Reg.
func ( *Asm) () Reg {
	return Reg{.Arch.regAltCarry}
}

// Imm returns a Reg representing an immediate (constant) value.
func ( *Asm) ( int) Reg {
	if  == 0 && .Arch.reg0 != "" {
		return Reg{.Arch.reg0}
	}
	return Reg{fmt.Sprintf("$%d", )}
}

// IsZero reports whether r is a zero immediate or the zero register.
func ( *Asm) ( Reg) bool {
	return .name == "$0" || .Arch.reg0 != "" && .name == .Arch.reg0
}

// Reg allocates a new register.
func ( *Asm) () Reg {
	 := bits.TrailingZeros64(.regavail)
	if  == 64 {
		.Fatalf("out of registers")
	}
	.regavail ^= 1 << 
	return Reg{.Arch.regs[]}
}

// RegHint allocates a new register, with a hint as to its purpose.
func ( *Asm) ( Hint) Reg {
	if  := .hint();  != "" {
		 := slices.Index(.Arch.regs, )
		if  < 0 {
			return Reg{}
		}
		if .regavail&(1<<) == 0 {
			.Fatalf("hint for already allocated register %s", )
		}
		.regavail &^= 1 << 
		return Reg{}
	}
	return .Reg()
}

// Free frees a previously allocated register.
// If r is not a register (if it's an immediate or a memory reference), Free is a no-op.
func ( *Asm) ( Reg) {
	 := slices.Index(.Arch.regs, .name)
	if  < 0 {
		return
	}
	if .regavail&(1<<) != 0 {
		.Fatalf("register %s already freed", .name)
	}
	.regavail |= 1 << 
}

// Unfree reallocates a previously freed register r.
// If r is not a register (if it's an immediate or a memory reference), Unfree is a no-op.
// If r is not free for allocation, Unfree panics.
// A Free paired with Unfree can release a register for use temporarily
// but then reclaim it, such as at the end of a loop body when it must be restored.
func ( *Asm) ( Reg) {
	 := slices.Index(.Arch.regs, .name)
	if  < 0 {
		return
	}
	if .regavail&(1<<) == 0 {
		.Fatalf("register %s not free", .name)
	}
	.regavail &^= 1 << 
}

// A RegsUsed is a snapshot of which registers are allocated.
type RegsUsed struct {
	avail uint64
}

// RegsUsed returns a snapshot of which registers are currently allocated,
// which can be passed to a future call to [Asm.SetRegsUsed].
func ( *Asm) () RegsUsed {
	return RegsUsed{.regavail}
}

// SetRegsUsed sets which registers are currently allocated.
// The argument should have been returned from a previous
// call to [Asm.RegsUsed].
func ( *Asm) ( RegsUsed) {
	.regavail = .avail
}

// FreeAll frees all known registers.
func ( *Asm) () {
	.regavail = 1<<len(.Arch.regs) - 1
}

// Printf emits to the assembly output.
func ( *Asm) ( string,  ...any) {
	 := fmt.Sprintf(, ...)
	if strings.Contains(, "%!") {
		.Fatalf("printf error: %s", )
	}
	.out.WriteString()
}

// Comment emits a line comment to the assembly output.
func ( *Asm) ( string,  ...any) {
	fmt.Fprintf(&.out, "\t// %s\n", fmt.Sprintf(, ...))
}

// EOL appends an end-of-line comment to the previous line.
func ( *Asm) ( string,  ...any) {
	 := .out.Bytes()
	if len() > 0 && [len()-1] == '\n' {
		.out.Truncate(.out.Len() - 1)
	}
	.Comment(, ...)
}

// JmpEnable emits a test for the optional CPU feature that jumps to label if the feature is present.
// If JmpEnable returns false, the feature is not available on this architecture and no code was emitted.
func ( *Asm) ( Option,  string) bool {
	 := .Arch.options[]
	if  == nil {
		return false
	}
	(, )
	return true
}

// Enabled reports whether the optional CPU feature is considered
// to be enabled at this point in the assembly output.
func ( *Asm) ( Option) bool {
	return .enabled[]
}

// SetOption changes whether the optional CPU feature should be
// considered to be enabled.
func ( *Asm) ( Option,  bool) {
	.enabled[] = 
}

// op3 emits a 3-operand instruction op src1, src2, dst,
// taking care to handle 2-operand machines and also
// to simplify the printout when src2==dst.
func ( *Asm) ( string, , ,  Reg) {
	if  == "" {
		.Fatalf("missing instruction")
	}
	if  ==  {
		// src2 and dst are same; print as 2-op form.
		.Printf("\t%s %s, %s\n", , , )
	} else if .Arch.op3 != nil && !.Arch.op3() {
		// Machine does not have 3-op form for op; convert to 2-op.
		if  ==  {
			.Fatalf("implicit mov %s, %s would smash src1", , )
		}
		.Mov(, )
		.Printf("\t%s %s, %s\n", , , )
	} else {
		// Full 3-op form.
		.Printf("\t%s %s, %s, %s\n", , , , )
	}
}

// Mov emits dst = src.
func ( *Asm) (,  Reg) {
	if  !=  {
		.Printf("\t%s %s, %s\n", .Arch.mov, , )
	}
}

// AddWords emits dst = src1*WordBytes + src2.
// It does not set or use the carry flag.
func ( *Asm) ( Reg, ,  RegPtr) {
	if .Arch.addWords == "" {
		// Note: Assuming that Lsh does not clobber the carry flag.
		// Architectures where this is not true (x86) need to provide Arch.addWords.
		 := .Reg()
		.Lsh(.Imm(bits.TrailingZeros(uint(.Arch.WordBytes))), , )
		.Add(, Reg(), Reg(), KeepCarry)
		.Free()
		return
	}
	.Printf("\t"+.Arch.addWords+"\n", , , )
}

// And emits dst = src1 & src2
// It may modify the carry flag.
func ( *Asm) (, ,  Reg) {
	.op3(.Arch.and, , , )
}

// Or emits dst = src1 | src2
// It may modify the carry flag.
func ( *Asm) (, ,  Reg) {
	.op3(.Arch.or, , , )
}

// Xor emits dst = src1 ^ src2
// It may modify the carry flag.
func ( *Asm) (, ,  Reg) {
	.op3(.Arch.xor, , , )
}

// Neg emits dst = -src.
// It may modify the carry flag.
func ( *Asm) (,  Reg) {
	if .Arch.neg == "" {
		if .Arch.rsb != "" {
			.Printf("\t%s $0, %s, %s\n", .Arch.rsb, , )
			return
		}
		if .Arch.sub != "" && .Arch.reg0 != "" {
			.Printf("\t%s %s, %s, %s\n", .Arch.sub, , .Arch.reg0, )
			return
		}
		.Fatalf("missing neg")
	}
	if  ==  {
		.Printf("\t%s %s\n", .Arch.neg, )
	} else {
		.Printf("\t%s %s, %s\n", .Arch.neg, , )
	}
}

// HasRegShift reports whether the architecture can use shift expressions as operands.
func ( *Asm) () bool {
	return .Arch.regShift
}

// LshReg returns a shift-expression operand src<<shift.
// If a.HasRegShift() == false, LshReg panics.
func ( *Asm) (,  Reg) Reg {
	if !.HasRegShift() {
		.Fatalf("no reg shift")
	}
	return Reg{fmt.Sprintf("%s<<%s", , strings.TrimPrefix(.name, "$"))}
}

// Lsh emits dst = src << shift.
// It may modify the carry flag.
func ( *Asm) (, ,  Reg) {
	if  := .hint(HintShiftCount);  != "" && .name !=  && !.IsImm() {
		.Fatalf("shift count not in %s", )
	}
	if .HasRegShift() {
		.Mov(.LshReg(, ), )
		return
	}
	.op3(.Arch.lsh, , , )
}

// LshWide emits dst = src << shift with low bits shifted from adj.
// It may modify the carry flag.
func ( *Asm) (, , ,  Reg) {
	if .Arch.lshd == "" {
		.Fatalf("no lshwide on %s", .Arch.Name)
	}
	if  := .hint(HintShiftCount);  != "" && .name !=  && !.IsImm() {
		.Fatalf("shift count not in %s", )
	}
	.op3(fmt.Sprintf("%s %s,", .Arch.lshd, ), , , )
}

// RshReg returns a shift-expression operand src>>shift.
// If a.HasRegShift() == false, RshReg panics.
func ( *Asm) (,  Reg) Reg {
	if !.HasRegShift() {
		.Fatalf("no reg shift")
	}
	return Reg{fmt.Sprintf("%s>>%s", , strings.TrimPrefix(.name, "$"))}
}

// Rsh emits dst = src >> shift.
// It may modify the carry flag.
func ( *Asm) (, ,  Reg) {
	if  := .hint(HintShiftCount);  != "" && .name !=  && !.IsImm() {
		.Fatalf("shift count not in %s", )
	}
	if .HasRegShift() {
		.Mov(.RshReg(, ), )
		return
	}
	.op3(.Arch.rsh, , , )
}

// RshWide emits dst = src >> shift with high bits shifted from adj.
// It may modify the carry flag.
func ( *Asm) (, , ,  Reg) {
	if .Arch.lshd == "" {
		.Fatalf("no rshwide on %s", .Arch.Name)
	}
	if  := .hint(HintShiftCount);  != "" && .name !=  && !.IsImm() {
		.Fatalf("shift count not in %s", )
	}
	.op3(fmt.Sprintf("%s %s,", .Arch.rshd, ), , , )
}

// SLTU emits dst = src2 < src1 (0 or 1), using an unsigned comparison.
func ( *Asm) (, ,  Reg) {
	switch {
	default:
		.Fatalf("arch has no sltu/sgtu")
	case .Arch.sltu != "":
		.Printf("\t%s %s, %s, %s\n", .Arch.sltu, , , )
	case .Arch.sgtu != "":
		.Printf("\t%s %s, %s, %s\n", .Arch.sgtu, , , )
	}
}

// Add emits dst = src1+src2, with the specified carry behavior.
func ( *Asm) (, ,  Reg,  Carry) {
	switch {
	default:
		.Fatalf("unsupported carry behavior")
	case .Arch.addF != nil && .Arch.addF(, , , , ):
		// handled
	case .Arch.add != "" && ( == KeepCarry ||  == SmashCarry):
		.op3(.Arch.add, , , )
	case .Arch.adds != "" && ( == SetCarry ||  == SmashCarry):
		.op3(.Arch.adds, , , )
	case .Arch.adc != "" && ( == UseCarry ||  == UseCarry|SmashCarry):
		.op3(.Arch.adc, , , )
	case .Arch.adcs != "" && ( == UseCarry|SetCarry ||  == UseCarry|SmashCarry):
		.op3(.Arch.adcs, , , )
	case .Arch.lea != "" && ( == KeepCarry ||  == SmashCarry):
		if .IsImm() {
			.Printf("\t%s %s(%s), %s\n", .Arch.lea, .name[1:], , ) // name[1:] removes $
		} else {
			.Printf("\t%s (%s)(%s), %s\n", .Arch.lea, , , )
		}
		if  ==  {
			.EOL("ADD %s, %s", , )
		} else {
			.EOL("ADD %s, %s, %s", , , )
		}

	case .Arch.add != "" && .Arch.regCarry != "":
		// Machine has no carry flag; instead we've dedicated a register
		// and use SLTU/SGTU (set less-than/greater-than unsigned)
		// to compute the carry flags as needed.
		// For ADD x, y, z, SLTU x/y, z, c computes the carry (borrow) bit.
		// Either of x or y can be used as the second argument, provided
		// it is not aliased to z.
		// To make the output less of a wall of instructions,
		// we comment the “higher-level” operation, with ... marking
		// continued instructions implementing the operation.
		 := .Carry()
		if &AltCarry != 0 {
			 = .AltCarry()
			if !.Valid() {
				.Fatalf("alt carry not supported")
			}
			 &^= AltCarry
		}
		 := .tmp()
		if !.Valid() {
			.Fatalf("cannot simulate sub carry without regTmp")
		}
		switch  {
		default:
			.Fatalf("unsupported carry behavior")
		case UseCarry, UseCarry | SmashCarry:
			// Easy case, just add the carry afterward.
			if .IsZero() {
				// Only here to use the carry.
				.(, , , KeepCarry)
				.EOL("ADC $0, %s, %s", , )
				break
			}
			.(, , , KeepCarry)
			.EOL("ADC %s, %s, %s (cr=%s)", , , , )
			.(, , , KeepCarry)
			.EOL("...")

		case SetCarry:
			if .IsZero() &&  ==  {
				// Only here to clear the carry flag. (Caller will comment.)
				.Xor(, , )
				break
			}
			var  Reg // old is a src distinct from dst
			switch {
			case  != :
				 = 
			case  != :
				 = 
			default:
				// src1 == src2 == dst.
				// Overflows if and only if the high bit is set, so copy high bit to carry.
				.Rsh(.Imm(.Arch.WordBits-1), , )
				.EOL("ADDS %s, %s, %s (cr=%s)", , , , )
				.(, , , KeepCarry)
				.EOL("...")
				return
			}
			.(, , , KeepCarry)
			.EOL("ADDS %s, %s, %s (cr=%s)", , , , )
			.SLTU(, , ) // dst < old (one of the src) implies carry
			.EOL("...")

		case UseCarry | SetCarry:
			if .IsZero() {
				// Only here to use and then set the carry.
				// Easy since carry is not aliased to dst.
				.(, , , KeepCarry)
				.EOL("ADCS $0, %s, %s (cr=%s)", , , )
				.SLTU(, , ) // dst < cr implies carry
				.EOL("...")
				break
			}
			// General case. Need to do two different adds (src1 + src2 + cr),
			// computing carry bits for both, and add'ing them together.
			// Start with src1+src2.
			var  Reg // old is a src distinct from dst
			switch {
			case  != :
				 = 
			case  != :
				 = 
			}
			if .Valid() {
				.(, , , KeepCarry)
				.EOL("ADCS %s, %s, %s (cr=%s)", , , , )
				.SLTU(, , ) // // dst < old (one of the src) implies carry
				.EOL("...")
			} else {
				// src1 == src2 == dst, like above. Sign bit is carry bit,
				// but we copy it into tmp, not cr.
				.Rsh(.Imm(.Arch.WordBits-1), , )
				.EOL("ADCS %s, %s, %s (cr=%s)", , , , )
				.(, , , KeepCarry)
				.EOL("...")
			}
			// Add cr to dst.
			.(, , , KeepCarry)
			.EOL("...")
			.SLTU(, , ) // sum < cr implies carry
			.EOL("...")
			// Add the two carry bits (at most one can be set, because (2⁶⁴-1)+(2⁶⁴-1)+1 < 2·2⁶⁴).
			.(, , , KeepCarry)
			.EOL("...")
		}
	}
}

// Sub emits dst = src2-src1, with the specified carry behavior.
func ( *Asm) (, ,  Reg,  Carry) {
	switch {
	default:
		.Fatalf("unsupported carry behavior")
	case .Arch.subF != nil && .Arch.subF(, , , , ):
		// handled
	case .Arch.sub != "" && ( == KeepCarry ||  == SmashCarry):
		.op3(.Arch.sub, , , )
	case .Arch.subs != "" && ( == SetCarry ||  == SmashCarry):
		.op3(.Arch.subs, , , )
	case .Arch.sbc != "" && ( == UseCarry ||  == UseCarry|SmashCarry):
		.op3(.Arch.sbc, , , )
	case .Arch.sbcs != "" && ( == UseCarry|SetCarry ||  == UseCarry|SmashCarry):
		.op3(.Arch.sbcs, , , )
	case strings.HasPrefix(.name, "$") && ( == KeepCarry ||  == SmashCarry):
		// Running out of options; if this is an immediate
		// and we don't need to worry about carry semantics,
		// try adding the negation.
		if strings.HasPrefix(.name, "$-") {
			.name = "$" + .name[2:]
		} else {
			.name = "$-" + .name[1:]
		}
		.Add(, , , )

	case .Arch.sub != "" && .Arch.regCarry != "":
		// Machine has no carry flag; instead we've dedicated a register
		// and use SLTU/SGTU (set less-than/greater-than unsigned)
		// to compute the carry bits as needed.
		// For SUB x, y, z, SLTU x, y, c computes the carry (borrow) bit.
		// To make the output less of a wall of instructions,
		// we comment the “higher-level” operation, with ... marking
		// continued instructions implementing the operation.
		// Be careful! Subtract and add have different overflow behaviors,
		// so the details here are NOT the same as in Add above.
		 := .Carry()
		if &AltCarry != 0 {
			.Fatalf("alt carry not supported")
		}
		 := .tmp()
		if !.Valid() {
			.Fatalf("cannot simulate carry without regTmp")
		}
		switch  {
		default:
			.Fatalf("unsupported carry behavior")
		case UseCarry, UseCarry | SmashCarry:
			// Easy case, just subtract the carry afterward.
			if .IsZero() {
				// Only here to use the carry.
				.(, , , KeepCarry)
				.EOL("SBC $0, %s, %s", , )
				break
			}
			.(, , , KeepCarry)
			.EOL("SBC %s, %s, %s", , , )
			.(, , , KeepCarry)
			.EOL("...")

		case SetCarry:
			if .IsZero() &&  ==  {
				// Only here to clear the carry flag.
				.Xor(, , )
				break
			}
			// Compute the new carry first, in case dst is src1 or src2.
			.SLTU(, , )
			.EOL("SUBS %s, %s, %s", , , )
			.(, , , KeepCarry)
			.EOL("...")

		case UseCarry | SetCarry:
			if .IsZero() {
				// Only here to use and then set the carry.
				if  ==  {
					// Unfortunate case. Using src2==dst is common (think x -= y)
					// and also more efficient on two-operand machines (like x86),
					// but here subtracting from dst will smash src2, making it
					// impossible to recover the carry information after the SUB.
					// But we want to use the carry, so we can't compute it before
					// the SUB either. Compute into a temporary and MOV.
					.SLTU(, , )
					.EOL("SBCS $0, %s, %s", , )
					.(, , , KeepCarry)
					.EOL("...")
					.Mov(, )
					.EOL("...")
					break
				}
				.(, , , KeepCarry) // src2 not dst, so src2 preserved
				.SLTU(, , )
				break
			}
			// General case. Need to do two different subtracts (src2 - cr - src1),
			// computing carry bits for both, and add'ing them together.
			// Doing src2 - cr first frees up cr to store the carry from the sub of src1.
			.SLTU(, , )
			.EOL("SBCS %s, %s, %s", , , )
			.(, , , KeepCarry)
			.EOL("...")
			.SLTU(, , )
			.EOL("...")
			.(, , , KeepCarry)
			.EOL("...")
			.Add(, , , KeepCarry)
			.EOL("...")
		}
	}
}

// ClearCarry clears the carry flag.
// The ‘which’ parameter must be AddCarry or SubCarry to specify how the flag will be used.
// (On some systems, the sub carry's actual processor bit is inverted from its usual value.)
func ( *Asm) ( Carry) {
	 := Reg{.Arch.regs[0]} // not actually modified
	switch  & (AddCarry | SubCarry) {
	default:
		.Fatalf("bad carry")
	case AddCarry:
		.Add(.Imm(0), , , SetCarry|&AltCarry)
	case SubCarry:
		.Sub(.Imm(0), , , SetCarry|&AltCarry)
	}
	.EOL("clear carry")
}

// SaveCarry saves the carry flag into dst.
// The meaning of the bits in dst is architecture-dependent.
// The carry flag is left in an undefined state.
func ( *Asm) ( Reg) {
	// Note: As implemented here, the carry flag is actually left unmodified,
	// but we say it is in an undefined state in case that changes in the future.
	// (The SmashCarry could be changed to SetCarry if so.)
	if  := .Carry(); .Valid() {
		if  ==  {
			return // avoid EOL
		}
		.Mov(, )
	} else {
		.Sub(, , , UseCarry|SmashCarry)
	}
	.EOL("save carry")
}

// RestoreCarry restores the carry flag from src.
// src is left in an undefined state.
func ( *Asm) ( Reg) {
	if  := .Carry(); .Valid() {
		if  ==  {
			return // avoid EOL
		}
		.Mov(, )
	} else if .Arch.subCarryIsBorrow {
		.Add(, , , SetCarry)
	} else {
		// SaveCarry saved the sub carry flag with an encoding of 0, 1 -> 0, ^0.
		// Restore it by subtracting from a value less than ^0, which will carry if src != 0.
		// If there is no zero register, the SP register is guaranteed to be less than ^0.
		// (This may seem too clever, but on GOARCH=arm we have no other good options.)
		.Sub(, cmp.Or(.ZR(), Reg{"SP"}), , SetCarry)
	}
	.EOL("restore carry")
}

// ConvertCarry converts the carry flag in dst from the internal format to a 0 or 1.
// The carry flag is left in an undefined state.
func ( *Asm) ( Carry,  Reg) {
	if .Carry().Valid() { // already 0 or 1
		return
	}
	switch  {
	case AddCarry:
		if .Arch.subCarryIsBorrow {
			.Neg(, )
		} else {
			.Add(.Imm(1), , , SmashCarry)
		}
		.EOL("convert add carry")
	case SubCarry:
		.Neg(, )
		.EOL("convert sub carry")
	}
}

// SaveConvertCarry saves and converts the carry flag into dst: 0 unset, 1 set.
// The carry flag is left in an undefined state.
func ( *Asm) ( Carry,  Reg) {
	switch  {
	default:
		.Fatalf("bad carry")
	case AddCarry:
		if (.Arch.adc != "" || .Arch.adcs != "") && .ZR().Valid() {
			.Add(.ZR(), .ZR(), , UseCarry|SmashCarry)
			.EOL("save & convert add carry")
			return
		}
	case SubCarry:
		// no special cases
	}
	.SaveCarry()
	.ConvertCarry(, )
}

// MulWide emits dstlo = src1 * src2 and dsthi = (src1 * src2) >> WordBits.
// The carry flag is left in an undefined state.
// If dstlo or dsthi is the zero Reg, then those outputs are discarded.
func ( *Asm) (, , ,  Reg) {
	switch {
	default:
		.Fatalf("mulwide not available")
	case .Arch.mulWideF != nil:
		.Arch.mulWideF(, , , , )
	case .Arch.mul != "" && !.Valid():
		.op3(.Arch.mul, , , )
	case .Arch.mulhi != "" && !.Valid():
		.op3(.Arch.mulhi, , , )
	case .Arch.mul != "" && .Arch.mulhi != "" &&  !=  &&  != :
		.op3(.Arch.mul, , , )
		.op3(.Arch.mulhi, , , )
	case .Arch.mul != "" && .Arch.mulhi != "" &&  !=  &&  != :
		.op3(.Arch.mulhi, , , )
		.op3(.Arch.mul, , , )
	}
}

// Jmp jumps to the label.
func ( *Asm) ( string) {
	// Note: Some systems prefer the spelling B or BR, but all accept JMP.
	.Printf("\tJMP %s\n", )
}

// JmpZero jumps to the label if src is zero.
// It may modify the carry flag unless a.Arch.CarrySafeLoop is true.
func ( *Asm) ( Reg,  string) {
	.Printf("\t"+.Arch.jmpZero+"\n", , )
}

// JmpNonZero jumps to the label if src is non-zero.
// It may modify the carry flag unless a.Arch,CarrySafeLoop is true.
func ( *Asm) ( Reg,  string) {
	.Printf("\t"+.Arch.jmpNonZero+"\n", , )
}

// Label emits a label with the given name.
func ( *Asm) ( string) {
	.Printf("%s:\n", )
}

// Ret returns.
func ( *Asm) () {
	.Printf("\tRET\n")
}