Source file src/math/big/internal/asmgen/arch.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  import (
     8  	"fmt"
     9  	"strings"
    10  )
    11  
    12  // Note: Exported fields and methods are expected to be used
    13  // by function generators (like the ones in add.go and so on).
    14  // Unexported fields and methods should not be.
    15  
    16  // An Arch defines how to generate assembly for a specific architecture.
    17  type Arch struct {
    18  	Name          string // name of architecture
    19  	Build         string // build tag
    20  	WordBits      int    // length of word in bits (32 or 64)
    21  	WordBytes     int    // length of word in bytes (4 or 8)
    22  	CarrySafeLoop bool   // whether loops preserve carry flag across iterations
    23  
    24  	// Registers.
    25  	regs        []string // usable general registers, in allocation order
    26  	reg0        string   // dedicated zero register
    27  	regCarry    string   // dedicated carry register, for systems with no hardware carry bits
    28  	regAltCarry string   // dedicated secondary carry register, for systems with no hardware carry bits
    29  	regTmp      string   // dedicated temporary register
    30  
    31  	// regShift indicates that the architecture supports
    32  	// using REG1>>REG2 and REG1<<REG2 as the first source
    33  	// operand in an arithmetic instruction. (32-bit ARM does this.)
    34  	regShift bool
    35  
    36  	// setup is called to emit any per-architecture function prologue,
    37  	// immediately after the TEXT line has been emitted.
    38  	// If setup is nil, it is taken to be a no-op.
    39  	setup func(*Func)
    40  
    41  	// hint returns the register to use for a given hint.
    42  	// Returning an empty string indicates no preference.
    43  	// If hint is nil, it is considered to return an empty string.
    44  	hint func(*Asm, Hint) string
    45  
    46  	// op3 reports whether the named opcode accepts 3 operands
    47  	// (true on most instructions on most systems, but not true of x86 instructions).
    48  	// The assembler unconditionally turns op x,z,z into op x,z.
    49  	// If op3 returns false, then the assembler will turn op x,y,z into mov y,z; op x,z.
    50  	// If op3 is nil, then all opcodes are assumed to accept 3 operands.
    51  	op3 func(name string) bool
    52  
    53  	// memOK indicates that arithmetic instructions can use memory references (like on x86)
    54  	memOK bool
    55  
    56  	// maxColumns is the default maximum number of vector columns
    57  	// to process in a single [Pipe.Loop] block.
    58  	// 0 means unlimited.
    59  	// [Pipe.SetMaxColumns] overrides this.
    60  	maxColumns int
    61  
    62  	// Instruction names.
    63  	mov   string // move (word-sized)
    64  	add   string // add with no carry involvement
    65  	adds  string // add, setting but not using carry
    66  	adc   string // add, using but not setting carry
    67  	adcs  string // add, setting and using carry
    68  	sub   string // sub with no carry involvement
    69  	subs  string // sub, setting but not using carry
    70  	sbc   string // sub, using but not setting carry
    71  	sbcs  string // sub, setting and using carry
    72  	mul   string // multiply
    73  	mulhi string // multiply producing high bits
    74  	lsh   string // left shift
    75  	lshd  string // double-width left shift
    76  	rsh   string // right shift
    77  	rshd  string // double-width right shift
    78  	and   string // bitwise and
    79  	or    string // bitwise or
    80  	xor   string // bitwise xor
    81  	neg   string // negate
    82  	rsb   string // reverse subtract
    83  	sltu  string // set less-than unsigned (dst = src2 < src1), for carry-less systems
    84  	sgtu  string // set greater-than unsigned (dst = src2 > src1), for carry-less systems
    85  	lea   string // load effective address
    86  
    87  	// addF and subF implement a.Add and a.Sub
    88  	// on systems where the situation is more complicated than
    89  	// the six basic instructions (add, adds, adcs, sub, subs, sbcs).
    90  	// They return a boolean indicating whether the operation was handled.
    91  	addF func(a *Asm, src1, src2, dst Reg, carry Carry) bool
    92  	subF func(a *Asm, src1, src2, dst Reg, carry Carry) bool
    93  
    94  	// mulF and mulWideF implement Mul and MulWide.
    95  	// They call Fatalf if the operation is unsupported.
    96  	// An architecture can set the mul field instead of mulF.
    97  	// mulWide is optional, but otherwise mulhi should be set.
    98  	mulWideF func(a *Asm, src1, src2, dstlo, dsthi Reg)
    99  
   100  	// addWords is a printf format taking src1, src2, dst
   101  	// and sets dst = WordBytes*src1+src2.
   102  	// It may modify the carry flag.
   103  	addWords string
   104  
   105  	// subCarryIsBorrow is true when the actual processor carry bit used in subtraction
   106  	// is really a “borrow” bit, meaning 1 means borrow and 0 means no borrow.
   107  	// In contrast, most systems (except x86) use a carry bit with the opposite
   108  	// meaning: 0 means a borrow happened, and 1 means it didn't.
   109  	subCarryIsBorrow bool
   110  
   111  	// Jump instruction printf formats.
   112  	// jmpZero and jmpNonZero are printf formats taking src, label
   113  	// and jump to label if src is zero / non-zero.
   114  	jmpZero    string
   115  	jmpNonZero string
   116  
   117  	// loopTop is a printf format taking src, label that should
   118  	// jump to label if src is zero, or else set up for a loop.
   119  	// If loopTop is not set, jmpZero is used.
   120  	loopTop string
   121  
   122  	// loopBottom is a printf format taking dst, label that should
   123  	// decrement dst and then jump to label if src is non-zero.
   124  	// If loopBottom is not set, a subtraction is used followed by
   125  	// use of jmpNonZero.
   126  	loopBottom string
   127  
   128  	// loopBottomNeg is like loopBottom but used in negative-index
   129  	// loops, which only happen memIndex is also set (only on 386).
   130  	// It increments dst instead of decrementing it.
   131  	loopBottomNeg string
   132  
   133  	// Indexed memory access.
   134  	// If set, memIndex returns a memory reference for a mov instruction
   135  	// addressing off(ptr)(ix*WordBytes).
   136  	// Using memIndex costs an extra register but allows the end-of-loop
   137  	// to do a single increment/decrement instead of advancing two or three pointers.
   138  	// This is particularly important on 386.
   139  	memIndex func(a *Asm, off int, ix Reg, ptr RegPtr) Reg
   140  
   141  	// Incrementing/decrementing memory access.
   142  	// loadIncN loads memory at ptr into regs, incrementing ptr by WordBytes after each reg.
   143  	// loadDecN loads memory at ptr into regs, decrementing ptr by WordBytes before each reg.
   144  	// storeIncN and storeDecN are the same, but storing from regs instead of loading into regs.
   145  	// If missing, the assembler accesses memory and advances pointers using separate instructions.
   146  	loadIncN  func(a *Asm, ptr RegPtr, regs []Reg)
   147  	loadDecN  func(a *Asm, ptr RegPtr, regs []Reg)
   148  	storeIncN func(a *Asm, ptr RegPtr, regs []Reg)
   149  	storeDecN func(a *Asm, ptr RegPtr, regs []Reg)
   150  
   151  	// options is a map from optional CPU features to functions that test for them.
   152  	// The test function should jump to label if the feature is available.
   153  	options map[Option]func(a *Asm, label string)
   154  }
   155  
   156  // HasShiftWide reports whether the Arch has working LshWide/RshWide instructions.
   157  // If not, calling them will panic.
   158  func (a *Arch) HasShiftWide() bool {
   159  	return a.lshd != ""
   160  }
   161  
   162  // A Hint is a hint about what a register will be used for,
   163  // so that an appropriate one can be selected.
   164  type Hint uint
   165  
   166  const (
   167  	HintNone       Hint = iota
   168  	HintShiftCount      // shift count (CX on x86)
   169  	HintMulSrc          // mul source operand (AX on x86)
   170  	HintMulHi           // wide mul high output (DX on x86)
   171  	HintMemOK           // a memory reference is okay
   172  	HintCarry           // carry flag
   173  	HintAltCarry        // secondary carry flag
   174  )
   175  
   176  // A Reg is an allocated register or other assembly operand.
   177  // (For example, a constant might have name "$123"
   178  // and a memory reference might have name "0(R8)".)
   179  type Reg struct{ name string }
   180  
   181  // IsImm reports whether r is an immediate value.
   182  func (r Reg) IsImm() bool { return strings.HasPrefix(r.name, "$") }
   183  
   184  // IsMem reports whether r is a memory value.
   185  func (r Reg) IsMem() bool { return strings.HasSuffix(r.name, ")") }
   186  
   187  // String returns the assembly syntax for r.
   188  func (r Reg) String() string { return r.name }
   189  
   190  // Valid reports whether is valid, meaning r is not the zero value of Reg (a register with no name).
   191  func (r Reg) Valid() bool { return r.name != "" }
   192  
   193  // A RegPtr is like a Reg but expected to hold a pointer.
   194  // The separate Go type helps keeps pointers and scalars separate and avoid mistakes;
   195  // it is okay to convert to Reg as needed to use specific routines.
   196  type RegPtr struct{ name string }
   197  
   198  // String returns the assembly syntax for r.
   199  func (r RegPtr) String() string { return r.name }
   200  
   201  // Valid reports whether is valid, meaning r is not the zero value of RegPtr (a register with no name).
   202  func (r RegPtr) Valid() bool { return r.name != "" }
   203  
   204  // mem returns a memory reference to off bytes from the pointer r.
   205  func (r *RegPtr) mem(off int) Reg { return Reg{fmt.Sprintf("%d(%s)", off, r)} }
   206  
   207  // A Carry is a flag field explaining how an instruction sets and uses the carry flags.
   208  // Different operations expect different sets of bits.
   209  // Add and Sub expect: UseCarry or 0, SetCarry, KeepCarry, or SmashCarry; and AltCarry or 0.
   210  // ClearCarry, SaveCarry, and ConvertCarry expect: AddCarry or SubCarry; and AltCarry or 0.
   211  type Carry uint
   212  
   213  const (
   214  	SetCarry   Carry = 1 << iota // sets carry
   215  	UseCarry                     // uses carry
   216  	KeepCarry                    // must preserve carry
   217  	SmashCarry                   // can modify carry or not, whatever is easiest
   218  
   219  	AltCarry // use the secondary carry flag
   220  	AddCarry // use add carry flag semantics (for ClearCarry, ConvertCarry)
   221  	SubCarry // use sub carry flag semantics (for ClearCarry, ConvertCarry)
   222  )
   223  
   224  // An Option denotes an optional CPU feature that can be tested at runtime.
   225  type Option int
   226  
   227  const (
   228  	_ Option = iota
   229  
   230  	// OptionAltCarry checks whether there is an add instruction
   231  	// that uses a secondary carry flag, so that two different sums
   232  	// can be accumulated in parallel with independent carry flags.
   233  	// Some architectures (MIPS, Loong64, RISC-V) provide this
   234  	// functionality natively, indicated by asm.Carry().Valid() being true.
   235  	OptionAltCarry
   236  )
   237  

View as plain text