| // 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 | |
| // addOrSubVV generates addVV or subVV, | |
| // which do z, c = x ± y. | |
| // The caller guarantees that len(z) == len(x) == len(y). | |
| func addOrSubVV(a *Asm, name string) { | |
| f := a.Func("func " + name + "(z, x, y []Word) (c Word)") | |
| add := a.Add | |
| which := AddCarry | |
| if name == "subVV" { | |
| add = a.Sub | |
| which = SubCarry | |
| } | |
| n := f.Arg("z_len") | |
| p := f.Pipe() | |
| p.SetHint("y", HintMemOK) // allow y to be used from memory on x86 | |
| p.Start(n, 1, 4) | |
| var c Reg | |
| if !a.Arch.CarrySafeLoop { | |
| // Carry smashed by loop tests; allocate and save in register | |
| // around unrolled blocks. | |
| c = a.Reg() | |
| a.Mov(a.Imm(0), c) | |
| a.EOL("clear saved carry") | |
| p.AtUnrollStart(func() { a.RestoreCarry(c); a.Free(c) }) | |
| p.AtUnrollEnd(func() { a.Unfree(c); a.SaveCarry(c) }) | |
| } else { | |
| // Carry preserved by loop; clear now, ahead of loop | |
| // (but after Start, which may have modified it). | |
| a.ClearCarry(which) | |
| } | |
| p.Loop(func(in, out [][]Reg) { | |
| for i, x := range in[0] { | |
| y := in[1][i] | |
| add(y, x, x, SetCarry|UseCarry) | |
| } | |
| p.StoreN(in[:1]) | |
| }) | |
| p.Done() | |
| // Copy carry to output. | |
| if c.Valid() { | |
| a.ConvertCarry(which, c) | |
| } else { | |
| c = a.RegHint(HintCarry) | |
| a.SaveConvertCarry(which, c) | |
| } | |
| f.StoreArg(c, "c") | |
| a.Free(c) | |
| a.Ret() | |
| } | |