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 "bytes"
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 []byte // name of architecture
19 Build []byte // 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 [][]byte // usable general registers, in allocation order
26 reg0 []byte // dedicated zero register
27 regCarry []byte // dedicated carry register, for systems with no hardware carry bits
28 regAltCarry []byte // dedicated secondary carry register, for systems with no hardware carry bits
29 regTmp []byte // 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) []byte
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 []byte) 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 []byte // move (word-sized)
64 add []byte // add with no carry involvement
65 adds []byte // add, setting but not using carry
66 adc []byte // add, using but not setting carry
67 adcs []byte // add, setting and using carry
68 sub []byte // sub with no carry involvement
69 subs []byte // sub, setting but not using carry
70 sbc []byte // sub, using but not setting carry
71 sbcs []byte // sub, setting and using carry
72 mul []byte // multiply
73 mulhi []byte // multiply producing high bits
74 lsh []byte // left shift
75 lshd []byte // double-width left shift
76 rsh []byte // right shift
77 rshd []byte // double-width right shift
78 and []byte // bitwise and
79 or []byte // bitwise or
80 xor []byte // bitwise xor
81 neg []byte // negate
82 rsb []byte // reverse subtract
83 sltu []byte // set less-than unsigned (dst = src2 < src1), for carry-less systems
84 sgtu []byte // set greater-than unsigned (dst = src2 > src1), for carry-less systems
85 lea []byte // 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 []byte
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 []byte
115 jmpNonZero []byte
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 []byte
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 []byte
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 []byte
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 []byte)
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 []byte }
180 181 // IsImm reports whether r is an immediate value.
182 func (r Reg) IsImm() bool { return bytes.HasPrefix(r.name, "$") }
183 184 // IsMem reports whether r is a memory value.
185 func (r Reg) IsMem() bool { return bytes.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 []byte }
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