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