From a6ae84dfdf8a1dcb993f0b9cae97c63c8f0dbe35 Mon Sep 17 00:00:00 2001 From: limeidan Date: Thu, 27 Feb 2025 09:46:37 +0800 Subject: [PATCH] [Feature]optimize the packages runtime, math/big, internal/bytealg on LoongArch64 to#IBP9MU - add internal linker support on loong64 - optimize the internal/bytealg package on loong64 - optimize the math/big package on loong64 - add new instructions support on loong64 - optimize memory operation function of runtime on loong64 Project: TC2024110201 Signed-off-by: limeidan --- ...l-add-support-for-internal-linking-o.patch | 457 ++++ ...l-platform-enable-internal-linking-f.patch | 83 + ...time-enable-race-detector-on-loong64.patch | 626 +++++ ...n-register-ABI-fallback-path-for-rac.patch | 111 + ...-loong64-remove-unused-register-alia.patch | 27 + ...-optimize-IndexByte-and-IndexByteStr.patch | 160 ++ ...-optimize-memequal-and-memequal_varl.patch | 142 ++ ...-optimize-Index-and-IndexString-func.patch | 299 +++ ...-optimize-Count-and-CountString-func.patch | 153 ++ ...-adjust-the-format-of-assembly-files.patch | 583 +++++ ...j-loong64-optimize-immediate-loading.patch | 776 ++++++ ...-optimize-addVV-function-for-loong64.patch | 85 + ...-optimize-addVW-function-for-loong64.patch | 82 + ...-optimize-subVV-function-for-loong64.patch | 77 + ...-optimize-subVW-function-for-loong64.patch | 82 + ...-optimize-shlVU-function-for-loong64.patch | 92 + ...-optimize-shrVU-function-for-loong64.patch | 92 + ...imize-mulAddVWW-function-for-loong64.patch | 77 + ...imize-addMulVVW-function-for-loong64.patch | 77 + ...-constant-shift-with-extension-on-lo.patch | 376 +++ ...-the-matching-instructions-inside-pl.patch | 31 + ...mize-shifts-of-int32-and-uint32-on-l.patch | 1064 ++++++++ ...le-simplify-bounded-shift-on-loong64.patch | 2206 +++++++++++++++++ ...nternal-on-syscall-and-other-sys.stu.patch | 505 ++++ ...ect-memory-barrier-in-exitThread-fun.patch | 34 + ...-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch | 410 +++ ...-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch | 207 ++ ...-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch | 181 ++ ...-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch | 599 +++++ ...-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch | 166 ++ ...-loong64-add-V-XV-NEG-B-H-W-V-instru.patch | 135 + ...-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch | 235 ++ ...-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch | 283 +++ ...-loong64-add-V-XV-BITCLR-BITSET-BITR.patch | 341 +++ ...ha20-add-loong64-SIMD-implementation.patch | 490 ++++ ...alg-optimize-Count-String-in-loong64.patch | 268 ++ ...-cmd-asm-reclassify-32-bit-immediate.patch | 690 ++++++ ...poly1305-implement-function-update-i.patch | 298 +++ ...-the-implementation-of-memclrNoHeapP.patch | 374 +++ ...-the-implementation-of-atomic.-Or-An.patch | 75 + ...-loong64-add-F-MAXA-MINA-.-S-D-instr.patch | 107 + ...unc-archExp-and-archExp2-in-assembly.patch | 358 +++ ...-func-archLog-in-assembly-on-loong64.patch | 217 ++ ...work-allow-a-bunch-of-loong64-specif.patch | 126 + golang.spec | 61 +- race_linux_loong64.syso | Bin 645456 -> 1412368 bytes 46 files changed, 13914 insertions(+), 4 deletions(-) create mode 100644 0001-cmd-link-internal-add-support-for-internal-linking-o.patch create mode 100644 0002-cmd-dist-internal-platform-enable-internal-linking-f.patch create mode 100644 0003-cmd-runtime-enable-race-detector-on-loong64.patch create mode 100644 0004-runtime-delete-on-register-ABI-fallback-path-for-rac.patch create mode 100644 0005-cmd-internal-obj-loong64-remove-unused-register-alia.patch create mode 100644 0006-internal-bytealg-optimize-IndexByte-and-IndexByteStr.patch create mode 100644 0007-internal-bytealg-optimize-memequal-and-memequal_varl.patch create mode 100644 0008-internal-bytealg-optimize-Index-and-IndexString-func.patch create mode 100644 0009-internal-bytealg-optimize-Count-and-CountString-func.patch create mode 100644 0010-internal-bytealg-adjust-the-format-of-assembly-files.patch create mode 100644 0011-cmd-internal-obj-loong64-optimize-immediate-loading.patch create mode 100644 0012-math-big-optimize-addVV-function-for-loong64.patch create mode 100644 0013-math-big-optimize-addVW-function-for-loong64.patch create mode 100644 0014-math-big-optimize-subVV-function-for-loong64.patch create mode 100644 0015-math-big-optimize-subVW-function-for-loong64.patch create mode 100644 0016-math-big-optimize-shlVU-function-for-loong64.patch create mode 100644 0017-math-big-optimize-shrVU-function-for-loong64.patch create mode 100644 0018-math-big-optimize-mulAddVWW-function-for-loong64.patch create mode 100644 0019-math-big-optimize-addMulVVW-function-for-loong64.patch create mode 100644 0020-cmd-compile-fold-constant-shift-with-extension-on-lo.patch create mode 100644 0021-test-codegen-fix-the-matching-instructions-inside-pl.patch create mode 100644 0022-cmd-compile-optimize-shifts-of-int32-and-uint32-on-l.patch create mode 100644 0023-cmd-compile-simplify-bounded-shift-on-loong64.patch create mode 100644 0024-runtime-use-ABIInternal-on-syscall-and-other-sys.stu.patch create mode 100644 0025-runtime-use-correct-memory-barrier-in-exitThread-fun.patch create mode 100644 0026-cmd-internal-obj-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch create mode 100644 0027-cmd-internal-obj-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch create mode 100644 0028-cmd-internal-obj-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch create mode 100644 0029-cmd-internal-obj-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch create mode 100644 0030-cmd-internal-obj-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch create mode 100644 0031-cmd-internal-obj-loong64-add-V-XV-NEG-B-H-W-V-instru.patch create mode 100644 0032-cmd-internal-obj-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch create mode 100644 0033-cmd-internal-obj-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch create mode 100644 0034-cmd-internal-obj-loong64-add-V-XV-BITCLR-BITSET-BITR.patch create mode 100644 0035-crypto-chacha20-add-loong64-SIMD-implementation.patch create mode 100644 0036-internal-bytealg-optimize-Count-String-in-loong64.patch create mode 100644 0037-cmd-internal-obj-cmd-asm-reclassify-32-bit-immediate.patch create mode 100644 0038-crypto-internal-poly1305-implement-function-update-i.patch create mode 100644 0039-runtime-optimize-the-implementation-of-memclrNoHeapP.patch create mode 100644 0040-runtime-race-add-the-implementation-of-atomic.-Or-An.patch create mode 100644 0041-cmd-internal-obj-loong64-add-F-MAXA-MINA-.-S-D-instr.patch create mode 100644 0042-math-implement-func-archExp-and-archExp2-in-assembly.patch create mode 100644 0043-math-implement-func-archLog-in-assembly-on-loong64.patch create mode 100644 0044-cmd-go-internal-work-allow-a-bunch-of-loong64-specif.patch diff --git a/0001-cmd-link-internal-add-support-for-internal-linking-o.patch b/0001-cmd-link-internal-add-support-for-internal-linking-o.patch new file mode 100644 index 0000000..8f49bc5 --- /dev/null +++ b/0001-cmd-link-internal-add-support-for-internal-linking-o.patch @@ -0,0 +1,457 @@ +From 2730907e506ac1fdcc25fbb263df89a03c12b309 Mon Sep 17 00:00:00 2001 +From: limeidan +Date: Mon, 9 Oct 2023 17:31:14 +0800 +Subject: [PATCH 01/44] cmd/link/internal: add support for internal linking on + loong64 + +Change-Id: Ic0d36f27481ac707d04aaf7001f26061e510dd8f +--- + src/cmd/link/internal/loadelf/ldelf.go | 24 ++ + src/cmd/link/internal/loong64/asm.go | 356 ++++++++++++++++++++++++- + 2 files changed, 375 insertions(+), 5 deletions(-) + +diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go +index e0363b5535..be14cc3bb2 100644 +--- a/src/cmd/link/internal/loadelf/ldelf.go ++++ b/src/cmd/link/internal/loadelf/ldelf.go +@@ -602,6 +602,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, + // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809 + continue + } ++ ++ if arch.Family == sys.Loong64 && (strings.HasPrefix(elfsym.name, ".L") || elfsym.name == "L0\001") { ++ // Symbols generated by the relax feature of gcc and binutils on loong64. ++ continue ++ } + } + + if strings.HasPrefix(elfsym.name, ".Linfo_string") { +@@ -682,6 +687,12 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, + l.SetAttrOnList(s, true) + textp = append(textp, s) + for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) { ++ if arch.Family == sys.Loong64 && (strings.HasPrefix(l.SymName(ss), ".L") || l.SymName(ss) == "L0\001") { ++ // Symbols generated by the relax feature of gcc and binutils on loong64. ++ // We ignore them here because there are too many symbols of this type, ++ // resulting in insufficient space in findfunctable. ++ continue ++ } + if l.AttrOnList(ss) { + return errorf("symbol %s listed multiple times", + l.SymName(ss)) +@@ -1018,7 +1029,14 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) { + MIPS64 | uint32(elf.R_MIPS_PC32)<<16: + return 4, 4, nil + ++ // These are informational annotations to assist linker optimizations. ++ case LOONG64 | uint32(elf.R_LARCH_ALIGN)<<16, ++ LOONG64 | uint32(elf.R_LARCH_RELAX)<<16: ++ return 0, 0, nil ++ + case LOONG64 | uint32(elf.R_LARCH_ADD8)<<16, ++ LOONG64 | uint32(elf.R_LARCH_ADD6)<<16, ++ LOONG64 | uint32(elf.R_LARCH_SUB6)<<16, + LOONG64 | uint32(elf.R_LARCH_SUB8)<<16: + return 1, 1, nil + +@@ -1032,7 +1050,13 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) { + LOONG64 | uint32(elf.R_LARCH_ADD32)<<16, + LOONG64 | uint32(elf.R_LARCH_SUB24)<<16, + LOONG64 | uint32(elf.R_LARCH_SUB32)<<16, ++ LOONG64 | uint32(elf.R_LARCH_B16)<<16, ++ LOONG64 | uint32(elf.R_LARCH_B21)<<16, + LOONG64 | uint32(elf.R_LARCH_B26)<<16, ++ LOONG64 | uint32(elf.R_LARCH_PCALA_HI20)<<16, ++ LOONG64 | uint32(elf.R_LARCH_PCALA_LO12)<<16, ++ LOONG64 | uint32(elf.R_LARCH_GOT_PC_HI20)<<16, ++ LOONG64 | uint32(elf.R_LARCH_GOT_PC_LO12)<<16, + LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16: + return 4, 4, nil + +diff --git a/src/cmd/link/internal/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go +index 2e69594f92..3a83f1a5ad 100644 +--- a/src/cmd/link/internal/loong64/asm.go ++++ b/src/cmd/link/internal/loong64/asm.go +@@ -58,10 +58,328 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) { + } + + func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { +- log.Fatalf("adddynrel not implemented") ++ targ := r.Sym() ++ var targType sym.SymKind ++ if targ != 0 { ++ targType = ldr.SymType(targ) ++ } ++ ++ switch r.Type() { ++ default: ++ if r.Type() >= objabi.ElfRelocOffset { ++ ldr.Errorf(s, "adddynrel: unexpected reloction type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) ++ return false ++ } ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_64): ++ if targType == sym.SDYNIMPORT { ++ ldr.Errorf(s, "unexpected R_LARCH_64 relocation for dynamic symbol %s", ldr.SymName(targ)) ++ } ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocType(rIdx, objabi.R_ADDR) ++ if target.IsPIE() && target.IsInternal() { ++ // For internal linking PIE, this R_ADDR relocation cannot ++ // be resolved statically. We need to generate a dynamic ++ // relocation. Let the code below handle it. ++ break ++ } ++ return true ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B26): ++ if targType == sym.SDYNIMPORT { ++ addpltsym(target, ldr, syms, targ) ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocSym(rIdx, syms.PLT) ++ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) ++ } ++ if targType == 0 || targType == sym.SXREF { ++ ldr.Errorf(s, "unknown symbol %s in callloong64", ldr.SymName(targ)) ++ } ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocType(rIdx, objabi.R_CALLLOONG64) ++ return true ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_HI20), ++ objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_LO12): ++ if targType != sym.SDYNIMPORT { ++ // TODO: turn LDR of GOT entry into ADR of symbol itself ++ } ++ ++ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64)) ++ su := ldr.MakeSymbolUpdater(s) ++ if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_GOT_PC_HI20) { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI) ++ } else { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO) ++ } ++ su.SetRelocSym(rIdx, syms.GOT) ++ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) ++ return true ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA_HI20), ++ objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA_LO12): ++ if targType == sym.SDYNIMPORT { ++ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) ++ } ++ if targType == 0 || targType == sym.SXREF { ++ ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ)) ++ } ++ ++ su := ldr.MakeSymbolUpdater(s) ++ if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_PCALA_HI20) { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI) ++ } else { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO) ++ } ++ return true ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_ADD64), ++ objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_SUB64): ++ su := ldr.MakeSymbolUpdater(s) ++ if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_ADD64) { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADD64) ++ } else { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_SUB64) ++ } ++ return true ++ ++ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B16), ++ objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B21): ++ if targType == sym.SDYNIMPORT { ++ addpltsym(target, ldr, syms, targ) ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocSym(rIdx, syms.PLT) ++ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) ++ } ++ if targType == 0 || targType == sym.SXREF { ++ ldr.Errorf(s, "unknown symbol %s in R_JMPxxLOONG64", ldr.SymName(targ)) ++ } ++ su := ldr.MakeSymbolUpdater(s) ++ if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_B16) { ++ su.SetRelocType(rIdx, objabi.R_JMP16LOONG64) ++ } else { ++ su.SetRelocType(rIdx, objabi.R_JMP21LOONG64) ++ } ++ return true ++ } ++ ++ relocs := ldr.Relocs(s) ++ r = relocs.At(rIdx) ++ ++ switch r.Type() { ++ case objabi.R_CALLLOONG64: ++ if targType != sym.SDYNIMPORT { ++ return true ++ } ++ if target.IsExternal() { ++ return true ++ } ++ ++ // Internal linking. ++ if r.Add() != 0 { ++ ldr.Errorf(s, "PLT call with no-zero addend (%v)", r.Add()) ++ } ++ ++ // Build a PLT entry and change the relocation target to that entry. ++ addpltsym(target, ldr, syms, targ) ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocSym(rIdx, syms.PLT) ++ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) ++ return true ++ ++ case objabi.R_ADDR: ++ if ldr.SymType(s) == sym.STEXT && target.IsElf() { ++ // The code is asking for the address of an external ++ // function. We provide it with the address of the ++ // correspondent GOT symbol. ++ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64)) ++ su := ldr.MakeSymbolUpdater(s) ++ su.SetRelocSym(rIdx, syms.GOT) ++ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) ++ return true ++ } ++ ++ // Process dynamic relocations for the data sections. ++ if target.IsPIE() && target.IsInternal() { ++ // When internally linking, generate dynamic relocations ++ // for all typical R_ADDR relocations. The exception ++ // are those R_ADDR that are created as part of generating ++ // the dynamic relocations and must be resolved statically. ++ // ++ // There are three phases relevant to understanding this: ++ // ++ // dodata() // we are here ++ // address() // symbol address assignment ++ // reloc() // resolution of static R_ADDR relocs ++ // ++ // At this point symbol addresses have not been ++ // assigned yet (as the final size of the .rela section ++ // will affect the addresses), and so we cannot write ++ // the Elf64_Rela.r_offset now. Instead we delay it ++ // until after the 'address' phase of the linker is ++ // complete. We do this via Addaddrplus, which creates ++ // a new R_ADDR relocation which will be resolved in ++ // the 'reloc' phase. ++ // ++ // These synthetic static R_ADDR relocs must be skipped ++ // now, or else we will be caught in an infinite loop ++ // of generating synthetic relocs for our synthetic ++ // relocs. ++ // ++ // Furthermore, the rela sections contain dynamic ++ // relocations with R_ADDR relocations on ++ // Elf64_Rela.r_offset. This field should contain the ++ // symbol offset as determined by reloc(), not the ++ // final dynamically linked address as a dynamic ++ // relocation would provide. ++ switch ldr.SymName(s) { ++ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": ++ return false ++ } ++ } else { ++ // Either internally linking a static executable, ++ // in which case we can resolve these relocations ++ // statically in the 'reloc' phase, or externally ++ // linking, in which case the relocation will be ++ // prepared in the 'reloc' phase and passed to the ++ // external linker in the 'asmb' phase. ++ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA { ++ break ++ } ++ } ++ ++ if target.IsElf() { ++ // Generate R_LARCH_RELATIVE relocations for best ++ // efficiency in the dynamic linker. ++ // ++ // As noted above, symbol addresses have not been ++ // assigned yet, so we can't generate the final reloc ++ // entry yet. We ultimately want: ++ // ++ // r_offset = s + r.Off ++ // r_info = R_LARCH_RELATIVE ++ // r_addend = targ + r.Add ++ // ++ // The dynamic linker will set *offset = base address + ++ // addend. ++ // ++ // AddAddrPlus is used for r_offset and r_addend to ++ // generate new R_ADDR relocations that will update ++ // these fields in the 'reloc' phase. ++ rela := ldr.MakeSymbolUpdater(syms.Rela) ++ rela.AddAddrPlus(target.Arch, s, int64(r.Off())) ++ if r.Siz() == 8 { ++ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_LARCH_RELATIVE))) ++ } else { ++ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) ++ } ++ rela.AddAddrPlus(target.Arch, targ, int64(r.Add())) ++ return true ++ } ++ ++ case objabi.R_LOONG64_GOT_HI, ++ objabi.R_LOONG64_GOT_LO: ++ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64)) ++ su := ldr.MakeSymbolUpdater(s) ++ if r.Type() == objabi.R_LOONG64_GOT_HI { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI) ++ } else { ++ su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO) ++ } ++ su.SetRelocSym(rIdx, syms.GOT) ++ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) ++ return true ++ } + return false + } + ++func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { ++ if plt.Size() == 0 { ++ // pcalau12i $r14, imm ++ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_HI, 4) ++ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x1a00000e) ++ ++ // sub.d $r13, $r13, $r15 ++ plt.AddUint32(ctxt.Arch, 0x0011bdad) ++ ++ // ld.d $r15, $r14, imm ++ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_LO, 4) ++ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x28c001cf) ++ ++ // addi.d $r13, $r13, -40 ++ plt.AddUint32(ctxt.Arch, 0x02ff61ad) ++ ++ // addi.d $r12, $r14, imm ++ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_LO, 4) ++ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x2c001cc) ++ ++ // srli.d $r13, $r13, 1 ++ plt.AddUint32(ctxt.Arch, 0x004505ad) ++ ++ // ld.d $r12, $r12, 8 ++ plt.AddUint32(ctxt.Arch, 0x28c0218c) ++ ++ // jirl $r0, $r15, 0 ++ plt.AddUint32(ctxt.Arch, 0x4c0001e0) ++ ++ // check gotplt.size == 0 ++ if gotplt.Size() != 0 { ++ ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning") ++ } ++ ++ gotplt.AddUint64(ctxt.Arch, 0) ++ gotplt.AddUint64(ctxt.Arch, 0) ++ } ++} ++ ++func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { ++ if ldr.SymPlt(s) >= 0 { ++ return ++ } ++ ++ ld.Adddynsym(ldr, target, syms, s) ++ ++ if target.IsElf() { ++ plt := ldr.MakeSymbolUpdater(syms.PLT) ++ gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT) ++ rela := ldr.MakeSymbolUpdater(syms.RelaPLT) ++ if plt.Size() == 0 { ++ panic("plt is not set up") ++ } ++ ++ // pcalau12i $r15, imm ++ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) ++ plt.SetUint32(target.Arch, plt.Size()-4, 0x1a00000f) ++ relocs := plt.Relocs() ++ plt.SetRelocType(relocs.Count()-1, objabi.R_LOONG64_ADDR_HI) ++ ++ // ld.d $r15, $r15, imm ++ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) ++ plt.SetUint32(target.Arch, plt.Size()-4, 0x28c001ef) ++ relocs = plt.Relocs() ++ plt.SetRelocType(relocs.Count()-1, objabi.R_LOONG64_ADDR_LO) ++ ++ // pcaddu12i $r13, 0 ++ plt.AddUint32(target.Arch, 0x1c00000d) ++ ++ // jirl r0, r15, 0 ++ plt.AddUint32(target.Arch, 0x4c0001e0) ++ ++ // add to got.plt: pointer to plt[0] ++ gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0) ++ ++ // rela ++ rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8) ++ sDynid := ldr.SymDynid(s) ++ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_LARCH_JUMP_SLOT))) ++ rela.AddUint64(target.Arch, 0) ++ ++ ldr.SetPlt(s, int32(plt.Size()-16)) ++ } else { ++ ldr.Errorf(s, "addpltsym: unsupport binary format") ++ } ++} ++ + func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + // loong64 ELF relocation (endian neutral) + // offset uint64 +@@ -134,10 +452,6 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, + return true + } + +-func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { +- return +-} +- + func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false + } +@@ -197,6 +511,38 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade + pc := ldr.SymValue(s) + int64(r.Off()) + t := ldr.SymAddr(rs) + r.Add() - pc + return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), noExtReloc, isOk ++ ++ case objabi.R_JMP16LOONG64, ++ objabi.R_JMP21LOONG64: ++ pc := ldr.SymValue(s) + int64(r.Off()) ++ t := ldr.SymAddr(rs) + r.Add() - pc ++ if r.Type() == objabi.R_JMP16LOONG64 { ++ return int64(val&0xfc0003ff | (((t >> 2) & 0xffff) << 10)), noExtReloc, isOk ++ } ++ return int64(val&0xfc0003e0 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x1f0000) >> 16)), noExtReloc, isOk ++ ++ case objabi.R_LOONG64_TLS_IE_HI, ++ objabi.R_LOONG64_TLS_IE_LO: ++ if target.IsPIE() && target.IsElf() { ++ if !target.IsLinux() { ++ ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType) ++ } ++ t := ldr.SymAddr(rs) + r.Add() ++ if r.Type() == objabi.R_LOONG64_TLS_IE_HI { ++ // pcalau12i -> lu12i.w ++ return (0x14000000 | (val & 0x1f) | ((t >> 12) << 5)), noExtReloc, isOk ++ } ++ // ld.d -> ori ++ return (0x03800000 | (val & 0x3ff) | ((t & 0xfff) << 10)), noExtReloc, isOk ++ } else { ++ log.Fatalf("cannot handle R_LOONG64_TLS_IE_x (sym %s) when linking internally", ldr.SymName(rs)) ++ } ++ ++ case objabi.R_LOONG64_ADD64, objabi.R_LOONG64_SUB64: ++ if r.Type() == objabi.R_LOONG64_ADD64 { ++ return int64(val + ldr.SymAddr(rs) + r.Add()), noExtReloc, isOk ++ } ++ return int64(val - (ldr.SymAddr(rs) + r.Add())), noExtReloc, isOk + } + + return val, 0, false +-- +2.38.1 + diff --git a/0002-cmd-dist-internal-platform-enable-internal-linking-f.patch b/0002-cmd-dist-internal-platform-enable-internal-linking-f.patch new file mode 100644 index 0000000..8f2e4c1 --- /dev/null +++ b/0002-cmd-dist-internal-platform-enable-internal-linking-f.patch @@ -0,0 +1,83 @@ +From d404dccc7f089ddbd81b95c3d97f19acc6cb0329 Mon Sep 17 00:00:00 2001 +From: limeidan +Date: Mon, 9 Oct 2023 17:32:03 +0800 +Subject: [PATCH 02/44] cmd/dist, internal/platform: enable internal linking + feature and test on loong64 + +Change-Id: Ifea676e9eb44281465832fc4050f6286e50f4543 +--- + src/cmd/dist/build.go | 4 +++- + src/cmd/dist/test.go | 4 ++-- + src/internal/platform/supported.go | 6 ++++-- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go +index 1f467647f5..b71d6c393e 100644 +--- a/src/cmd/dist/build.go ++++ b/src/cmd/dist/build.go +@@ -624,10 +624,12 @@ func setup() { + func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool { + if cgoEnabled { + switch goarch { +- case "loong64", "mips", "mipsle", "mips64", "mips64le": ++ case "mips", "mipsle", "mips64", "mips64le": + // Internally linking cgo is incomplete on some architectures. + // https://golang.org/issue/14449 + return true ++ case "loong64": ++ return false + case "arm64": + if goos == "windows" { + // windows/arm64 internal linking is not implemented. +diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go +index 0c992118f4..9728ef29cb 100644 +--- a/src/cmd/dist/test.go ++++ b/src/cmd/dist/test.go +@@ -1164,7 +1164,7 @@ func (t *tester) internalLink() bool { + // Internally linking cgo is incomplete on some architectures. + // https://golang.org/issue/10373 + // https://golang.org/issue/14449 +- if goarch == "loong64" || goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" { ++ if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" { + return false + } + if goos == "aix" { +@@ -1185,7 +1185,7 @@ func (t *tester) internalLinkPIE() bool { + } + switch goos + "-" + goarch { + case "darwin-amd64", "darwin-arm64", +- "linux-amd64", "linux-arm64", "linux-ppc64le", ++ "linux-amd64", "linux-arm64", "linux-loong64", "linux-ppc64le", + "android-arm64", + "windows-amd64", "windows-386", "windows-arm": + return true +diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go +index e864c37d68..79ed6d4b1c 100644 +--- a/src/internal/platform/supported.go ++++ b/src/internal/platform/supported.go +@@ -85,10 +85,12 @@ func FuzzInstrumented(goos, goarch string) bool { + func MustLinkExternal(goos, goarch string, withCgo bool) bool { + if withCgo { + switch goarch { +- case "loong64", "mips", "mipsle", "mips64", "mips64le": ++ case "mips", "mipsle", "mips64", "mips64le": + // Internally linking cgo is incomplete on some architectures. + // https://go.dev/issue/14449 + return true ++ case "loong64": ++ return false + case "arm64": + if goos == "windows" { + // windows/arm64 internal linking is not implemented. +@@ -225,7 +227,7 @@ func InternalLinkPIESupported(goos, goarch string) bool { + switch goos + "/" + goarch { + case "android/arm64", + "darwin/amd64", "darwin/arm64", +- "linux/amd64", "linux/arm64", "linux/ppc64le", ++ "linux/amd64", "linux/arm64", "linux/loong64", "linux/ppc64le", + "windows/386", "windows/amd64", "windows/arm", "windows/arm64": + return true + } +-- +2.38.1 + diff --git a/0003-cmd-runtime-enable-race-detector-on-loong64.patch b/0003-cmd-runtime-enable-race-detector-on-loong64.patch new file mode 100644 index 0000000..0d61dcc --- /dev/null +++ b/0003-cmd-runtime-enable-race-detector-on-loong64.patch @@ -0,0 +1,626 @@ +From f84142ce620b086cc90f728861a76e5066c22ed9 Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Sat, 19 Aug 2023 09:22:34 +0800 +Subject: [PATCH 03/44] cmd,runtime: enable race detector on loong64 + +The race feature depends on llvm. And support for building the tsan library on +linux/loong64 has been added in this patch [1], which has been merged into the +branch main and will be supported in the upcoming llvm18. + +[1]: https://github.com/llvm/llvm-project/pull/72819 + +Co-authored-by: Xiaolin Zhao +Change-Id: If389318215476890295ed771297c6c088cfc84b3 +--- + src/cmd/dist/test.go | 2 +- + src/internal/platform/supported.go | 2 +- + src/race.bash | 3 +- + src/runtime/asm_loong64.s | 1 + + src/runtime/race/README | 3 +- + src/runtime/race/race.go | 2 +- + src/runtime/race_loong64.s | 509 +++++++++++++++++++++++ + 8 files changed, 517 insertions(+), 5 deletions(-) + create mode 100644 src/runtime/race_loong64.s + +diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go +index 9728ef29cb..044268ada0 100644 +--- a/src/cmd/dist/test.go ++++ b/src/cmd/dist/test.go +@@ -1674,7 +1674,7 @@ func (t *tester) makeGOROOTUnwritable() (undo func()) { + func raceDetectorSupported(goos, goarch string) bool { + switch goos { + case "linux": +- return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" ++ return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" || goarch == "loong64" + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "windows": +diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go +index 79ed6d4b1c..52cad096cb 100644 +--- a/src/internal/platform/supported.go ++++ b/src/internal/platform/supported.go +@@ -23,7 +23,7 @@ func (p OSArch) String() string { + func RaceDetectorSupported(goos, goarch string) bool { + switch goos { + case "linux": +- return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" ++ return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" || goarch == "loong64" + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "windows": +diff --git a/src/race.bash b/src/race.bash +index f1a168bfbb..ae9f57ffd7 100755 +--- a/src/race.bash ++++ b/src/race.bash +@@ -9,7 +9,7 @@ + set -e + + function usage { +- echo 'race detector is only supported on linux/amd64, linux/ppc64le, linux/arm64, linux/s390x, freebsd/amd64, netbsd/amd64, openbsd/amd64, darwin/amd64, and darwin/arm64' 1>&2 ++ echo 'race detector is only supported on linux/amd64, linux/ppc64le, linux/arm64, linux/loong64, linux/s390x, freebsd/amd64, netbsd/amd64, openbsd/amd64, darwin/amd64, and darwin/arm64' 1>&2 + exit 1 + } + +@@ -19,6 +19,7 @@ case $(uname -s -m) in + "Linux x86_64") ;; + "Linux ppc64le") ;; + "Linux aarch64") ;; ++ "Linux loongarch64") ;; + "Linux s390x") ;; + "FreeBSD amd64") ;; + "NetBSD amd64") ;; +diff --git a/src/runtime/asm_loong64.s b/src/runtime/asm_loong64.s +index 1c5ced4512..1bd8276835 100644 +--- a/src/runtime/asm_loong64.s ++++ b/src/runtime/asm_loong64.s +@@ -37,6 +37,7 @@ TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 + JAL (R25) + + nocgo: ++ JAL runtime·save_g(SB) + // update stackguard after _cgo_init + MOVV (g_stack+stack_lo)(g), R19 + ADDV $const_stackGuard, R19 +diff --git a/src/runtime/race/README b/src/runtime/race/README +index 47c51ca9c1..06865d2b34 100644 +--- a/src/runtime/race/README ++++ b/src/runtime/race/README +@@ -13,5 +13,6 @@ internal/amd64v1/race_windows.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d + internal/amd64v3/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. + race_darwin_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. + race_linux_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. +-race_linux_ppc64le.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. ++race_linux_loong64.syso built with LLVM 9d3fbf97bef3f19da4e0a047f017b8142f59b3fd and Go 988b718f4130ab5b3ce5a5774e1a58e83c92a163. ++race_linux_ppc64le.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. + race_linux_s390x.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. +diff --git a/src/runtime/race/race.go b/src/runtime/race/race.go +index 9c508ebc2b..9fd75424ca 100644 +--- a/src/runtime/race/race.go ++++ b/src/runtime/race/race.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build race && ((linux && (amd64 || arm64 || ppc64le || s390x)) || ((freebsd || netbsd || openbsd || windows) && amd64)) ++//go:build race && ((linux && (amd64 || arm64 || loong64 || ppc64le || s390x)) || ((freebsd || netbsd || openbsd || windows) && amd64)) + + package race + +diff --git a/src/runtime/race_loong64.s b/src/runtime/race_loong64.s +new file mode 100644 +index 0000000000..0512efc045 +--- /dev/null ++++ b/src/runtime/race_loong64.s +@@ -0,0 +1,509 @@ ++// Copyright 2023 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. ++ ++//go:build race ++ ++#include "go_asm.h" ++#include "funcdata.h" ++#include "textflag.h" ++#include "cgo/abi_loong64.h" ++ ++// The following thunks allow calling the gcc-compiled race runtime directly ++// from Go code without going all the way through cgo. ++// First, it's much faster (up to 50% speedup for real Go programs). ++// Second, it eliminates race-related special cases from cgocall and scheduler. ++// Third, in long-term it will allow to remove cyclic runtime/race dependency on cmd/go. ++ ++// A brief recap of the loong64 calling convention. ++// Arguments are passed in R4...R11, the rest is on stack. ++// Callee-saved registers are: R23...R30. ++// Temporary registers are: R12...R20 ++// SP must be 16-byte aligned. ++ ++// When calling racecalladdr, R20 is the call target address. ++ ++// The race ctx, ThreadState *thr below, is passed in R4 and loaded in racecalladdr. ++ ++// Load g from TLS. (See tls_loong64.s) ++#define load_g \ ++ MOVV runtime·tls_g(SB), g ++ ++#define RARG0 R4 ++#define RARG1 R5 ++#define RARG2 R6 ++#define RARG3 R7 ++#define RCALL R20 ++ ++// func runtime·raceread(addr uintptr) ++// Called from instrumented code. ++// Defined as ABIInternal so as to avoid introducing a wrapper, ++// which would make caller's PC ineffective. ++TEXT runtime·raceread(SB), NOSPLIT, $0-8 ++#ifdef GOEXPERIMENT_regabiargs ++ MOVV R4, RARG1 ++#else ++ MOVV addr+0(FP), RARG1 ++#endif ++ MOVV R1, RARG2 ++ // void __tsan_read(ThreadState *thr, void *addr, void *pc); ++ MOVV $__tsan_read(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·RaceRead(addr uintptr) ++TEXT runtime·RaceRead(SB), NOSPLIT, $0-8 ++ // This needs to be a tail call, because raceread reads caller pc. ++ JMP runtime·raceread(SB) ++ ++// func runtime·racereadpc(void *addr, void *callpc, void *pc) ++TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 ++ MOVV addr+0(FP), RARG1 ++ MOVV callpc+8(FP), RARG2 ++ MOVV pc+16(FP), RARG3 ++ // void __tsan_read_pc(ThreadState *thr, void *addr, void *callpc, void *pc); ++ MOVV $__tsan_read_pc(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·racewrite(addr uintptr) ++// Called from instrumented code. ++// Defined as ABIInternal so as to avoid introducing a wrapper, ++// which would make caller's PC ineffective. ++TEXT runtime·racewrite(SB), NOSPLIT, $0-8 ++#ifdef GOEXPERIMENT_regabiargs ++ MOVV R4, RARG1 ++#else ++ MOVV addr+0(FP), RARG1 ++#endif ++ MOVV R1, RARG2 ++ // void __tsan_write(ThreadState *thr, void *addr, void *pc); ++ MOVV $__tsan_write(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·RaceWrite(addr uintptr) ++TEXT runtime·RaceWrite(SB), NOSPLIT, $0-8 ++ // This needs to be a tail call, because racewrite reads caller pc. ++ JMP runtime·racewrite(SB) ++ ++// func runtime·racewritepc(void *addr, void *callpc, void *pc) ++TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 ++ MOVV addr+0(FP), RARG1 ++ MOVV callpc+8(FP), RARG2 ++ MOVV pc+16(FP), RARG3 ++ // void __tsan_write_pc(ThreadState *thr, void *addr, void *callpc, void *pc); ++ MOVV $__tsan_write_pc(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·racereadrange(addr, size uintptr) ++// Called from instrumented code. ++// Defined as ABIInternal so as to avoid introducing a wrapper, ++// which would make caller's PC ineffective. ++TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 ++#ifdef GOEXPERIMENT_regabiargs ++ MOVV R5, RARG2 ++ MOVV R4, RARG1 ++#else ++ MOVV addr+0(FP), RARG1 ++ MOVV size+8(FP), RARG2 ++#endif ++ MOVV R1, RARG3 ++ // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); ++ MOVV $__tsan_read_range(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·RaceReadRange(addr, size uintptr) ++TEXT runtime·RaceReadRange(SB), NOSPLIT, $0-16 ++ // This needs to be a tail call, because racereadrange reads caller pc. ++ JMP runtime·racereadrange(SB) ++ ++// func runtime·racereadrangepc1(void *addr, uintptr sz, void *pc) ++TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 ++ MOVV addr+0(FP), RARG1 ++ MOVV size+8(FP), RARG2 ++ MOVV pc+16(FP), RARG3 ++ ADDV $4, RARG3 // pc is function start, tsan wants return address. ++ // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); ++ MOVV $__tsan_read_range(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·racewriterange(addr, size uintptr) ++// Called from instrumented code. ++// Defined as ABIInternal so as to avoid introducing a wrapper, ++// which would make caller's PC ineffective. ++TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 ++#ifdef GOEXPERIMENT_regabiargs ++ MOVV R5, RARG2 ++ MOVV R4, RARG1 ++#else ++ MOVV addr+0(FP), RARG1 ++ MOVV size+8(FP), RARG2 ++#endif ++ MOVV R1, RARG3 ++ // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); ++ MOVV $__tsan_write_range(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// func runtime·RaceWriteRange(addr, size uintptr) ++TEXT runtime·RaceWriteRange(SB), NOSPLIT, $0-16 ++ // This needs to be a tail call, because racewriterange reads caller pc. ++ JMP runtime·racewriterange(SB) ++ ++// func runtime·racewriterangepc1(void *addr, uintptr sz, void *pc) ++TEXT runtime·racewriterangepc1(SB), NOSPLIT, $0-24 ++ MOVV addr+0(FP), RARG1 ++ MOVV size+8(FP), RARG2 ++ MOVV pc+16(FP), RARG3 ++ ADDV $4, RARG3 // pc is function start, tsan wants return address. ++ // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); ++ MOVV $__tsan_write_range(SB), RCALL ++ JMP racecalladdr<>(SB) ++ ++// Call a __tsan function from Go code. ++// ++// RCALL = tsan function address ++// RARG0 = *ThreadState a.k.a. g_racectx from g ++// RARG1 = addr passed to __tsan function ++// ++// If addr (RARG1) is out of range, do nothing. Otherwise, setup goroutine ++// context and invoke racecall. Other arguments already set. ++TEXT racecalladdr<>(SB), NOSPLIT, $0-0 ++ // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). ++ MOVV runtime·racearenastart(SB), R12 ++ BLT RARG1, R12, data ++ MOVV runtime·racearenaend(SB), R12 ++ BLT RARG1, R12, call ++data: ++ MOVV runtime·racedatastart(SB), R12 ++ BLT RARG1, R12, ret ++ MOVV runtime·racedataend(SB), R12 ++ BGE RARG1, R12, ret ++call: ++ load_g ++ MOVV g_racectx(g), RARG0 ++ JMP racecall<>(SB) ++ret: ++ RET ++ ++// func runtime·racefuncenter(pc uintptr) ++// Called from instrumented code. ++TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 ++#ifdef GOEXPERIMENT_regabiargs ++ MOVV R4, RCALL ++#else ++ MOVV callpc+0(FP), RCALL ++#endif ++ JMP racefuncenter<>(SB) ++ ++// Common code for racefuncenter ++// RCALL = caller's return address ++TEXT racefuncenter<>(SB), NOSPLIT, $0-0 ++ load_g ++ MOVV g_racectx(g), RARG0 // goroutine racectx ++ MOVV RCALL, RARG1 ++ // void __tsan_func_enter(ThreadState *thr, void *pc); ++ MOVV $__tsan_func_enter(SB), RCALL ++ JAL racecall<>(SB) ++ RET ++ ++// func runtime·racefuncexit() ++// Called from instrumented code. ++TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 ++ load_g ++ MOVV g_racectx(g), RARG0 // race context ++ // void __tsan_func_exit(ThreadState *thr); ++ MOVV $__tsan_func_exit(SB), RCALL ++ JMP racecall<>(SB) ++ ++// Atomic operations for sync/atomic package. ++// R7 = addr of arguments passed to this function, it can ++// be fetched at 24(R3) in racecallatomic after two times JAL ++// RARG0, RARG1, RARG2 set in racecallatomic ++ ++// Load ++TEXT sync∕atomic·LoadInt32(SB), NOSPLIT, $0-12 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_load(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·LoadInt64(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_load(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·LoadUint32(SB), NOSPLIT, $0-12 ++ GO_ARGS ++ JMP sync∕atomic·LoadInt32(SB) ++ ++TEXT sync∕atomic·LoadUint64(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ JMP sync∕atomic·LoadInt64(SB) ++ ++TEXT sync∕atomic·LoadUintptr(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ JMP sync∕atomic·LoadInt64(SB) ++ ++TEXT sync∕atomic·LoadPointer(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ JMP sync∕atomic·LoadInt64(SB) ++ ++// Store ++TEXT sync∕atomic·StoreInt32(SB), NOSPLIT, $0-12 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_store(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·StoreInt64(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_store(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·StoreUint32(SB), NOSPLIT, $0-12 ++ GO_ARGS ++ JMP sync∕atomic·StoreInt32(SB) ++ ++TEXT sync∕atomic·StoreUint64(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ JMP sync∕atomic·StoreInt64(SB) ++ ++TEXT sync∕atomic·StoreUintptr(SB), NOSPLIT, $0-16 ++ GO_ARGS ++ JMP sync∕atomic·StoreInt64(SB) ++ ++// Swap ++TEXT sync∕atomic·SwapInt32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_exchange(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·SwapInt64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_exchange(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·SwapUint32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ JMP sync∕atomic·SwapInt32(SB) ++ ++TEXT sync∕atomic·SwapUint64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·SwapInt64(SB) ++ ++TEXT sync∕atomic·SwapUintptr(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·SwapInt64(SB) ++ ++// Add ++TEXT sync∕atomic·AddInt32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_fetch_add(SB), RCALL ++ JAL racecallatomic<>(SB) ++ MOVW add+8(FP), RARG0 // convert fetch_add to add_fetch ++ MOVW ret+16(FP), RARG1 ++ ADD RARG0, RARG1, RARG0 ++ MOVW RARG0, ret+16(FP) ++ RET ++ ++TEXT sync∕atomic·AddInt64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_fetch_add(SB), RCALL ++ JAL racecallatomic<>(SB) ++ MOVV add+8(FP), RARG0 // convert fetch_add to add_fetch ++ MOVV ret+16(FP), RARG1 ++ ADDV RARG0, RARG1, RARG0 ++ MOVV RARG0, ret+16(FP) ++ RET ++ ++TEXT sync∕atomic·AddUint32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ JMP sync∕atomic·AddInt32(SB) ++ ++TEXT sync∕atomic·AddUint64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·AddInt64(SB) ++ ++TEXT sync∕atomic·AddUintptr(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·AddInt64(SB) ++ ++// CompareAndSwap ++TEXT sync∕atomic·CompareAndSwapInt32(SB), NOSPLIT, $0-17 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_compare_exchange(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·CompareAndSwapInt64(SB), NOSPLIT, $0-25 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_compare_exchange(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·CompareAndSwapUint32(SB), NOSPLIT, $0-17 ++ GO_ARGS ++ JMP sync∕atomic·CompareAndSwapInt32(SB) ++ ++TEXT sync∕atomic·CompareAndSwapUint64(SB), NOSPLIT, $0-25 ++ GO_ARGS ++ JMP sync∕atomic·CompareAndSwapInt64(SB) ++ ++TEXT sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-25 ++ GO_ARGS ++ JMP sync∕atomic·CompareAndSwapInt64(SB) ++ ++// Generic atomic operation implementation. ++// RCALL = addr of target function ++TEXT racecallatomic<>(SB), NOSPLIT, $0 ++ // Set up these registers ++ // RARG0 = *ThreadState ++ // RARG1 = caller pc ++ // RARG2 = pc ++ // RARG3 = addr of incoming arg list ++ ++ // Trigger SIGSEGV early. ++ MOVV 24(R3), RARG3 // 1st arg is addr. after two times JAL, get it at 24(R3) ++ MOVB (RARG3), R12 // segv here if addr is bad ++ ++ // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). ++ MOVV runtime·racearenastart(SB), R12 ++ BLT RARG3, R12, racecallatomic_data ++ MOVV runtime·racearenaend(SB), R12 ++ BLT RARG3, R12, racecallatomic_ok ++ ++racecallatomic_data: ++ MOVV runtime·racedatastart(SB), R12 ++ BLT RARG3, R12, racecallatomic_ignore ++ MOVV runtime·racedataend(SB), R12 ++ BGE RARG3, R12, racecallatomic_ignore ++ ++racecallatomic_ok: ++ // Addr is within the good range, call the atomic function. ++ load_g ++ MOVV g_racectx(g), RARG0 // goroutine context ++ MOVV 8(R3), RARG1 // caller pc ++ MOVV RCALL, RARG2 // pc ++ ADDV $24, R3, RARG3 ++ JAL racecall<>(SB) // does not return ++ RET ++ ++racecallatomic_ignore: ++ // Addr is outside the good range. ++ // Call __tsan_go_ignore_sync_begin to ignore synchronization during the atomic op. ++ // An attempt to synchronize on the address would cause crash. ++ MOVV RCALL, R25 // remember the original function ++ MOVV $__tsan_go_ignore_sync_begin(SB), RCALL ++ load_g ++ MOVV g_racectx(g), RARG0 // goroutine context ++ JAL racecall<>(SB) ++ MOVV R25, RCALL // restore the original function ++ ++ // Call the atomic function. ++ // racecall will call LLVM race code which might clobber R22 (g) ++ load_g ++ MOVV g_racectx(g), RARG0 // goroutine context ++ MOVV 8(R3), RARG1 // caller pc ++ MOVV RCALL, RARG2 // pc ++ ADDV $24, R3, RARG3 // arguments ++ JAL racecall<>(SB) ++ ++ // Call __tsan_go_ignore_sync_end. ++ MOVV $__tsan_go_ignore_sync_end(SB), RCALL ++ MOVV g_racectx(g), RARG0 // goroutine context ++ JAL racecall<>(SB) ++ RET ++ ++// func runtime·racecall(void(*f)(...), ...) ++// Calls C function f from race runtime and passes up to 4 arguments to it. ++// The arguments are never heap-object-preserving pointers, so we pretend there are no arguments. ++TEXT runtime·racecall(SB), NOSPLIT, $0-0 ++ MOVV fn+0(FP), RCALL ++ MOVV arg0+8(FP), RARG0 ++ MOVV arg1+16(FP), RARG1 ++ MOVV arg2+24(FP), RARG2 ++ MOVV arg3+32(FP), RARG3 ++ JMP racecall<>(SB) ++ ++// Switches SP to g0 stack and calls (RCALL). Arguments already set. ++TEXT racecall<>(SB), NOSPLIT|NOFRAME, $0-0 ++ MOVV g_m(g), R12 ++ // Switch to g0 stack. ++ MOVV R3, R23 // callee-saved, preserved across the CALL ++ MOVV R1, R24 // callee-saved, preserved across the CALL ++ MOVV m_g0(R12), R13 ++ BEQ R13, g, call // already on g0 ++ MOVV (g_sched+gobuf_sp)(R13), R3 ++call: ++ JAL (RCALL) ++ MOVV R23, R3 ++ JAL (R24) ++ RET ++ ++// C->Go callback thunk that allows to call runtime·racesymbolize from C code. ++// Direct Go->C race call has only switched SP, finish g->g0 switch by setting correct g. ++// The overall effect of Go->C->Go call chain is similar to that of mcall. ++// RARG0 contains command code. RARG1 contains command-specific context. ++// See racecallback for command codes. ++TEXT runtime·racecallbackthunk(SB), NOSPLIT|NOFRAME, $0 ++ // Handle command raceGetProcCmd (0) here. ++ // First, code below assumes that we are on curg, while raceGetProcCmd ++ // can be executed on g0. Second, it is called frequently, so will ++ // benefit from this fast path. ++ BNE RARG0, R0, rest ++ MOVV g, R15 ++ load_g ++ MOVV g_m(g), RARG0 ++ MOVV m_p(RARG0), RARG0 ++ MOVV p_raceprocctx(RARG0), RARG0 ++ MOVV RARG0, (RARG1) ++ MOVV R15, g ++ JMP (R1) ++rest: ++ // Save callee-saved registers (Go code won't respect that). ++ // 8(R3) and 16(R3) are for args passed through racecallback ++ ADDV $-176, R3 ++ MOVV R1, 0(R3) ++ ++ SAVE_R22_TO_R31(8*3) ++ SAVE_F24_TO_F31(8*13) ++ // Set g = g0. ++ load_g ++ MOVV g_m(g), R15 ++ MOVV m_g0(R15), R14 ++ BEQ R14, g, noswitch // branch if already on g0 ++ MOVV R14, g ++ ++#ifdef GOEXPERIMENT_regabiargs ++ JAL runtime·racecallback(SB) ++#else ++ MOVV RARG0, 8(R3) // func arg ++ MOVV RARG1, 16(R3) // func arg ++ JAL runtime·racecallback(SB) ++#endif ++ // All registers are smashed after Go code, reload. ++ MOVV g_m(g), R15 ++ MOVV m_curg(R15), g // g = m->curg ++ret: ++ // Restore callee-saved registers. ++ MOVV 0(R3), R1 ++ RESTORE_F24_TO_F31(8*13) ++ RESTORE_R22_TO_R31(8*3) ++ ADDV $176, R3 ++ JMP (R1) ++ ++noswitch: ++ // already on g0 ++#ifdef GOEXPERIMENT_regabiargs ++ JAL runtime·racecallback(SB) ++#else ++ MOVV RARG0, 8(R3) // func arg ++ MOVV RARG1, 16(R3) // func arg ++ JAL runtime·racecallback(SB) ++#endif ++ JMP ret ++ ++// tls_g, g value for each thread in TLS ++GLOBL runtime·tls_g+0(SB), TLSBSS+DUPOK, $8 +-- +2.38.1 + diff --git a/0004-runtime-delete-on-register-ABI-fallback-path-for-rac.patch b/0004-runtime-delete-on-register-ABI-fallback-path-for-rac.patch new file mode 100644 index 0000000..54922d9 --- /dev/null +++ b/0004-runtime-delete-on-register-ABI-fallback-path-for-rac.patch @@ -0,0 +1,111 @@ +From 5623cd585fd5891d1f6d6d93256e4252b95b9dae Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Mon, 6 Nov 2023 17:13:43 +0800 +Subject: [PATCH 04/44] runtime: delete on-register ABI fallback path for race + of loong64 + +Co-authored-by: Xiaolin Zhao +Change-Id: Ie8c4a137205e29dd7dc63825f502b1f6b2f1c205 +--- + src/runtime/race_loong64.s | 34 ---------------------------------- + 1 file changed, 34 deletions(-) + +diff --git a/src/runtime/race_loong64.s b/src/runtime/race_loong64.s +index 0512efc045..04f264b21b 100644 +--- a/src/runtime/race_loong64.s ++++ b/src/runtime/race_loong64.s +@@ -40,11 +40,7 @@ + // Defined as ABIInternal so as to avoid introducing a wrapper, + // which would make caller's PC ineffective. + TEXT runtime·raceread(SB), NOSPLIT, $0-8 +-#ifdef GOEXPERIMENT_regabiargs + MOVV R4, RARG1 +-#else +- MOVV addr+0(FP), RARG1 +-#endif + MOVV R1, RARG2 + // void __tsan_read(ThreadState *thr, void *addr, void *pc); + MOVV $__tsan_read(SB), RCALL +@@ -69,11 +65,7 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 + // Defined as ABIInternal so as to avoid introducing a wrapper, + // which would make caller's PC ineffective. + TEXT runtime·racewrite(SB), NOSPLIT, $0-8 +-#ifdef GOEXPERIMENT_regabiargs + MOVV R4, RARG1 +-#else +- MOVV addr+0(FP), RARG1 +-#endif + MOVV R1, RARG2 + // void __tsan_write(ThreadState *thr, void *addr, void *pc); + MOVV $__tsan_write(SB), RCALL +@@ -98,13 +90,8 @@ TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 + // Defined as ABIInternal so as to avoid introducing a wrapper, + // which would make caller's PC ineffective. + TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 +-#ifdef GOEXPERIMENT_regabiargs + MOVV R5, RARG2 + MOVV R4, RARG1 +-#else +- MOVV addr+0(FP), RARG1 +- MOVV size+8(FP), RARG2 +-#endif + MOVV R1, RARG3 + // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVV $__tsan_read_range(SB), RCALL +@@ -130,13 +117,8 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 + // Defined as ABIInternal so as to avoid introducing a wrapper, + // which would make caller's PC ineffective. + TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 +-#ifdef GOEXPERIMENT_regabiargs + MOVV R5, RARG2 + MOVV R4, RARG1 +-#else +- MOVV addr+0(FP), RARG1 +- MOVV size+8(FP), RARG2 +-#endif + MOVV R1, RARG3 + // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVV $__tsan_write_range(SB), RCALL +@@ -186,11 +168,7 @@ ret: + // func runtime·racefuncenter(pc uintptr) + // Called from instrumented code. + TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 +-#ifdef GOEXPERIMENT_regabiargs + MOVV R4, RCALL +-#else +- MOVV callpc+0(FP), RCALL +-#endif + JMP racefuncenter<>(SB) + + // Common code for racefuncenter +@@ -476,13 +454,7 @@ rest: + BEQ R14, g, noswitch // branch if already on g0 + MOVV R14, g + +-#ifdef GOEXPERIMENT_regabiargs + JAL runtime·racecallback(SB) +-#else +- MOVV RARG0, 8(R3) // func arg +- MOVV RARG1, 16(R3) // func arg +- JAL runtime·racecallback(SB) +-#endif + // All registers are smashed after Go code, reload. + MOVV g_m(g), R15 + MOVV m_curg(R15), g // g = m->curg +@@ -496,13 +468,7 @@ ret: + + noswitch: + // already on g0 +-#ifdef GOEXPERIMENT_regabiargs + JAL runtime·racecallback(SB) +-#else +- MOVV RARG0, 8(R3) // func arg +- MOVV RARG1, 16(R3) // func arg +- JAL runtime·racecallback(SB) +-#endif + JMP ret + + // tls_g, g value for each thread in TLS +-- +2.38.1 + diff --git a/0005-cmd-internal-obj-loong64-remove-unused-register-alia.patch b/0005-cmd-internal-obj-loong64-remove-unused-register-alia.patch new file mode 100644 index 0000000..34a43a0 --- /dev/null +++ b/0005-cmd-internal-obj-loong64-remove-unused-register-alia.patch @@ -0,0 +1,27 @@ +From 2ecb3ca09093ce12b2e47d97cbff223a950de0bb Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Thu, 16 Nov 2023 17:28:46 +0800 +Subject: [PATCH 05/44] cmd/internal/obj/loong64: remove unused register alias + definitions + +Change-Id: Ie788747372cd47cb3780e75b35750bb08bd166fc +--- + src/cmd/internal/obj/loong64/a.out.go | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index e6984dcba7..53b005af4d 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -225,8 +225,6 @@ const ( + REGZERO = REG_R0 // set to zero + REGLINK = REG_R1 + REGSP = REG_R3 +- REGRET = REG_R20 // not use +- REGARG = -1 // -1 disables passing the first argument in register + REGRT1 = REG_R20 // reserved for runtime, duffzero and duffcopy + REGRT2 = REG_R21 // reserved for runtime, duffcopy + REGCTXT = REG_R29 // context for closures +-- +2.38.1 + diff --git a/0006-internal-bytealg-optimize-IndexByte-and-IndexByteStr.patch b/0006-internal-bytealg-optimize-IndexByte-and-IndexByteStr.patch new file mode 100644 index 0000000..b295cb6 --- /dev/null +++ b/0006-internal-bytealg-optimize-IndexByte-and-IndexByteStr.patch @@ -0,0 +1,160 @@ +From 0b580e45412ffc11f3a1c7ed7165f7a81e51adec Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Fri, 17 May 2024 17:10:59 +0800 +Subject: [PATCH 06/44] internal/bytealg: optimize IndexByte and + IndexByteString function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_indexbyte.log │ test/new_3c5000_indexbyte.log │ + │ sec/op │ sec/op vs base │ +IndexByte/10 19.72n ± 0% 13.72n ± 0% -30.44% (p=0.000 n=20) +IndexByte/32 58.27n ± 0% 21.54n ± 0% -63.04% (p=0.000 n=20) +IndexByte/4K 5.609µ ± 0% 2.349µ ± 0% -58.13% (p=0.000 n=20) +IndexByte/4M 3.844m ± 2% 2.408m ± 1% -37.36% (p=0.000 n=20) +IndexByte/64M 62.38m ± 0% 41.83m ± 2% -32.94% (p=0.000 n=20) +geomean 17.29µ 9.309µ -46.17% + +Change-Id: I9d60af0196a0078e829669ccd88f93b5f7a5db0a +--- + src/internal/bytealg/indexbyte_loong64.s | 105 ++++++++++++++++++----- + 1 file changed, 82 insertions(+), 23 deletions(-) + +diff --git a/src/internal/bytealg/indexbyte_loong64.s b/src/internal/bytealg/indexbyte_loong64.s +index c9591b3cda..7811741423 100644 +--- a/src/internal/bytealg/indexbyte_loong64.s ++++ b/src/internal/bytealg/indexbyte_loong64.s +@@ -10,41 +10,100 @@ TEXT ·IndexByte(SB),NOSPLIT,$0-40 + // R5 = b_len + // R6 = b_cap (unused) + // R7 = byte to find +- AND $0xff, R7 ++ ADDV R4, R5 // end + MOVV R4, R6 // store base for later ++ AND $0xff, R7 ++ JMP indexbytebody<>(SB) ++ ++TEXT ·IndexByteString(SB),NOSPLIT,$0-32 ++ // R4 = s_base ++ // R5 = s_len ++ // R6 = byte to find ++ AND $0xff, R6, R7 + ADDV R4, R5 // end +- ADDV $-1, R4 ++ MOVV R4, R6 // store base for later ++ JMP indexbytebody<>(SB) + +- PCALIGN $16 ++// input: ++// R4: b_base ++// R5: end ++// R6: store base for later ++// R7: byte to find ++TEXT indexbytebody<>(SB),NOSPLIT,$0 + loop: ++ ADDV $8, R4, R10 ++ BLT R5, R10, tail ++ MOVV (R4), R8 ++ ++ AND $0xff, R8, R9 ++ BEQ R7, R9, found ++ ++ WORD $0xcf2109 // bstrpick.w r9, r8, 15, 8 ++ BEQ R7, R9, byte_1th ++ ++ WORD $0xd74109 // bstrpick.w r9, r8, 23, 16 ++ BEQ R7, R9, byte_2th ++ ++ WORD $0xdf6109 // bstrpick.w r9, r8, 31, 24 ++ BEQ R7, R9, byte_3th ++ ++ WORD $0xe78109 // bstrpick.w r9, r8, 39, 32 ++ BEQ R7, R9, byte_4th ++ ++ WORD $0xefa109 // bstrpick.w r9, r8, 47, 40 ++ BEQ R7, R9, byte_5th ++ ++ WORD $0xf7c109 // bstrpick.w r9, r8, 55, 48 ++ BEQ R7, R9, byte_6th ++ ++ WORD $0xffe109 // bstrpick.w r9, r8, 63, 56 ++ BEQ R7, R9, byte_7th ++ ++ MOVV R10, R4 ++ JMP loop ++ ++tail: ++ BEQ R4, R5, notfound ++ MOVBU (R4), R8 ++ BEQ R7, R8, found + ADDV $1, R4 +- BEQ R4, R5, notfound +- MOVBU (R4), R8 +- BNE R7, R8, loop ++ JMP tail + +- SUBV R6, R4 // remove base ++byte_1th: ++ ADDV $1, R4 ++ SUBV R6, R4 + RET + +-notfound: +- MOVV $-1, R4 ++byte_2th: ++ ADDV $2, R4 ++ SUBV R6, R4 + RET + +-TEXT ·IndexByteString(SB),NOSPLIT,$0-32 +- // R4 = s_base +- // R5 = s_len +- // R6 = byte to find +- MOVV R4, R7 // store base for later +- ADDV R4, R5 // end +- ADDV $-1, R4 ++byte_3th: ++ ADDV $3, R4 ++ SUBV R6, R4 ++ RET + +- PCALIGN $16 +-loop: +- ADDV $1, R4 +- BEQ R4, R5, notfound +- MOVBU (R4), R8 +- BNE R6, R8, loop ++byte_4th: ++ ADDV $4, R4 ++ SUBV R6, R4 ++ RET ++ ++byte_5th: ++ ADDV $5, R4 ++ SUBV R6, R4 ++ RET + +- SUBV R7, R4 // remove base ++byte_6th: ++ ADDV $6, R4 ++ SUBV R6, R4 ++ RET ++ ++byte_7th: ++ ADDV $7, R4 ++ ++found: ++ SUBV R6, R4 // remove base + RET + + notfound: +-- +2.38.1 + diff --git a/0007-internal-bytealg-optimize-memequal-and-memequal_varl.patch b/0007-internal-bytealg-optimize-memequal-and-memequal_varl.patch new file mode 100644 index 0000000..7e97b4a --- /dev/null +++ b/0007-internal-bytealg-optimize-memequal-and-memequal_varl.patch @@ -0,0 +1,142 @@ +From 83f497423050707a8cd27152256699ccd7819456 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Sat, 18 May 2024 11:00:57 +0800 +Subject: [PATCH 07/44] internal/bytealg: optimize memequal and memequal_varlen + function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_equal.log │ test/new_3c5000_equal.log │ + │ sec/op │ sec/op vs base │ +Equal/0 0.6824n ± 0% 0.6837n ± 0% +0.20% (p=0.000 n=20) +Equal/1 10.46n ± 0% 12.71n ± 0% +21.46% (p=0.000 n=20) +Equal/6 17.29n ± 0% 19.57n ± 0% +13.22% (p=0.000 n=20) +Equal/9 21.38n ± 0% 13.19n ± 0% -38.31% (p=0.000 n=20) +Equal/15 29.57n ± 0% 21.39n ± 0% -27.68% (p=0.000 n=20) +Equal/16 30.94n ± 0% 10.46n ± 0% -66.19% (p=0.000 n=20) +Equal/20 36.40n ± 0% 16.83n ± 0% -53.76% (p=0.000 n=20) +Equal/32 52.78n ± 0% 12.28n ± 0% -76.73% (p=0.000 n=20) +Equal/4K 5606.0n ± 0% 385.8n ± 0% -93.12% (p=0.000 n=20) +Equal/4M 5728.9µ ± 0% 746.4µ ± 0% -86.97% (p=0.000 n=20) +Equal/64M 92.02m ± 0% 14.13m ± 5% -84.65% (p=0.000 n=20) +EqualBothUnaligned/64_0 98.73n ± 0% 10.04n ± 0% -89.83% (p=0.000 n=20) +EqualBothUnaligned/64_1 98.73n ± 0% 10.29n ± 0% -89.58% (p=0.000 n=20) +EqualBothUnaligned/64_4 98.73n ± 0% 10.29n ± 0% -89.58% (p=0.000 n=20) +EqualBothUnaligned/64_7 98.73n ± 0% 10.28n ± 0% -89.59% (p=0.000 n=20) +EqualBothUnaligned/4096_0 5602.0n ± 0% 365.8n ± 0% -93.47% (p=0.000 n=20) +EqualBothUnaligned/4096_1 5602.0n ± 0% 437.2n ± 0% -92.19% (p=0.000 n=20) +EqualBothUnaligned/4096_4 5602.0n ± 0% 436.4n ± 0% -92.21% (p=0.000 n=20) +EqualBothUnaligned/4096_7 5602.0n ± 0% 439.2n ± 0% -92.16% (p=0.000 n=20) +EqualBothUnaligned/4194304_0 5729.0µ ± 0% 732.4µ ± 0% -87.22% (p=0.000 n=20) +EqualBothUnaligned/4194304_1 5729.2µ ± 0% 781.8µ ± 1% -86.35% (p=0.000 n=20) +EqualBothUnaligned/4194304_4 5729.3µ ± 0% 773.9µ ± 0% -86.49% (p=0.000 n=20) +EqualBothUnaligned/4194304_7 5729.3µ ± 0% 773.9µ ± 5% -86.49% (p=0.000 n=20) +EqualBothUnaligned/67108864_0 92.38m ± 0% 34.61m ± 38% -62.53% (p=0.000 n=20) +EqualBothUnaligned/67108864_1 92.38m ± 0% 33.07m ± 23% -64.20% (p=0.000 n=20) +EqualBothUnaligned/67108864_4 92.38m ± 0% 82.09m ± 32% -11.14% (p=0.000 n=20) +EqualBothUnaligned/67108864_7 92.39m ± 0% 61.47m ± 16% -33.46% (p=0.000 n=20) +geomean 11.86µ 2.654µ -77.62% + +Change-Id: Ib181f532238e6f6d82a3e9e6987abe121688b6eb +--- + src/internal/bytealg/equal_loong64.s | 72 +++++++++++++++++++--------- + 1 file changed, 49 insertions(+), 23 deletions(-) + +diff --git a/src/internal/bytealg/equal_loong64.s b/src/internal/bytealg/equal_loong64.s +index 830b09bd2c..4cc31d5e46 100644 +--- a/src/internal/bytealg/equal_loong64.s ++++ b/src/internal/bytealg/equal_loong64.s +@@ -9,36 +9,62 @@ + + // memequal(a, b unsafe.Pointer, size uintptr) bool + TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 +- BEQ R4, R5, eq +- ADDV R4, R6, R7 +- PCALIGN $16 +-loop: +- BNE R4, R7, test +- MOVV $1, R4 ++ // R4 = a_base ++ // R5 = b_base ++ // R6 = size ++ JMP equalbody<>(SB) ++ ++// memequal_varlen(a, b unsafe.Pointer) bool ++TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17 ++ // R4 = a_base ++ // R5 = b_base ++ MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure ++ JMP equalbody<>(SB) ++ ++TEXT equalbody<>(SB),NOSPLIT|NOFRAME,$0 ++ BEQ R4, R5, eq ++ ADDV R4, R6, R6 // end ++ ++loop_16byte: ++ ADDV $16, R4, R9 ++ BLT R6, R9, load8byte ++ MOVV (R4), R7 ++ MOVV (R5), R8 ++ MOVV 8(R4), R10 ++ MOVV 8(R5), R11 ++ MOVV R9, R4 ++ XOR R7, R8, R7 ++ XOR R10, R11, R10 ++ OR R10, R7, R7 ++ ADDV $16, R5 ++ BEQ R7, loop_16byte ++ ++ MOVB R0, R4 + RET +-test: +- MOVBU (R4), R9 ++ ++load8byte: ++ ADDV $8, R4, R9 ++ BLT R6, R9, tail ++ MOVV (R4), R7 ++ MOVV (R5), R8 ++ MOVV R9, R4 ++ ADDV $8, R5 ++ BEQ R7, R8, tail ++ ++ MOVB R0, R4 ++ RET ++ ++tail: ++ BEQ R4, R6, eq ++ MOVBU (R4), R7 ++ MOVBU (R5), R8 + ADDV $1, R4 +- MOVBU (R5), R10 + ADDV $1, R5 +- BEQ R9, R10, loop ++ BEQ R7, R8, tail + + MOVB R0, R4 + RET +-eq: +- MOVV $1, R4 +- RET + +-// memequal_varlen(a, b unsafe.Pointer) bool +-TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 +- BEQ R4, R5, eq +- MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure +- MOVV R4, 8(R3) +- MOVV R5, 16(R3) +- MOVV R6, 24(R3) +- JAL runtime·memequal(SB) +- MOVBU 32(R3), R4 +- RET + eq: + MOVV $1, R4 + RET +-- +2.38.1 + diff --git a/0008-internal-bytealg-optimize-Index-and-IndexString-func.patch b/0008-internal-bytealg-optimize-Index-and-IndexString-func.patch new file mode 100644 index 0000000..4fb2113 --- /dev/null +++ b/0008-internal-bytealg-optimize-Index-and-IndexString-func.patch @@ -0,0 +1,299 @@ +From 89d740fe5889c558dbb69b6ac3a80ec38cd5765c Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Thu, 23 May 2024 16:25:06 +0800 +Subject: [PATCH 08/44] internal/bytealg: optimize Index and IndexString + function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_index.log │ test/new_3c5000_index.log │ + │ sec/op │ sec/op vs base │ +Index/10 66.42n ± 0% 20.47n ± 0% -69.18% (p=0.000 n=20) +Index/32 196.1n ± 0% 105.7n ± 0% -46.12% (p=0.000 n=20) +Index/4K 13.622µ ± 0% 5.673µ ± 0% -58.35% (p=0.000 n=20) +Index/4M 14.005m ± 0% 5.734m ± 0% -59.06% (p=0.000 n=20) +Index/64M 224.50m ± 0% 91.94m ± 0% -59.05% (p=0.000 n=20) +IndexEasy/10 21.30n ± 0% 18.66n ± 0% -12.41% (p=0.000 n=20) +IndexEasy/32 41.40n ± 0% 33.91n ± 1% -18.09% (p=0.000 n=20) +IndexEasy/4K 4.141µ ± 4% 2.373µ ± 1% -42.70% (p=0.000 n=20) +IndexEasy/4M 3.830m ± 0% 2.392m ± 0% -37.55% (p=0.000 n=20) +IndexEasy/64M 62.54m ± 1% 39.86m ± 0% -36.26% (p=0.000 n=20) +geomean 29.43µ 15.73µ -46.57% + +goos: linux +goarch: loong64 +pkg: strings +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_indexstring.log │ test/new_3c5000_indexstring.log │ + │ sec/op │ sec/op vs base │ +Index 30.54n ± 0% 16.91n ± 0% -44.64% (p=0.000 n=20) + +Change-Id: I92739ada1637356c6d42761a8a596b0bffec405d +--- + src/internal/bytealg/index_generic.go | 2 +- + src/internal/bytealg/index_loong64.go | 23 ++++ + src/internal/bytealg/index_loong64.s | 190 ++++++++++++++++++++++++++ + src/internal/bytealg/index_native.go | 2 +- + 4 files changed, 215 insertions(+), 2 deletions(-) + create mode 100644 src/internal/bytealg/index_loong64.go + create mode 100644 src/internal/bytealg/index_loong64.s + +diff --git a/src/internal/bytealg/index_generic.go b/src/internal/bytealg/index_generic.go +index a59e32938e..2d89c41825 100644 +--- a/src/internal/bytealg/index_generic.go ++++ b/src/internal/bytealg/index_generic.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !amd64 && !arm64 && !s390x && !ppc64le && !ppc64 ++//go:build !amd64 && !arm64 && !s390x && !ppc64le && !ppc64 && !loong64 + + package bytealg + +diff --git a/src/internal/bytealg/index_loong64.go b/src/internal/bytealg/index_loong64.go +new file mode 100644 +index 0000000000..d6f43eb32c +--- /dev/null ++++ b/src/internal/bytealg/index_loong64.go +@@ -0,0 +1,23 @@ ++// Copyright 2018 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 bytealg ++ ++// Empirical data shows that using Index can get better ++// performance when len(s) <= 16. ++const MaxBruteForce = 16 ++ ++func init() { ++ // Optimize cases where the length of the substring is less than 32 bytes ++ MaxLen = 32 ++} ++ ++// Cutover reports the number of failures of IndexByte we should tolerate ++// before switching over to Index. ++// n is the number of bytes processed so far. ++// See the bytes.Index implementation for details. ++func Cutover(n int) int { ++ // 1 error per 8 characters, plus a few slop to start. ++ return (n + 16) / 8 ++} +diff --git a/src/internal/bytealg/index_loong64.s b/src/internal/bytealg/index_loong64.s +new file mode 100644 +index 0000000000..221d0332a4 +--- /dev/null ++++ b/src/internal/bytealg/index_loong64.s +@@ -0,0 +1,190 @@ ++// Copyright 2018 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. ++ ++#include "go_asm.h" ++#include "textflag.h" ++ ++TEXT ·Index(SB),NOSPLIT,$0-56 ++ MOVV R7, R6 // R6 = separator pointer ++ MOVV R8, R7 // R7 = separator length ++ JMP indexbody<>(SB) ++ ++TEXT ·IndexString(SB),NOSPLIT,$0-40 ++ JMP indexbody<>(SB) ++ ++// input: ++// R4 = string ++// R5 = length ++// R6 = separator pointer ++// R7 = separator length (2 <= len <= 32) ++TEXT indexbody<>(SB),NOSPLIT,$0 ++ // main idea is to load 'sep' into separate register(s) ++ // to avoid repeatedly re-load it again and again ++ // for sebsequent substring comparisons ++ SUBV R7, R5, R8 ++ ADDV $1, R4, R9 // store base for later ++ MOVV $8, R5 ++ ADDV R4, R8 // end ++ BLT R5, R7, len_gt_8 ++ ++len_le_8: ++ AND $0x8, R7, R5 ++ BNE R5, len_8 ++ AND $0x4, R7, R5 ++ BNE R5, len_4_7 ++ ++len_2_3: ++ AND $0x1, R7, R5 ++ BNE R5, len_3 ++ ++len_2: ++ MOVHU (R6), R10 ++loop_2: ++ BLT R8, R4, not_found ++ MOVHU (R4), R11 ++ ADDV $1, R4 ++ BNE R10, R11, loop_2 ++ JMP found ++ ++len_3: ++ MOVHU (R6), R10 ++ MOVBU 2(R6), R11 ++loop_3: ++ BLT R8, R4, not_found ++ MOVHU (R4), R12 ++ ADDV $1, R4 ++ BNE R10, R12, loop_3 ++ MOVBU 1(R4), R12 ++ BNE R11, R12, loop_3 ++ JMP found ++ ++len_4_7: ++ AND $0x2, R7, R5 ++ BNE R5, len_6_7 ++ AND $0x1, R7, R5 ++ BNE R5, len_5 ++ ++len_4: ++ MOVWU (R6), R10 ++loop_4: ++ BLT R8, R4, not_found ++ MOVWU (R4), R11 ++ ADDV $1, R4 ++ BNE R10, R11, loop_4 ++ JMP found ++len_5: ++ MOVWU (R6), R10 ++ MOVBU 4(R6), R11 ++loop_5: ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 ++ ADDV $1, R4 ++ BNE R10, R12, loop_5 ++ MOVBU 3(R4), R12 ++ BNE R11, R12, loop_5 ++ JMP found ++ ++len_6_7: ++ AND $0x1, R7, R5 ++ BNE R5, len_7 ++ ++len_6: ++ MOVWU (R6), R10 ++ MOVHU 4(R6), R11 ++loop_6: ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 ++ ADDV $1, R4 ++ BNE R10, R12, loop_6 ++ MOVHU 3(R4), R12 ++ BNE R11, R12, loop_6 ++ JMP found ++ ++len_7: ++ MOVWU (R6), R10 ++ MOVWU 3(R6), R11 ++loop_7: ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 ++ ADDV $1, R4 ++ BNE R10, R12, loop_7 ++ MOVWU 2(R4), R12 ++ BNE R11, R12, loop_7 ++ JMP found ++ ++len_8: ++ MOVV (R6), R10 ++loop_8: ++ BLT R8, R4, not_found ++ MOVV (R4), R11 ++ ADDV $1, R4 ++ BNE R10, R11, loop_8 ++ JMP found ++ ++len_gt_8: ++ MOVV $16, R5 ++ BLT R5, R7, len_gt_16 ++ ++len_9_16: ++ MOVV (R6), R10 ++ SUBV $8, R7 ++ MOVV (R6)(R7), R11 ++ SUBV $1, R7 ++loop_9_16: ++ BLT R8, R4, not_found ++ MOVV (R4), R12 ++ ADDV $1, R4 ++ BNE R10, R12, loop_9_16 ++ MOVV (R4)(R7), R12 ++ BNE R11, R12, loop_9_16 ++ JMP found ++ ++len_gt_16: ++ MOVV $24, R5 ++ BLT R5, R7, len_25_32 ++ ++len_17_24: ++ MOVV (R6), R10 ++ SUBV $8, R7 ++ MOVV 8(R6), R11 ++ MOVV (R6)(R7), R12 ++ SUBV $1, R7 ++loop_17_24: ++ BLT R8, R4, not_found ++ MOVV (R4), R13 ++ ADDV $1, R4 ++ BNE R10, R13, loop_17_24 ++ MOVV 7(R4), R13 ++ BNE R11, R13, loop_17_24 ++ MOVV (R4)(R7), R13 ++ BNE R12, R13, loop_17_24 ++ JMP found ++ ++len_25_32: ++ MOVV (R6), R10 ++ SUBV $8, R7 ++ MOVV 8(R6), R11 ++ MOVV 16(R6), R12 ++ MOVV (R6)(R7), R13 ++ SUBV $1, R7 ++loop_25_32: ++ BLT R8, R4, not_found ++ MOVV (R4), R14 ++ ADDV $1, R4 ++ BNE R10, R14, loop_25_32 ++ MOVV 7(R4), R14 ++ BNE R11, R14, loop_25_32 ++ MOVV 15(R4), R14 ++ BNE R12, R14, loop_25_32 ++ MOVV (R4)(R7), R14 ++ BNE R13, R14, loop_25_32 ++ JMP found ++ ++found: ++ SUBV R9, R4 ++ RET ++ ++not_found: ++ MOVV $-1, R4 ++ RET +diff --git a/src/internal/bytealg/index_native.go b/src/internal/bytealg/index_native.go +index 59c93f9d12..7aadaabe4e 100644 +--- a/src/internal/bytealg/index_native.go ++++ b/src/internal/bytealg/index_native.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build amd64 || arm64 || s390x || ppc64le || ppc64 ++//go:build amd64 || arm64 || s390x || ppc64le || ppc64 || loong64 + + package bytealg + +-- +2.38.1 + diff --git a/0009-internal-bytealg-optimize-Count-and-CountString-func.patch b/0009-internal-bytealg-optimize-Count-and-CountString-func.patch new file mode 100644 index 0000000..9b99d37 --- /dev/null +++ b/0009-internal-bytealg-optimize-Count-and-CountString-func.patch @@ -0,0 +1,153 @@ +From 37c73e45ea537b7e8662b968b630a2566b25ae59 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Wed, 29 May 2024 10:49:41 +0800 +Subject: [PATCH 09/44] internal/bytealg: optimize Count and CountString + function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_count.log │ test/new_3c5000_count.log │ + │ sec/op │ sec/op vs base │ +CountSingle/10 16.26n ± 0% 16.26n ± 0% ~ (p=0.653 n=20) +CountSingle/32 41.48n ± 0% 27.48n ± 0% -33.75% (p=0.000 n=20) +CountSingle/4K 4.998µ ± 0% 2.961µ ± 0% -40.76% (p=0.000 n=20) +CountSingle/4M 5.076m ± 0% 3.510m ± 8% -30.84% (p=0.000 n=20) +CountSingle/64M 88.70m ± 0% 58.15m ± 1% -34.45% (p=0.000 n=20) +geomean 17.23µ 12.20µ -29.19% + +Change-Id: Ic60d49fea83c9cf4f9b02bae3ce69b81206c7017 +--- + src/internal/bytealg/count_generic.go | 2 +- + src/internal/bytealg/count_loong64.s | 86 +++++++++++++++++++++++++++ + src/internal/bytealg/count_native.go | 2 +- + 3 files changed, 88 insertions(+), 2 deletions(-) + create mode 100644 src/internal/bytealg/count_loong64.s + +diff --git a/src/internal/bytealg/count_generic.go b/src/internal/bytealg/count_generic.go +index 932a7c584c..16f974539c 100644 +--- a/src/internal/bytealg/count_generic.go ++++ b/src/internal/bytealg/count_generic.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !amd64 && !arm && !arm64 && !ppc64le && !ppc64 && !riscv64 && !s390x ++//go:build !amd64 && !arm && !arm64 && !loong64 && !ppc64le && !ppc64 && !riscv64 && !s390x + + package bytealg + +diff --git a/src/internal/bytealg/count_loong64.s b/src/internal/bytealg/count_loong64.s +new file mode 100644 +index 0000000000..ca19c5f343 +--- /dev/null ++++ b/src/internal/bytealg/count_loong64.s +@@ -0,0 +1,86 @@ ++// Copyright 2020 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. ++ ++#include "go_asm.h" ++#include "textflag.h" ++ ++TEXT ·Count(SB),NOSPLIT,$0-40 ++ // R4 = b_base ++ // R5 = b_len ++ // R6 = b_cap (unused) ++ // R7 = byte to count (want in R6) ++ AND $0xff, R7, R6 ++ JMP countbody<>(SB) ++ ++TEXT ·CountString(SB),NOSPLIT,$0-32 ++ // R4 = s_base ++ // R5 = s_len ++ // R6 = byte to count ++ AND $0xff, R6 ++ JMP countbody<>(SB) ++ ++// input: ++// R4 = s_base ++// R5 = s_len ++// R6 = byte to count ++TEXT countbody<>(SB),NOSPLIT,$0 ++ MOVV R0, R7 // count ++ ADDV R4, R5 // end ++ MOVV $1, R17 ++ ++loop: ++ ADDV $8, R4, R9 ++ BLT R5, R9, tail ++ MOVV (R4), R8 ++ ++ AND $0xff, R8, R10 ++ WORD $0xcf210b // bstrpick.w r11, r8, 15, 8 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 ++ ++ WORD $0xd7410a // bstrpick.w r10, r8, 23, 16 ++ WORD $0xdf610b // bstrpick.w r11, r8, 31, 24 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 ++ ++ WORD $0xe7810a // bstrpick.w r10, r8, 39, 32 ++ WORD $0xefa10b // bstrpick.w r11, r8, 47, 40 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 ++ ++ WORD $0xf7c10a // bstrpick.w r10, r8, 55, 48 ++ WORD $0xffe10b // bstrpick.w r11, r8, 63, 56 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 ++ ++ MOVV R9, R4 ++ JMP loop ++ ++tail: ++ BEQ R4, R5, done ++ MOVBU (R4), R8 ++ ADDV $1, R4 ++ BNE R6, R8, tail ++ ADDV $1, R7 ++ JMP tail ++ ++done: ++ MOVV R7, R4 ++ RET +diff --git a/src/internal/bytealg/count_native.go b/src/internal/bytealg/count_native.go +index 90189c9fe0..eab64e8950 100644 +--- a/src/internal/bytealg/count_native.go ++++ b/src/internal/bytealg/count_native.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build amd64 || arm || arm64 || ppc64le || ppc64 || riscv64 || s390x ++//go:build amd64 || arm || arm64 || loong64 || ppc64le || ppc64 || riscv64 || s390x + + package bytealg + +-- +2.38.1 + diff --git a/0010-internal-bytealg-adjust-the-format-of-assembly-files.patch b/0010-internal-bytealg-adjust-the-format-of-assembly-files.patch new file mode 100644 index 0000000..85a21d3 --- /dev/null +++ b/0010-internal-bytealg-adjust-the-format-of-assembly-files.patch @@ -0,0 +1,583 @@ +From 14ffec301d84da6bcd5ef5757d6cd6445351225e Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Mon, 3 Jun 2024 15:43:32 +0800 +Subject: [PATCH 10/44] internal/bytealg: adjust the format of assembly files + {count, equal, index, indexbyte}_loong64.s + +Change-Id: I19e6650e6595148e449da7a82be6e735c6f01ab6 +--- + src/internal/bytealg/count_loong64.s | 92 +++++++------- + src/internal/bytealg/equal_loong64.s | 42 ++++--- + src/internal/bytealg/index_loong64.s | 148 +++++++++++------------ + src/internal/bytealg/indexbyte_loong64.s | 52 ++++---- + 4 files changed, 169 insertions(+), 165 deletions(-) + +diff --git a/src/internal/bytealg/count_loong64.s b/src/internal/bytealg/count_loong64.s +index ca19c5f343..db8ba2cb24 100644 +--- a/src/internal/bytealg/count_loong64.s ++++ b/src/internal/bytealg/count_loong64.s +@@ -1,4 +1,4 @@ +-// Copyright 2020 The Go Authors. All rights reserved. ++// Copyright 2024 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. + +@@ -9,77 +9,77 @@ TEXT ·Count(SB),NOSPLIT,$0-40 + // R4 = b_base + // R5 = b_len + // R6 = b_cap (unused) +- // R7 = byte to count (want in R6) +- AND $0xff, R7, R6 +- JMP countbody<>(SB) ++ // R7 = byte to count ++ AND $0xff, R7, R6 ++ JMP countbody<>(SB) + + TEXT ·CountString(SB),NOSPLIT,$0-32 + // R4 = s_base + // R5 = s_len + // R6 = byte to count +- AND $0xff, R6 +- JMP countbody<>(SB) ++ AND $0xff, R6 ++ JMP countbody<>(SB) + + // input: + // R4 = s_base + // R5 = s_len + // R6 = byte to count + TEXT countbody<>(SB),NOSPLIT,$0 +- MOVV R0, R7 // count +- ADDV R4, R5 // end +- MOVV $1, R17 ++ MOVV R0, R7 // count ++ ADDV R4, R5 // end ++ MOVV $1, R17 + + loop: + ADDV $8, R4, R9 +- BLT R5, R9, tail ++ BLT R5, R9, tail + MOVV (R4), R8 + +- AND $0xff, R8, R10 +- WORD $0xcf210b // bstrpick.w r11, r8, 15, 8 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 ++ AND $0xff, R8, R10 ++ WORD $0xcf210b // bstrpick.w r11, r8, 15, 8 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 + +- WORD $0xd7410a // bstrpick.w r10, r8, 23, 16 +- WORD $0xdf610b // bstrpick.w r11, r8, 31, 24 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 ++ WORD $0xd7410a // bstrpick.w r10, r8, 23, 16 ++ WORD $0xdf610b // bstrpick.w r11, r8, 31, 24 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 + +- WORD $0xe7810a // bstrpick.w r10, r8, 39, 32 +- WORD $0xefa10b // bstrpick.w r11, r8, 47, 40 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 ++ WORD $0xe7810a // bstrpick.w r10, r8, 39, 32 ++ WORD $0xefa10b // bstrpick.w r11, r8, 47, 40 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 + +- WORD $0xf7c10a // bstrpick.w r10, r8, 55, 48 +- WORD $0xffe10b // bstrpick.w r11, r8, 63, 56 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 ++ WORD $0xf7c10a // bstrpick.w r10, r8, 55, 48 ++ WORD $0xffe10b // bstrpick.w r11, r8, 63, 56 ++ XOR R6, R10, R10 ++ XOR R6, R11, R11 ++ MASKNEZ R10, R17, R12 ++ MASKNEZ R11, R17, R13 ++ ADDV R7, R12, R7 ++ ADDV R7, R13, R7 + + MOVV R9, R4 +- JMP loop ++ JMP loop + + tail: +- BEQ R4, R5, done ++ BEQ R4, R5, done + MOVBU (R4), R8 + ADDV $1, R4 +- BNE R6, R8, tail ++ BNE R6, R8, tail + ADDV $1, R7 +- JMP tail ++ JMP tail + + done: + MOVV R7, R4 +diff --git a/src/internal/bytealg/equal_loong64.s b/src/internal/bytealg/equal_loong64.s +index 4cc31d5e46..5d5d591a2c 100644 +--- a/src/internal/bytealg/equal_loong64.s ++++ b/src/internal/bytealg/equal_loong64.s +@@ -12,57 +12,61 @@ TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 + // R4 = a_base + // R5 = b_base + // R6 = size +- JMP equalbody<>(SB) ++ JMP equalbody<>(SB) + + // memequal_varlen(a, b unsafe.Pointer) bool + TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17 + // R4 = a_base + // R5 = b_base +- MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure +- JMP equalbody<>(SB) ++ MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure ++ JMP equalbody<>(SB) + ++// input: ++// R4 = a_base ++// R5 = b_base ++// R6 = size + TEXT equalbody<>(SB),NOSPLIT|NOFRAME,$0 +- BEQ R4, R5, eq +- ADDV R4, R6, R6 // end ++ BEQ R4, R5, eq ++ ADDV R4, R6, R6 // end + + loop_16byte: +- ADDV $16, R4, R9 +- BLT R6, R9, load8byte ++ ADDV $16, R4, R9 ++ BLT R6, R9, load8byte + MOVV (R4), R7 + MOVV (R5), R8 + MOVV 8(R4), R10 + MOVV 8(R5), R11 + MOVV R9, R4 +- XOR R7, R8, R7 +- XOR R10, R11, R10 +- OR R10, R7, R7 ++ XOR R7, R8, R7 ++ XOR R10, R11, R10 ++ OR R10, R7, R7 + ADDV $16, R5 +- BEQ R7, loop_16byte ++ BEQ R7, loop_16byte + +- MOVB R0, R4 ++ MOVB R0, R4 + RET + + load8byte: +- ADDV $8, R4, R9 +- BLT R6, R9, tail ++ ADDV $8, R4, R9 ++ BLT R6, R9, tail + MOVV (R4), R7 + MOVV (R5), R8 + MOVV R9, R4 + ADDV $8, R5 +- BEQ R7, R8, tail ++ BEQ R7, R8, tail + +- MOVB R0, R4 ++ MOVB R0, R4 + RET + + tail: +- BEQ R4, R6, eq ++ BEQ R4, R6, eq + MOVBU (R4), R7 + MOVBU (R5), R8 + ADDV $1, R4 + ADDV $1, R5 +- BEQ R7, R8, tail ++ BEQ R7, R8, tail + +- MOVB R0, R4 ++ MOVB R0, R4 + RET + + eq: +diff --git a/src/internal/bytealg/index_loong64.s b/src/internal/bytealg/index_loong64.s +index 221d0332a4..7f7190b3be 100644 +--- a/src/internal/bytealg/index_loong64.s ++++ b/src/internal/bytealg/index_loong64.s +@@ -1,4 +1,4 @@ +-// Copyright 2018 The Go Authors. All rights reserved. ++// Copyright 2024 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. + +@@ -6,12 +6,12 @@ + #include "textflag.h" + + TEXT ·Index(SB),NOSPLIT,$0-56 +- MOVV R7, R6 // R6 = separator pointer +- MOVV R8, R7 // R7 = separator length +- JMP indexbody<>(SB) ++ MOVV R7, R6 // R6 = separator pointer ++ MOVV R8, R7 // R7 = separator length ++ JMP indexbody<>(SB) + + TEXT ·IndexString(SB),NOSPLIT,$0-40 +- JMP indexbody<>(SB) ++ JMP indexbody<>(SB) + + // input: + // R4 = string +@@ -23,108 +23,108 @@ TEXT indexbody<>(SB),NOSPLIT,$0 + // to avoid repeatedly re-load it again and again + // for sebsequent substring comparisons + SUBV R7, R5, R8 +- ADDV $1, R4, R9 // store base for later ++ ADDV $1, R4, R9 // store base for later + MOVV $8, R5 +- ADDV R4, R8 // end +- BLT R5, R7, len_gt_8 ++ ADDV R4, R8 // end ++ BLT R5, R7, len_gt_8 + + len_le_8: +- AND $0x8, R7, R5 +- BNE R5, len_8 +- AND $0x4, R7, R5 +- BNE R5, len_4_7 ++ AND $0x8, R7, R5 ++ BNE R5, len_8 ++ AND $0x4, R7, R5 ++ BNE R5, len_4_7 + + len_2_3: +- AND $0x1, R7, R5 +- BNE R5, len_3 ++ AND $0x1, R7, R5 ++ BNE R5, len_3 + + len_2: +- MOVHU (R6), R10 ++ MOVHU (R6), R10 + loop_2: +- BLT R8, R4, not_found +- MOVHU (R4), R11 ++ BLT R8, R4, not_found ++ MOVHU (R4), R11 + ADDV $1, R4 +- BNE R10, R11, loop_2 +- JMP found ++ BNE R10, R11, loop_2 ++ JMP found + + len_3: + MOVHU (R6), R10 + MOVBU 2(R6), R11 + loop_3: +- BLT R8, R4, not_found +- MOVHU (R4), R12 ++ BLT R8, R4, not_found ++ MOVHU (R4), R12 + ADDV $1, R4 +- BNE R10, R12, loop_3 +- MOVBU 1(R4), R12 +- BNE R11, R12, loop_3 +- JMP found ++ BNE R10, R12, loop_3 ++ MOVBU 1(R4), R12 ++ BNE R11, R12, loop_3 ++ JMP found + + len_4_7: +- AND $0x2, R7, R5 +- BNE R5, len_6_7 +- AND $0x1, R7, R5 +- BNE R5, len_5 ++ AND $0x2, R7, R5 ++ BNE R5, len_6_7 ++ AND $0x1, R7, R5 ++ BNE R5, len_5 + + len_4: +- MOVWU (R6), R10 ++ MOVWU (R6), R10 + loop_4: +- BLT R8, R4, not_found +- MOVWU (R4), R11 ++ BLT R8, R4, not_found ++ MOVWU (R4), R11 + ADDV $1, R4 +- BNE R10, R11, loop_4 +- JMP found ++ BNE R10, R11, loop_4 ++ JMP found + len_5: + MOVWU (R6), R10 + MOVBU 4(R6), R11 + loop_5: +- BLT R8, R4, not_found +- MOVWU (R4), R12 ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 + ADDV $1, R4 +- BNE R10, R12, loop_5 +- MOVBU 3(R4), R12 +- BNE R11, R12, loop_5 +- JMP found ++ BNE R10, R12, loop_5 ++ MOVBU 3(R4), R12 ++ BNE R11, R12, loop_5 ++ JMP found + + len_6_7: +- AND $0x1, R7, R5 +- BNE R5, len_7 ++ AND $0x1, R7, R5 ++ BNE R5, len_7 + + len_6: + MOVWU (R6), R10 + MOVHU 4(R6), R11 + loop_6: +- BLT R8, R4, not_found +- MOVWU (R4), R12 ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 + ADDV $1, R4 +- BNE R10, R12, loop_6 +- MOVHU 3(R4), R12 +- BNE R11, R12, loop_6 +- JMP found ++ BNE R10, R12, loop_6 ++ MOVHU 3(R4), R12 ++ BNE R11, R12, loop_6 ++ JMP found + + len_7: + MOVWU (R6), R10 + MOVWU 3(R6), R11 + loop_7: +- BLT R8, R4, not_found +- MOVWU (R4), R12 ++ BLT R8, R4, not_found ++ MOVWU (R4), R12 + ADDV $1, R4 +- BNE R10, R12, loop_7 +- MOVWU 2(R4), R12 +- BNE R11, R12, loop_7 +- JMP found ++ BNE R10, R12, loop_7 ++ MOVWU 2(R4), R12 ++ BNE R11, R12, loop_7 ++ JMP found + + len_8: + MOVV (R6), R10 + loop_8: +- BLT R8, R4, not_found ++ BLT R8, R4, not_found + MOVV (R4), R11 + ADDV $1, R4 +- BNE R10, R11, loop_8 +- JMP found ++ BNE R10, R11, loop_8 ++ JMP found + + len_gt_8: + MOVV $16, R5 +- BLT R5, R7, len_gt_16 ++ BLT R5, R7, len_gt_16 + + len_9_16: + MOVV (R6), R10 +@@ -132,17 +132,17 @@ len_9_16: + MOVV (R6)(R7), R11 + SUBV $1, R7 + loop_9_16: +- BLT R8, R4, not_found ++ BLT R8, R4, not_found + MOVV (R4), R12 + ADDV $1, R4 +- BNE R10, R12, loop_9_16 ++ BNE R10, R12, loop_9_16 + MOVV (R4)(R7), R12 +- BNE R11, R12, loop_9_16 +- JMP found ++ BNE R11, R12, loop_9_16 ++ JMP found + + len_gt_16: + MOVV $24, R5 +- BLT R5, R7, len_25_32 ++ BLT R5, R7, len_25_32 + + len_17_24: + MOVV (R6), R10 +@@ -151,15 +151,15 @@ len_17_24: + MOVV (R6)(R7), R12 + SUBV $1, R7 + loop_17_24: +- BLT R8, R4, not_found ++ BLT R8, R4, not_found + MOVV (R4), R13 + ADDV $1, R4 +- BNE R10, R13, loop_17_24 ++ BNE R10, R13, loop_17_24 + MOVV 7(R4), R13 +- BNE R11, R13, loop_17_24 ++ BNE R11, R13, loop_17_24 + MOVV (R4)(R7), R13 +- BNE R12, R13, loop_17_24 +- JMP found ++ BNE R12, R13, loop_17_24 ++ JMP found + + len_25_32: + MOVV (R6), R10 +@@ -169,17 +169,17 @@ len_25_32: + MOVV (R6)(R7), R13 + SUBV $1, R7 + loop_25_32: +- BLT R8, R4, not_found ++ BLT R8, R4, not_found + MOVV (R4), R14 + ADDV $1, R4 +- BNE R10, R14, loop_25_32 ++ BNE R10, R14, loop_25_32 + MOVV 7(R4), R14 +- BNE R11, R14, loop_25_32 ++ BNE R11, R14, loop_25_32 + MOVV 15(R4), R14 +- BNE R12, R14, loop_25_32 ++ BNE R12, R14, loop_25_32 + MOVV (R4)(R7), R14 +- BNE R13, R14, loop_25_32 +- JMP found ++ BNE R13, R14, loop_25_32 ++ JMP found + + found: + SUBV R9, R4 +diff --git a/src/internal/bytealg/indexbyte_loong64.s b/src/internal/bytealg/indexbyte_loong64.s +index 7811741423..b5f8f9cdbc 100644 +--- a/src/internal/bytealg/indexbyte_loong64.s ++++ b/src/internal/bytealg/indexbyte_loong64.s +@@ -12,17 +12,17 @@ TEXT ·IndexByte(SB),NOSPLIT,$0-40 + // R7 = byte to find + ADDV R4, R5 // end + MOVV R4, R6 // store base for later +- AND $0xff, R7 +- JMP indexbytebody<>(SB) ++ AND $0xff, R7 ++ JMP indexbytebody<>(SB) + + TEXT ·IndexByteString(SB),NOSPLIT,$0-32 + // R4 = s_base + // R5 = s_len + // R6 = byte to find +- AND $0xff, R6, R7 ++ AND $0xff, R6, R7 + ADDV R4, R5 // end + MOVV R4, R6 // store base for later +- JMP indexbytebody<>(SB) ++ JMP indexbytebody<>(SB) + + // input: + // R4: b_base +@@ -32,42 +32,42 @@ TEXT ·IndexByteString(SB),NOSPLIT,$0-32 + TEXT indexbytebody<>(SB),NOSPLIT,$0 + loop: + ADDV $8, R4, R10 +- BLT R5, R10, tail ++ BLT R5, R10, tail + MOVV (R4), R8 + +- AND $0xff, R8, R9 +- BEQ R7, R9, found ++ AND $0xff, R8, R9 ++ BEQ R7, R9, found + +- WORD $0xcf2109 // bstrpick.w r9, r8, 15, 8 +- BEQ R7, R9, byte_1th ++ WORD $0xcf2109 // bstrpick.w r9, r8, 15, 8 ++ BEQ R7, R9, byte_1th + +- WORD $0xd74109 // bstrpick.w r9, r8, 23, 16 +- BEQ R7, R9, byte_2th ++ WORD $0xd74109 // bstrpick.w r9, r8, 23, 16 ++ BEQ R7, R9, byte_2th + +- WORD $0xdf6109 // bstrpick.w r9, r8, 31, 24 +- BEQ R7, R9, byte_3th ++ WORD $0xdf6109 // bstrpick.w r9, r8, 31, 24 ++ BEQ R7, R9, byte_3th + +- WORD $0xe78109 // bstrpick.w r9, r8, 39, 32 +- BEQ R7, R9, byte_4th ++ WORD $0xe78109 // bstrpick.w r9, r8, 39, 32 ++ BEQ R7, R9, byte_4th + +- WORD $0xefa109 // bstrpick.w r9, r8, 47, 40 +- BEQ R7, R9, byte_5th ++ WORD $0xefa109 // bstrpick.w r9, r8, 47, 40 ++ BEQ R7, R9, byte_5th + +- WORD $0xf7c109 // bstrpick.w r9, r8, 55, 48 +- BEQ R7, R9, byte_6th ++ WORD $0xf7c109 // bstrpick.w r9, r8, 55, 48 ++ BEQ R7, R9, byte_6th + +- WORD $0xffe109 // bstrpick.w r9, r8, 63, 56 +- BEQ R7, R9, byte_7th ++ WORD $0xffe109 // bstrpick.w r9, r8, 63, 56 ++ BEQ R7, R9, byte_7th + + MOVV R10, R4 +- JMP loop ++ JMP loop + + tail: +- BEQ R4, R5, notfound +- MOVBU (R4), R8 +- BEQ R7, R8, found ++ BEQ R4, R5, notfound ++ MOVBU (R4), R8 ++ BEQ R7, R8, found + ADDV $1, R4 +- JMP tail ++ JMP tail + + byte_1th: + ADDV $1, R4 +-- +2.38.1 + diff --git a/0011-cmd-internal-obj-loong64-optimize-immediate-loading.patch b/0011-cmd-internal-obj-loong64-optimize-immediate-loading.patch new file mode 100644 index 0000000..6136b63 --- /dev/null +++ b/0011-cmd-internal-obj-loong64-optimize-immediate-loading.patch @@ -0,0 +1,776 @@ +From a08a479c526bcc63bf24e69ff7fa1d37a1179e1f Mon Sep 17 00:00:00 2001 +From: limeidan +Date: Thu, 11 Jul 2024 21:03:45 +0800 +Subject: [PATCH 11/44] cmd/internal/obj/loong64: optimize immediate loading +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + + | old | new | + | sec/op | sec/op vs base | +BinaryTree17 11.08 ± 2% 11.16 ± 1% ~ (p=0.529 n=10) +Fannkuch11 2.716 ± 0% 2.737 ± 0% +0.79% (p=0.000 n=10) +FmtFprintfEmpty 67.37n ± 0% 66.42n ± 0% -1.41% (p=0.000 n=10) +FmtFprintfString 95.28n ± 0% 90.85n ± 0% -4.64% (p=0.000 n=10) +FmtFprintfInt 97.69n ± 0% 98.06n ± 0% +0.38% (p=0.000 n=10) +FmtFprintfIntInt 149.1n ± 0% 147.4n ± 0% -1.14% (p=0.000 n=10) +FmtFprintfPrefixedInt 223.6n ± 0% 196.5n ± 0% -12.10% (p=0.000 n=10) +FmtFprintfFloat 290.9n ± 0% 281.6n ± 1% -3.21% (p=0.000 n=10) +FmtManyArgs 670.6n ± 0% 642.6n ± 0% -4.18% (p=0.000 n=10) +GobDecode 10.26m ± 1% 10.23m ± 1% ~ (p=0.105 n=10) +GobEncode 12.09m ± 1% 11.94m ± 1% -1.24% (p=0.000 n=10) +Gzip 316.9m ± 0% 315.9m ± 0% -0.32% (p=0.001 n=10) +Gunzip 65.48m ± 0% 59.77m ± 0% -8.72% (p=0.000 n=10) +HTTPClientServer 70.36µ ± 0% 68.72µ ± 0% -2.34% (p=0.000 n=10) +JSONEncode 13.61m ± 1% 13.19m ± 1% -3.13% (p=0.000 n=10) +JSONDecode 57.52m ± 1% 54.15m ± 1% -5.86% (p=0.000 n=10) +Mandelbrot200 4.577m ± 0% 4.572m ± 0% -0.10% (p=0.002 n=10) +GoParse 6.466m ± 0% 6.363m ± 0% -1.58% (p=0.000 n=10) +RegexpMatchEasy0_32 89.20n ± 0% 87.72n ± 0% -1.65% (p=0.000 n=10) +RegexpMatchEasy0_1K 748.6n ± 0% 907.6n ± 0% +21.22% (p=0.000 n=10) +RegexpMatchEasy1_32 94.14n ± 0% 93.81n ± 0% -0.35% (p=0.000 n=10) +RegexpMatchEasy1_1K 832.1n ± 0% 953.6n ± 0% +14.59% (p=0.000 n=10) +RegexpMatchMedium_32 982.7n ± 0% 1018.0n ± 0% +3.59% (p=0.000 n=10) +RegexpMatchMedium_1K 30.51µ ± 0% 30.00µ ± 0% -1.65% (p=0.000 n=10) +RegexpMatchHard_32 1.721µ ± 0% 1.664µ ± 0% -3.34% (p=0.000 n=10) +RegexpMatchHard_1K 50.76µ ± 0% 50.92µ ± 0% +0.32% (p=0.000 n=10) +Revcomp 870.5m ± 0% 710.5m ± 0% -18.38% (p=0.000 n=10) +Template 93.18m ± 1% 93.67m ± 1% ~ (p=0.123 n=10) +TimeParse 309.2n ± 0% 307.8n ± 0% -0.45% (p=0.000 n=10) +TimeFormat 401.5n ± 0% 394.2n ± 0% -1.82% (p=0.000 n=10) +geomean 72.73µ 71.70µ -1.41% + +Change-Id: Id8d342ef3bb82a420434b2b841674683efef67be +--- + src/cmd/asm/internal/asm/endtoend_test.go | 2 + + .../asm/internal/asm/testdata/loong64enc1.s | 24 ++ + .../asm/internal/asm/testdata/loong64enc2.s | 46 +++ + .../asm/internal/asm/testdata/loong64enc3.s | 65 ++++ + .../asm/internal/asm/testdata/loong64enc4.s | 42 +++ + .../asm/internal/asm/testdata/loong64enc5.s | 17 + + src/cmd/internal/obj/loong64/a.out.go | 54 ++- + src/cmd/internal/obj/loong64/asm.go | 321 +++++++++++++++++- + src/cmd/internal/obj/loong64/cnames.go | 14 + + 9 files changed, 579 insertions(+), 6 deletions(-) + create mode 100644 src/cmd/asm/internal/asm/testdata/loong64enc4.s + create mode 100644 src/cmd/asm/internal/asm/testdata/loong64enc5.s + +diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go +index 6e1aa1cd95..3760b77625 100644 +--- a/src/cmd/asm/internal/asm/endtoend_test.go ++++ b/src/cmd/asm/internal/asm/endtoend_test.go +@@ -465,6 +465,8 @@ func TestLOONG64Encoder(t *testing.T) { + testEndToEnd(t, "loong64", "loong64enc1") + testEndToEnd(t, "loong64", "loong64enc2") + testEndToEnd(t, "loong64", "loong64enc3") ++ testEndToEnd(t, "loong64", "loong64enc4") ++ testEndToEnd(t, "loong64", "loong64enc5") + testEndToEnd(t, "loong64", "loong64") + } + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 4a88aca031..3a3eb10a74 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -516,3 +516,27 @@ lable2: + XVPCNTH X3, X2 // 62249c76 + XVPCNTW X3, X2 // 62289c76 + XVPCNTV X3, X2 // 622c9c76 ++ ++ // MOVV C_DCON12_0, r ++ MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 ++ MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 ++ ++ // MOVV C_UCON, r ++ MOVV $0x54321000, R4 // MOVV $1412567040, R4 // 2464a814 ++ MOVV $0xffffffff8432f000, R4 // MOVV $-2077036544, R4 // e4650815 ++ ++ // MOVV C_ADDCON, r ++ MOVV $0xfffffffffffff821, R4 // MOVV $-2015, R4 // 0484e002 ++ ++ // MOVV C_ANDCON, r ++ MOVV $0x821, R4 // MOVV $2081, R4 // 0484a003 ++ ++ // ADDV C_SCON, [r1], r2 ++ ADDV $0x321, R4 // ADDV $801, R4 // 8484cc02 ++ ADDV $0x321, R5, R4 // ADDV $801, R5, R4 // a484cc02 ++ ADDV $0xfffffffffffffc21, R4 // ADDV $-991, R4 // 8484f002 ++ ADDV $0xfffffffffffffc21, R5, R4 // ADDV $-991, R5, R4 // a484f002 ++ ++ // AND C_SCON, [r1], r2 ++ AND $0x321, R4 // AND $801, R4 // 84844c03 ++ AND $0x321, R5, R4 // AND $801, R5, R4 // a4844c03 +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc2.s b/src/cmd/asm/internal/asm/testdata/loong64enc2.s +index e497b83627..ee3bad74b1 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc2.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc2.s +@@ -77,3 +77,49 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 + MOVH name(SB), R4 // 1e00001ac4034028 + MOVHU R4, name(SB) // 1e00001ac4034029 + MOVHU name(SB), R4 // 1e00001ac403402a ++ ++ // MOVV C_DCON12_20S, r ++ MOVV $0x273fffff80000000, R4 // MOVV $2828260563841187840, R4 // 0400001584cc0903 ++ MOVV $0xf73fffff80000000, R4 // MOVV $-630503949979353088, R4 // 0400001584cc3d03 ++ ++ // MOVV C_DCON20S_20, r ++ MOVV $0xfff800000f000000, R4 // MOVV $-2251799562027008, R4 // 04001e1404000017 ++ ++ // MOVV C_DCON12_12S, r ++ MOVV $0x273ffffffffff800, R4 // MOVV $2828260565988669440, R4 // 0400e00284cc0903 ++ MOVV $0xf73ffffffffff800, R4 // MOVV $-630503947831871488, R4 // 0400e00284cc3d03 ++ ++ // MOVV C_DCON20S_12S, r ++ MOVV $0xfff80000fffff800, R4 // MOVV $-2251795518720000, R4 // 0400a00204000017 ++ MOVV $0xfff8000000000000, R4 // MOVV $-2251799813685248, R4 // 0400800204000017 ++ ++ // MOVV C_DCON12_12U, r ++ MOVV $0x2730000000000800, R4 // MOVV $2823756966361303040, R4 // 0400a00384cc0903 ++ MOVV $0xf730000000000800, R4 // MOVV $-635007547459237888, R4 // 0400a00384cc3d03 ++ ++ // MOVV C_DCON20S_12U, r ++ MOVV $0xfff8000000000800, R4 // MOVV $-2251799813683200, R4 // 0400a00304000017 ++ ++ // ADDV/AND C_DCON12_0, [r1], r2 ++ ADDV $0x3210000000000000, R4 // ADDV $3607383301523767296, R4 // 1e840c0384f81000 ++ ADDV $0x3210000000000000, R5, R4 // ADDV $3607383301523767296, R5, R4 // 1e840c03a4f81000 ++ ADDV $0xc210000000000000, R4 // ADDV $-4463067230724161536, R4 // 1e84300384f81000 ++ ADDV $0xc210000000000000, R5, R4 // ADDV $-4463067230724161536, R5, R4 // 1e843003a4f81000 ++ AND $0x3210000000000000, R4 // AND $3607383301523767296, R4 // 1e840c0384f81400 ++ AND $0x3210000000000000, R5, R4 // AND $3607383301523767296, R5, R4 // 1e840c03a4f81400 ++ AND $0xc210000000000000, R4 // AND $-4463067230724161536, R4 // 1e84300384f81400 ++ AND $0xc210000000000000, R5, R4 // AND $-4463067230724161536, R5, R4 // 1e843003a4f81400 ++ ++ // ADDV/AND C_UCON, [r1], r2 ++ ADDV $0x43210000, R4 // ADDV $1126236160, R4 // 1e42861484f81000 ++ ADDV $0x43210000, R5, R4 // ADDV $1126236160, R5, R4 // 1e428614a4f81000 ++ ADDV $0xffffffffc3210000, R4 // ADDV $-1021247488, R4 // 1e42861584f81000 ++ ADDV $0xffffffffc3210000, R5, R4 // ADDV $-1021247488, R5, R4 // 1e428615a4f81000 ++ AND $0x43210000, R4 // AND $1126236160, R4 // 1e42861484f81400 ++ AND $0x43210000, R5, R4 // AND $1126236160, R5, R4 // 1e428614a4f81400 ++ AND $0xffffffffc3210000, R4 // AND $-1021247488, R4 // 1e42861584f81400 ++ AND $0xffffffffc3210000, R5, R4 // AND $-1021247488, R5, R4 // 1e428615a4f81400 ++ ++ // AND C_ADDCON, [r1], r2 ++ AND $0xfffffffffffffc21, R4 // AND $-991, R4 // 1e84b00284f81400 ++ AND $0xfffffffffffffc21, R5, R4 // AND $-991, R5, R4 // 1e84b002a4f81400 +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc3.s b/src/cmd/asm/internal/asm/testdata/loong64enc3.s +index 2600884309..2d83bd719a 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc3.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc3.s +@@ -121,3 +121,68 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 + XOR $74565, R4, R5 // 5e020014de178d0385f81500 + XOR $4097, R4 // 3e000014de07800384f81500 + XOR $4097, R4, R5 // 3e000014de07800385f81500 ++ ++ // MOVV C_DCON32_12S, r ++ MOVV $0x27312345fffff800, R4 // MOVV $2824077224892692480, R4 // 0400a002a468241684cc0903 ++ MOVV $0xf7312345fffff800, R4 // MOVV $-634687288927848448, R4 // 0400a002a468241684cc3d03 ++ ++ // MOVV C_DCON32_0, r ++ MOVV $0x2731234500000000, R4 // MOVV $2824077220597727232, R4 // 04008002a468241684cc0903 ++ MOVV $0xf731234500000000, R4 // MOVV $-634687293222813696, R4 // 04008002a468241684cc3d03 ++ ++ // MOVV C_DCON32_20, r ++ MOVV $0x2731234512345000, R4 // MOVV $2824077220903145472, R4 // a4682414a468241684cc0903 ++ MOVV $0xf731234512345000, R4 // MOVV $-634687292917395456, R4 // a4682414a468241684cc3d03 ++ ++ // MOVV C_DCON12_32S, r ++ MOVV $0x273fffff80000800, R4 // MOVV $2828260563841189888, R4 // 040000158400a00384cc0903 ++ MOVV $0xf73fffff80000800, R4 // MOVV $-630503949979351040, R4 // 040000158400a00384cc3d03 ++ ++ // MOVV C_DCON20S_32, r ++ MOVV $0xfff8000080000800, R4 // MOVV $-2251797666199552, R4 // 040000158400a00304000017 ++ ++ // MOVV C_DCON32_12U, r ++ MOVV $0x2731234500000800, R4 // MOVV $2824077220597729280, R4 // 0400a003a468241684cc0903 ++ MOVV $0xf731234500000800, R4 // MOVV $-634687293222811648, R4 // 0400a003a468241684cc3d03 ++ ++ // ADDV/AND C_DCON12_20S, [r1], r2 ++ ADDV $0x273fffff80000000, R4 // ADDV $2828260563841187840, R4 // 1e000015decf090384f81000 ++ ADDV $0x273fffff80000000, R4, R5 // ADDV $2828260563841187840, R4, R5 // 1e000015decf090385f81000 ++ AND $0x273fffff80000000, R4 // AND $2828260563841187840, R4 // 1e000015decf090384f81400 ++ AND $0x273fffff80000000, R4, R5 // AND $2828260563841187840, R4, R5 // 1e000015decf090385f81400 ++ ++ // ADDV/AND C_DCON20S_20, [r1], r2 ++ ADDV $0xfff800000f000000, R4 // ADDV $-2251799562027008, R4 // 1e001e141e00001784f81000 ++ ADDV $0xfff800000f000000, R4, R5 // ADDV $-2251799562027008, R4, R5 // 1e001e141e00001785f81000 ++ AND $0xfff800000f000000, R4 // AND $-2251799562027008, R4 // 1e001e141e00001784f81400 ++ AND $0xfff800000f000000, R4, R5 // AND $-2251799562027008, R4, R5 // 1e001e141e00001785f81400 ++ ++ // ADDV/AND C_DCON12_12S, [r1], r2 ++ ADDV $0x273ffffffffff800, R4 // ADDV $2828260565988669440, R4 // 1e00e002decf090384f81000 ++ ADDV $0x273ffffffffff800, R4, R5 // ADDV $2828260565988669440, R4, R5 // 1e00e002decf090385f81000 ++ AND $0x273ffffffffff800, R4 // AND $2828260565988669440, R4 // 1e00e002decf090384f81400 ++ AND $0x273ffffffffff800, R4, R5 // AND $2828260565988669440, R4, R5 // 1e00e002decf090385f81400 ++ ++ // ADDV/AND C_DCON20S_12S, [r1], r2 ++ ADDV $0xfff80000fffff800, R4 // ADDV $-2251795518720000, R4 // 1e00a0021e00001784f81000 ++ ADDV $0xfff80000fffff800, R4, R5 // ADDV $-2251795518720000, R4, R5 // 1e00a0021e00001785f81000 ++ AND $0xfff80000fffff800, R4 // AND $-2251795518720000, R4 // 1e00a0021e00001784f81400 ++ AND $0xfff80000fffff800, R4, R5 // AND $-2251795518720000, R4, R5 // 1e00a0021e00001785f81400 ++ ++ // ADDV/AND C_DCON20S_0, [r1], r2 ++ ADDV $0xfff8000000000000, R4 // ADDV $-2251799813685248, R4 // 1e0080021e00001784f81000 ++ ADDV $0xfff8000000000000, R4, R5 // ADDV $-2251799813685248, R4, R5 // 1e0080021e00001785f81000 ++ AND $0xfff8000000000000, R4 // AND $-2251799813685248, R4 // 1e0080021e00001784f81400 ++ AND $0xfff8000000000000, R4, R5 // AND $-2251799813685248, R4, R5 // 1e0080021e00001785f81400 ++ ++ // ADDV/AND C_DCON12_12U, [r1], r2 ++ ADDV $0x2730000000000800, R4 // ADDV $2823756966361303040, R4 // 1e00a003decf090384f81000 ++ ADDV $0x2730000000000800, R4, R5 // ADDV $2823756966361303040, R4, R5 // 1e00a003decf090385f81000 ++ AND $0x2730000000000800, R4 // AND $2823756966361303040, R4 // 1e00a003decf090384f81400 ++ AND $0x2730000000000800, R4, R5 // AND $2823756966361303040, R4, R5 // 1e00a003decf090385f81400 ++ ++ // ADDV/AND C_DCON20S_12U, [r1], r2 ++ ADDV $0xfff8000000000800, R4 // ADDV $-2251799813683200, R4 // 1e00a0031e00001784f81000 ++ ADDV $0xfff8000000000800, R4, R5 // ADDV $-2251799813683200, R4, R5 // 1e00a0031e00001785f81000 ++ AND $0xfff8000000000800, R4 // AND $-2251799813683200, R4 // 1e00a0031e00001784f81400 ++ AND $0xfff8000000000800, R4, R5 // AND $-2251799813683200, R4, R5 // 1e00a0031e00001785f81400 +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc4.s b/src/cmd/asm/internal/asm/testdata/loong64enc4.s +new file mode 100644 +index 0000000000..16c06a3501 +--- /dev/null ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc4.s +@@ -0,0 +1,42 @@ ++// Copyright 2024 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. ++ ++#include "../../../../../runtime/textflag.h" ++ ++TEXT asmtest(SB),DUPOK|NOSPLIT,$0 ++ // ADDV/AND C_DCON32_12S, [r1], r2 ++ ADDV $0x27312345fffff800, R4 // ADDV $2824077224892692480, R4 // 1e00a002be682416decf090384f81000 ++ ADDV $0x27312345fffff800, R4, R5 // ADDV $2824077224892692480, R4, R5 // 1e00a002be682416decf090385f81000 ++ AND $0x27312345fffff800, R4 // AND $2824077224892692480, R4 // 1e00a002be682416decf090384f81400 ++ AND $0x27312345fffff800, R4, R5 // AND $2824077224892692480, R4, R5 // 1e00a002be682416decf090385f81400 ++ ++ // ADDV/AND C_DCON32_0, [r1], r2 ++ ADDV $0x2731234500000000, R4 // ADDV $2824077220597727232, R4 // 1e008002be682416decf090384f81000 ++ ADDV $0x2731234500000000, R4, R5 // ADDV $2824077220597727232, R4, R5 // 1e008002be682416decf090385f81000 ++ AND $0x2731234500000000, R4 // AND $2824077220597727232, R4 // 1e008002be682416decf090384f81400 ++ AND $0x2731234500000000, R4, R5 // AND $2824077220597727232, R4, R5 // 1e008002be682416decf090385f81400 ++ ++ // ADDV/AND C_DCON32_20, [r1], r2 ++ ADDV $0x2731234512345000, R4 // ADDV $2824077220903145472, R4 // be682414be682416decf090384f81000 ++ ADDV $0x2731234512345000, R4, R5 // ADDV $2824077220903145472, R4, R5 // be682414be682416decf090385f81000 ++ AND $0x2731234512345000, R4 // AND $2824077220903145472, R4 // be682414be682416decf090384f81400 ++ AND $0x2731234512345000, R4, R5 // AND $2824077220903145472, R4, R5 // be682414be682416decf090385f81400 ++ ++ // ADDV/AND C_DCON12_32S, [r1], r2 ++ ADDV $0x273fffff80000800, R4 // ADDV $2828260563841189888, R4 // 1e000015de03a003decf090384f81000 ++ ADDV $0x273fffff80000800, R4, R5 // ADDV $2828260563841189888, R4, R5 // 1e000015de03a003decf090385f81000 ++ AND $0x273fffff80000800, R4 // AND $2828260563841189888, R4 // 1e000015de03a003decf090384f81400 ++ AND $0x273fffff80000800, R4, R5 // AND $2828260563841189888, R4, R5 // 1e000015de03a003decf090385f81400 ++ ++ // ADDV/AND C_DCON20S_32, [r1], r2 ++ ADDV $0xfff8000080000800, R4 // ADDV $-2251797666199552, R4 // 1e000015de03a0031e00001784f81000 ++ ADDV $0xfff8000080000800, R4, R5 // ADDV $-2251797666199552, R4, R5 // 1e000015de03a0031e00001785f81000 ++ AND $0xfff8000080000800, R4 // AND $-2251797666199552, R4 // 1e000015de03a0031e00001784f81400 ++ AND $0xfff8000080000800, R4, R5 // AND $-2251797666199552, R4, R5 // 1e000015de03a0031e00001785f81400 ++ ++ // ADDV/AND C_DCON32_12U, [r1], r2 ++ ADDV $0x2731234500000800, R4 // ADDV $2824077220597729280, R4 // 1e00a003be682416decf090384f81000 ++ ADDV $0x2731234500000800, R4, R5 // ADDV $2824077220597729280, R4, R5 // 1e00a003be682416decf090385f81000 ++ AND $0x2731234500000800, R4 // AND $2824077220597729280, R4 // 1e00a003be682416decf090384f81400 ++ AND $0x2731234500000800, R4, R5 // AND $2824077220597729280, R4, R5 // 1e00a003be682416decf090385f81400 +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc5.s b/src/cmd/asm/internal/asm/testdata/loong64enc5.s +new file mode 100644 +index 0000000000..423e5c3b01 +--- /dev/null ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc5.s +@@ -0,0 +1,17 @@ ++// Copyright 2024 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. ++ ++#include "../../../../../runtime/textflag.h" ++ ++TEXT asmtest(SB),DUPOK|NOSPLIT,$0 ++ // ADDV/AND C_DCON, [r1], r2 ++ ADDV $0xfedcba9876543210, R4 // ADDV $-81985529216486896, R4 // 7ea8ec14de4388031e539717deb73f0384f81000 ++ ADDV $0xfedcba9876543210, R5, R4 // ADDV $-81985529216486896, R5, R4 // 7ea8ec14de4388031e539717deb73f03a4f81000 ++ ADDV $0x4edcba9876543210, R4 // ADDV $5682621993817747984, R4 // 7ea8ec14de4388031e539717deb7130384f81000 ++ ADDV $0x4edcba9876543210, R5, R4 // ADDV $5682621993817747984, R5, R4 // 7ea8ec14de4388031e539717deb71303a4f81000 ++ AND $0x4edcba9876543210, R4 // AND $5682621993817747984, R4 // 7ea8ec14de4388031e539717deb7130384f81400 ++ AND $0x4edcba9876543210, R5, R4 // AND $5682621993817747984, R5, R4 // 7ea8ec14de4388031e539717deb71303a4f81400 ++ AND $0xfedcba9876543210, R4 // AND $-81985529216486896, R4 // 7ea8ec14de4388031e539717deb73f0384f81400 ++ AND $0xfedcba9876543210, R5, R4 // AND $-81985529216486896, R5, R4 // 7ea8ec14de4388031e539717deb73f03a4f81400 ++ +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 53b005af4d..b2207c2523 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -328,12 +328,58 @@ const ( + C_ZCON + C_SCON // 12 bit signed + C_UCON // 32 bit signed, low 12 bits 0 ++ ++ // When the immediate value is SCON, it can choose either the ADDCON implementation ++ // or the ANDCON implementation, using ADD0CON/AND0CON to distinguish them, so that ++ // the program can choose the implementation with fewer instructions. + C_ADD0CON + C_AND0CON +- C_ADDCON // -0x800 <= v < 0 +- C_ANDCON // 0 < v <= 0xFFF +- C_LCON // other 32 +- C_DCON // other 64 (could subdivide further) ++ ++ C_ADDCON // -0x800 <= v < 0 ++ C_ANDCON // 0 < v <= 0xFFF ++ C_LCON // other 32 ++ ++ // 64 bit signed, lo32 bits 0, hi20 bits are not 0, hi12 bits can ++ // be obtained by sign extension of the hi20 bits. ++ C_DCON20S_0 ++ // 64 bit signed, lo52 bits 0, hi12 bits are not 0. ++ C_DCON12_0 ++ // 64 bit signed, lo32 bits 0, hi32 bits are not 0. ++ C_DCON32_0 ++ // 64 bit signed, lo12 bits 0, lo20 bits are not 0, hi20 bits can be ++ // obtained by sign extension of the lo20 bits, other bits are not 0. ++ C_DCON12_20S ++ // 64 bit signed, lo12 bits 0, hi20 bits are not 0, hi12 bits can be ++ // obtained by sign extension of the hi20 bits, other bits are not 0. ++ C_DCON20S_20 ++ // 64 bit signed, lo12 bits 0, other bits are not 0. ++ C_DCON32_20 ++ // 64 bit signed, lo12 bits are not 0, 12~51 bits can be obtained ++ // by sign extension of the lo12 bits, other bits are not 0. ++ C_DCON12_12S ++ // 64 bit signed, hi20 bits and lo12 bits are not 0, hi12 bits can ++ // be obtained by sign extension of the hi20 bits, lo20 bits can ++ // be obtained by sign extension of the lo12 bits. ++ C_DCON20S_12S ++ // 64 bit signed, lo12 bits are not 0, lo20 bits can be obtained by sign ++ // extension of the lo12 bits, other bits are not 0. ++ C_DCON32_12S ++ // 64 bit signed, lo20 and lo12 bits are not 0, hi20 bits can be obtained by sign ++ // extension of the lo20 bits. other bits are not 0. ++ C_DCON12_32S ++ // 64 bit signed, hi20 bits are not 0, hi12 bits can be obtained by sign ++ // extension of the hi20 bits, lo32 bits are not 0. ++ C_DCON20S_32 ++ // 64 bit signed, 12~51 bits 0, other bits are not 0. ++ C_DCON12_12U ++ // 64 bit signed, lo20 bits 0, hi20 bits are not 0, hi12 bits can be ++ // obtained by sign extension of the hi20 bits, lo12 bits are not 0. ++ C_DCON20S_12U ++ // 64 bit signed, lo20 bits 0, other bits are not 0. ++ C_DCON32_12U ++ // other 64 ++ C_DCON ++ + C_SACON // $n(REG) where n <= int12 + C_LACON // $n(REG) where int12 < n <= int32 + C_DACON // $n(REG) where int32 < n +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 9024c5e53e..5757c3c452 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -9,6 +9,7 @@ import ( + "cmd/internal/objabi" + "fmt" + "log" ++ "math/bits" + "slices" + ) + +@@ -192,6 +193,9 @@ var optab = []Optab{ + {AMOVV, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0}, + {AMOVW, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, + {AMOVV, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, ++ {AMOVV, C_DCON12_0, C_NONE, C_NONE, C_REG, C_NONE, 67, 4, 0, NOTUSETMP}, ++ {AMOVV, C_DCON12_20S, C_NONE, C_NONE, C_REG, C_NONE, 68, 8, 0, NOTUSETMP}, ++ {AMOVV, C_DCON32_12S, C_NONE, C_NONE, C_REG, C_NONE, 69, 12, 0, NOTUSETMP}, + {AMOVV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 59, 16, 0, NOTUSETMP}, + + {AADD, C_ADD0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +@@ -225,6 +229,20 @@ var optab = []Optab{ + + {AADDV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, + {AADDV, C_DCON, C_REG, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, ++ {AAND, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, ++ {AAND, C_DCON, C_REG, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, ++ {AADDV, C_DCON12_0, C_NONE, C_NONE, C_REG, C_NONE, 70, 8, 0, 0}, ++ {AADDV, C_DCON12_0, C_REG, C_NONE, C_REG, C_NONE, 70, 8, 0, 0}, ++ {AAND, C_DCON12_0, C_NONE, C_NONE, C_REG, C_NONE, 70, 8, 0, 0}, ++ {AAND, C_DCON12_0, C_REG, C_NONE, C_REG, C_NONE, 70, 8, 0, 0}, ++ {AADDV, C_DCON12_20S, C_NONE, C_NONE, C_REG, C_NONE, 71, 12, 0, 0}, ++ {AADDV, C_DCON12_20S, C_REG, C_NONE, C_REG, C_NONE, 71, 12, 0, 0}, ++ {AAND, C_DCON12_20S, C_NONE, C_NONE, C_REG, C_NONE, 71, 12, 0, 0}, ++ {AAND, C_DCON12_20S, C_REG, C_NONE, C_REG, C_NONE, 71, 12, 0, 0}, ++ {AADDV, C_DCON32_12S, C_NONE, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, ++ {AADDV, C_DCON32_12S, C_REG, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, ++ {AAND, C_DCON32_12S, C_NONE, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, ++ {AAND, C_DCON32_12S, C_REG, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, + + {ASLL, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, + {ASLL, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, +@@ -790,7 +808,7 @@ func (c *ctxt0) aclass(a *obj.Addr) int { + } + + if c.instoffset != int64(int32(c.instoffset)) { +- return C_DCON ++ return dconClass(c.instoffset) + } + + if c.instoffset >= 0 { +@@ -830,6 +848,159 @@ func (c *ctxt0) aclass(a *obj.Addr) int { + return C_GOK + } + ++// The constants here define the data characteristics within the bit field range. ++// ++// ALL1: The data in the bit field is all 1 ++// ALL0: The data in the bit field is all 0 ++// ST1: The data in the bit field starts with 1, but not all 1 ++// ST0: The data in the bit field starts with 0, but not all 0 ++const ( ++ ALL1 = iota ++ ALL0 ++ ST1 ++ ST0 ++) ++ ++// mask returns the mask of the specified bit field, which is used to help determine ++// the data characteristics of the immediate value at the specified bit. ++func mask(suf int8, len int8) (uint64, uint64) { ++ if len == 12 { ++ if suf == 0 { ++ return 0xfff, 0x800 ++ } else { // suf == 52 ++ return 0xfff0000000000000, 0x8000000000000000 ++ } ++ } else { // len == 20 ++ if suf == 12 { ++ return 0xfffff000, 0x80000000 ++ } else { // suf == 32 ++ return 0xfffff00000000, 0x8000000000000 ++ } ++ } ++} ++ ++// bitField return a number represent status of val in bit field ++// ++// suf: The starting bit of the bit field ++// len: The length of the bit field ++func bitField(val int64, suf int8, len int8) int8 { ++ mask1, mask2 := mask(suf, len) ++ if uint64(val)&mask1 == mask1 { ++ return ALL1 ++ } else if uint64(val)&mask1 == 0x0 { ++ return ALL0 ++ } else if uint64(val)&mask2 == mask2 { ++ return ST1 ++ } else { ++ return ST0 ++ } ++} ++ ++// Loading an immediate value larger than 32 bits requires four instructions ++// on loong64 (lu12i.w + ori + lu32i.d + lu52i.d), but in some special cases, ++// we can use the sign extension and zero extension features of the instruction ++// to fill in the high-order data (all 0 or all 1), which can save one to ++// three instructions. ++// ++// | 63 ~ 52 | 51 ~ 32 | 31 ~ 12 | 11 ~ 0 | ++// | lu52i.d | lu32i.d | lu12i.w | ori | ++func dconClass(offset int64) int { ++ tzb := bits.TrailingZeros64(uint64(offset)) ++ hi12 := bitField(offset, 52, 12) ++ hi20 := bitField(offset, 32, 20) ++ lo20 := bitField(offset, 12, 20) ++ lo12 := bitField(offset, 0, 12) ++ if tzb >= 52 { ++ return C_DCON12_0 // lu52i.d ++ } ++ if tzb >= 32 { ++ if ((hi20 == ALL1 || hi20 == ST1) && hi12 == ALL1) || ((hi20 == ALL0 || hi20 == ST0) && hi12 == ALL0) { ++ return C_DCON20S_0 // addi.w + lu32i.d ++ } ++ return C_DCON32_0 // addi.w + lu32i.d + lu52i.d ++ } ++ if tzb >= 12 { ++ if lo20 == ST1 || lo20 == ALL1 { ++ if hi20 == ALL1 { ++ return C_DCON12_20S // lu12i.w + lu52i.d ++ } ++ if (hi20 == ST1 && hi12 == ALL1) || ((hi20 == ST0 || hi20 == ALL0) && hi12 == ALL0) { ++ return C_DCON20S_20 // lu12i.w + lu32i.d ++ } ++ return C_DCON32_20 // lu12i.w + lu32i.d + lu52i.d ++ } ++ if hi20 == ALL0 { ++ return C_DCON12_20S // lu12i.w + lu52i.d ++ } ++ if (hi20 == ST0 && hi12 == ALL0) || ((hi20 == ST1 || hi20 == ALL1) && hi12 == ALL1) { ++ return C_DCON20S_20 // lu12i.w + lu32i.d ++ } ++ return C_DCON32_20 // lu12i.w + lu32i.d + lu52i.d ++ } ++ if lo12 == ST1 || lo12 == ALL1 { ++ if lo20 == ALL1 { ++ if hi20 == ALL1 { ++ return C_DCON12_12S // addi.d + lu52i.d ++ } ++ if (hi20 == ST1 && hi12 == ALL1) || ((hi20 == ST0 || hi20 == ALL0) && hi12 == ALL0) { ++ return C_DCON20S_12S // addi.w + lu32i.d ++ } ++ return C_DCON32_12S // addi.w + lu32i.d + lu52i.d ++ } ++ if lo20 == ST1 { ++ if hi20 == ALL1 { ++ ++ return C_DCON12_32S // lu12i.w + ori + lu52i.d ++ } ++ if (hi20 == ST1 && hi12 == ALL1) || ((hi20 == ST0 || hi20 == ALL0) && hi12 == ALL0) { ++ return C_DCON20S_32 // lu12i.w + ori + lu32i.d ++ } ++ return C_DCON // lu12i.w + ori + lu32i.d + lu52i.d ++ } ++ if lo20 == ALL0 { ++ if hi20 == ALL0 { ++ return C_DCON12_12U // ori + lu52i.d ++ } ++ if ((hi20 == ST1 || hi20 == ALL1) && hi12 == ALL1) || (hi20 == ST0 && hi12 == ALL0) { ++ return C_DCON20S_12U // ori + lu32i.d ++ } ++ return C_DCON32_12U // ori + lu32i.d + lu52i.d ++ } ++ if hi20 == ALL0 { ++ return C_DCON12_32S // lu12i.w + ori + lu52i.d ++ } ++ if ((hi20 == ST1 || hi20 == ALL1) && hi12 == ALL1) || (hi20 == ST0 && hi12 == ALL0) { ++ return C_DCON20S_32 // lu12i.w + ori + lu32i.d ++ } ++ return C_DCON // lu12i.w + ori + lu32i.d + lu52i.d ++ } ++ if lo20 == ALL0 { ++ if hi20 == ALL0 { ++ return C_DCON12_12U // ori + lu52i.d ++ } ++ if ((hi20 == ST1 || hi20 == ALL1) && hi12 == ALL1) || (hi20 == ST0 && hi12 == ALL0) { ++ return C_DCON20S_12U // ori + lu32i.d ++ } ++ return C_DCON32_12U // ori + lu32i.d + lu52i.d ++ } ++ if lo20 == ST1 || lo20 == ALL1 { ++ if hi20 == ALL1 { ++ return C_DCON12_32S // lu12i.w + ori + lu52i.d ++ } ++ if (hi20 == ST1 && hi12 == ALL1) || ((hi20 == ST0 || hi20 == ALL0) && hi12 == ALL0) { ++ return C_DCON20S_32 // lu12i.w + ori + lu32i.d ++ } ++ return C_DCON ++ } ++ if hi20 == ALL0 { ++ return C_DCON12_32S // lu12i.w + ori + lu52i.d ++ } ++ if ((hi20 == ST1 || hi20 == ALL1) && hi12 == ALL1) || (hi20 == ST0 && hi12 == ALL0) { ++ return C_DCON20S_32 // lu12i.w + ori + lu32i.d ++ } ++ return C_DCON ++} ++ + // In Loong64,there are 8 CFRs, denoted as fcc0-fcc7. + // There are 4 FCSRs, denoted as fcsr0-fcsr3. + func (c *ctxt0) rclass(r int16) int { +@@ -935,7 +1106,14 @@ func cmp(a int, b int) bool { + } + switch a { + case C_DCON: +- if b == C_LCON { ++ if b == C_LCON || b == C_DCON32_0 || ++ b == C_DCON12_0 || b == C_DCON20S_0 || ++ b == C_DCON12_20S || b == C_DCON12_12S || ++ b == C_DCON20S_20 || b == C_DCON32_20 || ++ b == C_DCON20S_12S || b == C_DCON32_12S || ++ b == C_DCON12_32S || b == C_DCON20S_32 || ++ b == C_DCON12_12U || b == C_DCON20S_12U || ++ b == C_DCON32_12U { + return true + } + fallthrough +@@ -944,6 +1122,22 @@ func cmp(a int, b int) bool { + return true + } + ++ case C_DCON12_0: ++ ++ case C_DCON12_20S: ++ if b == C_DCON20S_20 || b == C_DCON12_12S || ++ b == C_DCON20S_12S || b == C_DCON12_12U || ++ b == C_DCON20S_12U || b == C_DCON20S_0 { ++ return true ++ } ++ ++ case C_DCON32_12S: ++ if b == C_DCON32_20 || b == C_DCON12_32S || ++ b == C_DCON20S_32 || b == C_DCON32_12U || ++ b == C_DCON32_0 { ++ return true ++ } ++ + case C_ADD0CON: + if b == C_ADDCON { + return true +@@ -2015,6 +2209,129 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + c.ctxt.Diag("illegal register combination: %v\n", p) + } + o1 = OP_RRR(atomicInst[p.As], uint32(rk), uint32(rj), uint32(rd)) ++ ++ case 67: // mov $dcon12_0, r ++ v := c.vregoff(&p.From) ++ o1 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(0), uint32(p.To.Reg)) ++ ++ case 68: // mov $dcon12_20S, r ++ v := c.vregoff(&p.From) ++ contype := c.aclass(&p.From) ++ switch contype { ++ default: // C_DCON12_20S ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON20S_20: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ case C_DCON12_12S: ++ o1 = OP_12IRR(c.opirr(AADDV), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON20S_12S, C_DCON20S_0: ++ o1 = OP_12IRR(c.opirr(AADD), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ case C_DCON12_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON20S_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ } ++ ++ case 69: // mov $dcon32_12S, r ++ v := c.vregoff(&p.From) ++ contype := c.aclass(&p.From) ++ switch contype { ++ default: // C_DCON32_12S, C_DCON32_0 ++ o1 = OP_12IRR(c.opirr(AADD), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON32_20: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON12_32S: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg)) ++ o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ case C_DCON20S_32: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg)) ++ o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) ++ o3 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ case C_DCON32_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(p.To.Reg)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg)) ++ } ++ ++ case 70: // add $dcon12_0,[r1],r2 ++ v := c.vregoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ o1 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(0), uint32(REGTMP)) ++ o2 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) ++ ++ case 71: // add $dcon12_20S,[r1],r2 ++ v := c.vregoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ contype := c.aclass(&p.From) ++ switch contype { ++ default: // C_DCON12_20S ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON20S_20: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ case C_DCON12_12S: ++ o1 = OP_12IRR(c.opirr(AADDV), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON20S_12S, C_DCON20S_0: ++ o1 = OP_12IRR(c.opirr(AADD), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ case C_DCON12_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON20S_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ } ++ o3 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) ++ ++ case 72: // add $dcon32_12S,[r1],r2 ++ v := c.vregoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ contype := c.aclass(&p.From) ++ switch contype { ++ default: // C_DCON32_12S, C_DCON32_0 ++ o1 = OP_12IRR(c.opirr(AADD), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON32_20: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON12_32S: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP)) ++ o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ case C_DCON20S_32: ++ o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP)) ++ o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) ++ o3 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ case C_DCON32_12U: ++ o1 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(0), uint32(REGTMP)) ++ o2 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP)) ++ o3 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP)) ++ } ++ o4 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) + } + + out[0] = o1 +diff --git a/src/cmd/internal/obj/loong64/cnames.go b/src/cmd/internal/obj/loong64/cnames.go +index ce76109d2a..a2f04a22ee 100644 +--- a/src/cmd/internal/obj/loong64/cnames.go ++++ b/src/cmd/internal/obj/loong64/cnames.go +@@ -21,6 +21,20 @@ var cnames0 = []string{ + "ADDCON", + "ANDCON", + "LCON", ++ "DCON20S_0", ++ "DCON12_0", ++ "DCON32_0", ++ "DCON12_20S", ++ "DCON20S_20", ++ "DCON32_20", ++ "DCON12_12S", ++ "DCON20S_12S", ++ "DCON32_12S", ++ "DCON12_32S", ++ "DCON20S_32", ++ "DCON12_12U", ++ "DCON20S_12U", ++ "DCON32_12U", + "DCON", + "SACON", + "LACON", +-- +2.38.1 + diff --git a/0012-math-big-optimize-addVV-function-for-loong64.patch b/0012-math-big-optimize-addVV-function-for-loong64.patch new file mode 100644 index 0000000..8d91ab4 --- /dev/null +++ b/0012-math-big-optimize-addVV-function-for-loong64.patch @@ -0,0 +1,85 @@ +From a7a4eb8120aaf7d5f8d2146f190c64118c7e1235 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Thu, 6 Jun 2024 15:30:20 +0800 +Subject: [PATCH 12/44] math/big: optimize addVV function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_addvv.log │ test/new_3c5000_addvv.log │ + │ sec/op │ sec/op vs base │ +AddVV/1 10.920n ± 0% 7.671n ± 0% -29.75% (p=0.000 n=20) +AddVV/2 14.100n ± 0% 8.849n ± 0% -37.24% (p=0.000 n=20) +AddVV/3 16.38n ± 0% 11.07n ± 0% -32.42% (p=0.000 n=20) +AddVV/4 18.65n ± 0% 12.86n ± 0% -31.05% (p=0.000 n=20) +AddVV/5 20.93n ± 0% 15.01n ± 0% -28.28% (p=0.000 n=20) +AddVV/10 31.84n ± 0% 22.75n ± 0% -28.53% (p=0.000 n=20) +AddVV/100 242.4n ± 0% 149.7n ± 0% -38.24% (p=0.000 n=20) +AddVV/1000 2.290µ ± 0% 1.378µ ± 0% -39.83% (p=0.000 n=20) +AddVV/10000 32.73µ ± 0% 19.36µ ± 0% -40.84% (p=0.000 n=20) +AddVV/100000 340.9µ ± 0% 238.5µ ± 0% -30.03% (p=0.000 n=20) +geomean 213.2n 141.2n -33.79% + +Change-Id: I7983a93d9b97d4e9ebe96a49107ec6db9194b013 +--- + src/math/big/arith_loong64.s | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index 847e3127fb..bd7204cf06 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -2,15 +2,42 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !math_big_pure_go && loong64 ++//go:build !math_big_pure_go + + #include "textflag.h" + + // This file provides fast assembly versions for the elementary + // arithmetic operations on vectors implemented in arith.go. + ++// func addVV(z, x, y []Word) (c Word) + TEXT ·addVV(SB),NOSPLIT,$0 +- JMP ·addVV_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ MOVV z+0(FP), R4 ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV y+48(FP), R10 ++ MOVV $0, R6 ++ SLLV $3, R5 ++ MOVV $0, R8 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R9 ++ MOVV (R6)(R10), R11 ++ ADDV R9, R11, R11 // x1 + y1 = z1', if z1' < x1 then z1' overflow ++ ADDV R8, R11, R12 // z1' + c0 = z1, if z1 < z1' then z1 overflow ++ SGTU R9, R11, R9 ++ SGTU R11, R12, R11 ++ MOVV R12, (R6)(R4) ++ OR R9, R11, R8 ++ ADDV $8, R6 ++ JMP loop ++done: ++ MOVV R8, c+72(FP) ++ RET + + TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) +-- +2.38.1 + diff --git a/0013-math-big-optimize-addVW-function-for-loong64.patch b/0013-math-big-optimize-addVW-function-for-loong64.patch new file mode 100644 index 0000000..853a0d4 --- /dev/null +++ b/0013-math-big-optimize-addVW-function-for-loong64.patch @@ -0,0 +1,82 @@ +From 94a6bdcacffb17b8adf57ce0919a3d31ac70b646 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Tue, 11 Jun 2024 16:09:10 +0800 +Subject: [PATCH 13/44] math/big: optimize addVW function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_addvw.log │ test/new_3c5000_addvw.log │ + │ sec/op │ sec/op vs base │ +AddVW/1 9.555n ± 0% 5.915n ± 0% -38.09% (p=0.000 n=20) +AddVW/2 11.370n ± 0% 6.825n ± 0% -39.97% (p=0.000 n=20) +AddVW/3 12.485n ± 0% 7.970n ± 0% -36.16% (p=0.000 n=20) +AddVW/4 14.980n ± 0% 9.718n ± 0% -35.13% (p=0.000 n=20) +AddVW/5 16.73n ± 0% 10.63n ± 0% -36.46% (p=0.000 n=20) +AddVW/10 24.57n ± 0% 15.18n ± 0% -38.23% (p=0.000 n=20) +AddVW/100 184.9n ± 0% 102.4n ± 0% -44.62% (p=0.000 n=20) +AddVW/1000 1721.0n ± 0% 921.4n ± 0% -46.46% (p=0.000 n=20) +AddVW/10000 16.83µ ± 0% 11.68µ ± 0% -30.58% (p=0.000 n=20) +AddVW/100000 184.7µ ± 0% 131.3µ ± 0% -28.93% (p=0.000 n=20) +AddVWext/1 9.554n ± 0% 5.915n ± 0% -38.09% (p=0.000 n=20) +AddVWext/2 11.370n ± 0% 6.825n ± 0% -39.97% (p=0.000 n=20) +AddVWext/3 12.505n ± 0% 7.969n ± 0% -36.27% (p=0.000 n=20) +AddVWext/4 14.980n ± 0% 9.718n ± 0% -35.13% (p=0.000 n=20) +AddVWext/5 16.70n ± 0% 10.63n ± 0% -36.33% (p=0.000 n=20) +AddVWext/10 24.54n ± 0% 15.18n ± 0% -38.13% (p=0.000 n=20) +AddVWext/100 185.0n ± 0% 102.4n ± 0% -44.65% (p=0.000 n=20) +AddVWext/1000 1721.0n ± 0% 921.4n ± 0% -46.46% (p=0.000 n=20) +AddVWext/10000 16.83µ ± 0% 11.68µ ± 0% -30.60% (p=0.000 n=20) +AddVWext/100000 184.9µ ± 0% 130.4µ ± 0% -29.51% (p=0.000 n=20) +geomean 155.5n 96.87n -37.70% + +Change-Id: I824a90cb365e09d7d0d4a2c53ff4b30cf057a75e +--- + src/math/big/arith_loong64.s | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index bd7204cf06..bd6fec1b8d 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -42,8 +42,30 @@ done: + TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) + ++// func addVW(z, x []Word, y Word) (c Word) + TEXT ·addVW(SB),NOSPLIT,$0 +- JMP ·addVW_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ MOVV z+0(FP), R4 ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV y+48(FP), R10 ++ MOVV $0, R6 ++ SLLV $3, R5 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R8 ++ ADDV R8, R10, R9 // x1 + c = z1, if z1 < x1 then z1 overflow ++ SGTU R8, R9, R10 ++ MOVV R9, (R6)(R4) ++ ADDV $8, R6 ++ JMP loop ++done: ++ MOVV R10, c+56(FP) ++ RET + + TEXT ·subVW(SB),NOSPLIT,$0 + JMP ·subVW_g(SB) +-- +2.38.1 + diff --git a/0014-math-big-optimize-subVV-function-for-loong64.patch b/0014-math-big-optimize-subVV-function-for-loong64.patch new file mode 100644 index 0000000..1a9c5dd --- /dev/null +++ b/0014-math-big-optimize-subVV-function-for-loong64.patch @@ -0,0 +1,77 @@ +From 7939ebdcaa1156ef4e9d8f896f4877df88d7636c Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Tue, 11 Jun 2024 19:06:29 +0800 +Subject: [PATCH 14/44] math/big: optimize subVV function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_subvv.log │ test/new_3c5000_subvv.log │ + │ sec/op │ sec/op vs base │ +SubVV/1 10.920n ± 0% 7.657n ± 0% -29.88% (p=0.000 n=20) +SubVV/2 14.100n ± 0% 8.841n ± 0% -37.30% (p=0.000 n=20) +SubVV/3 16.38n ± 0% 11.06n ± 0% -32.48% (p=0.000 n=20) +SubVV/4 18.65n ± 0% 12.85n ± 0% -31.10% (p=0.000 n=20) +SubVV/5 20.93n ± 0% 14.79n ± 0% -29.34% (p=0.000 n=20) +SubVV/10 32.30n ± 0% 22.29n ± 0% -30.99% (p=0.000 n=20) +SubVV/100 244.3n ± 0% 149.2n ± 0% -38.93% (p=0.000 n=20) +SubVV/1000 2.292µ ± 0% 1.378µ ± 0% -39.88% (p=0.000 n=20) +SubVV/10000 26.26µ ± 0% 25.64µ ± 0% -2.33% (p=0.000 n=20) +SubVV/100000 341.3µ ± 0% 238.0µ ± 0% -30.26% (p=0.000 n=20) +geomean 209.1n 144.5n -30.86% + +Change-Id: I3863c2c6728f1b0f8fecbf77de13254299c5b1cb +--- + src/math/big/arith_loong64.s | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index bd6fec1b8d..8016c25207 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -39,8 +39,35 @@ done: + MOVV R8, c+72(FP) + RET + ++// func subVV(z, x, y []Word) (c Word) + TEXT ·subVV(SB),NOSPLIT,$0 +- JMP ·subVV_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ MOVV z+0(FP), R4 ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV y+48(FP), R10 ++ MOVV $0, R6 ++ SLLV $3, R5 ++ MOVV $0, R8 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R9 ++ MOVV (R6)(R10), R11 ++ SUBV R11, R9, R11 // x1 - y1 = z1', if z1' > x1 then overflow ++ SUBV R8, R11, R12 // z1' - c0 = z1, if z1 > z1' then overflow ++ SGTU R11, R9, R9 ++ SGTU R12, R11, R11 ++ MOVV R12, (R6)(R4) ++ OR R9, R11, R8 ++ ADDV $8, R6 ++ JMP loop ++done: ++ MOVV R8, c+72(FP) ++ RET + + // func addVW(z, x []Word, y Word) (c Word) + TEXT ·addVW(SB),NOSPLIT,$0 +-- +2.38.1 + diff --git a/0015-math-big-optimize-subVW-function-for-loong64.patch b/0015-math-big-optimize-subVW-function-for-loong64.patch new file mode 100644 index 0000000..82c8a1c --- /dev/null +++ b/0015-math-big-optimize-subVW-function-for-loong64.patch @@ -0,0 +1,82 @@ +From b8516483f552400ef8708645b8a10bed5f666dba Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Tue, 11 Jun 2024 20:33:50 +0800 +Subject: [PATCH 15/44] math/big: optimize subVW function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3C5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3C5000 @ 2200.00MHz + │ test/old_3c5000_subvw.log │ test/new_3c5000_subvw.log │ + │ sec/op │ sec/op vs base │ +SubVW/1 8.564n ± 0% 5.915n ± 0% -30.93% (p=0.000 n=20) +SubVW/2 11.675n ± 0% 6.825n ± 0% -41.54% (p=0.000 n=20) +SubVW/3 13.410n ± 0% 7.969n ± 0% -40.57% (p=0.000 n=20) +SubVW/4 15.300n ± 0% 9.740n ± 0% -36.34% (p=0.000 n=20) +SubVW/5 17.34n ± 1% 10.66n ± 0% -38.55% (p=0.000 n=20) +SubVW/10 26.55n ± 0% 15.21n ± 0% -42.70% (p=0.000 n=20) +SubVW/100 199.2n ± 0% 102.5n ± 0% -48.52% (p=0.000 n=20) +SubVW/1000 1866.5n ± 1% 924.6n ± 0% -50.46% (p=0.000 n=20) +SubVW/10000 17.67µ ± 2% 12.04µ ± 2% -31.83% (p=0.000 n=20) +SubVW/100000 186.4µ ± 0% 132.0µ ± 0% -29.17% (p=0.000 n=20) +SubVWext/1 8.616n ± 0% 5.949n ± 0% -30.95% (p=0.000 n=20) +SubVWext/2 11.410n ± 0% 7.008n ± 1% -38.58% (p=0.000 n=20) +SubVWext/3 13.255n ± 1% 8.073n ± 0% -39.09% (p=0.000 n=20) +SubVWext/4 15.095n ± 0% 9.893n ± 0% -34.47% (p=0.000 n=20) +SubVWext/5 16.87n ± 0% 10.86n ± 0% -35.63% (p=0.000 n=20) +SubVWext/10 26.00n ± 0% 15.54n ± 0% -40.22% (p=0.000 n=20) +SubVWext/100 196.0n ± 0% 104.3n ± 1% -46.76% (p=0.000 n=20) +SubVWext/1000 1847.0n ± 0% 923.7n ± 0% -49.99% (p=0.000 n=20) +SubVWext/10000 17.30µ ± 1% 11.71µ ± 1% -32.31% (p=0.000 n=20) +SubVWext/100000 187.5µ ± 0% 131.6µ ± 0% -29.82% (p=0.000 n=20) +geomean 159.7n 97.79n -38.79% + +Change-Id: I21a6903e79b02cb22282e80c9bfe2ae9f1a87589 +--- + src/math/big/arith_loong64.s | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index 8016c25207..02d8262129 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -94,8 +94,30 @@ done: + MOVV R10, c+56(FP) + RET + ++// func subVW(z, x []Word, y Word) (c Word) + TEXT ·subVW(SB),NOSPLIT,$0 +- JMP ·subVW_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ MOVV z+0(FP), R4 ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV y+48(FP), R10 ++ MOVV $0, R6 ++ SLLV $3, R5 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R8 ++ SUBV R10, R8, R11 // x1 - c = z1, if z1 > x1 then overflow ++ SGTU R11, R8, R10 ++ MOVV R11, (R6)(R4) ++ ADDV $8, R6 ++ JMP loop ++done: ++ MOVV R10, c+56(FP) ++ RET + + TEXT ·shlVU(SB),NOSPLIT,$0 + JMP ·shlVU_g(SB) +-- +2.38.1 + diff --git a/0016-math-big-optimize-shlVU-function-for-loong64.patch b/0016-math-big-optimize-shlVU-function-for-loong64.patch new file mode 100644 index 0000000..a7fb046 --- /dev/null +++ b/0016-math-big-optimize-shlVU-function-for-loong64.patch @@ -0,0 +1,92 @@ +From 3d520765bbff022132512b918379fe1a5e788f2e Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Thu, 13 Jun 2024 11:36:30 +0800 +Subject: [PATCH 16/44] math/big: optimize shlVU function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3A5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3A5000-HV @ 2500.00MHz + │ old_3a5000_shlvu.log │ new_3a5000_shlvu_1st.log │ + │ sec/op │ sec/op vs base │ +NonZeroShifts/1/shlVU 7.606n ± 0% 5.304n ± 0% -30.27% (p=0.000 n=20) +NonZeroShifts/2/shlVU 9.608n ± 0% 6.164n ± 0% -35.85% (p=0.000 n=20) +NonZeroShifts/3/shlVU 11.610n ± 0% 6.984n ± 0% -39.84% (p=0.000 n=20) +NonZeroShifts/4/shlVU 12.210n ± 0% 8.869n ± 0% -27.36% (p=0.000 n=20) +NonZeroShifts/5/shlVU 14.11n ± 0% 10.41n ± 0% -26.22% (p=0.000 n=20) +NonZeroShifts/10/shlVU 22.02n ± 0% 14.77n ± 0% -32.92% (p=0.000 n=20) +NonZeroShifts/100/shlVU 161.30n ± 0% 91.15n ± 0% -43.49% (p=0.000 n=20) +NonZeroShifts/1000/shlVU 1514.0n ± 0% 811.7n ± 0% -46.39% (p=0.000 n=20) +NonZeroShifts/10000/shlVU 21.53µ ± 0% 10.54µ ± 0% -51.04% (p=0.000 n=20) +NonZeroShifts/100000/shlVU 208.1µ ± 0% 113.0µ ± 0% -45.69% (p=0.000 n=20) +geomean 142.8n 87.87n -38.46% + +Change-Id: I8e13eb0af27ac3d6846e559cdb61d2b544b05353 +--- + src/math/big/arith_loong64.s | 44 +++++++++++++++++++++++++++++++++++- + 1 file changed, 43 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index 02d8262129..1820988d3f 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -119,8 +119,50 @@ done: + MOVV R10, c+56(FP) + RET + ++// func shlVU(z, x []Word, s uint) (c Word) + TEXT ·shlVU(SB),NOSPLIT,$0 +- JMP ·shlVU_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: s ++ MOVV z_len+8(FP), R5 ++ MOVV s+48(FP), R10 ++ MOVV z+0(FP), R4 ++ MOVV x+24(FP), R7 ++ BEQ R5, len0 ++ SLLV $3, R5 ++ BEQ R10, copy ++ MOVV $64, R9 ++ ADDV $-8, R7 // &x[-1] ++ SUB R10, R9 // ŝ = 64 - s ++ MOVV (R5)(R7), R6 ++ SRLV R9, R6, R8 // c = x[len(z)-1] >> ŝ ++loop: ++ ADDV $-8, R5 ++ BEQ R5, done ++ SLLV R10, R6, R12 ++ MOVV (R5)(R7), R6 ++ SRLV R9, R6, R11 ++ OR R11, R12 ++ MOVV R12, (R5)(R4) // z[i] = x[i]<>ŝ ++ JMP loop ++done: ++ SLLV R10, R6 ++ MOVV R8, c+56(FP) ++ MOVV R6, 0(R4) // z[0] = x[0] << s ++ RET ++copy: ++ BEQ R7, R4, len0 ++copyloop: ++ ADDV $-8, R5 ++ BLT R5, R0, len0 ++ MOVV (R5)(R7), R9 ++ MOVV R9, (R5)(R4) ++ JMP copyloop ++len0: ++ MOVV R0, c+56(FP) ++ RET + + TEXT ·shrVU(SB),NOSPLIT,$0 + JMP ·shrVU_g(SB) +-- +2.38.1 + diff --git a/0017-math-big-optimize-shrVU-function-for-loong64.patch b/0017-math-big-optimize-shrVU-function-for-loong64.patch new file mode 100644 index 0000000..5045019 --- /dev/null +++ b/0017-math-big-optimize-shrVU-function-for-loong64.patch @@ -0,0 +1,92 @@ +From 14d44d92f1d59c42e85bd89797a3730f48699dc6 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Tue, 18 Jun 2024 02:00:38 +0000 +Subject: [PATCH 17/44] math/big: optimize shrVU function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3A5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3A5000-HV @ 2500.00MHz + │ test/old_3a5000_shrvu.log │ test/new_3a5000_shrvu.log │ + │ sec/op │ sec/op vs base │ +NonZeroShifts/1/shrVU 7.968n ± 0% 5.210n ± 0% -34.62% (p=0.000 n=20) +NonZeroShifts/2/shrVU 9.608n ± 0% 6.178n ± 0% -35.70% (p=0.000 n=20) +NonZeroShifts/3/shrVU 11.400n ± 0% 7.419n ± 0% -34.92% (p=0.000 n=20) +NonZeroShifts/4/shrVU 13.350n ± 0% 9.159n ± 0% -31.39% (p=0.000 n=20) +NonZeroShifts/5/shrVU 15.93n ± 0% 10.58n ± 0% -33.58% (p=0.000 n=20) +NonZeroShifts/10/shrVU 24.42n ± 0% 15.70n ± 0% -35.71% (p=0.000 n=20) +NonZeroShifts/100/shrVU 190.60n ± 0% 90.87n ± 0% -52.32% (p=0.000 n=20) +NonZeroShifts/1000/shrVU 1782.0n ± 0% 811.5n ± 0% -54.46% (p=0.000 n=20) +NonZeroShifts/10000/shrVU 21.54µ ± 0% 12.55µ ± 0% -41.76% (p=0.000 n=20) +NonZeroShifts/100000/shrVU 224.1µ ± 0% 126.2µ ± 0% -43.71% (p=0.000 n=20) +geomean 153.9n 91.78n -40.35% + +Change-Id: I86f1f3ac44d60ad8dc2e77bdb9b541f55eb18e74 +--- + src/math/big/arith_loong64.s | 45 +++++++++++++++++++++++++++++++++++- + 1 file changed, 44 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index 1820988d3f..bdaaf14821 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -165,7 +165,50 @@ len0: + RET + + TEXT ·shrVU(SB),NOSPLIT,$0 +- JMP ·shrVU_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: s ++ MOVV z_len+8(FP), R5 ++ MOVV s+48(FP), R10 ++ MOVV z+0(FP), R4 ++ MOVV x+24(FP), R7 ++ BEQ R5, len0 ++ SLLV $3, R5 ++ BEQ R10, copy ++ MOVV 0(R7), R6 ++ MOVV $64, R9 ++ MOVV $8, R8 ++ SUB R10, R9 // ŝ = 64 - s ++ ADDV $-8, R4 // &z[-1] ++ SLLV R9, R6, R13 // c = x[0] << ŝ ++loop: ++ BEQ R5, R8, done ++ SRLV R10, R6, R12 ++ MOVV (R8)(R7), R6 ++ SLLV R9, R6, R11 ++ OR R11, R12 ++ MOVV R12, (R8)(R4) // z[i-1] = x[i-1]>>s | x[i]<<ŝ ++ ADDV $8, R8 ++ JMP loop ++done: ++ SRLV R10, R6 ++ MOVV R13, c+56(FP) ++ MOVV R6, (R8)(R4) // z[len(z)-1] = x[len(z)-1] >> s ++ RET ++copy: ++ MOVV $0, R8 ++ BEQ R7, R4, len0 ++copyloop: ++ BEQ R5, R8, len0 ++ MOVV (R8)(R7), R9 ++ MOVV R9, (R8)(R4) ++ ADDV $8, R8 ++ JMP copyloop ++len0: ++ MOVV R0, c+56(FP) ++ RET + + TEXT ·mulAddVWW(SB),NOSPLIT,$0 + JMP ·mulAddVWW_g(SB) +-- +2.38.1 + diff --git a/0018-math-big-optimize-mulAddVWW-function-for-loong64.patch b/0018-math-big-optimize-mulAddVWW-function-for-loong64.patch new file mode 100644 index 0000000..0ad375f --- /dev/null +++ b/0018-math-big-optimize-mulAddVWW-function-for-loong64.patch @@ -0,0 +1,77 @@ +From b956f69c885cd7fdf5305fd4047fd939000c9745 Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Wed, 19 Jun 2024 06:31:00 +0000 +Subject: [PATCH 18/44] math/big: optimize mulAddVWW function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3A5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3A5000-HV @ 2500.00MHz + │ test/old_3a5000_muladdvww.log │ test/new_3a5000_muladdvww.log │ + │ sec/op │ sec/op vs base │ +MulAddVWW/1 7.606n ± 0% 6.987n ± 0% -8.14% (p=0.000 n=20) +MulAddVWW/2 9.207n ± 0% 8.567n ± 0% -6.95% (p=0.000 n=20) +MulAddVWW/3 10.810n ± 0% 9.223n ± 0% -14.68% (p=0.000 n=20) +MulAddVWW/4 13.01n ± 0% 12.41n ± 0% -4.61% (p=0.000 n=20) +MulAddVWW/5 15.79n ± 0% 12.99n ± 0% -17.73% (p=0.000 n=20) +MulAddVWW/10 25.62n ± 0% 20.02n ± 0% -21.86% (p=0.000 n=20) +MulAddVWW/100 217.0n ± 0% 170.9n ± 0% -21.24% (p=0.000 n=20) +MulAddVWW/1000 2.064µ ± 0% 1.612µ ± 0% -21.90% (p=0.000 n=20) +MulAddVWW/10000 24.50µ ± 0% 16.74µ ± 0% -31.66% (p=0.000 n=20) +MulAddVWW/100000 239.1µ ± 0% 171.1µ ± 0% -28.45% (p=0.000 n=20) +geomean 159.2n 130.3n -18.18% + +Change-Id: I063434bc382f4f1234f879172ab671a3d6f2eb80 +--- + src/math/big/arith_loong64.s | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index bdaaf14821..fe7c971120 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -210,8 +210,35 @@ len0: + MOVV R0, c+56(FP) + RET + ++// func mulAddVWW(z, x []Word, y, r Word) (c Word) + TEXT ·mulAddVWW(SB),NOSPLIT,$0 +- JMP ·mulAddVWW_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ // R11: r ++ MOVV z+0(FP), R4 ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV y+48(FP), R10 ++ MOVV r+56(FP), R11 ++ SLLV $3, R5 ++ MOVV $0, R6 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R8 ++ MULV R8, R10, R9 ++ MULHVU R8, R10, R12 ++ ADDV R9, R11, R8 ++ SGTU R9, R8, R11 // if (c' = lo + c) < lo then overflow ++ MOVV R8, (R6)(R4) ++ ADDV R12, R11 ++ ADDV $8, R6 ++ JMP loop ++done: ++ MOVV R11, c+64(FP) ++ RET + + TEXT ·addMulVVW(SB),NOSPLIT,$0 + JMP ·addMulVVW_g(SB) +-- +2.38.1 + diff --git a/0019-math-big-optimize-addMulVVW-function-for-loong64.patch b/0019-math-big-optimize-addMulVVW-function-for-loong64.patch new file mode 100644 index 0000000..965a89c --- /dev/null +++ b/0019-math-big-optimize-addMulVVW-function-for-loong64.patch @@ -0,0 +1,77 @@ +From e7a6135d5c0fc4685ad18a82e770acf9f226b08e Mon Sep 17 00:00:00 2001 +From: Huang Qiqi +Date: Wed, 19 Jun 2024 08:05:24 +0000 +Subject: [PATCH 19/44] math/big: optimize addMulVVW function for loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark results on Loongson 3A5000 (which is an LA464 implementation): + +goos: linux +goarch: loong64 +pkg: math/big +cpu: Loongson-3A5000-HV @ 2500.00MHz + │ test/old_3a5000_addmulvvw.log │ test/new_3a5000_addmulvvw.log │ + │ sec/op │ sec/op vs base │ +AddMulVVW/1 9.208n ± 0% 5.777n ± 0% -37.26% (p=0.000 n=20) +AddMulVVW/2 11.950n ± 0% 7.763n ± 0% -35.04% (p=0.000 n=20) +AddMulVVW/3 14.01n ± 0% 10.41n ± 0% -25.70% (p=0.000 n=20) +AddMulVVW/4 16.01n ± 0% 13.21n ± 0% -17.49% (p=0.000 n=20) +AddMulVVW/5 18.01n ± 0% 14.12n ± 0% -21.57% (p=0.000 n=20) +AddMulVVW/10 29.60n ± 0% 23.35n ± 0% -21.11% (p=0.000 n=20) +AddMulVVW/100 273.4n ± 0% 173.8n ± 0% -36.43% (p=0.000 n=20) +AddMulVVW/1000 2.516µ ± 0% 1.615µ ± 0% -35.81% (p=0.000 n=20) +AddMulVVW/10000 30.31µ ± 0% 21.54µ ± 0% -28.93% (p=0.000 n=20) +AddMulVVW/100000 322.5µ ± 0% 234.1µ ± 0% -27.41% (p=0.000 n=20) +geomean 197.1n 139.9n -29.00% + +Change-Id: Ib7e95b50f7af893abee72ec26948a65115455692 +--- + src/math/big/arith_loong64.s | 32 +++++++++++++++++++++++++++++++- + 1 file changed, 31 insertions(+), 1 deletion(-) + +diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s +index fe7c971120..012af94f5c 100644 +--- a/src/math/big/arith_loong64.s ++++ b/src/math/big/arith_loong64.s +@@ -240,5 +240,35 @@ done: + MOVV R11, c+64(FP) + RET + ++// func addMulVVW(z, x []Word, y Word) (c Word) + TEXT ·addMulVVW(SB),NOSPLIT,$0 +- JMP ·addMulVVW_g(SB) ++ // input: ++ // R4: z ++ // R5: z_len ++ // R7: x ++ // R10: y ++ MOVV z_len+8(FP), R5 ++ MOVV x+24(FP), R7 ++ MOVV z+0(FP), R4 ++ MOVV y+48(FP), R10 ++ MOVV $0, R6 ++ SLLV $3, R5 ++ MOVV $0, R11 ++loop: ++ BEQ R5, R6, done ++ MOVV (R6)(R7), R8 ++ MOVV (R6)(R4), R9 ++ MULV R8, R10, R12 ++ MULHVU R8, R10, R13 ++ ADDV R12, R9, R8 ++ SGTU R12, R8, R9 ++ ADDV R13, R9 ++ ADDV R8, R11, R12 ++ SGTU R8, R12, R11 ++ MOVV R12, (R6)(R4) ++ ADDV $8, R6 ++ ADDV R9, R11 ++ JMP loop ++done: ++ MOVV R11, c+56(FP) ++ RET +-- +2.38.1 + diff --git a/0020-cmd-compile-fold-constant-shift-with-extension-on-lo.patch b/0020-cmd-compile-fold-constant-shift-with-extension-on-lo.patch new file mode 100644 index 0000000..48553de --- /dev/null +++ b/0020-cmd-compile-fold-constant-shift-with-extension-on-lo.patch @@ -0,0 +1,376 @@ +From f10d1a3db9650a738d0254a58aadb62ec89eaca9 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Tue, 24 Sep 2024 16:59:06 +0800 +Subject: [PATCH 20/44] cmd/compile: fold constant shift with extension on + loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: test/bench/go1 +cpu: Loongson-3A6000 @ 2500.00MHz + │ bench.old │ bench.new │ + │ sec/op │ sec/op vs base │ +BinaryTree17 7.775 ± 1% 7.747 ± 1% ~ (p=0.713 n=15) +Fannkuch11 2.645 ± 0% 2.646 ± 0% +0.05% (p=0.002 n=15) +FmtFprintfEmpty 35.87n ± 0% 35.85n ± 0% -0.06% (p=0.000 n=15) +FmtFprintfString 59.50n ± 0% 59.17n ± 0% -0.55% (p=0.000 n=15) +FmtFprintfInt 62.03n ± 0% 62.38n ± 0% +0.56% (p=0.000 n=15) +FmtFprintfIntInt 97.73n ± 0% 96.51n ± 0% -1.25% (p=0.000 n=15) +FmtFprintfPrefixedInt 116.6n ± 0% 118.8n ± 0% +1.89% (p=0.000 n=15) +FmtFprintfFloat 204.1n ± 0% 200.3n ± 0% -1.86% (p=0.000 n=15) +FmtManyArgs 455.1n ± 0% 464.8n ± 0% +2.13% (p=0.000 n=15) +GobDecode 7.127m ± 1% 7.063m ± 1% -0.89% (p=0.033 n=15) +GobEncode 8.061m ± 1% 8.069m ± 5% ~ (p=0.870 n=15) +Gzip 279.8m ± 0% 271.4m ± 0% -3.00% (p=0.000 n=15) +Gunzip 32.63m ± 0% 31.68m ± 0% -2.93% (p=0.000 n=15) +HTTPClientServer 53.39µ ± 0% 53.12µ ± 0% -0.51% (p=0.000 n=15) +JSONEncode 9.323m ± 0% 8.990m ± 1% -3.57% (p=0.000 n=15) +JSONDecode 46.65m ± 1% 46.58m ± 0% ~ (p=0.050 n=15) +Mandelbrot200 4.600m ± 0% 4.603m ± 0% +0.06% (p=0.000 n=15) +GoParse 4.651m ± 0% 4.765m ± 1% +2.45% (p=0.000 n=15) +RegexpMatchEasy0_32 59.64n ± 0% 58.26n ± 0% -2.31% (p=0.000 n=15) +RegexpMatchEasy0_1K 457.3n ± 0% 458.0n ± 0% +0.15% (p=0.002 n=15) +RegexpMatchEasy1_32 59.24n ± 0% 60.12n ± 0% +1.49% (p=0.000 n=15) +RegexpMatchEasy1_1K 556.6n ± 0% 556.9n ± 0% +0.05% (p=0.002 n=15) +RegexpMatchMedium_32 801.5n ± 0% 799.5n ± 0% -0.25% (p=0.000 n=15) +RegexpMatchMedium_1K 27.25µ ± 0% 27.21µ ± 0% -0.15% (p=0.001 n=15) +RegexpMatchHard_32 1.382µ ± 0% 1.412µ ± 0% +2.17% (p=0.000 n=15) +RegexpMatchHard_1K 40.84µ ± 0% 40.91µ ± 0% +0.18% (p=0.000 n=15) +Revcomp 474.5m ± 0% 473.9m ± 0% ~ (p=0.081 n=15) +Template 76.85m ± 1% 74.71m ± 1% -2.79% (p=0.000 n=15) +TimeParse 271.1n ± 0% 269.1n ± 0% -0.74% (p=0.000 n=15) +TimeFormat 289.5n ± 0% 287.5n ± 0% -0.69% (p=0.000 n=15) +geomean 51.59µ 51.40µ -0.38% + +Change-Id: I721e930c30b3d1cb88a79306ec51990505d850f1 +--- + .../internal/ssa/_gen/LOONG64latelower.rules | 19 ++ + src/cmd/compile/internal/ssa/config.go | 2 + + .../internal/ssa/rewriteLOONG64latelower.go | 246 ++++++++++++++++++ + test/codegen/shift.go | 3 + + 4 files changed, 270 insertions(+) + create mode 100644 src/cmd/compile/internal/ssa/_gen/LOONG64latelower.rules + create mode 100644 src/cmd/compile/internal/ssa/rewriteLOONG64latelower.go + +diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64latelower.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64latelower.rules +new file mode 100644 +index 0000000000..1158f84422 +--- /dev/null ++++ b/src/cmd/compile/internal/ssa/_gen/LOONG64latelower.rules +@@ -0,0 +1,19 @@ ++// Copyright 2024 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. ++ ++// Fold constant shift with extension. ++(SRAVconst (MOVBreg x) [c]) && c < 8 => (SRAVconst (SLLVconst x [56]) [56+c]) ++(SRAVconst (MOVHreg x) [c]) && c < 16 => (SRAVconst (SLLVconst x [48]) [48+c]) ++(SRAVconst (MOVWreg x) [c]) && c < 32 => (SRAVconst (SLLVconst x [32]) [32+c]) ++(SRLVconst (MOVBUreg x) [c]) && c < 8 => (SRLVconst (SLLVconst x [56]) [56+c]) ++(SRLVconst (MOVHUreg x) [c]) && c < 16 => (SRLVconst (SLLVconst x [48]) [48+c]) ++(SRLVconst (MOVWUreg x) [c]) && c < 32 => (SRLVconst (SLLVconst x [32]) [32+c]) ++(SLLVconst (MOVBUreg x) [c]) && c <= 56 => (SRLVconst (SLLVconst x [56]) [56-c]) ++(SLLVconst (MOVHUreg x) [c]) && c <= 48 => (SRLVconst (SLLVconst x [48]) [48-c]) ++(SLLVconst (MOVWUreg x) [c]) && c <= 32 => (SRLVconst (SLLVconst x [32]) [32-c]) ++ ++// Shift by zero. ++(SRAVconst x [0]) => x ++(SRLVconst x [0]) => x ++(SLLVconst x [0]) => x +diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go +index d674cca009..9c4f60f613 100644 +--- a/src/cmd/compile/internal/ssa/config.go ++++ b/src/cmd/compile/internal/ssa/config.go +@@ -280,6 +280,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo + c.RegSize = 8 + c.lowerBlock = rewriteBlockLOONG64 + c.lowerValue = rewriteValueLOONG64 ++ c.lateLowerBlock = rewriteBlockLOONG64latelower ++ c.lateLowerValue = rewriteValueLOONG64latelower + c.registers = registersLOONG64[:] + c.gpRegMask = gpRegMaskLOONG64 + c.fpRegMask = fpRegMaskLOONG64 +diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64latelower.go b/src/cmd/compile/internal/ssa/rewriteLOONG64latelower.go +new file mode 100644 +index 0000000000..f092b0a1ef +--- /dev/null ++++ b/src/cmd/compile/internal/ssa/rewriteLOONG64latelower.go +@@ -0,0 +1,246 @@ ++// Code generated from _gen/LOONG64latelower.rules using 'go generate'; DO NOT EDIT. ++ ++package ssa ++ ++func rewriteValueLOONG64latelower(v *Value) bool { ++ switch v.Op { ++ case OpLOONG64SLLVconst: ++ return rewriteValueLOONG64latelower_OpLOONG64SLLVconst(v) ++ case OpLOONG64SRAVconst: ++ return rewriteValueLOONG64latelower_OpLOONG64SRAVconst(v) ++ case OpLOONG64SRLVconst: ++ return rewriteValueLOONG64latelower_OpLOONG64SRLVconst(v) ++ } ++ return false ++} ++func rewriteValueLOONG64latelower_OpLOONG64SLLVconst(v *Value) bool { ++ v_0 := v.Args[0] ++ b := v.Block ++ typ := &b.Func.Config.Types ++ // match: (SLLVconst (MOVBUreg x) [c]) ++ // cond: c <= 56 ++ // result: (SRLVconst (SLLVconst x [56]) [56-c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVBUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c <= 56) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(56 - c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(56) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SLLVconst (MOVHUreg x) [c]) ++ // cond: c <= 48 ++ // result: (SRLVconst (SLLVconst x [48]) [48-c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVHUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c <= 48) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(48 - c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(48) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SLLVconst (MOVWUreg x) [c]) ++ // cond: c <= 32 ++ // result: (SRLVconst (SLLVconst x [32]) [32-c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c <= 32) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(32 - c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(32) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SLLVconst x [0]) ++ // result: x ++ for { ++ if auxIntToInt64(v.AuxInt) != 0 { ++ break ++ } ++ x := v_0 ++ v.copyOf(x) ++ return true ++ } ++ return false ++} ++func rewriteValueLOONG64latelower_OpLOONG64SRAVconst(v *Value) bool { ++ v_0 := v.Args[0] ++ b := v.Block ++ typ := &b.Func.Config.Types ++ // match: (SRAVconst (MOVBreg x) [c]) ++ // cond: c < 8 ++ // result: (SRAVconst (SLLVconst x [56]) [56+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVBreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 8) { ++ break ++ } ++ v.reset(OpLOONG64SRAVconst) ++ v.AuxInt = int64ToAuxInt(56 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.Int64) ++ v0.AuxInt = int64ToAuxInt(56) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRAVconst (MOVHreg x) [c]) ++ // cond: c < 16 ++ // result: (SRAVconst (SLLVconst x [48]) [48+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVHreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 16) { ++ break ++ } ++ v.reset(OpLOONG64SRAVconst) ++ v.AuxInt = int64ToAuxInt(48 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.Int64) ++ v0.AuxInt = int64ToAuxInt(48) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRAVconst (MOVWreg x) [c]) ++ // cond: c < 32 ++ // result: (SRAVconst (SLLVconst x [32]) [32+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 32) { ++ break ++ } ++ v.reset(OpLOONG64SRAVconst) ++ v.AuxInt = int64ToAuxInt(32 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.Int64) ++ v0.AuxInt = int64ToAuxInt(32) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRAVconst x [0]) ++ // result: x ++ for { ++ if auxIntToInt64(v.AuxInt) != 0 { ++ break ++ } ++ x := v_0 ++ v.copyOf(x) ++ return true ++ } ++ return false ++} ++func rewriteValueLOONG64latelower_OpLOONG64SRLVconst(v *Value) bool { ++ v_0 := v.Args[0] ++ b := v.Block ++ typ := &b.Func.Config.Types ++ // match: (SRLVconst (MOVBUreg x) [c]) ++ // cond: c < 8 ++ // result: (SRLVconst (SLLVconst x [56]) [56+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVBUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 8) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(56 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(56) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRLVconst (MOVHUreg x) [c]) ++ // cond: c < 16 ++ // result: (SRLVconst (SLLVconst x [48]) [48+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVHUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 16) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(48 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(48) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRLVconst (MOVWUreg x) [c]) ++ // cond: c < 32 ++ // result: (SRLVconst (SLLVconst x [32]) [32+c]) ++ for { ++ c := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWUreg { ++ break ++ } ++ x := v_0.Args[0] ++ if !(c < 32) { ++ break ++ } ++ v.reset(OpLOONG64SRLVconst) ++ v.AuxInt = int64ToAuxInt(32 + c) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, typ.UInt64) ++ v0.AuxInt = int64ToAuxInt(32) ++ v0.AddArg(x) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRLVconst x [0]) ++ // result: x ++ for { ++ if auxIntToInt64(v.AuxInt) != 0 { ++ break ++ } ++ x := v_0 ++ v.copyOf(x) ++ return true ++ } ++ return false ++} ++func rewriteBlockLOONG64latelower(b *Block) bool { ++ return false ++} +diff --git a/test/codegen/shift.go b/test/codegen/shift.go +index 2d8cf86857..ad69d69aa5 100644 +--- a/test/codegen/shift.go ++++ b/test/codegen/shift.go +@@ -61,18 +61,21 @@ func rshConst64x64Overflow8(v int8) int64 { + func lshConst32x64(v int32) int32 { + // ppc64x:"SLW" + // riscv64:"SLLI",-"AND",-"SLTIU", -"MOVW" ++ // loong64:"SLLV" + return v << uint64(29) + } + + func rshConst32Ux64(v uint32) uint32 { + // ppc64x:"SRW" + // riscv64:"SRLIW",-"AND",-"SLTIU", -"MOVW" ++ // loong64:"SLLV","SRLV",-"MOVWU" + return v >> uint64(29) + } + + func rshConst32x64(v int32) int32 { + // ppc64x:"SRAW" + // riscv64:"SRAIW",-"OR",-"SLTIU", -"MOVW" ++ // loong64:"SLLV","SRAV",-"MOVW" + return v >> uint64(29) + } + +-- +2.38.1 + diff --git a/0021-test-codegen-fix-the-matching-instructions-inside-pl.patch b/0021-test-codegen-fix-the-matching-instructions-inside-pl.patch new file mode 100644 index 0000000..f9e0b7b --- /dev/null +++ b/0021-test-codegen-fix-the-matching-instructions-inside-pl.patch @@ -0,0 +1,31 @@ +From 53fc992fd2ba2f64eb436c5cf210e31e70282fc0 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Tue, 8 Oct 2024 16:23:56 +0800 +Subject: [PATCH 21/44] test/codegen: fix the matching instructions inside + plain comments for func rshConst32Ux64 on loong64 + +after add rules for (x << lc) >> rc in commit "cmd/compile: add patterns +for bitfield opcodes on loong64", the generated assembly from func +rshConst32Ux64 matches BSTRPICKV, not SLLV and SRLV. + +Change-Id: I4348716156abc3410134495edb977a88727139f8 +--- + test/codegen/shift.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/codegen/shift.go b/test/codegen/shift.go +index ad69d69aa5..6112a989b9 100644 +--- a/test/codegen/shift.go ++++ b/test/codegen/shift.go +@@ -68,7 +68,7 @@ func lshConst32x64(v int32) int32 { + func rshConst32Ux64(v uint32) uint32 { + // ppc64x:"SRW" + // riscv64:"SRLIW",-"AND",-"SLTIU", -"MOVW" +- // loong64:"SLLV","SRLV",-"MOVWU" ++ // loong64:"BSTRPICKV",-"SLLV",-"SRLV",-"MOVWU" + return v >> uint64(29) + } + +-- +2.38.1 + diff --git a/0022-cmd-compile-optimize-shifts-of-int32-and-uint32-on-l.patch b/0022-cmd-compile-optimize-shifts-of-int32-and-uint32-on-l.patch new file mode 100644 index 0000000..c119f2e --- /dev/null +++ b/0022-cmd-compile-optimize-shifts-of-int32-and-uint32-on-l.patch @@ -0,0 +1,1064 @@ +From 2ab1123adf4a080d91ef549b76572bf4b22f907f Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Thu, 24 Oct 2024 17:41:01 +0800 +Subject: [PATCH 22/44] cmd/compile: optimize shifts of int32 and uint32 on + loong64 + +Change-Id: I6b8d110cfed8d55e2b753259a45f55e09b8f759d +--- + src/cmd/compile/internal/loong64/ssa.go | 6 + + .../compile/internal/ssa/_gen/LOONG64.rules | 39 +- + .../compile/internal/ssa/_gen/LOONG64Ops.go | 6 + + src/cmd/compile/internal/ssa/opGen.go | 90 ++++ + .../compile/internal/ssa/rewriteLOONG64.go | 431 +++++++++++++----- + test/codegen/shift.go | 20 +- + 6 files changed, 462 insertions(+), 130 deletions(-) + +diff --git a/src/cmd/compile/internal/loong64/ssa.go b/src/cmd/compile/internal/loong64/ssa.go +index 0ba9efa1d3..bd761c407e 100644 +--- a/src/cmd/compile/internal/loong64/ssa.go ++++ b/src/cmd/compile/internal/loong64/ssa.go +@@ -165,8 +165,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { + ssa.OpLOONG64OR, + ssa.OpLOONG64XOR, + ssa.OpLOONG64NOR, ++ ssa.OpLOONG64SLL, + ssa.OpLOONG64SLLV, ++ ssa.OpLOONG64SRL, + ssa.OpLOONG64SRLV, ++ ssa.OpLOONG64SRA, + ssa.OpLOONG64SRAV, + ssa.OpLOONG64ROTR, + ssa.OpLOONG64ROTRV, +@@ -274,8 +277,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { + ssa.OpLOONG64ORconst, + ssa.OpLOONG64XORconst, + ssa.OpLOONG64NORconst, ++ ssa.OpLOONG64SLLconst, + ssa.OpLOONG64SLLVconst, ++ ssa.OpLOONG64SRLconst, + ssa.OpLOONG64SRLVconst, ++ ssa.OpLOONG64SRAconst, + ssa.OpLOONG64SRAVconst, + ssa.OpLOONG64ROTRconst, + ssa.OpLOONG64ROTRVconst, +diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +index 00a0a84f33..014cd6fb05 100644 +--- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules ++++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +@@ -62,10 +62,10 @@ + (Lsh64x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + (Lsh64x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + +-(Lsh32x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) +-(Lsh32x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Lsh32x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Lsh32x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++(Lsh32x64 x y) => (MASKEQZ (SLL x y) (SGTU (MOVVconst [32]) y)) ++(Lsh32x32 x y) => (MASKEQZ (SLL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) ++(Lsh32x16 x y) => (MASKEQZ (SLL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) ++(Lsh32x8 x y) => (MASKEQZ (SLL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + + (Lsh16x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) + (Lsh16x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +@@ -82,10 +82,10 @@ + (Rsh64Ux16 x y) => (MASKEQZ (SRLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + (Rsh64Ux8 x y) => (MASKEQZ (SRLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + +-(Rsh32Ux64 x y) => (MASKEQZ (SRLV (ZeroExt32to64 x) y) (SGTU (MOVVconst [64]) y)) +-(Rsh32Ux32 x y) => (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Rsh32Ux16 x y) => (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Rsh32Ux8 x y) => (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++(Rsh32Ux64 x y) => (MASKEQZ (SRL x y) (SGTU (MOVVconst [32]) y)) ++(Rsh32Ux32 x y) => (MASKEQZ (SRL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) ++(Rsh32Ux16 x y) => (MASKEQZ (SRL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) ++(Rsh32Ux8 x y) => (MASKEQZ (SRL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + + (Rsh16Ux64 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) y) (SGTU (MOVVconst [64]) y)) + (Rsh16Ux32 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +@@ -102,10 +102,10 @@ + (Rsh64x16 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + (Rsh64x8 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + +-(Rsh32x64 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +-(Rsh32x32 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +-(Rsh32x16 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +-(Rsh32x8 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) ++(Rsh32x64 x y) => (SRA x (OR (NEGV (SGTU y (MOVVconst [31]))) y)) ++(Rsh32x32 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [31]))) (ZeroExt32to64 y))) ++(Rsh32x16 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [31]))) (ZeroExt16to64 y))) ++(Rsh32x8 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [31]))) (ZeroExt8to64 y))) + + (Rsh16x64 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + (Rsh16x32 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +@@ -683,15 +683,30 @@ + (XOR x (MOVVconst [c])) && is32Bit(c) => (XORconst [c] x) + (NOR x (MOVVconst [c])) && is32Bit(c) => (NORconst [c] x) + ++(SLL _ (MOVVconst [c])) && uint64(c)>=32 => (MOVVconst [0]) + (SLLV _ (MOVVconst [c])) && uint64(c)>=64 => (MOVVconst [0]) ++(SRL _ (MOVVconst [c])) && uint64(c)>=32 => (MOVVconst [0]) + (SRLV _ (MOVVconst [c])) && uint64(c)>=64 => (MOVVconst [0]) ++(SRA x (MOVVconst [c])) && uint64(c)>=32 => (SRAconst x [31]) + (SRAV x (MOVVconst [c])) && uint64(c)>=64 => (SRAVconst x [63]) ++(SLL x (MOVVconst [c])) && uint64(c) >=0 && uint64(c) <= 31 => (SLLconst x [c]) + (SLLV x (MOVVconst [c])) => (SLLVconst x [c]) ++(SRL x (MOVVconst [c])) && uint64(c) >=0 && uint64(c) <= 31 => (SRLconst x [c]) + (SRLV x (MOVVconst [c])) => (SRLVconst x [c]) ++(SRA x (MOVVconst [c])) && uint64(c) >=0 && uint64(c) <= 31 => (SRAconst x [c]) + (SRAV x (MOVVconst [c])) => (SRAVconst x [c]) + (ROTR x (MOVVconst [c])) => (ROTRconst x [c&31]) + (ROTRV x (MOVVconst [c])) => (ROTRVconst x [c&63]) + ++// Avoid unnecessary zero and sign extension when right shifting. ++(SRLVconst [rc] (MOVWUreg y)) && rc >= 0 && rc <= 31 => (SRLconst [int64(rc)] y) ++(SRAVconst [rc] (MOVWreg y)) && rc >= 0 && rc <= 31 => (SRAconst [int64(rc)] y) ++ ++// Replace right shifts that exceed size of signed type. ++(SRAVconst [rc] (MOVBreg y)) && rc >= 8 => (SRAVconst [63] (SLLVconst [56] y)) ++(SRAVconst [rc] (MOVHreg y)) && rc >= 16 => (SRAVconst [63] (SLLVconst [48] y)) ++(SRAVconst [rc] (MOVWreg y)) && rc >= 32 => (SRAconst [31] y) ++ + // If the shift amount is larger than the datasize(32, 16, 8), we can optimize to constant 0. + (MOVWUreg (SLLVconst [lc] x)) && lc >= 32 => (MOVVconst [0]) + (MOVHUreg (SLLVconst [lc] x)) && lc >= 16 => (MOVVconst [0]) +diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go +index 8f17158b64..4b3f1fd689 100644 +--- a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go ++++ b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go +@@ -240,11 +240,17 @@ func init() { + {name: "FCOPYSGD", argLength: 2, reg: fp21, asm: "FCOPYSGD"}, // float64 + + // shifts ++ {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 32 + {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 ++ {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int64"}, // arg0 << auxInt + {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt ++ {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 32 + {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 ++ {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int64"}, // arg0 >> auxInt, unsigned + {name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned ++ {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 32 + {name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"}, // arg0 >> arg1, signed, shift amount is mod 64 ++ {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int64"}, // arg0 >> auxInt, signed + {name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed + {name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"}, // arg0 right rotate by (arg1 mod 32) bits + {name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"}, // arg0 right rotate by (arg1 mod 64) bits +diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go +index df1ddfa69e..643d012ca1 100644 +--- a/src/cmd/compile/internal/ssa/opGen.go ++++ b/src/cmd/compile/internal/ssa/opGen.go +@@ -1824,11 +1824,17 @@ const ( + OpLOONG64MASKEQZ + OpLOONG64MASKNEZ + OpLOONG64FCOPYSGD ++ OpLOONG64SLL + OpLOONG64SLLV ++ OpLOONG64SLLconst + OpLOONG64SLLVconst ++ OpLOONG64SRL + OpLOONG64SRLV ++ OpLOONG64SRLconst + OpLOONG64SRLVconst ++ OpLOONG64SRA + OpLOONG64SRAV ++ OpLOONG64SRAconst + OpLOONG64SRAVconst + OpLOONG64ROTR + OpLOONG64ROTRV +@@ -24541,6 +24547,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SLL", ++ argLen: 2, ++ asm: loong64.ASLL, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SLLV", + argLen: 2, +@@ -24555,6 +24575,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SLLconst", ++ auxType: auxInt64, ++ argLen: 1, ++ asm: loong64.ASLL, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SLLVconst", + auxType: auxInt64, +@@ -24569,6 +24603,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SRL", ++ argLen: 2, ++ asm: loong64.ASRL, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SRLV", + argLen: 2, +@@ -24583,6 +24631,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SRLconst", ++ auxType: auxInt64, ++ argLen: 1, ++ asm: loong64.ASRL, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SRLVconst", + auxType: auxInt64, +@@ -24597,6 +24659,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SRA", ++ argLen: 2, ++ asm: loong64.ASRA, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SRAV", + argLen: 2, +@@ -24611,6 +24687,20 @@ var opcodeTable = [...]opInfo{ + }, + }, + }, ++ { ++ name: "SRAconst", ++ auxType: auxInt64, ++ argLen: 1, ++ asm: loong64.ASRA, ++ reg: regInfo{ ++ inputs: []inputInfo{ ++ {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ outputs: []outputInfo{ ++ {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 ++ }, ++ }, ++ }, + { + name: "SRAVconst", + auxType: auxInt64, +diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go +index ab39040de1..93bf95eb51 100644 +--- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go ++++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go +@@ -440,14 +440,20 @@ func rewriteValueLOONG64(v *Value) bool { + return rewriteValueLOONG64_OpLOONG64SGTUconst(v) + case OpLOONG64SGTconst: + return rewriteValueLOONG64_OpLOONG64SGTconst(v) ++ case OpLOONG64SLL: ++ return rewriteValueLOONG64_OpLOONG64SLL(v) + case OpLOONG64SLLV: + return rewriteValueLOONG64_OpLOONG64SLLV(v) + case OpLOONG64SLLVconst: + return rewriteValueLOONG64_OpLOONG64SLLVconst(v) ++ case OpLOONG64SRA: ++ return rewriteValueLOONG64_OpLOONG64SRA(v) + case OpLOONG64SRAV: + return rewriteValueLOONG64_OpLOONG64SRAV(v) + case OpLOONG64SRAVconst: + return rewriteValueLOONG64_OpLOONG64SRAVconst(v) ++ case OpLOONG64SRL: ++ return rewriteValueLOONG64_OpLOONG64SRL(v) + case OpLOONG64SRLV: + return rewriteValueLOONG64_OpLOONG64SRLV(v) + case OpLOONG64SRLVconst: +@@ -5953,6 +5959,43 @@ func rewriteValueLOONG64_OpLOONG64SGTconst(v *Value) bool { + } + return false + } ++func rewriteValueLOONG64_OpLOONG64SLL(v *Value) bool { ++ v_1 := v.Args[1] ++ v_0 := v.Args[0] ++ // match: (SLL _ (MOVVconst [c])) ++ // cond: uint64(c)>=32 ++ // result: (MOVVconst [0]) ++ for { ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 32) { ++ break ++ } ++ v.reset(OpLOONG64MOVVconst) ++ v.AuxInt = int64ToAuxInt(0) ++ return true ++ } ++ // match: (SLL x (MOVVconst [c])) ++ // cond: uint64(c) >=0 && uint64(c) <= 31 ++ // result: (SLLconst x [c]) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 0 && uint64(c) <= 31) { ++ break ++ } ++ v.reset(OpLOONG64SLLconst) ++ v.AuxInt = int64ToAuxInt(c) ++ v.AddArg(x) ++ return true ++ } ++ return false ++} + func rewriteValueLOONG64_OpLOONG64SLLV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] +@@ -6002,6 +6045,45 @@ func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { + } + return false + } ++func rewriteValueLOONG64_OpLOONG64SRA(v *Value) bool { ++ v_1 := v.Args[1] ++ v_0 := v.Args[0] ++ // match: (SRA x (MOVVconst [c])) ++ // cond: uint64(c)>=32 ++ // result: (SRAconst x [31]) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 32) { ++ break ++ } ++ v.reset(OpLOONG64SRAconst) ++ v.AuxInt = int64ToAuxInt(31) ++ v.AddArg(x) ++ return true ++ } ++ // match: (SRA x (MOVVconst [c])) ++ // cond: uint64(c) >=0 && uint64(c) <= 31 ++ // result: (SRAconst x [c]) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 0 && uint64(c) <= 31) { ++ break ++ } ++ v.reset(OpLOONG64SRAconst) ++ v.AuxInt = int64ToAuxInt(c) ++ v.AddArg(x) ++ return true ++ } ++ return false ++} + func rewriteValueLOONG64_OpLOONG64SRAV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] +@@ -6039,6 +6121,85 @@ func rewriteValueLOONG64_OpLOONG64SRAV(v *Value) bool { + } + func rewriteValueLOONG64_OpLOONG64SRAVconst(v *Value) bool { + v_0 := v.Args[0] ++ b := v.Block ++ // match: (SRAVconst [rc] (MOVWreg y)) ++ // cond: rc >= 0 && rc <= 31 ++ // result: (SRAconst [int64(rc)] y) ++ for { ++ t := v.Type ++ rc := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWreg { ++ break ++ } ++ y := v_0.Args[0] ++ if !(rc >= 0 && rc <= 31) { ++ break ++ } ++ v.reset(OpLOONG64SRAconst) ++ v.Type = t ++ v.AuxInt = int64ToAuxInt(int64(rc)) ++ v.AddArg(y) ++ return true ++ } ++ // match: (SRAVconst [rc] (MOVBreg y)) ++ // cond: rc >= 8 ++ // result: (SRAVconst [63] (SLLVconst [56] y)) ++ for { ++ t := v.Type ++ rc := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVBreg { ++ break ++ } ++ y := v_0.Args[0] ++ if !(rc >= 8) { ++ break ++ } ++ v.reset(OpLOONG64SRAVconst) ++ v.AuxInt = int64ToAuxInt(63) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, t) ++ v0.AuxInt = int64ToAuxInt(56) ++ v0.AddArg(y) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRAVconst [rc] (MOVHreg y)) ++ // cond: rc >= 16 ++ // result: (SRAVconst [63] (SLLVconst [48] y)) ++ for { ++ t := v.Type ++ rc := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVHreg { ++ break ++ } ++ y := v_0.Args[0] ++ if !(rc >= 16) { ++ break ++ } ++ v.reset(OpLOONG64SRAVconst) ++ v.AuxInt = int64ToAuxInt(63) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLLVconst, t) ++ v0.AuxInt = int64ToAuxInt(48) ++ v0.AddArg(y) ++ v.AddArg(v0) ++ return true ++ } ++ // match: (SRAVconst [rc] (MOVWreg y)) ++ // cond: rc >= 32 ++ // result: (SRAconst [31] y) ++ for { ++ rc := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWreg { ++ break ++ } ++ y := v_0.Args[0] ++ if !(rc >= 32) { ++ break ++ } ++ v.reset(OpLOONG64SRAconst) ++ v.AuxInt = int64ToAuxInt(31) ++ v.AddArg(y) ++ return true ++ } + // match: (SRAVconst [c] (MOVVconst [d])) + // result: (MOVVconst [d>>uint64(c)]) + for { +@@ -6053,6 +6214,43 @@ func rewriteValueLOONG64_OpLOONG64SRAVconst(v *Value) bool { + } + return false + } ++func rewriteValueLOONG64_OpLOONG64SRL(v *Value) bool { ++ v_1 := v.Args[1] ++ v_0 := v.Args[0] ++ // match: (SRL _ (MOVVconst [c])) ++ // cond: uint64(c)>=32 ++ // result: (MOVVconst [0]) ++ for { ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 32) { ++ break ++ } ++ v.reset(OpLOONG64MOVVconst) ++ v.AuxInt = int64ToAuxInt(0) ++ return true ++ } ++ // match: (SRL x (MOVVconst [c])) ++ // cond: uint64(c) >=0 && uint64(c) <= 31 ++ // result: (SRLconst x [c]) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64MOVVconst { ++ break ++ } ++ c := auxIntToInt64(v_1.AuxInt) ++ if !(uint64(c) >= 0 && uint64(c) <= 31) { ++ break ++ } ++ v.reset(OpLOONG64SRLconst) ++ v.AuxInt = int64ToAuxInt(c) ++ v.AddArg(x) ++ return true ++ } ++ return false ++} + func rewriteValueLOONG64_OpLOONG64SRLV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] +@@ -6157,6 +6355,25 @@ func rewriteValueLOONG64_OpLOONG64SRLVconst(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SRLVconst [rc] (MOVWUreg y)) ++ // cond: rc >= 0 && rc <= 31 ++ // result: (SRLconst [int64(rc)] y) ++ for { ++ t := v.Type ++ rc := auxIntToInt64(v.AuxInt) ++ if v_0.Op != OpLOONG64MOVWUreg { ++ break ++ } ++ y := v_0.Args[0] ++ if !(rc >= 0 && rc <= 31) { ++ break ++ } ++ v.reset(OpLOONG64SRLconst) ++ v.Type = t ++ v.AuxInt = int64ToAuxInt(int64(rc)) ++ v.AddArg(y) ++ return true ++ } + // match: (SRLVconst [rc] (MOVWUreg x)) + // cond: rc >= 32 + // result: (MOVVconst [0]) +@@ -7262,19 +7479,19 @@ func rewriteValueLOONG64_OpLsh32x16(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x16 x y) +- // result: (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++ // result: (MASKEQZ (SLL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v1.AddArg(y) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v3.AuxInt = int64ToAuxInt(64) ++ v3.AuxInt = int64ToAuxInt(32) + v2.AddArg2(v3, v1) + v.AddArg2(v0, v2) + return true +@@ -7286,19 +7503,19 @@ func rewriteValueLOONG64_OpLsh32x32(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x32 x y) +- // result: (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++ // result: (MASKEQZ (SLL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v1.AddArg(y) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v3.AuxInt = int64ToAuxInt(64) ++ v3.AuxInt = int64ToAuxInt(32) + v2.AddArg2(v3, v1) + v.AddArg2(v0, v2) + return true +@@ -7310,17 +7527,17 @@ func rewriteValueLOONG64_OpLsh32x64(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x64 x y) +- // result: (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) ++ // result: (MASKEQZ (SLL x y) (SGTU (MOVVconst [32]) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v2.AuxInt = int64ToAuxInt(64) ++ v2.AuxInt = int64ToAuxInt(32) + v1.AddArg2(v2, y) + v.AddArg2(v0, v1) + return true +@@ -7332,19 +7549,19 @@ func rewriteValueLOONG64_OpLsh32x8(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x8 x y) +- // result: (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ // result: (MASKEQZ (SLL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v1.AddArg(y) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v3.AuxInt = int64ToAuxInt(64) ++ v3.AuxInt = int64ToAuxInt(32) + v2.AddArg2(v3, v1) + v.AddArg2(v0, v2) + return true +@@ -8694,23 +8911,21 @@ func rewriteValueLOONG64_OpRsh32Ux16(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux16 x y) +- // result: (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++ // result: (MASKEQZ (SRL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) +- v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v1.AddArg(x) +- v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +- v2.AddArg(y) +- v0.AddArg2(v1, v2) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v4.AuxInt = int64ToAuxInt(64) +- v3.AddArg2(v4, v2) +- v.AddArg2(v0, v3) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) ++ v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v1.AddArg(y) ++ v0.AddArg2(x, v1) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v3.AuxInt = int64ToAuxInt(32) ++ v2.AddArg2(v3, v1) ++ v.AddArg2(v0, v2) + return true + } + } +@@ -8720,23 +8935,21 @@ func rewriteValueLOONG64_OpRsh32Ux32(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux32 x y) +- // result: (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++ // result: (MASKEQZ (SRL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v1.AddArg(x) +- v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v2.AddArg(y) +- v0.AddArg2(v1, v2) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v4.AuxInt = int64ToAuxInt(64) +- v3.AddArg2(v4, v2) +- v.AddArg2(v0, v3) ++ v1.AddArg(y) ++ v0.AddArg2(x, v1) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v3.AuxInt = int64ToAuxInt(32) ++ v2.AddArg2(v3, v1) ++ v.AddArg2(v0, v2) + return true + } + } +@@ -8746,21 +8959,19 @@ func rewriteValueLOONG64_OpRsh32Ux64(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux64 x y) +- // result: (MASKEQZ (SRLV (ZeroExt32to64 x) y) (SGTU (MOVVconst [64]) y)) ++ // result: (MASKEQZ (SRL x y) (SGTU (MOVVconst [32]) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) +- v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v1.AddArg(x) +- v0.AddArg2(v1, y) +- v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v3.AuxInt = int64ToAuxInt(64) +- v2.AddArg2(v3, y) +- v.AddArg2(v0, v2) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) ++ v0.AddArg2(x, y) ++ v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v2.AuxInt = int64ToAuxInt(32) ++ v1.AddArg2(v2, y) ++ v.AddArg2(v0, v1) + return true + } + } +@@ -8770,23 +8981,21 @@ func rewriteValueLOONG64_OpRsh32Ux8(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux8 x y) +- // result: (MASKEQZ (SRLV (ZeroExt32to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ // result: (MASKEQZ (SRL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64MASKEQZ) +- v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) +- v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v1.AddArg(x) +- v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +- v2.AddArg(y) +- v0.AddArg2(v1, v2) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v4.AuxInt = int64ToAuxInt(64) +- v3.AddArg2(v4, v2) +- v.AddArg2(v0, v3) ++ v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) ++ v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v1.AddArg(y) ++ v0.AddArg2(x, v1) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v3.AuxInt = int64ToAuxInt(32) ++ v2.AddArg2(v3, v1) ++ v.AddArg2(v0, v2) + return true + } + } +@@ -8796,25 +9005,23 @@ func rewriteValueLOONG64_OpRsh32x16(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x16 x y) +- // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) ++ // result: (SRA x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [31]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 +- v.reset(OpLOONG64SRAV) +- v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) +- v0.AddArg(x) +- v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) +- v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +- v4.AddArg(y) +- v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v5.AuxInt = int64ToAuxInt(63) +- v3.AddArg2(v4, v5) +- v2.AddArg(v3) +- v1.AddArg2(v2, v4) +- v.AddArg2(v0, v1) ++ v.reset(OpLOONG64SRA) ++ v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) ++ v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v3.AddArg(y) ++ v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v4.AuxInt = int64ToAuxInt(31) ++ v2.AddArg2(v3, v4) ++ v1.AddArg(v2) ++ v0.AddArg2(v1, v3) ++ v.AddArg2(x, v0) + return true + } + } +@@ -8824,25 +9031,23 @@ func rewriteValueLOONG64_OpRsh32x32(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x32 x y) +- // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) ++ // result: (SRA x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [31]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 +- v.reset(OpLOONG64SRAV) +- v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) +- v0.AddArg(x) +- v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) +- v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +- v4.AddArg(y) +- v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v5.AuxInt = int64ToAuxInt(63) +- v3.AddArg2(v4, v5) +- v2.AddArg(v3) +- v1.AddArg2(v2, v4) +- v.AddArg2(v0, v1) ++ v.reset(OpLOONG64SRA) ++ v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) ++ v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) ++ v3.AddArg(y) ++ v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v4.AuxInt = int64ToAuxInt(31) ++ v2.AddArg2(v3, v4) ++ v1.AddArg(v2) ++ v0.AddArg2(v1, v3) ++ v.AddArg2(x, v0) + return true + } + } +@@ -8852,23 +9057,21 @@ func rewriteValueLOONG64_OpRsh32x64(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x64 x y) +- // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) ++ // result: (SRA x (OR (NEGV (SGTU y (MOVVconst [31]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 +- v.reset(OpLOONG64SRAV) +- v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) +- v0.AddArg(x) +- v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) +- v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v4.AuxInt = int64ToAuxInt(63) +- v3.AddArg2(y, v4) +- v2.AddArg(v3) +- v1.AddArg2(v2, y) +- v.AddArg2(v0, v1) ++ v.reset(OpLOONG64SRA) ++ v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) ++ v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v3.AuxInt = int64ToAuxInt(31) ++ v2.AddArg2(y, v3) ++ v1.AddArg(v2) ++ v0.AddArg2(v1, y) ++ v.AddArg2(x, v0) + return true + } + } +@@ -8878,25 +9081,23 @@ func rewriteValueLOONG64_OpRsh32x8(v *Value) bool { + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x8 x y) +- // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) ++ // result: (SRA x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [31]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 +- v.reset(OpLOONG64SRAV) +- v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) +- v0.AddArg(x) +- v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) +- v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +- v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) +- v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +- v4.AddArg(y) +- v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) +- v5.AuxInt = int64ToAuxInt(63) +- v3.AddArg2(v4, v5) +- v2.AddArg(v3) +- v1.AddArg2(v2, v4) +- v.AddArg2(v0, v1) ++ v.reset(OpLOONG64SRA) ++ v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) ++ v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) ++ v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) ++ v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v3.AddArg(y) ++ v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) ++ v4.AuxInt = int64ToAuxInt(31) ++ v2.AddArg2(v3, v4) ++ v1.AddArg(v2) ++ v0.AddArg2(v1, v3) ++ v.AddArg2(x, v0) + return true + } + } +diff --git a/test/codegen/shift.go b/test/codegen/shift.go +index 6112a989b9..3c669edcb2 100644 +--- a/test/codegen/shift.go ++++ b/test/codegen/shift.go +@@ -11,87 +11,99 @@ package codegen + // ------------------ // + + func lshConst64x64(v int64) int64 { ++ // loong64:"SLLV" + // ppc64x:"SLD" + // riscv64:"SLLI",-"AND",-"SLTIU" + return v << uint64(33) + } + + func rshConst64Ux64(v uint64) uint64 { ++ // loong64:"SRLV" + // ppc64x:"SRD" + // riscv64:"SRLI\t",-"AND",-"SLTIU" + return v >> uint64(33) + } + + func rshConst64Ux64Overflow32(v uint32) uint64 { ++ // loong64:"MOVV\t\\$0,",-"SRL\t" + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 32 + } + + func rshConst64Ux64Overflow16(v uint16) uint64 { ++ // loong64:"MOVV\t\\$0,",-"SRLV" + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 16 + } + + func rshConst64Ux64Overflow8(v uint8) uint64 { ++ // loong64:"MOVV\t\\$0,",-"SRLV" + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 8 + } + + func rshConst64x64(v int64) int64 { ++ // loong64:"SRAV" + // ppc64x:"SRAD" + // riscv64:"SRAI\t",-"OR",-"SLTIU" + return v >> uint64(33) + } + + func rshConst64x64Overflow32(v int32) int64 { ++ // loong64:"SRA\t\\$31" + // riscv64:"SRAIW",-"SLLI",-"SRAI\t" + return int64(v) >> 32 + } + + func rshConst64x64Overflow16(v int16) int64 { ++ // loong64:"SLLV\t\\$48","SRAV\t\\$63" + // riscv64:"SLLI","SRAI",-"SRAIW" + return int64(v) >> 16 + } + + func rshConst64x64Overflow8(v int8) int64 { ++ // loong64:"SLLV\t\\$56","SRAV\t\\$63" + // riscv64:"SLLI","SRAI",-"SRAIW" + return int64(v) >> 8 + } + + func lshConst32x64(v int32) int32 { ++ // loong64:"SLL\t" + // ppc64x:"SLW" + // riscv64:"SLLI",-"AND",-"SLTIU", -"MOVW" +- // loong64:"SLLV" + return v << uint64(29) + } + + func rshConst32Ux64(v uint32) uint32 { ++ // loong64:"SRL\t" + // ppc64x:"SRW" + // riscv64:"SRLIW",-"AND",-"SLTIU", -"MOVW" +- // loong64:"BSTRPICKV",-"SLLV",-"SRLV",-"MOVWU" + return v >> uint64(29) + } + + func rshConst32x64(v int32) int32 { ++ // loong64:"SRA\t" + // ppc64x:"SRAW" + // riscv64:"SRAIW",-"OR",-"SLTIU", -"MOVW" +- // loong64:"SLLV","SRAV",-"MOVW" + return v >> uint64(29) + } + + func lshConst64x32(v int64) int64 { ++ // loong64:"SLLV" + // ppc64x:"SLD" + // riscv64:"SLLI",-"AND",-"SLTIU" + return v << uint32(33) + } + + func rshConst64Ux32(v uint64) uint64 { ++ // loong64:"SRLV" + // ppc64x:"SRD" + // riscv64:"SRLI\t",-"AND",-"SLTIU" + return v >> uint32(33) + } + + func rshConst64x32(v int64) int64 { ++ // loong64:"SRAV" + // ppc64x:"SRAD" + // riscv64:"SRAI\t",-"OR",-"SLTIU" + return v >> uint32(33) +@@ -253,6 +265,7 @@ func rshGuarded64U(v uint64, s uint) uint64 { + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // wasm:-"Select",-".*LtU" + // arm64:"LSR",-"CSEL" ++ // loong64:"SRLV" + return v >> s + } + panic("shift too large") +@@ -264,6 +277,7 @@ func rshGuarded64(v int64, s uint) int64 { + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // wasm:-"Select",-".*LtU" + // arm64:"ASR",-"CSEL" ++ // loong64:"SRAV" + return v >> s + } + panic("shift too large") +-- +2.38.1 + diff --git a/0023-cmd-compile-simplify-bounded-shift-on-loong64.patch b/0023-cmd-compile-simplify-bounded-shift-on-loong64.patch new file mode 100644 index 0000000..c4ba508 --- /dev/null +++ b/0023-cmd-compile-simplify-bounded-shift-on-loong64.patch @@ -0,0 +1,2206 @@ +From 03f91ceb084274b0840d7c2cf7a7cb83a7fb2ed0 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Fri, 15 Nov 2024 17:28:07 +0800 +Subject: [PATCH 23/44] cmd/compile: simplify bounded shift on loong64 + +Use the shiftIsBounded function to generate more efficient shift instructions. +Also optimize shift ops when the shift value is v&63 and v&31. + +Change-Id: I12548101a7cea6bca7f5fef2b12c4b8af8a20bb3 +--- + .../compile/internal/ssa/_gen/LOONG64.rules | 146 +-- + .../compile/internal/ssa/rewriteLOONG64.go | 968 ++++++++++++++++++ + test/codegen/shift.go | 16 + + 3 files changed, 1071 insertions(+), 59 deletions(-) + +diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +index 014cd6fb05..9d0435f434 100644 +--- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules ++++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +@@ -57,65 +57,84 @@ + // shifts + // hardware instruction uses only the low 6 bits of the shift + // we compare to 64 to ensure Go semantics for large shifts +-(Lsh64x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) +-(Lsh64x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Lsh64x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Lsh64x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Lsh32x64 x y) => (MASKEQZ (SLL x y) (SGTU (MOVVconst [32]) y)) +-(Lsh32x32 x y) => (MASKEQZ (SLL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) +-(Lsh32x16 x y) => (MASKEQZ (SLL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) +-(Lsh32x8 x y) => (MASKEQZ (SLL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) +- +-(Lsh16x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) +-(Lsh16x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Lsh16x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Lsh16x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Lsh8x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) +-(Lsh8x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Lsh8x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Lsh8x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Rsh64Ux64 x y) => (MASKEQZ (SRLV x y) (SGTU (MOVVconst [64]) y)) +-(Rsh64Ux32 x y) => (MASKEQZ (SRLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Rsh64Ux16 x y) => (MASKEQZ (SRLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Rsh64Ux8 x y) => (MASKEQZ (SRLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Rsh32Ux64 x y) => (MASKEQZ (SRL x y) (SGTU (MOVVconst [32]) y)) +-(Rsh32Ux32 x y) => (MASKEQZ (SRL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) +-(Rsh32Ux16 x y) => (MASKEQZ (SRL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) +-(Rsh32Ux8 x y) => (MASKEQZ (SRL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) +- +-(Rsh16Ux64 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) y) (SGTU (MOVVconst [64]) y)) +-(Rsh16Ux32 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Rsh16Ux16 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Rsh16Ux8 x y) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Rsh8Ux64 x y) => (MASKEQZ (SRLV (ZeroExt8to64 x) y) (SGTU (MOVVconst [64]) y)) +-(Rsh8Ux32 x y) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) +-(Rsh8Ux16 x y) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) +-(Rsh8Ux8 x y) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) +- +-(Rsh64x64 x y) => (SRAV x (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +-(Rsh64x32 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +-(Rsh64x16 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +-(Rsh64x8 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) +- +-(Rsh32x64 x y) => (SRA x (OR (NEGV (SGTU y (MOVVconst [31]))) y)) +-(Rsh32x32 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [31]))) (ZeroExt32to64 y))) +-(Rsh32x16 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [31]))) (ZeroExt16to64 y))) +-(Rsh32x8 x y) => (SRA x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [31]))) (ZeroExt8to64 y))) +- +-(Rsh16x64 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +-(Rsh16x32 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +-(Rsh16x16 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +-(Rsh16x8 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) +- +-(Rsh8x64 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +-(Rsh8x32 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +-(Rsh8x16 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +-(Rsh8x8 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) ++ ++// left shift ++(Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y) ++(Lsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y) ++(Lsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y) ++(Lsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y) ++ ++(Lsh64x64 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) ++(Lsh64x32 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Lsh64x16 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Lsh64x8 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++(Lsh32x64 x y) && !shiftIsBounded(v) => (MASKEQZ (SLL x y) (SGTU (MOVVconst [32]) y)) ++(Lsh32x32 x y) && !shiftIsBounded(v) => (MASKEQZ (SLL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) ++(Lsh32x16 x y) && !shiftIsBounded(v) => (MASKEQZ (SLL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) ++(Lsh32x8 x y) && !shiftIsBounded(v) => (MASKEQZ (SLL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) ++ ++(Lsh16x64 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) ++(Lsh16x32 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Lsh16x16 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Lsh16x8 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++(Lsh8x64 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) ++(Lsh8x32 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Lsh8x16 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Lsh8x8 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++// unsigned right shift ++(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRLV x y) ++(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL x y) ++(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRLV (ZeroExt16to64 x) y) ++(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRLV (ZeroExt8to64 x) y) ++ ++(Rsh64Ux64 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV x y) (SGTU (MOVVconst [64]) y)) ++(Rsh64Ux32 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Rsh64Ux16 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Rsh64Ux8 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++(Rsh32Ux64 x y) && !shiftIsBounded(v) => (MASKEQZ (SRL x y) (SGTU (MOVVconst [32]) y)) ++(Rsh32Ux32 x y) && !shiftIsBounded(v) => (MASKEQZ (SRL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) ++(Rsh32Ux16 x y) && !shiftIsBounded(v) => (MASKEQZ (SRL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) ++(Rsh32Ux8 x y) && !shiftIsBounded(v) => (MASKEQZ (SRL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) ++ ++(Rsh16Ux64 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt16to64 x) y) (SGTU (MOVVconst [64]) y)) ++(Rsh16Ux32 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Rsh16Ux16 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Rsh16Ux8 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++(Rsh8Ux64 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt8to64 x) y) (SGTU (MOVVconst [64]) y)) ++(Rsh8Ux32 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) ++(Rsh8Ux16 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) ++(Rsh8Ux8 x y) && !shiftIsBounded(v) => (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) ++ ++// signed right shift ++(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRAV x y) ++(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y) ++(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SRAV (SignExt16to64 x) y) ++(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SRAV (SignExt8to64 x) y) ++ ++(Rsh64x64 x y) && !shiftIsBounded(v) => (SRAV x (OR (NEGV (SGTU y (MOVVconst [63]))) y)) ++(Rsh64x32 x y) && !shiftIsBounded(v) => (SRAV x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) ++(Rsh64x16 x y) && !shiftIsBounded(v) => (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) ++(Rsh64x8 x y) && !shiftIsBounded(v) => (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) ++ ++(Rsh32x64 x y) && !shiftIsBounded(v) => (SRA x (OR (NEGV (SGTU y (MOVVconst [31]))) y)) ++(Rsh32x32 x y) && !shiftIsBounded(v) => (SRA x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [31]))) (ZeroExt32to64 y))) ++(Rsh32x16 x y) && !shiftIsBounded(v) => (SRA x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [31]))) (ZeroExt16to64 y))) ++(Rsh32x8 x y) && !shiftIsBounded(v) => (SRA x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [31]))) (ZeroExt8to64 y))) ++ ++(Rsh16x64 x y) && !shiftIsBounded(v) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) ++(Rsh16x32 x y) && !shiftIsBounded(v) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) ++(Rsh16x16 x y) && !shiftIsBounded(v) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) ++(Rsh16x8 x y) && !shiftIsBounded(v) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) ++ ++(Rsh8x64 x y) && !shiftIsBounded(v) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) ++(Rsh8x32 x y) && !shiftIsBounded(v) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) ++(Rsh8x16 x y) && !shiftIsBounded(v) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) ++(Rsh8x8 x y) && !shiftIsBounded(v) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + + // bitfield ops + +@@ -698,6 +717,15 @@ + (ROTR x (MOVVconst [c])) => (ROTRconst x [c&31]) + (ROTRV x (MOVVconst [c])) => (ROTRVconst x [c&63]) + ++// SLLV/SRLV/SRAV only considers the bottom 6 bits of y, similarly SLL/SRL/SRA only considers the ++// bottom 5 bits of y. ++(SLL x (ANDconst [31] y)) => (SLL x y) ++(SRL x (ANDconst [31] y)) => (SRL x y) ++(SRA x (ANDconst [31] y)) => (SRA x y) ++(SLLV x (ANDconst [63] y)) => (SLLV x y) ++(SRLV x (ANDconst [63] y)) => (SRLV x y) ++(SRAV x (ANDconst [63] y)) => (SRAV x y) ++ + // Avoid unnecessary zero and sign extension when right shifting. + (SRLVconst [rc] (MOVWUreg y)) && rc >= 0 && rc <= 31 => (SRLconst [int64(rc)] y) + (SRAVconst [rc] (MOVWreg y)) && rc >= 0 && rc <= 31 => (SRAconst [int64(rc)] y) +diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go +index 93bf95eb51..9efdca9c9c 100644 +--- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go ++++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go +@@ -5994,6 +5994,18 @@ func rewriteValueLOONG64_OpLOONG64SLL(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SLL x (ANDconst [31] y)) ++ // result: (SLL x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 31 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SLL) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SLLV(v *Value) bool { +@@ -6027,6 +6039,18 @@ func rewriteValueLOONG64_OpLOONG64SLLV(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SLLV x (ANDconst [63] y)) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { +@@ -6082,6 +6106,18 @@ func rewriteValueLOONG64_OpLOONG64SRA(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SRA x (ANDconst [31] y)) ++ // result: (SRA x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 31 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SRA) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SRAV(v *Value) bool { +@@ -6117,6 +6153,18 @@ func rewriteValueLOONG64_OpLOONG64SRAV(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SRAV x (ANDconst [63] y)) ++ // result: (SRAV x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SRAV) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SRAVconst(v *Value) bool { +@@ -6249,6 +6297,18 @@ func rewriteValueLOONG64_OpLOONG64SRL(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SRL x (ANDconst [31] y)) ++ // result: (SRL x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 31 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SRL) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SRLV(v *Value) bool { +@@ -6282,6 +6342,18 @@ func rewriteValueLOONG64_OpLOONG64SRLV(v *Value) bool { + v.AddArg(x) + return true + } ++ // match: (SRLV x (ANDconst [63] y)) ++ // result: (SRLV x y) ++ for { ++ x := v_0 ++ if v_1.Op != OpLOONG64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { ++ break ++ } ++ y := v_1.Args[0] ++ v.reset(OpLOONG64SRLV) ++ v.AddArg2(x, y) ++ return true ++ } + return false + } + func rewriteValueLOONG64_OpLOONG64SRLVconst(v *Value) bool { +@@ -7384,12 +7456,29 @@ func rewriteValueLOONG64_OpLsh16x16(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh16x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh16x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -7402,18 +7491,36 @@ func rewriteValueLOONG64_OpLsh16x16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh16x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh16x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh16x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -7426,18 +7533,36 @@ func rewriteValueLOONG64_OpLsh16x32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh16x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh16x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh16x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v0.AddArg2(x, y) +@@ -7448,18 +7573,36 @@ func rewriteValueLOONG64_OpLsh16x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh16x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh16x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh16x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -7472,18 +7615,36 @@ func rewriteValueLOONG64_OpLsh16x8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh32x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh32x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh32x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -7496,18 +7657,36 @@ func rewriteValueLOONG64_OpLsh32x16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh32x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh32x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh32x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -7520,18 +7699,36 @@ func rewriteValueLOONG64_OpLsh32x32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh32x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh32x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh32x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLL x y) (SGTU (MOVVconst [32]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v0.AddArg2(x, y) +@@ -7542,18 +7739,36 @@ func rewriteValueLOONG64_OpLsh32x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh32x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh32x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh32x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -7566,18 +7781,36 @@ func rewriteValueLOONG64_OpLsh32x8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh64x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh64x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh64x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -7590,18 +7823,36 @@ func rewriteValueLOONG64_OpLsh64x16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh64x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh64x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh64x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -7614,18 +7865,36 @@ func rewriteValueLOONG64_OpLsh64x32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh64x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh64x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh64x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v0.AddArg2(x, y) +@@ -7636,18 +7905,36 @@ func rewriteValueLOONG64_OpLsh64x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh64x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh64x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh64x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -7660,18 +7947,36 @@ func rewriteValueLOONG64_OpLsh64x8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh8x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh8x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh8x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -7684,18 +7989,36 @@ func rewriteValueLOONG64_OpLsh8x16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh8x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh8x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh8x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -7708,18 +8031,36 @@ func rewriteValueLOONG64_OpLsh8x32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh8x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh8x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v0.AddArg2(x, y) +@@ -7730,18 +8071,36 @@ func rewriteValueLOONG64_OpLsh8x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpLsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Lsh8x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SLLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SLLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Lsh8x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -7754,6 +8113,7 @@ func rewriteValueLOONG64_OpLsh8x8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpMod16(v *Value) bool { + v_1 := v.Args[1] +@@ -8698,12 +9058,31 @@ func rewriteValueLOONG64_OpRsh16Ux16(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16Ux16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16Ux16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -8718,18 +9097,38 @@ func rewriteValueLOONG64_OpRsh16Ux16(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16Ux32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16Ux32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -8744,18 +9143,38 @@ func rewriteValueLOONG64_OpRsh16Ux32(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16Ux64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16Ux64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt16to64 x) y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -8768,18 +9187,38 @@ func rewriteValueLOONG64_OpRsh16Ux64(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16Ux8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16Ux8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt16to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -8794,18 +9233,38 @@ func rewriteValueLOONG64_OpRsh16Ux8(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) +@@ -8822,18 +9281,38 @@ func rewriteValueLOONG64_OpRsh16x16(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) +@@ -8850,18 +9329,38 @@ func rewriteValueLOONG64_OpRsh16x32(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) +@@ -8876,18 +9375,38 @@ func rewriteValueLOONG64_OpRsh16x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh16x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh16x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt16to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh16x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) +@@ -8904,18 +9423,36 @@ func rewriteValueLOONG64_OpRsh16x8(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32Ux16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32Ux16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRL x (ZeroExt16to64 y)) (SGTU (MOVVconst [32]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -8928,18 +9465,36 @@ func rewriteValueLOONG64_OpRsh32Ux16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32Ux32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32Ux32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRL x (ZeroExt32to64 y)) (SGTU (MOVVconst [32]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -8952,18 +9507,36 @@ func rewriteValueLOONG64_OpRsh32Ux32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32Ux64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32Ux64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRL x y) (SGTU (MOVVconst [32]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) + v0.AddArg2(x, y) +@@ -8974,18 +9547,36 @@ func rewriteValueLOONG64_OpRsh32Ux64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32Ux8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRL x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRL) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32Ux8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRL x (ZeroExt8to64 y)) (SGTU (MOVVconst [32]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRL, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -8998,18 +9589,36 @@ func rewriteValueLOONG64_OpRsh32Ux8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRA x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRA) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRA x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [31]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRA) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9024,18 +9633,36 @@ func rewriteValueLOONG64_OpRsh32x16(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRA x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRA) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRA x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [31]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRA) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9050,18 +9677,36 @@ func rewriteValueLOONG64_OpRsh32x32(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRA x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRA) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRA x (OR (NEGV (SGTU y (MOVVconst [31]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRA) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9074,18 +9719,36 @@ func rewriteValueLOONG64_OpRsh32x64(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh32x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh32x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRA x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRA) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh32x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRA x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [31]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRA) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9100,18 +9763,36 @@ func rewriteValueLOONG64_OpRsh32x8(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64Ux16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64Ux16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) +@@ -9124,18 +9805,36 @@ func rewriteValueLOONG64_OpRsh64Ux16(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64Ux32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64Ux32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) +@@ -9148,18 +9847,36 @@ func rewriteValueLOONG64_OpRsh64Ux32(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64Ux64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64Ux64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV x y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v0.AddArg2(x, y) +@@ -9170,18 +9887,36 @@ func rewriteValueLOONG64_OpRsh64Ux64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64Ux8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64Ux8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -9194,18 +9929,36 @@ func rewriteValueLOONG64_OpRsh64Ux8(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9220,18 +9973,36 @@ func rewriteValueLOONG64_OpRsh64x16(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9246,18 +10017,36 @@ func rewriteValueLOONG64_OpRsh64x32(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV x (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9270,18 +10059,36 @@ func rewriteValueLOONG64_OpRsh64x64(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh64x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh64x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV x y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v.AddArg2(x, y) ++ return true ++ } + // match: (Rsh64x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) +@@ -9296,18 +10103,38 @@ func rewriteValueLOONG64_OpRsh64x8(v *Value) bool { + v.AddArg2(x, v0) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8Ux16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8Ux16 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -9322,18 +10149,38 @@ func rewriteValueLOONG64_OpRsh8Ux16(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8Ux32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8Ux32 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -9348,18 +10195,38 @@ func rewriteValueLOONG64_OpRsh8Ux32(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8Ux64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8Ux64 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt8to64 x) y) (SGTU (MOVVconst [64]) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -9372,18 +10239,38 @@ func rewriteValueLOONG64_OpRsh8Ux64(v *Value) bool { + v.AddArg2(v0, v2) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8Ux8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRLV (ZeroExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRLV) ++ v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8Ux8 x y) ++ // cond: !shiftIsBounded(v) + // result: (MASKEQZ (SRLV (ZeroExt8to64 x) (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64MASKEQZ) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) +@@ -9398,18 +10285,38 @@ func rewriteValueLOONG64_OpRsh8Ux8(v *Value) bool { + v.AddArg2(v0, v3) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8x16 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8x16 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) +@@ -9426,18 +10333,38 @@ func rewriteValueLOONG64_OpRsh8x16(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8x32 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8x32 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) +@@ -9454,18 +10381,38 @@ func rewriteValueLOONG64_OpRsh8x32(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8x64 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8x64 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) +@@ -9480,18 +10427,38 @@ func rewriteValueLOONG64_OpRsh8x64(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpRsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types ++ // match: (Rsh8x8 x y) ++ // cond: shiftIsBounded(v) ++ // result: (SRAV (SignExt8to64 x) y) ++ for { ++ x := v_0 ++ y := v_1 ++ if !(shiftIsBounded(v)) { ++ break ++ } ++ v.reset(OpLOONG64SRAV) ++ v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) ++ v0.AddArg(x) ++ v.AddArg2(v0, y) ++ return true ++ } + // match: (Rsh8x8 x y) ++ // cond: !shiftIsBounded(v) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 ++ if !(!shiftIsBounded(v)) { ++ break ++ } + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) +@@ -9508,6 +10475,7 @@ func rewriteValueLOONG64_OpRsh8x8(v *Value) bool { + v.AddArg2(v0, v1) + return true + } ++ return false + } + func rewriteValueLOONG64_OpSelect0(v *Value) bool { + v_0 := v.Args[0] +diff --git a/test/codegen/shift.go b/test/codegen/shift.go +index 3c669edcb2..db4e6409a8 100644 +--- a/test/codegen/shift.go ++++ b/test/codegen/shift.go +@@ -115,6 +115,7 @@ func rshConst64x32(v int64) int64 { + + func lshMask64x64(v int64, s uint64) int64 { + // arm64:"LSL",-"AND" ++ // loong64:"SLLV",-"AND" + // ppc64x:"RLDICL",-"ORN",-"ISEL" + // riscv64:"SLL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -123,6 +124,7 @@ func lshMask64x64(v int64, s uint64) int64 { + + func rshMask64Ux64(v uint64, s uint64) uint64 { + // arm64:"LSR",-"AND",-"CSEL" ++ // loong64:"SRLV",-"AND" + // ppc64x:"RLDICL",-"ORN",-"ISEL" + // riscv64:"SRL\t",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -131,6 +133,7 @@ func rshMask64Ux64(v uint64, s uint64) uint64 { + + func rshMask64x64(v int64, s uint64) int64 { + // arm64:"ASR",-"AND",-"CSEL" ++ // loong64:"SRAV",-"AND" + // ppc64x:"RLDICL",-"ORN",-"ISEL" + // riscv64:"SRA\t",-"OR",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -139,14 +142,21 @@ func rshMask64x64(v int64, s uint64) int64 { + + func lshMask32x64(v int32, s uint64) int32 { + // arm64:"LSL",-"AND" ++ // loong64:"SLL\t","AND","SGTU","MASKEQZ" + // ppc64x:"ISEL",-"ORN" + // riscv64:"SLL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + return v << (s & 63) + } + ++func lsh5Mask32x64(v int32, s uint64) int32 { ++ // loong64:"SLL\t",-"AND" ++ return v << (s & 31) ++} ++ + func rshMask32Ux64(v uint32, s uint64) uint32 { + // arm64:"LSR",-"AND" ++ // loong64:"SRL\t","AND","SGTU","MASKEQZ" + // ppc64x:"ISEL",-"ORN" + // riscv64:"SRLW","SLTIU","NEG","AND\t",-"SRL\t" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -154,12 +164,14 @@ func rshMask32Ux64(v uint32, s uint64) uint32 { + } + + func rsh5Mask32Ux64(v uint32, s uint64) uint32 { ++ // loong64:"SRL\t",-"AND" + // riscv64:"SRLW",-"AND\t",-"SLTIU",-"SRL\t" + return v >> (s & 31) + } + + func rshMask32x64(v int32, s uint64) int32 { + // arm64:"ASR",-"AND" ++ // loong64:"SRA\t","AND","SGTU","SUBVU","OR" + // ppc64x:"ISEL",-"ORN" + // riscv64:"SRAW","OR","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -167,12 +179,14 @@ func rshMask32x64(v int32, s uint64) int32 { + } + + func rsh5Mask32x64(v int32, s uint64) int32 { ++ // loong64:"SRA\t",-"AND" + // riscv64:"SRAW",-"OR",-"SLTIU" + return v >> (s & 31) + } + + func lshMask64x32(v int64, s uint32) int64 { + // arm64:"LSL",-"AND" ++ // loong64:"SLLV",-"AND" + // ppc64x:"RLDICL",-"ORN" + // riscv64:"SLL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -181,6 +195,7 @@ func lshMask64x32(v int64, s uint32) int64 { + + func rshMask64Ux32(v uint64, s uint32) uint64 { + // arm64:"LSR",-"AND",-"CSEL" ++ // loong64:"SRLV",-"AND" + // ppc64x:"RLDICL",-"ORN" + // riscv64:"SRL\t",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +@@ -189,6 +204,7 @@ func rshMask64Ux32(v uint64, s uint32) uint64 { + + func rshMask64x32(v int64, s uint32) int64 { + // arm64:"ASR",-"AND",-"CSEL" ++ // loong64:"SRAV",-"AND" + // ppc64x:"RLDICL",-"ORN",-"ISEL" + // riscv64:"SRA\t",-"OR",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" +-- +2.38.1 + diff --git a/0024-runtime-use-ABIInternal-on-syscall-and-other-sys.stu.patch b/0024-runtime-use-ABIInternal-on-syscall-and-other-sys.stu.patch new file mode 100644 index 0000000..bc81e40 --- /dev/null +++ b/0024-runtime-use-ABIInternal-on-syscall-and-other-sys.stu.patch @@ -0,0 +1,505 @@ +From 7e54d3bbc1af00ca94819f9c1bbb61f822d37439 Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Tue, 26 Nov 2024 15:44:28 +0800 +Subject: [PATCH 24/44] runtime: use ABIInternal on syscall and other sys.stuff + for loong64 + +Change-Id: Ieeb3f2af02c55a9ad62a19d0085b0e082a182db4 +--- + src/runtime/sys_linux_loong64.s | 227 +++++++++++--------------------- + 1 file changed, 79 insertions(+), 148 deletions(-) + +diff --git a/src/runtime/sys_linux_loong64.s b/src/runtime/sys_linux_loong64.s +index 57cee99da7..b4e9930755 100644 +--- a/src/runtime/sys_linux_loong64.s ++++ b/src/runtime/sys_linux_loong64.s +@@ -47,8 +47,7 @@ + #define SYS_timer_delete 111 + + // func exit(code int32) +-TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4 +- MOVW code+0(FP), R4 ++TEXT runtime·exit(SB),NOSPLIT,$0 + MOVV $SYS_exit_group, R11 + SYSCALL + RET +@@ -67,48 +66,49 @@ TEXT runtime·exitThread(SB),NOSPLIT|NOFRAME,$0-8 + JMP 0(PC) + + // func open(name *byte, mode, perm int32) int32 +-TEXT runtime·open(SB),NOSPLIT|NOFRAME,$0-20 ++TEXT runtime·open(SB),NOSPLIT,$0 ++ // before: ++ // R4: name ++ // R5: mode ++ // R6: perm ++ ++ // after: ++ // R4: AT_FDCWD ++ // R5: name ++ // R6: mode ++ // R7: perm ++ ++ MOVW R6, R7 ++ MOVW R5, R6 ++ MOVV R4, R5 + MOVW $AT_FDCWD, R4 // AT_FDCWD, so this acts like open +- MOVV name+0(FP), R5 +- MOVW mode+8(FP), R6 +- MOVW perm+12(FP), R7 ++ + MOVV $SYS_openat, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVW $-1, R4 +- MOVW R4, ret+16(FP) + RET + + // func closefd(fd int32) int32 +-TEXT runtime·closefd(SB),NOSPLIT|NOFRAME,$0-12 +- MOVW fd+0(FP), R4 ++TEXT runtime·closefd(SB),NOSPLIT,$0 + MOVV $SYS_close, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVW $-1, R4 +- MOVW R4, ret+8(FP) + RET + + // func write1(fd uintptr, p unsafe.Pointer, n int32) int32 +-TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28 +- MOVV fd+0(FP), R4 +- MOVV p+8(FP), R5 +- MOVW n+16(FP), R6 ++TEXT runtime·write1(SB),NOSPLIT,$0 + MOVV $SYS_write, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func read(fd int32, p unsafe.Pointer, n int32) int32 +-TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 +- MOVW fd+0(FP), R4 +- MOVV p+8(FP), R5 +- MOVW n+16(FP), R6 ++TEXT runtime·read(SB),NOSPLIT,$0 + MOVV $SYS_read, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func pipe2(flags int32) (r, w int32, errno int32) +@@ -121,16 +121,15 @@ TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 + RET + + // func usleep(usec uint32) +-TEXT runtime·usleep(SB),NOSPLIT,$16-4 +- MOVWU usec+0(FP), R7 ++TEXT runtime·usleep(SB),NOSPLIT,$16 + MOVV $1000, R6 +- MULVU R6, R7, R7 ++ MULVU R6, R4, R4 + MOVV $1000000000, R6 + +- DIVVU R6, R7, R5 // ts->tv_sec +- REMVU R6, R7, R4 // ts->tv_nsec ++ DIVVU R6, R4, R5 // ts->tv_sec ++ REMVU R6, R4, R8 // ts->tv_nsec + MOVV R5, 8(R3) +- MOVV R4, 16(R3) ++ MOVV R8, 16(R3) + + // nanosleep(&ts, 0) + ADDV $8, R3, R4 +@@ -140,14 +139,14 @@ TEXT runtime·usleep(SB),NOSPLIT,$16-4 + RET + + // func gettid() uint32 +-TEXT runtime·gettid(SB),NOSPLIT,$0-4 ++TEXT runtime·gettid(SB),NOSPLIT,$0 + MOVV $SYS_gettid, R11 + SYSCALL +- MOVW R4, ret+0(FP) + RET + + // func raise(sig uint32) +-TEXT runtime·raise(SB),NOSPLIT|NOFRAME,$0 ++TEXT runtime·raise(SB),NOSPLIT,$0 ++ MOVW R4, R24 // backup sig + MOVV $SYS_getpid, R11 + SYSCALL + MOVW R4, R23 +@@ -155,87 +154,66 @@ TEXT runtime·raise(SB),NOSPLIT|NOFRAME,$0 + SYSCALL + MOVW R4, R5 // arg 2 tid + MOVW R23, R4 // arg 1 pid +- MOVW sig+0(FP), R6 // arg 3 ++ MOVW R24, R6 // arg 3 + MOVV $SYS_tgkill, R11 + SYSCALL + RET + + // func raiseproc(sig uint32) +-TEXT runtime·raiseproc(SB),NOSPLIT|NOFRAME,$0 ++TEXT runtime·raiseproc(SB),NOSPLIT,$0 ++ MOVW R4, R24 // backup sig + MOVV $SYS_getpid, R11 + SYSCALL + //MOVW R4, R4 // arg 1 pid +- MOVW sig+0(FP), R5 // arg 2 ++ MOVW R24, R5 // arg 2 + MOVV $SYS_kill, R11 + SYSCALL + RET + + // func getpid() int +-TEXT ·getpid(SB),NOSPLIT|NOFRAME,$0-8 ++TEXT ·getpid(SB),NOSPLIT,$0 + MOVV $SYS_getpid, R11 + SYSCALL +- MOVV R4, ret+0(FP) + RET + + // func tgkill(tgid, tid, sig int) +-TEXT ·tgkill(SB),NOSPLIT|NOFRAME,$0-24 +- MOVV tgid+0(FP), R4 +- MOVV tid+8(FP), R5 +- MOVV sig+16(FP), R6 ++TEXT ·tgkill(SB),NOSPLIT,$0 + MOVV $SYS_tgkill, R11 + SYSCALL + RET + + // func setitimer(mode int32, new, old *itimerval) +-TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 +- MOVW mode+0(FP), R4 +- MOVV new+8(FP), R5 +- MOVV old+16(FP), R6 ++TEXT runtime·setitimer(SB),NOSPLIT,$0 + MOVV $SYS_setitimer, R11 + SYSCALL + RET + + // func timer_create(clockid int32, sevp *sigevent, timerid *int32) int32 +-TEXT runtime·timer_create(SB),NOSPLIT,$0-28 +- MOVW clockid+0(FP), R4 +- MOVV sevp+8(FP), R5 +- MOVV timerid+16(FP), R6 ++TEXT runtime·timer_create(SB),NOSPLIT,$0 + MOVV $SYS_timer_create, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 +-TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 +- MOVW timerid+0(FP), R4 +- MOVW flags+4(FP), R5 +- MOVV new+8(FP), R6 +- MOVV old+16(FP), R7 ++TEXT runtime·timer_settime(SB),NOSPLIT,$0 + MOVV $SYS_timer_settime, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func timer_delete(timerid int32) int32 +-TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 +- MOVW timerid+0(FP), R4 ++TEXT runtime·timer_delete(SB),NOSPLIT,$0 + MOVV $SYS_timer_delete, R11 + SYSCALL +- MOVW R4, ret+8(FP) + RET + + // func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32 +-TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 +- MOVV dst+16(FP), R6 ++TEXT runtime·mincore(SB),NOSPLIT,$0 + MOVV $SYS_mincore, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func walltime() (sec int64, nsec int32) +-TEXT runtime·walltime(SB),NOSPLIT,$24-12 ++TEXT runtime·walltime(SB),NOSPLIT,$24 + MOVV R3, R23 // R23 is unchanged by C code + MOVV R3, R25 + +@@ -291,7 +269,7 @@ nosaveg: + JAL (R20) + + finish: +- MOVV 0(R3), R7 // sec ++ MOVV 0(R3), R4 // sec + MOVV 8(R3), R5 // nsec + + MOVV R23, R3 // restore SP +@@ -304,9 +282,6 @@ finish: + MOVV R25, m_vdsoSP(R24) + MOVV 8(R3), R25 + MOVV R25, m_vdsoPC(R24) +- +- MOVV R7, sec+0(FP) +- MOVW R5, nsec+8(FP) + RET + + fallback: +@@ -315,7 +290,7 @@ fallback: + JMP finish + + // func nanotime1() int64 +-TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 ++TEXT runtime·nanotime1(SB),NOSPLIT,$24 + MOVV R3, R23 // R23 is unchanged by C code + MOVV R3, R25 + +@@ -389,8 +364,7 @@ finish: + // return nsec in R7 + MOVV $1000000000, R4 + MULVU R4, R7, R7 +- ADDVU R5, R7 +- MOVV R7, ret+0(FP) ++ ADDVU R5, R7, R4 + RET + + fallback: +@@ -399,11 +373,7 @@ fallback: + JMP finish + + // func rtsigprocmask(how int32, new, old *sigset, size int32) +-TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 +- MOVW how+0(FP), R4 +- MOVV new+8(FP), R5 +- MOVV old+16(FP), R6 +- MOVW size+24(FP), R7 ++TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0 + MOVV $SYS_rt_sigprocmask, R11 + SYSCALL + MOVW $-4096, R5 +@@ -412,22 +382,21 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 + RET + + // func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 +-TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 +- MOVV sig+0(FP), R4 +- MOVV new+8(FP), R5 +- MOVV old+16(FP), R6 +- MOVV size+24(FP), R7 ++TEXT runtime·rt_sigaction(SB),NOSPLIT,$0 + MOVV $SYS_rt_sigaction, R11 + SYSCALL +- MOVW R4, ret+32(FP) + RET + + // func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) +-TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 +- MOVW sig+8(FP), R4 +- MOVV info+16(FP), R5 +- MOVV ctx+24(FP), R6 +- MOVV fn+0(FP), R20 ++TEXT runtime·sigfwd(SB),NOSPLIT,$0 ++ // before: ++ // R4: fn, R5: sig, R6: info, R7: ctx ++ // after: ++ // R20: fn, R4: sig, R5: info, R6: ctx ++ MOVV R4, R20 ++ MOVV R5, R4 ++ MOVV R6, R5 ++ MOVV R7, R6 + JAL (R20) + RET + +@@ -460,48 +429,31 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + JMP runtime·sigtramp(SB) + + // func sysMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (p unsafe.Pointer, err int) +-TEXT runtime·sysMmap(SB),NOSPLIT|NOFRAME,$0 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 +- MOVW prot+16(FP), R6 +- MOVW flags+20(FP), R7 +- MOVW fd+24(FP), R8 +- MOVW off+28(FP), R9 +- ++TEXT runtime·sysMmap(SB),NOSPLIT,$0 + MOVV $SYS_mmap, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, ok +- MOVV $0, p+32(FP) +- SUBVU R4, R0, R4 +- MOVV R4, err+40(FP) ++ SUBVU R4, R0, R5 ++ MOVV $0, R4 + RET + ok: +- MOVV R4, p+32(FP) +- MOVV $0, err+40(FP) ++ MOVV $0, R5 + RET + + // Call the function stored in _cgo_mmap using the GCC calling convention. + // This must be called on the system stack. + // func callCgoMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uintptr +-TEXT runtime·callCgoMmap(SB),NOSPLIT,$0 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 +- MOVW prot+16(FP), R6 +- MOVW flags+20(FP), R7 +- MOVW fd+24(FP), R8 +- MOVW off+28(FP), R9 ++TEXT runtime·callCgoMmap(SB),NOSPLIT,$0 + MOVV _cgo_mmap(SB), R13 + SUBV $16, R3 // reserve 16 bytes for sp-8 where fp may be saved. + JAL (R13) + ADDV $16, R3 +- MOVV R4, ret+32(FP) ++ MOVV R4, R4 + RET + + // func sysMunmap(addr unsafe.Pointer, n uintptr) +-TEXT runtime·sysMunmap(SB),NOSPLIT|NOFRAME,$0 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 ++TEXT runtime·sysMunmap(SB),NOSPLIT,$0 + MOVV $SYS_munmap, R11 + SYSCALL + MOVW $-4096, R5 +@@ -512,9 +464,7 @@ TEXT runtime·sysMunmap(SB),NOSPLIT|NOFRAME,$0 + // Call the function stored in _cgo_munmap using the GCC calling convention. + // This must be called on the system stack. + // func callCgoMunmap(addr unsafe.Pointer, n uintptr) +-TEXT runtime·callCgoMunmap(SB),NOSPLIT,$0 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 ++TEXT runtime·callCgoMunmap(SB),NOSPLIT,$0 + MOVV _cgo_munmap(SB), R13 + SUBV $16, R3 // reserve 16 bytes for sp-8 where fp may be saved. + JAL (R13) +@@ -522,38 +472,24 @@ TEXT runtime·callCgoMunmap(SB),NOSPLIT,$0 + RET + + // func madvise(addr unsafe.Pointer, n uintptr, flags int32) +-TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0 +- MOVV addr+0(FP), R4 +- MOVV n+8(FP), R5 +- MOVW flags+16(FP), R6 ++TEXT runtime·madvise(SB),NOSPLIT,$0 + MOVV $SYS_madvise, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 +-TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0 +- MOVV addr+0(FP), R4 +- MOVW op+8(FP), R5 +- MOVW val+12(FP), R6 +- MOVV ts+16(FP), R7 +- MOVV addr2+24(FP), R8 +- MOVW val3+32(FP), R9 ++TEXT runtime·futex(SB),NOSPLIT,$0 + MOVV $SYS_futex, R11 + SYSCALL +- MOVW R4, ret+40(FP) + RET + + // int64 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void)); +-TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 +- MOVW flags+0(FP), R4 +- MOVV stk+8(FP), R5 +- ++TEXT runtime·clone(SB),NOSPLIT,$0 + // Copy mp, gp, fn off parent stack for use by child. + // Careful: Linux system call clobbers ???. +- MOVV mp+16(FP), R23 +- MOVV gp+24(FP), R24 +- MOVV fn+32(FP), R25 ++ MOVV R6, R23 ++ MOVV R7, R24 ++ MOVV R8, R25 + + MOVV R23, -8(R5) + MOVV R24, -16(R5) +@@ -565,8 +501,7 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 + SYSCALL + + // In parent, return. +- BEQ R4, 3(PC) +- MOVW R4, ret+40(FP) ++ BEQ R4, 2(PC) + RET + + // In child, on new stack. +@@ -606,9 +541,7 @@ nog: + JMP -3(PC) // keep exiting + + // func sigaltstack(new, old *stackt) +-TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0 +- MOVV new+0(FP), R4 +- MOVV old+8(FP), R5 ++TEXT runtime·sigaltstack(SB),NOSPLIT,$0 + MOVV $SYS_sigaltstack, R11 + SYSCALL + MOVW $-4096, R5 +@@ -617,42 +550,40 @@ TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0 + RET + + // func osyield() +-TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0 ++TEXT runtime·osyield(SB),NOSPLIT,$0 + MOVV $SYS_sched_yield, R11 + SYSCALL + RET + + // func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 +-TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 +- MOVV pid+0(FP), R4 +- MOVV len+8(FP), R5 +- MOVV buf+16(FP), R6 ++TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0 + MOVV $SYS_sched_getaffinity, R11 + SYSCALL +- MOVW R4, ret+24(FP) + RET + + // func sbrk0() uintptr +-TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8 ++TEXT runtime·sbrk0(SB),NOSPLIT,$0 + // Implemented as brk(NULL). + MOVV $0, R4 + MOVV $SYS_brk, R11 + SYSCALL +- MOVV R4, ret+0(FP) + RET + ++// unimplemented, only needed for android; declared in stubs_linux.go + TEXT runtime·access(SB),$0-20 +- MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go ++ MOVV R0, 2(R0) + MOVW R0, ret+16(FP) // for vet + RET + ++// unimplemented, only needed for android; declared in stubs_linux.go + TEXT runtime·connect(SB),$0-28 +- MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go ++ MOVV R0, 2(R0) + MOVW R0, ret+24(FP) // for vet + RET + ++// unimplemented, only needed for android; declared in stubs_linux.go + TEXT runtime·socket(SB),$0-20 +- MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go ++ MOVV R0, 2(R0) + MOVW R0, ret+16(FP) // for vet + RET + +-- +2.38.1 + diff --git a/0025-runtime-use-correct-memory-barrier-in-exitThread-fun.patch b/0025-runtime-use-correct-memory-barrier-in-exitThread-fun.patch new file mode 100644 index 0000000..0b81cb1 --- /dev/null +++ b/0025-runtime-use-correct-memory-barrier-in-exitThread-fun.patch @@ -0,0 +1,34 @@ +From 5bb6b8ebb22faf46a01ff292c45a7dc72f2b5022 Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Tue, 26 Nov 2024 17:10:32 +0800 +Subject: [PATCH 25/44] runtime: use correct memory barrier in exitThread + function on loong64 + +In the runtime.exitThread function, a storeRelease barrier +is required instead of a full barrier. + +Change-Id: I614c6f74e8c9fd56c3badf3bf450b3314e3f377c +--- + src/runtime/sys_linux_loong64.s | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/runtime/sys_linux_loong64.s b/src/runtime/sys_linux_loong64.s +index b4e9930755..830eb9d099 100644 +--- a/src/runtime/sys_linux_loong64.s ++++ b/src/runtime/sys_linux_loong64.s +@@ -56,10 +56,8 @@ TEXT runtime·exit(SB),NOSPLIT,$0 + TEXT runtime·exitThread(SB),NOSPLIT|NOFRAME,$0-8 + MOVV wait+0(FP), R19 + // We're done using the stack. +- MOVW $0, R11 +- DBAR +- MOVW R11, (R19) +- DBAR ++ DBAR $0x12 // StoreRelease barrier ++ MOVW R0, (R19) + MOVW $0, R4 // exit code + MOVV $SYS_exit, R11 + SYSCALL +-- +2.38.1 + diff --git a/0026-cmd-internal-obj-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch b/0026-cmd-internal-obj-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch new file mode 100644 index 0000000..d73cc65 --- /dev/null +++ b/0026-cmd-internal-obj-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch @@ -0,0 +1,410 @@ +From 38ab8bc5eb69cb2746b32fd4a6ca7931adb7722b Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Fri, 29 Nov 2024 15:41:33 +0800 +Subject: [PATCH 26/44] cmd/internal/obj/loong64: add {V,XV}SEQI, + {V,XV}.{AND,OR,XOR,NOR} instructions support + +Go asm syntax: + VSEQB $1, V2, V3 + XVSEQB $2, X2, X3 + V{AND,OR,XOR,NOR}B $1, V2, V3 + XV{AND,OR,XOR,NOR}B $1, V2, V3 + V{AND,OR,XOR,NOR,ANDN,ORN}V V1, V2, V3 + XV{AND,OR,XOR,NOR,ANDN,ORN}V V1, V2, V3 + +Equivalent platform assembler syntax: + vseqi.b v3, v2, $1 + xvseqi.b x3, x2 ,$2 + v{and,or,xor,nor}.b v3, v2, $1 + xv{and,or,xor,nor}.b x3, x2, $1 + v{and,or,xor,nor,andn,orn}v v3, v2, v1 + xv{and,or,xor,nor,andn,orn}v x3, x2, x1 + +Change-Id: I56ae0db72c7f473755cbdc7f7171c1058a9def97 +--- + .../asm/internal/asm/testdata/loong64enc1.s | 38 ++++ + src/cmd/internal/obj/loong64/a.out.go | 21 +++ + src/cmd/internal/obj/loong64/anames.go | 20 ++ + src/cmd/internal/obj/loong64/asm.go | 173 ++++++++++++++++-- + 4 files changed, 238 insertions(+), 14 deletions(-) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 3a3eb10a74..2418412a3a 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -506,6 +506,16 @@ lable2: + XVSEQH X3, X2, X4 // 448c0074 + XVSEQW X3, X2, X4 // 440c0174 + XVSEQV X3, X2, X4 // 448c0174 ++ VSEQB $0, V2, V3 // 43008072 ++ VSEQH $1, V2, V3 // 43848072 ++ VSEQW $8, V2, V3 // 43208172 ++ VSEQV $15, V2, V3 // 43bc8172 ++ VSEQV $-15, V2, V3 // 43c48172 ++ XVSEQB $0, X2, X4 // 44008076 ++ XVSEQH $3, X2, X4 // 448c8076 ++ XVSEQW $12, X2, X4 // 44308176 ++ XVSEQV $15, X2, X4 // 44bc8176 ++ XVSEQV $-15, X2, X4 // 44c48176 + + // VPCNT{B,H,W,V}, XVPCNT{B,H,W,V} instruction + VPCNTB V1, V2 // 22209c72 +@@ -517,6 +527,34 @@ lable2: + XVPCNTW X3, X2 // 62289c76 + XVPCNTV X3, X2 // 622c9c76 + ++ // VANDV,VORV,VXORV,VNORV,VANDNV,VORNV ++ VANDV V1, V2, V3 // 43042671 ++ VORV V1, V2, V3 // 43842671 ++ VXORV V1, V2, V3 // 43042771 ++ VNORV V1, V2, V3 // 43842771 ++ VANDNV V1, V2, V3 // 43042871 ++ VORNV V1, V2, V3 // 43842871 ++ ++ // VANDB,VORB,VXORB,VNORB ++ VANDB $0, V2, V3 // 4300d073 ++ VORB $64, V2, V3 // 4300d573 ++ VXORB $128, V2, V3 // 4300da73 ++ VNORB $255, V2, V3 // 43fcdf73 ++ ++ // XVANDV,XVORV,XVXORV,XVNORV,XVANDNV,XVORNV ++ XVANDV X1, X2, X3 // 43042675 ++ XVORV X1, X2, X3 // 43842675 ++ XVXORV X1, X2, X3 // 43042775 ++ XVNORV X1, X2, X3 // 43842775 ++ XVANDNV X1, X2, X3 // 43042875 ++ XVORNV X1, X2, X3 // 43842875 ++ ++ // XVANDB,XVORB,XVXORB,XVNORB ++ XVANDB $0, X2, X3 // 4300d077 ++ XVORB $1, X2, X3 // 4304d477 ++ XVXORB $127, X2, X3 // 43fcd977 ++ XVNORB $255, X2, X3 // 43fcdf77 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index b2207c2523..bd3ce61826 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -726,6 +726,27 @@ const ( + AXVMOVQ + + // LSX and LASX Bit-manipulation Instructions ++ AVANDB ++ AVORB ++ AVXORB ++ AVNORB ++ AXVANDB ++ AXVORB ++ AXVXORB ++ AXVNORB ++ AVANDV ++ AVORV ++ AVXORV ++ AVNORV ++ AVANDNV ++ AVORNV ++ AXVANDV ++ AXVORV ++ AXVXORV ++ AXVNORV ++ AXVANDNV ++ AXVORNV ++ + AVPCNTB + AVPCNTH + AVPCNTW +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 3d2f329917..6c1537d123 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -257,6 +257,26 @@ var Anames = []string{ + "FTINTRNEVD", + "VMOVQ", + "XVMOVQ", ++ "VANDB", ++ "VORB", ++ "VXORB", ++ "VNORB", ++ "XVANDB", ++ "XVORB", ++ "XVXORB", ++ "XVNORB", ++ "VANDV", ++ "VORV", ++ "VXORV", ++ "VNORV", ++ "VANDNV", ++ "VORNV", ++ "XVANDV", ++ "XVORV", ++ "XVXORV", ++ "XVNORV", ++ "XVANDNV", ++ "XVORNV", + "VPCNTB", + "VPCNTH", + "VPCNTW", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 5757c3c452..7247193c95 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -51,6 +51,8 @@ const ( + // branchLoopHead marks loop entry. + // Used to insert padding for under-aligned loops. + branchLoopHead ++ immFiledSi5 // The encoding of the immediate field in the instruction is 5-bits ++ immFiledUi8 // The encoding of the immediate field in the instruction is 8-bits + ) + + var optab = []Optab{ +@@ -88,6 +90,17 @@ var optab = []Optab{ + {ACMPEQF, C_FREG, C_FREG, C_NONE, C_FCCREG, C_NONE, 2, 4, 0, 0}, + {AVSEQB, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSEQB, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVSEQB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 13, 4, 0, immFiledSi5}, ++ {AXVSEQB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 13, 4, 0, immFiledSi5}, ++ {AVSEQB, C_ADDCON, C_VREG, C_NONE, C_VREG, C_NONE, 13, 4, 0, immFiledSi5}, ++ {AXVSEQB, C_ADDCON, C_XREG, C_NONE, C_XREG, C_NONE, 13, 4, 0, immFiledSi5}, ++ ++ {AVANDV, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, ++ {AXVANDV, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVANDB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, immFiledUi8}, ++ {AXVANDB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, immFiledUi8}, ++ {AVANDB, C_ADDCON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, immFiledUi8}, ++ {AXVANDB, C_ADDCON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, immFiledUi8}, + + {ACLOW, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 9, 4, 0, 0}, + {AABSF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0}, +@@ -1499,6 +1512,7 @@ func buildop(ctxt *obj.Link) { + } + opset(i, r0) + } ++ + case AVSEQB: + opset(AVSEQH, r0) + opset(AVSEQW, r0) +@@ -1509,6 +1523,30 @@ func buildop(ctxt *obj.Link) { + opset(AXVSEQW, r0) + opset(AXVSEQV, r0) + ++ case AVANDB: ++ opset(AVORB, r0) ++ opset(AVXORB, r0) ++ opset(AVNORB, r0) ++ ++ case AXVANDB: ++ opset(AXVORB, r0) ++ opset(AXVXORB, r0) ++ opset(AXVNORB, r0) ++ ++ case AVANDV: ++ opset(AVORV, r0) ++ opset(AVXORV, r0) ++ opset(AVNORV, r0) ++ opset(AVANDNV, r0) ++ opset(AVORNV, r0) ++ ++ case AXVANDV: ++ opset(AXVORV, r0) ++ opset(AXVXORV, r0) ++ opset(AXVNORV, r0) ++ opset(AXVANDNV, r0) ++ opset(AXVORNV, r0) ++ + case AVPCNTB: + opset(AVPCNTH, r0) + opset(AVPCNTW, r0) +@@ -1551,6 +1589,14 @@ func OP_12IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { + return op | (i&0xFFF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 + } + ++func OP_8IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { ++ return op | (i&0xFF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 ++} ++ ++func OP_5IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { ++ return op | (i&0x1F)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 ++} ++ + func OP_IR(op uint32, i uint32, r2 uint32) uint32 { + return op | (i&0xFFFFF)<<5 | (r2&0x1F)<<0 // ui20, rd5 + } +@@ -1623,12 +1669,10 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + + case 4: // add $scon,[r1],r2 + v := c.regoff(&p.From) +- + r := int(p.Reg) + if r == 0 { + r = int(p.To.Reg) + } +- + o1 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 5: // syscall +@@ -1738,6 +1782,36 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + c.ctxt.Diag("unexpected encoding\n%v", p) + } + ++ case 13: // add $si5,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledSi5: ++ c.checkimmFiled(p, v, 5, true) ++ o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ ++ case 14: // add $ui8,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledUi8: ++ c.checkimmFiled(p, v, 8, false) ++ o1 = OP_8IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ + case 15: // teq $c r,r + v := c.regoff(&p.From) + r := int(p.Reg) +@@ -1760,18 +1834,18 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + o2 = OP_15I(c.opi(ABREAK), uint32(v)) + + case 16: // sll $c,[r1],r2 +- v := c.regoff(&p.From) +- r := int(p.Reg) +- if r == 0 { +- r = int(p.To.Reg) +- } +- +- // instruction ending with V:6-digit immediate, others:5-digit immediate +- if v >= 32 && vshift(p.As) { +- o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x3f, uint32(r), uint32(p.To.Reg)) +- } else { +- o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x1f, uint32(r), uint32(p.To.Reg)) +- } ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ // instruction ending with V:6-digit immediate, others:5-digit immediate ++ if v >= 32 && vshift(p.As) { ++ o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x3f, uint32(r), uint32(p.To.Reg)) ++ } else { ++ o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x1f, uint32(r), uint32(p.To.Reg)) ++ } + + case 17: // bstrpickw $msbw, r1, $lsbw, r2 + rd, rj := p.To.Reg, p.Reg +@@ -2348,6 +2422,21 @@ func (c *ctxt0) checkindex(p *obj.Prog, index uint32, mask uint32) { + } + } + ++// checkimmFiled checks whether the immediate value exceeds the valid encoding range ++func (c *ctxt0) checkimmFiled(p *obj.Prog, imm int32, bits uint8, isSigned bool) { ++ if isSigned { ++ bound := int32(1 << (bits - 1)) ++ if imm < -bound || imm > bound { ++ c.ctxt.Diag("signed immediate %v exceeds the %d-bit range: %v", imm, bits, p) ++ } ++ } else { ++ mask := uint32(0xffffffff) << bits ++ if uint32(imm) != (uint32(imm) & ^mask) { ++ c.ctxt.Diag("unsigned immediate %v exceeds the %d-bit range: %v", imm, bits, p) ++ } ++ } ++} ++ + func (c *ctxt0) vregoff(a *obj.Addr) int64 { + c.instoffset = 0 + c.aclass(a) +@@ -2588,6 +2677,30 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0x0e003 << 15 // vseq.d + case AXVSEQV: + return 0x0e803 << 15 // xvseq.d ++ case AVANDV: ++ return 0x0E24C << 15 // vand.v ++ case AVORV: ++ return 0x0E24D << 15 // vor.v ++ case AVXORV: ++ return 0x0E24E << 15 // vxor.v ++ case AVNORV: ++ return 0x0E24F << 15 // vnor.v ++ case AVANDNV: ++ return 0x0E250 << 15 // vandn.v ++ case AVORNV: ++ return 0x0E251 << 15 // vorn.v ++ case AXVANDV: ++ return 0x0EA4C << 15 // xvand.v ++ case AXVORV: ++ return 0x0EA4D << 15 // xvor.v ++ case AXVXORV: ++ return 0x0EA4E << 15 // xvxor.v ++ case AXVNORV: ++ return 0x0EA4F << 15 // xvnor.v ++ case AXVANDNV: ++ return 0x0EA50 << 15 // xvandn.v ++ case AXVORNV: ++ return 0x0EA51 << 15 // xvorn.v + } + + if a < 0 { +@@ -2915,6 +3028,38 @@ func (c *ctxt0) opirr(a obj.As) uint32 { + return 0x021 << 24 + case ASCV: + return 0x023 << 24 ++ case AVANDB: ++ return 0x1CF4 << 18 // vandi.b ++ case AVORB: ++ return 0x1CF5 << 18 // vori.b ++ case AVXORB: ++ return 0x1CF6 << 18 // xori.b ++ case AVNORB: ++ return 0x1CF7 << 18 // xnori.b ++ case AXVANDB: ++ return 0x1DF4 << 18 // xvandi.b ++ case AXVORB: ++ return 0x1DF5 << 18 // xvori.b ++ case AXVXORB: ++ return 0x1DF6 << 18 // xvxori.b ++ case AXVNORB: ++ return 0x1DF7 << 18 // xvnor.b ++ case AVSEQB: ++ return 0x0E500 << 15 //vseqi.b ++ case AVSEQH: ++ return 0x0E501 << 15 // vseqi.h ++ case AVSEQW: ++ return 0x0E502 << 15 //vseqi.w ++ case AVSEQV: ++ return 0x0E503 << 15 //vseqi.d ++ case AXVSEQB: ++ return 0x0ED00 << 15 //xvseqi.b ++ case AXVSEQH: ++ return 0x0ED01 << 15 // xvseqi.h ++ case AXVSEQW: ++ return 0x0ED02 << 15 // xvseqi.w ++ case AXVSEQV: ++ return 0x0ED03 << 15 // xvseqi.d + } + + if a < 0 { +-- +2.38.1 + diff --git a/0027-cmd-internal-obj-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch b/0027-cmd-internal-obj-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch new file mode 100644 index 0000000..2192272 --- /dev/null +++ b/0027-cmd-internal-obj-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch @@ -0,0 +1,207 @@ +From f5bbb15710944ebcc7d2c808fe9087892a690bc4 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 09:26:38 +0800 +Subject: [PATCH 27/44] cmd/internal/obj/loong64: add + {V,XV}{ADD/SUB}.{B,H,W,D,Q} instructions support + +Go asm syntax: + V{ADD/SUB}{B,H,W,V,Q} VK, VJ, VD + XV{ADD/SUB}{B,H,W,V,Q} XK, XJ, XD + +Equivalent platform assembler syntax: + v{add/sub}.{b,w,h,d,q} vd, vj, vk + xv{add/sub}.{b,w,h,d,q} xd, xj, xk + +Change-Id: Iadc28100c93d6d6c69e9641bfea78fa85d75bddf +--- + .../asm/internal/asm/testdata/loong64enc1.s | 22 +++++++ + src/cmd/internal/obj/loong64/a.out.go | 22 +++++++ + src/cmd/internal/obj/loong64/anames.go | 20 +++++++ + src/cmd/internal/obj/loong64/asm.go | 60 +++++++++++++++++++ + 4 files changed, 124 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 2418412a3a..76faf2d3cb 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -555,6 +555,28 @@ lable2: + XVXORB $127, X2, X3 // 43fcd977 + XVNORB $255, X2, X3 // 43fcdf77 + ++ // [X]VADD{B,H,W,V,Q}, [X]VSUB{B,H,W,V,Q} instructions ++ VADDB V1, V2, V3 // 43040a70 ++ VADDH V1, V2, V3 // 43840a70 ++ VADDW V1, V2, V3 // 43040b70 ++ VADDV V1, V2, V3 // 43840b70 ++ VADDQ V1, V2, V3 // 43042d71 ++ VSUBB V1, V2, V3 // 43040c70 ++ VSUBH V1, V2, V3 // 43840c70 ++ VSUBW V1, V2, V3 // 43040d70 ++ VSUBV V1, V2, V3 // 43840d70 ++ VSUBQ V1, V2, V3 // 43842d71 ++ XVADDB X3, X2, X1 // 410c0a74 ++ XVADDH X3, X2, X1 // 418c0a74 ++ XVADDW X3, X2, X1 // 410c0b74 ++ XVADDV X3, X2, X1 // 418c0b74 ++ XVADDQ X3, X2, X1 // 410c2d75 ++ XVSUBB X3, X2, X1 // 410c0c74 ++ XVSUBH X3, X2, X1 // 418c0c74 ++ XVSUBW X3, X2, X1 // 410c0d74 ++ XVSUBV X3, X2, X1 // 418c0d74 ++ XVSUBQ X3, X2, X1 // 418c2d75 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index bd3ce61826..3bef0da869 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -725,6 +725,28 @@ const ( + AVMOVQ + AXVMOVQ + ++ // LSX and LASX arithmetic instructions ++ AVADDB ++ AVADDH ++ AVADDW ++ AVADDV ++ AVADDQ ++ AXVADDB ++ AXVADDH ++ AXVADDW ++ AXVADDV ++ AXVADDQ ++ AVSUBB ++ AVSUBH ++ AVSUBW ++ AVSUBV ++ AVSUBQ ++ AXVSUBB ++ AXVSUBH ++ AXVSUBW ++ AXVSUBV ++ AXVSUBQ ++ + // LSX and LASX Bit-manipulation Instructions + AVANDB + AVORB +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 6c1537d123..194021219e 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -257,6 +257,26 @@ var Anames = []string{ + "FTINTRNEVD", + "VMOVQ", + "XVMOVQ", ++ "VADDB", ++ "VADDH", ++ "VADDW", ++ "VADDV", ++ "VADDQ", ++ "XVADDB", ++ "XVADDH", ++ "XVADDW", ++ "XVADDV", ++ "XVADDQ", ++ "VSUBB", ++ "VSUBH", ++ "VSUBW", ++ "VSUBV", ++ "VSUBQ", ++ "XVSUBB", ++ "XVSUBH", ++ "XVSUBW", ++ "XVSUBV", ++ "XVSUBQ", + "VANDB", + "VORB", + "VXORB", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 7247193c95..7489b4dbf6 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1539,6 +1539,16 @@ func buildop(ctxt *obj.Link) { + opset(AVNORV, r0) + opset(AVANDNV, r0) + opset(AVORNV, r0) ++ opset(AVADDB, r0) ++ opset(AVADDH, r0) ++ opset(AVADDW, r0) ++ opset(AVADDV, r0) ++ opset(AVADDQ, r0) ++ opset(AVSUBB, r0) ++ opset(AVSUBH, r0) ++ opset(AVSUBW, r0) ++ opset(AVSUBV, r0) ++ opset(AVSUBQ, r0) + + case AXVANDV: + opset(AXVORV, r0) +@@ -1546,6 +1556,16 @@ func buildop(ctxt *obj.Link) { + opset(AXVNORV, r0) + opset(AXVANDNV, r0) + opset(AXVORNV, r0) ++ opset(AXVADDB, r0) ++ opset(AXVADDH, r0) ++ opset(AXVADDW, r0) ++ opset(AXVADDV, r0) ++ opset(AXVADDQ, r0) ++ opset(AXVSUBB, r0) ++ opset(AXVSUBH, r0) ++ opset(AXVSUBW, r0) ++ opset(AXVSUBV, r0) ++ opset(AXVSUBQ, r0) + + case AVPCNTB: + opset(AVPCNTH, r0) +@@ -2701,6 +2721,46 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0x0EA50 << 15 // xvandn.v + case AXVORNV: + return 0x0EA51 << 15 // xvorn.v ++ case AVADDB: ++ return 0xE014 << 15 // vadd.b ++ case AVADDH: ++ return 0xE015 << 15 // vadd.h ++ case AVADDW: ++ return 0xE016 << 15 // vadd.w ++ case AVADDV: ++ return 0xE017 << 15 // vadd.d ++ case AVADDQ: ++ return 0xE25A << 15 // vadd.q ++ case AVSUBB: ++ return 0xE018 << 15 // vsub.b ++ case AVSUBH: ++ return 0xE019 << 15 // vsub.h ++ case AVSUBW: ++ return 0xE01A << 15 // vsub.w ++ case AVSUBV: ++ return 0xE01B << 15 // vsub.d ++ case AVSUBQ: ++ return 0xE25B << 15 // vsub.q ++ case AXVADDB: ++ return 0xE814 << 15 // xvadd.b ++ case AXVADDH: ++ return 0xE815 << 15 // xvadd.h ++ case AXVADDW: ++ return 0xE816 << 15 // xvadd.w ++ case AXVADDV: ++ return 0xE817 << 15 // xvadd.d ++ case AXVADDQ: ++ return 0xEA5A << 15 // xvadd.q ++ case AXVSUBB: ++ return 0xE818 << 15 // xvsub.b ++ case AXVSUBH: ++ return 0xE819 << 15 // xvsub.h ++ case AXVSUBW: ++ return 0xE81A << 15 // xvsub.w ++ case AXVSUBV: ++ return 0xE81B << 15 // xvsub.d ++ case AXVSUBQ: ++ return 0xEA5B << 15 // xvsub.q + } + + if a < 0 { +-- +2.38.1 + diff --git a/0028-cmd-internal-obj-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch b/0028-cmd-internal-obj-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch new file mode 100644 index 0000000..3167466 --- /dev/null +++ b/0028-cmd-internal-obj-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch @@ -0,0 +1,181 @@ +From db7ccba69b0c246434a610f3be2ab31c8406b163 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 10:24:13 +0800 +Subject: [PATCH 28/44] cmd/internal/obj/loong64: add {V,XV}ILV{L/H}.{B/H/W/D} + instructions support + +Go asm syntax: + VILV{L/H}{B/H/W/V} VK, VJ, VD + XVILV{L/H}{B/H/W/V} XK, XJ, XD +Equivalent platform assembler syntax: + vilv{l/h}.{b/h/w/d} vd, vj, vk + xvilv{l/h}.{b/h/w/d} xd, xj, xk + +Change-Id: If1f146fd5e049281494026bf4c24d302bcad1373 +--- + .../asm/internal/asm/testdata/loong64enc1.s | 18 +++++++ + src/cmd/internal/obj/loong64/a.out.go | 18 +++++++ + src/cmd/internal/obj/loong64/anames.go | 16 +++++++ + src/cmd/internal/obj/loong64/asm.go | 48 +++++++++++++++++++ + 4 files changed, 100 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 76faf2d3cb..419f257c4a 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -577,6 +577,24 @@ lable2: + XVSUBV X3, X2, X1 // 418c0d74 + XVSUBQ X3, X2, X1 // 418c2d75 + ++ // [X]VILV{L/H}{B,H,W,V} instructions ++ VILVLB V1, V2, V3 // 43041a71 ++ VILVLH V1, V2, V3 // 43841a71 ++ VILVLW V1, V2, V3 // 43041b71 ++ VILVLV V1, V2, V3 // 43841b71 ++ VILVHB V1, V2, V3 // 43041c71 ++ VILVHH V1, V2, V3 // 43841c71 ++ VILVHW V1, V2, V3 // 43041d71 ++ VILVHV V1, V2, V3 // 43841d71 ++ XVILVLB X3, X2, X1 // 410c1a75 ++ XVILVLH X3, X2, X1 // 418c1a75 ++ XVILVLW X3, X2, X1 // 410c1b75 ++ XVILVLV X3, X2, X1 // 418c1b75 ++ XVILVHB X3, X2, X1 // 410c1c75 ++ XVILVHH X3, X2, X1 // 418c1c75 ++ XVILVHW X3, X2, X1 // 410c1d75 ++ XVILVHV X3, X2, X1 // 418c1d75 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 3bef0da869..c7f4769395 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -788,6 +788,24 @@ const ( + AVSEQV + AXVSEQV + ++ // LSX and LASX move and shuffle instructions ++ AVILVLB ++ AVILVLH ++ AVILVLW ++ AVILVLV ++ AVILVHB ++ AVILVHH ++ AVILVHW ++ AVILVHV ++ AXVILVLB ++ AXVILVLH ++ AXVILVLW ++ AXVILVLV ++ AXVILVHB ++ AXVILVHH ++ AXVILVHW ++ AXVILVHV ++ + ALAST + + // aliases +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 194021219e..485940e19c 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -313,5 +313,21 @@ var Anames = []string{ + "XVSEQW", + "VSEQV", + "XVSEQV", ++ "VILVLB", ++ "VILVLH", ++ "VILVLW", ++ "VILVLV", ++ "VILVHB", ++ "VILVHH", ++ "VILVHW", ++ "VILVHV", ++ "XVILVLB", ++ "XVILVLH", ++ "XVILVLW", ++ "XVILVLV", ++ "XVILVHB", ++ "XVILVHH", ++ "XVILVHW", ++ "XVILVHV", + "LAST", + } +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 7489b4dbf6..9ef414a132 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1549,6 +1549,14 @@ func buildop(ctxt *obj.Link) { + opset(AVSUBW, r0) + opset(AVSUBV, r0) + opset(AVSUBQ, r0) ++ opset(AVILVLB, r0) ++ opset(AVILVLH, r0) ++ opset(AVILVLW, r0) ++ opset(AVILVLV, r0) ++ opset(AVILVHB, r0) ++ opset(AVILVHH, r0) ++ opset(AVILVHW, r0) ++ opset(AVILVHV, r0) + + case AXVANDV: + opset(AXVORV, r0) +@@ -1566,6 +1574,14 @@ func buildop(ctxt *obj.Link) { + opset(AXVSUBW, r0) + opset(AXVSUBV, r0) + opset(AXVSUBQ, r0) ++ opset(AXVILVLB, r0) ++ opset(AXVILVLH, r0) ++ opset(AXVILVLW, r0) ++ opset(AXVILVLV, r0) ++ opset(AXVILVHB, r0) ++ opset(AXVILVHH, r0) ++ opset(AXVILVHW, r0) ++ opset(AXVILVHV, r0) + + case AVPCNTB: + opset(AVPCNTH, r0) +@@ -2761,6 +2777,38 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0xE81B << 15 // xvsub.d + case AXVSUBQ: + return 0xEA5B << 15 // xvsub.q ++ case AVILVLB: ++ return 0xE234 << 15 // vilvl.b ++ case AVILVLH: ++ return 0xE235 << 15 // vilvl.h ++ case AVILVLW: ++ return 0xE236 << 15 // vilvl.w ++ case AVILVLV: ++ return 0xE237 << 15 // vilvl.d ++ case AVILVHB: ++ return 0xE238 << 15 // vilvh.b ++ case AVILVHH: ++ return 0xE239 << 15 // vilvh.h ++ case AVILVHW: ++ return 0xE23A << 15 // vilvh.w ++ case AVILVHV: ++ return 0xE23B << 15 // vilvh.d ++ case AXVILVLB: ++ return 0xEA34 << 15 // xvilvl.b ++ case AXVILVLH: ++ return 0xEA35 << 15 // xvilvl.h ++ case AXVILVLW: ++ return 0xEA36 << 15 // xvilvl.w ++ case AXVILVLV: ++ return 0xEA37 << 15 // xvilvl.d ++ case AXVILVHB: ++ return 0xEA38 << 15 // xvilvh.b ++ case AXVILVHH: ++ return 0xEA39 << 15 // xvilvh.h ++ case AXVILVHW: ++ return 0xEA3A << 15 // xvilvh.w ++ case AXVILVHV: ++ return 0xEA3B << 15 // xvilvh.d + } + + if a < 0 { +-- +2.38.1 + diff --git a/0029-cmd-internal-obj-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch b/0029-cmd-internal-obj-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch new file mode 100644 index 0000000..71ebec3 --- /dev/null +++ b/0029-cmd-internal-obj-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch @@ -0,0 +1,599 @@ +From d765027e47dec10f8869d04b0bf52661ac63f302 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 14:19:04 +0800 +Subject: [PATCH 29/44] cmd/internal/obj/loong64: add + {V,XV}{SLL/SRL/SRA/ROTR}[I].{B/H/W/D} instructions support + +Go asm syntax: + V{SLL/SRL/SRA/ROTR}{B/H/W/V} $1, V2, V3 + XV{SLL/SRL/SRA/ROTR}{B/H/W/V} $1, X2, X3 + V{SLL/SRL/SRA/ROTR}{B/H/W/V} VK, VJ, VD + XV{SLL/SRL/SRA/ROTR}{B/H/W/V} XK, XJ, XD + +Equivalent platform assembler syntax: + v{sll/srl/sra/rotr}i.{b/h/w/d} v3, v2, $1 + xv{sll/srl/sra/rotr}i.{b/h/w/d} x3, x2, $1 + v{sll/srl/sra/rotr}.{b/h/w/d} vd, vj, vk + xv{sll/srl/sra/rotr}.{b/h/w/d} xd, xj, xk + +Change-Id: I8693e15f3778057e5a1e636d618c6f46acc5042b +--- + .../asm/internal/asm/testdata/loong64enc1.s | 130 +++++++++ + src/cmd/internal/obj/loong64/a.out.go | 33 +++ + src/cmd/internal/obj/loong64/anames.go | 32 ++ + src/cmd/internal/obj/loong64/asm.go | 274 +++++++++++++++++- + 4 files changed, 468 insertions(+), 1 deletion(-) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 419f257c4a..79012784dc 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -595,6 +595,136 @@ lable2: + XVILVHW X3, X2, X1 // 410c1d75 + XVILVHV X3, X2, X1 // 418c1d75 + ++ // [X]{VSLL/VSRL/VSRA/VROTR}{B,H,W,V} instructions ++ VSLLB V1, V2, V3 // 4304e870 ++ VSLLH V1, V2, V3 // 4384e870 ++ VSLLW V1, V2, V3 // 4304e970 ++ VSLLV V1, V2, V3 // 4384e970 ++ VSRLB V1, V2, V3 // 4304ea70 ++ VSRLH V1, V2, V3 // 4384ea70 ++ VSRLW V1, V2, V3 // 4304eb70 ++ VSRLV V1, V2, V3 // 4384eb70 ++ VSRAB V1, V2, V3 // 4304ec70 ++ VSRAH V1, V2, V3 // 4384ec70 ++ VSRAW V1, V2, V3 // 4304ed70 ++ VSRAV V1, V2, V3 // 4384ed70 ++ VROTRB V1, V2, V3 // 4304ee70 ++ VROTRH V1, V2, V3 // 4384ee70 ++ VROTRW V1, V2, V3 // 4304ef70 ++ VROTRV V1, V2, V3 // 4384ef70 ++ XVSLLB X3, X2, X1 // 410ce874 ++ XVSLLH X3, X2, X1 // 418ce874 ++ XVSLLW X3, X2, X1 // 410ce974 ++ XVSLLV X3, X2, X1 // 418ce974 ++ XVSRLB X3, X2, X1 // 410cea74 ++ XVSRLH X3, X2, X1 // 418cea74 ++ XVSRLW X3, X2, X1 // 410ceb74 ++ XVSRLV X3, X2, X1 // 418ceb74 ++ XVSRAB X3, X2, X1 // 410cec74 ++ XVSRAH X3, X2, X1 // 418cec74 ++ XVSRAW X3, X2, X1 // 410ced74 ++ XVSRAV X3, X2, X1 // 418ced74 ++ XVROTRB X3, X2, X1 // 410cee74 ++ XVROTRH X3, X2, X1 // 418cee74 ++ XVROTRW X3, X2, X1 // 410cef74 ++ XVROTRV X3, X2, X1 // 418cef74 ++ VSLLB $0, V1, V2 // 22202c73 ++ VSLLB $7, V1, V2 // 223c2c73 ++ VSLLB $5, V1 // 21342c73 ++ VSLLH $0, V1, V2 // 22402c73 ++ VSLLH $15, V1, V2 // 227c2c73 ++ VSLLH $10, V1 // 21682c73 ++ VSLLW $0, V1, V2 // 22802c73 ++ VSLLW $31, V1, V2 // 22fc2c73 ++ VSLLW $11, V1 // 21ac2c73 ++ VSLLV $0, V1, V2 // 22002d73 ++ VSLLV $63, V1, V2 // 22fc2d73 ++ VSLLV $30, V1 // 21782d73 ++ VSRLB $0, V1, V2 // 22203073 ++ VSRLB $7, V1, V2 // 223c3073 ++ VSRLB $4, V1 // 21303073 ++ VSRLH $0, V1, V2 // 22403073 ++ VSRLH $15, V1, V2 // 227c3073 ++ VSRLH $9, V1 // 21643073 ++ VSRLW $0, V1, V2 // 22803073 ++ VSRLW $31, V1, V2 // 22fc3073 ++ VSRLW $16, V1 // 21c03073 ++ VSRLV $0, V1, V2 // 22003173 ++ VSRLV $63, V1, V2 // 22fc3173 ++ VSRLV $40, V1 // 21a03173 ++ VSRAB $0, V1, V2 // 22203473 ++ VSRAB $7, V1, V2 // 223c3473 ++ VSRAB $6, V1 // 21383473 ++ VSRAH $0, V1, V2 // 22403473 ++ VSRAH $15, V1, V2 // 227c3473 ++ VSRAH $8, V1 // 21603473 ++ VSRAW $0, V1, V2 // 22803473 ++ VSRAW $31, V1, V2 // 22fc3473 ++ VSRAW $12, V1 // 21b03473 ++ VSRAV $0, V1, V2 // 22003573 ++ VSRAV $63, V1, V2 // 22fc3573 ++ VSRAV $50, V1 // 21c83573 ++ VROTRB $0, V1, V2 // 2220a072 ++ VROTRB $7, V1, V2 // 223ca072 ++ VROTRB $3, V1 // 212ca072 ++ VROTRH $0, V1, V2 // 2240a072 ++ VROTRH $15, V1, V2 // 227ca072 ++ VROTRH $5, V1 // 2154a072 ++ VROTRW $0, V1, V2 // 2280a072 ++ VROTRW $31, V1, V2 // 22fca072 ++ VROTRW $18, V1 // 21c8a072 ++ VROTRV $0, V1, V2 // 2200a172 ++ VROTRV $63, V1, V2 // 22fca172 ++ VROTRV $52, V1 // 21d0a172 ++ XVSLLB $0, X2, X1 // 41202c77 ++ XVSLLB $7, X2, X1 // 413c2c77 ++ XVSLLB $4, X2 // 42302c77 ++ XVSLLH $0, X2, X1 // 41402c77 ++ XVSLLH $15, X2, X1 // 417c2c77 ++ XVSLLH $8, X2 // 42602c77 ++ XVSLLW $0, X2, X1 // 41802c77 ++ XVSLLW $31, X2, X1 // 41fc2c77 ++ XVSLLW $13, X2 // 42b42c77 ++ XVSLLV $0, X2, X1 // 41002d77 ++ XVSLLV $63, X2, X1 // 41fc2d77 ++ XVSLLV $36, X2 // 42902d77 ++ XVSRLB $0, X2, X1 // 41203077 ++ XVSRLB $7, X2, X1 // 413c3077 ++ XVSRLB $5, X2 // 42343077 ++ XVSRLH $0, X2, X1 // 41403077 ++ XVSRLH $15, X2, X1 // 417c3077 ++ XVSRLH $9, X2 // 42643077 ++ XVSRLW $0, X2, X1 // 41803077 ++ XVSRLW $31, X2, X1 // 41fc3077 ++ XVSRLW $14, X2 // 42b83077 ++ XVSRLV $0, X2, X1 // 41003177 ++ XVSRLV $63, X2, X1 // 41fc3177 ++ XVSRLV $45, X2 // 42b43177 ++ XVSRAB $0, X2, X1 // 41203477 ++ XVSRAB $7, X2, X1 // 413c3477 ++ XVSRAB $6, X2 // 42383477 ++ XVSRAH $0, X2, X1 // 41403477 ++ XVSRAH $15, X2, X1 // 417c3477 ++ XVSRAH $10, X2 // 42683477 ++ XVSRAW $0, X2, X1 // 41803477 ++ XVSRAW $31, X2, X1 // 41fc3477 ++ XVSRAW $16, X2 // 42c03477 ++ XVSRAV $0, X2, X1 // 41003577 ++ XVSRAV $63, X2, X1 // 41fc3577 ++ XVSRAV $48, X2 // 42c03577 ++ XVROTRB $0, X2, X1 // 4120a076 ++ XVROTRB $7, X2, X1 // 413ca076 ++ XVROTRB $3, X2 // 422ca076 ++ XVROTRH $0, X2, X1 // 4140a076 ++ XVROTRH $15, X2, X1 // 417ca076 ++ XVROTRH $13, X2 // 4274a076 ++ XVROTRW $0, X2, X1 // 4180a076 ++ XVROTRW $31, X2, X1 // 41fca076 ++ XVROTRW $24, X2 // 42e0a076 ++ XVROTRV $0, X2, X1 // 4100a176 ++ XVROTRV $63, X2, X1 // 41fca176 ++ XVROTRV $52, X2 // 42d0a176 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index c7f4769395..3257d376b4 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -769,6 +769,39 @@ const ( + AXVANDNV + AXVORNV + ++ AVSLLB ++ AVSLLH ++ AVSLLW ++ AVSLLV ++ AVSRLB ++ AVSRLH ++ AVSRLW ++ AVSRLV ++ AVSRAB ++ AVSRAH ++ AVSRAW ++ AVSRAV ++ AVROTRB ++ AVROTRH ++ AVROTRW ++ AVROTRV ++ AXVSLLB ++ AXVSLLH ++ AXVSLLW ++ AXVSLLV ++ AXVSRLB ++ AXVSRLH ++ AXVSRLW ++ AXVSRLV ++ AXVSRAB ++ AXVSRAH ++ AXVSRAW ++ AXVSRAV ++ AXVROTRB ++ AXVROTRH ++ AXVROTRW ++ AXVROTRV ++ + AVPCNTB + AVPCNTH + AVPCNTW +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 485940e19c..776e272a0b 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -297,6 +297,38 @@ var Anames = []string{ + "XVNORV", + "XVANDNV", + "XVORNV", ++ "VSLLB", ++ "VSLLH", ++ "VSLLW", ++ "VSLLV", ++ "VSRLB", ++ "VSRLH", ++ "VSRLW", ++ "VSRLV", ++ "VSRAB", ++ "VSRAH", ++ "VSRAW", ++ "VSRAV", ++ "VROTRB", ++ "VROTRH", ++ "VROTRW", ++ "VROTRV", ++ "XVSLLB", ++ "XVSLLH", ++ "XVSLLW", ++ "XVSLLV", ++ "XVSRLB", ++ "XVSRLH", ++ "XVSRLW", ++ "XVSRLV", ++ "XVSRAB", ++ "XVSRAH", ++ "XVSRAW", ++ "XVSRAV", ++ "XVROTRB", ++ "XVROTRH", ++ "XVROTRW", ++ "XVROTRV", + "VPCNTB", + "VPCNTH", + "VPCNTW", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 9ef414a132..25a40d736e 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -52,6 +52,10 @@ const ( + // Used to insert padding for under-aligned loops. + branchLoopHead + immFiledSi5 // The encoding of the immediate field in the instruction is 5-bits ++ immFiledUi3 // The encoding of the immediate field in the instruction is 3-bits ++ immFiledUi4 // The encoding of the immediate field in the instruction is 4-bits ++ immFiledUi5 // The encoding of the immediate field in the instruction is 5-bits ++ immFiledUi6 // The encoding of the immediate field in the instruction is 6-bits + immFiledUi8 // The encoding of the immediate field in the instruction is 8-bits + ) + +@@ -102,6 +106,34 @@ var optab = []Optab{ + {AVANDB, C_ADDCON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, immFiledUi8}, + {AXVANDB, C_ADDCON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, immFiledUi8}, + ++ {AVSLLB, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, ++ {AXVSLLB, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVSLLB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 29, 4, 0, immFiledUi3}, ++ {AXVSLLB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 29, 4, 0, immFiledUi3}, ++ {AVSLLB, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 29, 4, 0, immFiledUi3}, ++ {AXVSLLB, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 29, 4, 0, immFiledUi3}, ++ ++ {AVSLLH, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, ++ {AXVSLLH, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVSLLH, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 31, 4, 0, immFiledUi4}, ++ {AXVSLLH, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 31, 4, 0, immFiledUi4}, ++ {AVSLLH, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 31, 4, 0, immFiledUi4}, ++ {AXVSLLH, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 31, 4, 0, immFiledUi4}, ++ ++ {AVSLLW, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, ++ {AXVSLLW, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVSLLW, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 32, 4, 0, immFiledUi5}, ++ {AXVSLLW, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 32, 4, 0, immFiledUi5}, ++ {AVSLLW, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 32, 4, 0, immFiledUi5}, ++ {AXVSLLW, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 32, 4, 0, immFiledUi5}, ++ ++ {AVSLLV, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, ++ {AXVSLLV, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, ++ {AVSLLV, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 33, 4, 0, immFiledUi6}, ++ {AXVSLLV, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 33, 4, 0, immFiledUi6}, ++ {AVSLLV, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 33, 4, 0, immFiledUi6}, ++ {AXVSLLV, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 33, 4, 0, immFiledUi6}, ++ + {ACLOW, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 9, 4, 0, 0}, + {AABSF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0}, + {AMOVVF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0}, +@@ -1521,7 +1553,7 @@ func buildop(ctxt *obj.Link) { + case AXVSEQB: + opset(AXVSEQH, r0) + opset(AXVSEQW, r0) +- opset(AXVSEQV, r0) ++ opset(AXVSEQV, r0) + + case AVANDB: + opset(AVORB, r0) +@@ -1583,6 +1615,46 @@ func buildop(ctxt *obj.Link) { + opset(AXVILVHW, r0) + opset(AXVILVHV, r0) + ++ case AVSLLB: ++ opset(AVSRLB, r0) ++ opset(AVSRAB, r0) ++ opset(AVROTRB, r0) ++ ++ case AXVSLLB: ++ opset(AXVSRLB, r0) ++ opset(AXVSRAB, r0) ++ opset(AXVROTRB, r0) ++ ++ case AVSLLH: ++ opset(AVSRLH, r0) ++ opset(AVSRAH, r0) ++ opset(AVROTRH, r0) ++ ++ case AXVSLLH: ++ opset(AXVSRLH, r0) ++ opset(AXVSRAH, r0) ++ opset(AXVROTRH, r0) ++ ++ case AVSLLW: ++ opset(AVSRLW, r0) ++ opset(AVSRAW, r0) ++ opset(AVROTRW, r0) ++ ++ case AXVSLLW: ++ opset(AXVSRLW, r0) ++ opset(AXVSRAW, r0) ++ opset(AXVROTRW, r0) ++ ++ case AVSLLV: ++ opset(AVSRLV, r0) ++ opset(AVSRAV, r0) ++ opset(AVROTRV, r0) ++ ++ case AXVSLLV: ++ opset(AXVSRLV, r0) ++ opset(AXVSRAV, r0) ++ opset(AXVROTRV, r0) ++ + case AVPCNTB: + opset(AVPCNTH, r0) + opset(AVPCNTW, r0) +@@ -1629,10 +1701,22 @@ func OP_8IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { + return op | (i&0xFF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 + } + ++func OP_6IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { ++ return op | (i&0x3F)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 ++} ++ + func OP_5IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { + return op | (i&0x1F)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 + } + ++func OP_4IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { ++ return op | (i&0xF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 ++} ++ ++func OP_3IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 { ++ return op | (i&0x7)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0 ++} ++ + func OP_IR(op uint32, i uint32, r2 uint32) uint32 { + return op | (i&0xFFFFF)<<5 | (r2&0x1F)<<0 // ui20, rd5 + } +@@ -1994,10 +2078,70 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + o1 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.From.Reg)) + } + ++ case 29: // add $ui3,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledUi3: ++ c.checkimmFiled(p, v, 3, false) ++ o1 = OP_3IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ + case 30: // mov gr/fr/fcc/fcsr, fr/fcc/fcsr/gr + a := c.specialFpMovInst(p.As, oclass(&p.From), oclass(&p.To)) + o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg)) + ++ case 31: // add $ui4,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledUi4: ++ c.checkimmFiled(p, v, 4, false) ++ o1 = OP_4IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ ++ case 32: // add $ui5,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledUi5: ++ c.checkimmFiled(p, v, 5, false) ++ o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ ++ case 33: // add $ui6,[r1],r2 ++ v := c.regoff(&p.From) ++ r := int(p.Reg) ++ if r == 0 { ++ r = int(p.To.Reg) ++ } ++ ++ switch o.flag { ++ case immFiledUi6: ++ c.checkimmFiled(p, v, 6, false) ++ o1 = OP_6IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ default: ++ c.ctxt.Diag("Invalid immediate value type\n%v", p) ++ } ++ + case 34: // mov $con,fr + v := c.regoff(&p.From) + a := AADDU +@@ -2809,6 +2953,70 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0xEA3A << 15 // xvilvh.w + case AXVILVHV: + return 0xEA3B << 15 // xvilvh.d ++ case AVSLLB: ++ return 0xE1D0 << 15 // vsll.b ++ case AVSLLH: ++ return 0xE1D1 << 15 // vsll.h ++ case AVSLLW: ++ return 0xE1D2 << 15 // vsll.w ++ case AVSLLV: ++ return 0xE1D3 << 15 // vsll.d ++ case AVSRLB: ++ return 0xE1D4 << 15 // vsrl.b ++ case AVSRLH: ++ return 0xE1D5 << 15 // vsrl.h ++ case AVSRLW: ++ return 0xE1D6 << 15 // vsrl.w ++ case AVSRLV: ++ return 0xE1D7 << 15 // vsrl.d ++ case AVSRAB: ++ return 0xE1D8 << 15 // vsra.b ++ case AVSRAH: ++ return 0xE1D9 << 15 // vsra.h ++ case AVSRAW: ++ return 0xE1DA << 15 // vsra.w ++ case AVSRAV: ++ return 0xE1DB << 15 // vsra.d ++ case AVROTRB: ++ return 0xE1DC << 15 // vrotr.b ++ case AVROTRH: ++ return 0xE1DD << 15 // vrotr.h ++ case AVROTRW: ++ return 0xE1DE << 15 // vrotr.w ++ case AVROTRV: ++ return 0xE1DF << 15 // vrotr.d ++ case AXVSLLB: ++ return 0xE9D0 << 15 // xvsll.b ++ case AXVSLLH: ++ return 0xE9D1 << 15 // xvsll.h ++ case AXVSLLW: ++ return 0xE9D2 << 15 // xvsll.w ++ case AXVSLLV: ++ return 0xE9D3 << 15 // xvsll.d ++ case AXVSRLB: ++ return 0xE9D4 << 15 // xvsrl.b ++ case AXVSRLH: ++ return 0xE9D5 << 15 // xvsrl.h ++ case AXVSRLW: ++ return 0xE9D6 << 15 // xvsrl.w ++ case AXVSRLV: ++ return 0xE9D7 << 15 // xvsrl.d ++ case AXVSRAB: ++ return 0xE9D8 << 15 // xvsra.b ++ case AXVSRAH: ++ return 0xE9D9 << 15 // xvsra.h ++ case AXVSRAW: ++ return 0xE9DA << 15 // xvsra.w ++ case AXVSRAV: ++ return 0xE9DB << 15 // xvsra.d ++ case AXVROTRB: ++ return 0xE9DC << 15 // xvrotr.b ++ case AXVROTRH: ++ return 0xE9DD << 15 // xvrotr.h ++ case AXVROTRW: ++ return 0xE9DE << 15 // xvrotr.w ++ case AXVROTRV: ++ return 0xE9DF << 15 // xvrotr.d + } + + if a < 0 { +@@ -3168,6 +3376,70 @@ func (c *ctxt0) opirr(a obj.As) uint32 { + return 0x0ED02 << 15 // xvseqi.w + case AXVSEQV: + return 0x0ED03 << 15 // xvseqi.d ++ case AVROTRB: ++ return 0x1CA8<<18 | 0x1<<13 // vrotri.b ++ case AVROTRH: ++ return 0x1CA8<<18 | 0x1<<14 // vrotri.h ++ case AVROTRW: ++ return 0x1CA8<<18 | 0x1<<15 // vrotri.w ++ case AVROTRV: ++ return 0x1CA8<<18 | 0x1<<16 // vrotri.d ++ case AXVROTRB: ++ return 0x1DA8<<18 | 0x1<<13 // xvrotri.b ++ case AXVROTRH: ++ return 0x1DA8<<18 | 0x1<<14 // xvrotri.h ++ case AXVROTRW: ++ return 0x1DA8<<18 | 0x1<<15 // xvrotri.w ++ case AXVROTRV: ++ return 0x1DA8<<18 | 0x1<<16 // xvrotri.d ++ case AVSLLB: ++ return 0x1CCB<<18 | 0x1<<13 // vslli.b ++ case AVSLLH: ++ return 0x1CCB<<18 | 0x1<<14 // vslli.h ++ case AVSLLW: ++ return 0x1CCB<<18 | 0x1<<15 // vslli.w ++ case AVSLLV: ++ return 0x1CCB<<18 | 0x1<<16 // vslli.d ++ case AVSRLB: ++ return 0x1CCC<<18 | 0x1<<13 // vsrli.b ++ case AVSRLH: ++ return 0x1CCC<<18 | 0x1<<14 // vsrli.h ++ case AVSRLW: ++ return 0x1CCC<<18 | 0x1<<15 // vsrli.w ++ case AVSRLV: ++ return 0x1CCC<<18 | 0x1<<16 // vsrli.d ++ case AVSRAB: ++ return 0x1CCD<<18 | 0x1<<13 // vsrai.b ++ case AVSRAH: ++ return 0x1CCD<<18 | 0x1<<14 // vsrai.h ++ case AVSRAW: ++ return 0x1CCD<<18 | 0x1<<15 // vsrai.w ++ case AVSRAV: ++ return 0x1CCD<<18 | 0x1<<16 // vsrai.d ++ case AXVSLLB: ++ return 0x1DCB<<18 | 0x1<<13 // xvslli.b ++ case AXVSLLH: ++ return 0x1DCB<<18 | 0x1<<14 // xvslli.h ++ case AXVSLLW: ++ return 0x1DCB<<18 | 0x1<<15 // xvslli.w ++ case AXVSLLV: ++ return 0x1DCB<<18 | 0x1<<16 // xvslli.d ++ case AXVSRLB: ++ return 0x1DCC<<18 | 0x1<<13 // xvsrli.b ++ case AXVSRLH: ++ return 0x1DCC<<18 | 0x1<<14 // xvsrli.h ++ case AXVSRLW: ++ return 0x1DCC<<18 | 0x1<<15 // xvsrli.w ++ case AXVSRLV: ++ return 0x1DCC<<18 | 0x1<<16 // xvsrli.d ++ case AXVSRAB: ++ return 0x1DCD<<18 | 0x1<<13 // xvsrai.b ++ case AXVSRAH: ++ return 0x1DCD<<18 | 0x1<<14 // xvsrai.h ++ case AXVSRAW: ++ return 0x1DCD<<18 | 0x1<<15 // xvsrai.w ++ case AXVSRAV: ++ return 0x1DCD<<18 | 0x1<<16 // xvsrai.d + } + + if a < 0 { +-- +2.38.1 + diff --git a/0030-cmd-internal-obj-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch b/0030-cmd-internal-obj-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch new file mode 100644 index 0000000..ba20193 --- /dev/null +++ b/0030-cmd-internal-obj-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch @@ -0,0 +1,166 @@ +From 344852ff0ccb2b948dc77e0934f246cc5ddf9506 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 16:49:08 +0800 +Subject: [PATCH 30/44] cmd/internal/obj/loong64: add + {V,XV}{FSQRT/FRECIP/FRSQRT}.{S/D} instructions support + +Go asm syntax: + V{FSQRT/FRECIP/FRSQRT}{F/D} VJ, VD + XV{FSQRT/FRECIP/FRSQRT}{F/D} XJ, XD + +Equivalent platform assembler syntax: + v{fsqrt/frecip/frsqrt}.{s/d} vd, vj + xv{fsqrt/frecip/frsqrt}.{s/d} xd, xj + +Change-Id: Ied0b959e703d2199939c9ac0608eb3408ea249fa +--- + .../asm/internal/asm/testdata/loong64enc1.s | 14 +++++++ + src/cmd/internal/obj/loong64/a.out.go | 14 +++++++ + src/cmd/internal/obj/loong64/anames.go | 12 ++++++ + src/cmd/internal/obj/loong64/asm.go | 38 ++++++++++++++++++- + 4 files changed, 77 insertions(+), 1 deletion(-) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 79012784dc..e2e8a6de6c 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -725,6 +725,20 @@ lable2: + XVROTRV $63, X2, X1 // 41fca176 + XVROTRV $52, X2 // 42d0a176 + ++ // [X]VF{SQRT/RECIP/RSQRT}{F/D} instructions ++ VFSQRTF V1, V2 // 22e49c72 ++ VFSQRTD V1, V2 // 22e89c72 ++ VFRECIPF V1, V2 // 22f49c72 ++ VFRECIPD V1, V2 // 22f89c72 ++ VFRSQRTF V1, V2 // 22049d72 ++ VFRSQRTD V1, V2 // 22089d72 ++ XVFSQRTF X2, X1 // 41e49c76 ++ XVFSQRTD X2, X1 // 41e89c76 ++ XVFRECIPF X2, X1 // 41f49c76 ++ XVFRECIPD X2, X1 // 41f89c76 ++ XVFRSQRTF X2, X1 // 41049d76 ++ XVFRSQRTD X2, X1 // 41089d76 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 3257d376b4..bd2b1e8300 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -811,6 +811,20 @@ const ( + AXVPCNTW + AXVPCNTV + ++ // LSX and LASX floating point instructions ++ AVFSQRTF ++ AVFSQRTD ++ AVFRECIPF ++ AVFRECIPD ++ AVFRSQRTF ++ AVFRSQRTD ++ AXVFSQRTF ++ AXVFSQRTD ++ AXVFRECIPF ++ AXVFRECIPD ++ AXVFRSQRTF ++ AXVFRSQRTD ++ + // LSX and LASX integer comparison instruction + AVSEQB + AXVSEQB +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 776e272a0b..7dbe9b92e6 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -337,6 +337,18 @@ var Anames = []string{ + "XVPCNTH", + "XVPCNTW", + "XVPCNTV", ++ "VFSQRTF", ++ "VFSQRTD", ++ "VFRECIPF", ++ "VFRECIPD", ++ "VFRSQRTF", ++ "VFRSQRTD", ++ "XVFSQRTF", ++ "XVFSQRTD", ++ "XVFRECIPF", ++ "XVFRECIPD", ++ "XVFRSQRTF", ++ "XVFRSQRTD", + "VSEQB", + "XVSEQB", + "VSEQH", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 25a40d736e..af38bef3aa 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1553,7 +1553,7 @@ func buildop(ctxt *obj.Link) { + case AXVSEQB: + opset(AXVSEQH, r0) + opset(AXVSEQW, r0) +- opset(AXVSEQV, r0) ++ opset(AXVSEQV, r0) + + case AVANDB: + opset(AVORB, r0) +@@ -1659,11 +1659,23 @@ func buildop(ctxt *obj.Link) { + opset(AVPCNTH, r0) + opset(AVPCNTW, r0) + opset(AVPCNTV, r0) ++ opset(AVFSQRTF, r0) ++ opset(AVFSQRTD, r0) ++ opset(AVFRECIPF, r0) ++ opset(AVFRECIPD, r0) ++ opset(AVFRSQRTF, r0) ++ opset(AVFRSQRTD, r0) + + case AXVPCNTB: + opset(AXVPCNTH, r0) + opset(AXVPCNTW, r0) + opset(AXVPCNTV, r0) ++ opset(AXVFSQRTF, r0) ++ opset(AXVFSQRTD, r0) ++ opset(AXVFRECIPF, r0) ++ opset(AXVFRECIPD, r0) ++ opset(AXVFRSQRTF, r0) ++ opset(AXVFRSQRTD, r0) + } + } + } +@@ -3193,6 +3205,30 @@ func (c *ctxt0) oprr(a obj.As) uint32 { + return 0x1da70a << 10 // xvpcnt.w + case AXVPCNTV: + return 0x1da70b << 10 // xvpcnt.v ++ case AVFSQRTF: ++ return 0x1ca739 << 10 // vfsqrt.s ++ case AVFSQRTD: ++ return 0x1ca73a << 10 // vfsqrt.d ++ case AVFRECIPF: ++ return 0x1ca73d << 10 // vfrecip.s ++ case AVFRECIPD: ++ return 0x1ca73e << 10 // vfrecip.d ++ case AVFRSQRTF: ++ return 0x1ca741 << 10 // vfrsqrt.s ++ case AVFRSQRTD: ++ return 0x1ca742 << 10 // vfrsqrt.d ++ case AXVFSQRTF: ++ return 0x1da739 << 10 // xvfsqrt.s ++ case AXVFSQRTD: ++ return 0x1da73a << 10 // xvfsqrt.d ++ case AXVFRECIPF: ++ return 0x1da73d << 10 // xvfrecip.s ++ case AXVFRECIPD: ++ return 0x1da73e << 10 // xvfrecip.d ++ case AXVFRSQRTF: ++ return 0x1da741 << 10 // xvfrsqrt.s ++ case AXVFRSQRTD: ++ return 0x1da742 << 10 // xvfrsqrt.d + } + + c.ctxt.Diag("bad rr opcode %v", a) +-- +2.38.1 + diff --git a/0031-cmd-internal-obj-loong64-add-V-XV-NEG-B-H-W-V-instru.patch b/0031-cmd-internal-obj-loong64-add-V-XV-NEG-B-H-W-V-instru.patch new file mode 100644 index 0000000..40e749c --- /dev/null +++ b/0031-cmd-internal-obj-loong64-add-V-XV-NEG-B-H-W-V-instru.patch @@ -0,0 +1,135 @@ +From 6849aaa3deb1fec44bb7625a70ecc2a19f86a389 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 17:19:04 +0800 +Subject: [PATCH 31/44] cmd/internal/obj/loong64: add {V,XV}NEG{B/H/W/V} + instructions support + +Go asm syntax: + VNEG{B/H/W/V} VJ, VD + XVNEG{B/H/W/V} XJ, XD + +Equivalent platform assembler syntax: + vneg.{b/h/w/d} vd, vj + xvneg.{b/h/w/d} xd, xj + +Change-Id: Ib2df46b5386149efb44fe12e2485c01826339a5d +--- + .../asm/internal/asm/testdata/loong64enc1.s | 10 ++++++++ + src/cmd/internal/obj/loong64/a.out.go | 10 ++++++++ + src/cmd/internal/obj/loong64/anames.go | 8 +++++++ + src/cmd/internal/obj/loong64/asm.go | 24 +++++++++++++++++++ + 4 files changed, 52 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index e2e8a6de6c..9deb3cbafd 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -739,6 +739,16 @@ lable2: + XVFRSQRTF X2, X1 // 41049d76 + XVFRSQRTD X2, X1 // 41089d76 + ++ // [X]VNEG{B/H/W/V} instructions ++ VNEGB V1, V2 // 22309c72 ++ VNEGH V1, V2 // 22349c72 ++ VNEGW V1, V2 // 22389c72 ++ VNEGV V1, V2 // 223c9c72 ++ XVNEGB X2, X1 // 41309c76 ++ XVNEGH X2, X1 // 41349c76 ++ XVNEGW X2, X1 // 41389c76 ++ XVNEGV X2, X1 // 413c9c76 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index bd2b1e8300..486dc9fa89 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -747,6 +747,16 @@ const ( + AXVSUBV + AXVSUBQ + ++ // LSX and LASX integer neg instructions ++ AVNEGB ++ AVNEGH ++ AVNEGW ++ AVNEGV ++ AXVNEGB ++ AXVNEGH ++ AXVNEGW ++ AXVNEGV ++ + // LSX and LASX Bit-manipulation Instructions + AVANDB + AVORB +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 7dbe9b92e6..d697b73e71 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -277,6 +277,14 @@ var Anames = []string{ + "XVSUBW", + "XVSUBV", + "XVSUBQ", ++ "VNEGB", ++ "VNEGH", ++ "VNEGW", ++ "VNEGV", ++ "XVNEGB", ++ "XVNEGH", ++ "XVNEGW", ++ "XVNEGV", + "VANDB", + "VORB", + "VXORB", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index af38bef3aa..e2c7afd82d 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1665,6 +1665,10 @@ func buildop(ctxt *obj.Link) { + opset(AVFRECIPD, r0) + opset(AVFRSQRTF, r0) + opset(AVFRSQRTD, r0) ++ opset(AVNEGB, r0) ++ opset(AVNEGH, r0) ++ opset(AVNEGW, r0) ++ opset(AVNEGV, r0) + + case AXVPCNTB: + opset(AXVPCNTH, r0) +@@ -1676,6 +1680,10 @@ func buildop(ctxt *obj.Link) { + opset(AXVFRECIPD, r0) + opset(AXVFRSQRTF, r0) + opset(AXVFRSQRTD, r0) ++ opset(AXVNEGB, r0) ++ opset(AXVNEGH, r0) ++ opset(AXVNEGW, r0) ++ opset(AXVNEGV, r0) + } + } + } +@@ -3229,6 +3237,22 @@ func (c *ctxt0) oprr(a obj.As) uint32 { + return 0x1da741 << 10 // xvfrsqrt.s + case AXVFRSQRTD: + return 0x1da742 << 10 // xvfrsqrt.d ++ case AVNEGB: ++ return 0x1ca70c << 10 // vneg.b ++ case AVNEGH: ++ return 0x1ca70d << 10 // vneg.h ++ case AVNEGW: ++ return 0x1ca70e << 10 // vneg.w ++ case AVNEGV: ++ return 0x1ca70f << 10 // vneg.d ++ case AXVNEGB: ++ return 0x1da70c << 10 // xvneg.b ++ case AXVNEGH: ++ return 0x1da70d << 10 // xvneg.h ++ case AXVNEGW: ++ return 0x1da70e << 10 // xvneg.w ++ case AXVNEGV: ++ return 0x1da70f << 10 // xvneg.d + } + + c.ctxt.Diag("bad rr opcode %v", a) +-- +2.38.1 + diff --git a/0032-cmd-internal-obj-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch b/0032-cmd-internal-obj-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch new file mode 100644 index 0000000..67724ac --- /dev/null +++ b/0032-cmd-internal-obj-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch @@ -0,0 +1,235 @@ +From 984f12cbb1763c855882b3c8e89727ad560b38c1 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 17:46:09 +0800 +Subject: [PATCH 32/44] cmd/internal/obj/loong64: add {V,XV}MUL{B/H/W/V} and + {V,XV}MUH{B/H/W/V}[U] instructions support + +Go asm syntax: + VMUL{B/H/W/V} VK, VJ, VD + VMUH{B/H/W/V}[U] VK, VJ, VD + XVMUL{B/H/W/V} XK, XJ, XD + XVMUH{B/H/W/V}[U] XK, XJ, XD + +Equivalent platform assembler syntax: + vmul.{b/h/w/d} vd, vj, vk + vmuh.{b/h/w/d}[u] vd, vj, vk + xvmul.{b/h/w/d} xd, xj, xk + xvmuh.{b/h/w/d}[u] xd, xj, xk + +Change-Id: I8890f8a41100e4681a833c27067f0f76b593f731 +--- + .../asm/internal/asm/testdata/loong64enc1.s | 26 +++++++ + src/cmd/internal/obj/loong64/a.out.go | 26 +++++++ + src/cmd/internal/obj/loong64/anames.go | 24 +++++++ + src/cmd/internal/obj/loong64/asm.go | 72 +++++++++++++++++++ + 4 files changed, 148 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 9deb3cbafd..c8b490234f 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -749,6 +749,32 @@ lable2: + XVNEGW X2, X1 // 41389c76 + XVNEGV X2, X1 // 413c9c76 + ++ // [X]VMUL{B/H/W/V} and [X]VMUH{B/H/W/V}[U] instructions ++ VMULB V1, V2, V3 // 43048470 ++ VMULH V1, V2, V3 // 43848470 ++ VMULW V1, V2, V3 // 43048570 ++ VMULV V1, V2, V3 // 43848570 ++ VMUHB V1, V2, V3 // 43048670 ++ VMUHH V1, V2, V3 // 43848670 ++ VMUHW V1, V2, V3 // 43048770 ++ VMUHV V1, V2, V3 // 43848770 ++ VMUHBU V1, V2, V3 // 43048870 ++ VMUHHU V1, V2, V3 // 43848870 ++ VMUHWU V1, V2, V3 // 43048970 ++ VMUHVU V1, V2, V3 // 43848970 ++ XVMULB X3, X2, X1 // 410c8474 ++ XVMULH X3, X2, X1 // 418c8474 ++ XVMULW X3, X2, X1 // 410c8574 ++ XVMULV X3, X2, X1 // 418c8574 ++ XVMUHB X3, X2, X1 // 410c8674 ++ XVMUHH X3, X2, X1 // 418c8674 ++ XVMUHW X3, X2, X1 // 410c8774 ++ XVMUHV X3, X2, X1 // 418c8774 ++ XVMUHBU X3, X2, X1 // 410c8874 ++ XVMUHHU X3, X2, X1 // 418c8874 ++ XVMUHWU X3, X2, X1 // 410c8974 ++ XVMUHVU X3, X2, X1 // 418c8974 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 486dc9fa89..95744e77a1 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -757,6 +757,32 @@ const ( + AXVNEGW + AXVNEGV + ++ // LSX and LASX integer mul instructions ++ AVMULB ++ AVMULH ++ AVMULW ++ AVMULV ++ AVMUHB ++ AVMUHH ++ AVMUHW ++ AVMUHV ++ AVMUHBU ++ AVMUHHU ++ AVMUHWU ++ AVMUHVU ++ AXVMULB ++ AXVMULH ++ AXVMULW ++ AXVMULV ++ AXVMUHB ++ AXVMUHH ++ AXVMUHW ++ AXVMUHV ++ AXVMUHBU ++ AXVMUHHU ++ AXVMUHWU ++ AXVMUHVU ++ + // LSX and LASX Bit-manipulation Instructions + AVANDB + AVORB +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index d697b73e71..d0cd3a26fa 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -285,6 +285,30 @@ var Anames = []string{ + "XVNEGH", + "XVNEGW", + "XVNEGV", ++ "VMULB", ++ "VMULH", ++ "VMULW", ++ "VMULV", ++ "VMUHB", ++ "VMUHH", ++ "VMUHW", ++ "VMUHV", ++ "VMUHBU", ++ "VMUHHU", ++ "VMUHWU", ++ "VMUHVU", ++ "XVMULB", ++ "XVMULH", ++ "XVMULW", ++ "XVMULV", ++ "XVMUHB", ++ "XVMUHH", ++ "XVMUHW", ++ "XVMUHV", ++ "XVMUHBU", ++ "XVMUHHU", ++ "XVMUHWU", ++ "XVMUHVU", + "VANDB", + "VORB", + "VXORB", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index e2c7afd82d..7fb99f66e6 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1589,6 +1589,18 @@ func buildop(ctxt *obj.Link) { + opset(AVILVHH, r0) + opset(AVILVHW, r0) + opset(AVILVHV, r0) ++ opset(AVMULB, r0) ++ opset(AVMULH, r0) ++ opset(AVMULW, r0) ++ opset(AVMULV, r0) ++ opset(AVMUHB, r0) ++ opset(AVMUHH, r0) ++ opset(AVMUHW, r0) ++ opset(AVMUHV, r0) ++ opset(AVMUHBU, r0) ++ opset(AVMUHHU, r0) ++ opset(AVMUHWU, r0) ++ opset(AVMUHVU, r0) + + case AXVANDV: + opset(AXVORV, r0) +@@ -1614,6 +1626,18 @@ func buildop(ctxt *obj.Link) { + opset(AXVILVHH, r0) + opset(AXVILVHW, r0) + opset(AXVILVHV, r0) ++ opset(AXVMULB, r0) ++ opset(AXVMULH, r0) ++ opset(AXVMULW, r0) ++ opset(AXVMULV, r0) ++ opset(AXVMUHB, r0) ++ opset(AXVMUHH, r0) ++ opset(AXVMUHW, r0) ++ opset(AXVMUHV, r0) ++ opset(AXVMUHBU, r0) ++ opset(AXVMUHHU, r0) ++ opset(AXVMUHWU, r0) ++ opset(AXVMUHVU, r0) + + case AVSLLB: + opset(AVSRLB, r0) +@@ -3037,6 +3061,54 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0xE9DE << 15 // xvrotr.w + case AXVROTRV: + return 0xE9DF << 15 // xvrotr.d ++ case AVMULB: ++ return 0xe108 << 15 // vmul.b ++ case AVMULH: ++ return 0xe109 << 15 // vmul.h ++ case AVMULW: ++ return 0xe10a << 15 // vmul.w ++ case AVMULV: ++ return 0xe10b << 15 // vmul.d ++ case AVMUHB: ++ return 0xe10c << 15 // vmuh.b ++ case AVMUHH: ++ return 0xe10d << 15 // vmuh.h ++ case AVMUHW: ++ return 0xe10e << 15 // vmuh.w ++ case AVMUHV: ++ return 0xe10f << 15 // vmuh.d ++ case AVMUHBU: ++ return 0xe110 << 15 // vmuh.bu ++ case AVMUHHU: ++ return 0xe111 << 15 // vmuh.hu ++ case AVMUHWU: ++ return 0xe112 << 15 // vmuh.wu ++ case AVMUHVU: ++ return 0xe113 << 15 // vmuh.du ++ case AXVMULB: ++ return 0xe908 << 15 // xvmul.b ++ case AXVMULH: ++ return 0xe909 << 15 // xvmul.h ++ case AXVMULW: ++ return 0xe90a << 15 // xvmul.w ++ case AXVMULV: ++ return 0xe90b << 15 // xvmul.d ++ case AXVMUHB: ++ return 0xe90c << 15 // xvmuh.b ++ case AXVMUHH: ++ return 0xe90d << 15 // xvmuh.h ++ case AXVMUHW: ++ return 0xe90e << 15 // xvmuh.w ++ case AXVMUHV: ++ return 0xe90f << 15 // xvmuh.d ++ case AXVMUHBU: ++ return 0xe910 << 15 // xvmuh.bu ++ case AXVMUHHU: ++ return 0xe911 << 15 // xvmuh.hu ++ case AXVMUHWU: ++ return 0xe912 << 15 // xvmuh.wu ++ case AXVMUHVU: ++ return 0xe913 << 15 // xvmuh.du + } + + if a < 0 { +-- +2.38.1 + diff --git a/0033-cmd-internal-obj-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch b/0033-cmd-internal-obj-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch new file mode 100644 index 0000000..967f56d --- /dev/null +++ b/0033-cmd-internal-obj-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch @@ -0,0 +1,283 @@ +From 116a2261b3a110e6ff4f9608f447e6f07156d55f Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Wed, 11 Dec 2024 18:08:16 +0800 +Subject: [PATCH 33/44] cmd/internal/obj/loong64: add {V,XV}DIV{B/H/W/V}[U] and + {V,XV}MOD{B/H/W/V}[U] instructions support + +Go asm syntax: + VDIV{B/H/W/V}[U] VK, VJ, VD + XVDIV{B/H/W/V}[U] XK, XJ, XD + VMOD{B/H/W/V}[U] VK, VJ, VD + XVMOD{B/H/W/V}[U] XK, XJ, XD + +Equivalent platform assembler syntax: + vdiv.{b/h/w/d}[u] vd, vj, vk + xvdiv.{b/h/w/d}[u] xd, xj, xk + vmod.{b/h/w/d}[u] vd, vj, vk + xvmod.{b/h/w/d}[u] xd, xj, xk + +Change-Id: I27e9bc8999e6525a27f0bf12b21cc896c5a2a69c +--- + .../asm/internal/asm/testdata/loong64enc1.s | 34 +++++++ + src/cmd/internal/obj/loong64/a.out.go | 34 +++++++ + src/cmd/internal/obj/loong64/anames.go | 32 +++++++ + src/cmd/internal/obj/loong64/asm.go | 96 +++++++++++++++++++ + 4 files changed, 196 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index c8b490234f..bbac6036cf 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -775,6 +775,40 @@ lable2: + XVMUHWU X3, X2, X1 // 410c8974 + XVMUHVU X3, X2, X1 // 418c8974 + ++ // [X]VDIV{B/H/W/V}[U] and [X]VMOD{B/H/W/V}[U] instructions ++ VDIVB V1, V2, V3 // 4304e070 ++ VDIVH V1, V2, V3 // 4384e070 ++ VDIVW V1, V2, V3 // 4304e170 ++ VDIVV V1, V2, V3 // 4384e170 ++ VDIVBU V1, V2, V3 // 4304e470 ++ VDIVHU V1, V2, V3 // 4384e470 ++ VDIVWU V1, V2, V3 // 4304e570 ++ VDIVVU V1, V2, V3 // 4384e570 ++ VMODB V1, V2, V3 // 4304e270 ++ VMODH V1, V2, V3 // 4384e270 ++ VMODW V1, V2, V3 // 4304e370 ++ VMODV V1, V2, V3 // 4384e370 ++ VMODBU V1, V2, V3 // 4304e670 ++ VMODHU V1, V2, V3 // 4384e670 ++ VMODWU V1, V2, V3 // 4304e770 ++ VMODVU V1, V2, V3 // 4384e770 ++ XVDIVB X3, X2, X1 // 410ce074 ++ XVDIVH X3, X2, X1 // 418ce074 ++ XVDIVW X3, X2, X1 // 410ce174 ++ XVDIVV X3, X2, X1 // 418ce174 ++ XVDIVBU X3, X2, X1 // 410ce474 ++ XVDIVHU X3, X2, X1 // 418ce474 ++ XVDIVWU X3, X2, X1 // 410ce574 ++ XVDIVVU X3, X2, X1 // 418ce574 ++ XVMODB X3, X2, X1 // 410ce274 ++ XVMODH X3, X2, X1 // 418ce274 ++ XVMODW X3, X2, X1 // 410ce374 ++ XVMODV X3, X2, X1 // 418ce374 ++ XVMODBU X3, X2, X1 // 410ce674 ++ XVMODHU X3, X2, X1 // 418ce674 ++ XVMODWU X3, X2, X1 // 410ce774 ++ XVMODVU X3, X2, X1 // 418ce774 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 95744e77a1..9164e9d59f 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -783,6 +783,40 @@ const ( + AXVMUHWU + AXVMUHVU + ++ // LSX and LASX integer div and mod instructions ++ AVDIVB ++ AVDIVH ++ AVDIVW ++ AVDIVV ++ AVDIVBU ++ AVDIVHU ++ AVDIVWU ++ AVDIVVU ++ AVMODB ++ AVMODH ++ AVMODW ++ AVMODV ++ AVMODBU ++ AVMODHU ++ AVMODWU ++ AVMODVU ++ AXVDIVB ++ AXVDIVH ++ AXVDIVW ++ AXVDIVV ++ AXVDIVBU ++ AXVDIVHU ++ AXVDIVWU ++ AXVDIVVU ++ AXVMODB ++ AXVMODH ++ AXVMODW ++ AXVMODV ++ AXVMODBU ++ AXVMODHU ++ AXVMODWU ++ AXVMODVU ++ + // LSX and LASX Bit-manipulation Instructions + AVANDB + AVORB +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index d0cd3a26fa..15a264c8e2 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -309,6 +309,38 @@ var Anames = []string{ + "XVMUHHU", + "XVMUHWU", + "XVMUHVU", ++ "VDIVB", ++ "VDIVH", ++ "VDIVW", ++ "VDIVV", ++ "VDIVBU", ++ "VDIVHU", ++ "VDIVWU", ++ "VDIVVU", ++ "VMODB", ++ "VMODH", ++ "VMODW", ++ "VMODV", ++ "VMODBU", ++ "VMODHU", ++ "VMODWU", ++ "VMODVU", ++ "XVDIVB", ++ "XVDIVH", ++ "XVDIVW", ++ "XVDIVV", ++ "XVDIVBU", ++ "XVDIVHU", ++ "XVDIVWU", ++ "XVDIVVU", ++ "XVMODB", ++ "XVMODH", ++ "XVMODW", ++ "XVMODV", ++ "XVMODBU", ++ "XVMODHU", ++ "XVMODWU", ++ "XVMODVU", + "VANDB", + "VORB", + "VXORB", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 7fb99f66e6..7a14137374 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1601,6 +1601,22 @@ func buildop(ctxt *obj.Link) { + opset(AVMUHHU, r0) + opset(AVMUHWU, r0) + opset(AVMUHVU, r0) ++ opset(AVDIVB, r0) ++ opset(AVDIVH, r0) ++ opset(AVDIVW, r0) ++ opset(AVDIVV, r0) ++ opset(AVMODB, r0) ++ opset(AVMODH, r0) ++ opset(AVMODW, r0) ++ opset(AVMODV, r0) ++ opset(AVDIVBU, r0) ++ opset(AVDIVHU, r0) ++ opset(AVDIVWU, r0) ++ opset(AVDIVVU, r0) ++ opset(AVMODBU, r0) ++ opset(AVMODHU, r0) ++ opset(AVMODWU, r0) ++ opset(AVMODVU, r0) + + case AXVANDV: + opset(AXVORV, r0) +@@ -1638,6 +1654,22 @@ func buildop(ctxt *obj.Link) { + opset(AXVMUHHU, r0) + opset(AXVMUHWU, r0) + opset(AXVMUHVU, r0) ++ opset(AXVDIVB, r0) ++ opset(AXVDIVH, r0) ++ opset(AXVDIVW, r0) ++ opset(AXVDIVV, r0) ++ opset(AXVMODB, r0) ++ opset(AXVMODH, r0) ++ opset(AXVMODW, r0) ++ opset(AXVMODV, r0) ++ opset(AXVDIVBU, r0) ++ opset(AXVDIVHU, r0) ++ opset(AXVDIVWU, r0) ++ opset(AXVDIVVU, r0) ++ opset(AXVMODBU, r0) ++ opset(AXVMODHU, r0) ++ opset(AXVMODWU, r0) ++ opset(AXVMODVU, r0) + + case AVSLLB: + opset(AVSRLB, r0) +@@ -3109,6 +3141,70 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0xe912 << 15 // xvmuh.wu + case AXVMUHVU: + return 0xe913 << 15 // xvmuh.du ++ case AVDIVB: ++ return 0xe1c0 << 15 // vdiv.b ++ case AVDIVH: ++ return 0xe1c1 << 15 // vdiv.h ++ case AVDIVW: ++ return 0xe1c2 << 15 // vdiv.w ++ case AVDIVV: ++ return 0xe1c3 << 15 // vdiv.d ++ case AVMODB: ++ return 0xe1c4 << 15 // vmod.b ++ case AVMODH: ++ return 0xe1c5 << 15 // vmod.h ++ case AVMODW: ++ return 0xe1c6 << 15 // vmod.w ++ case AVMODV: ++ return 0xe1c7 << 15 // vmod.d ++ case AVDIVBU: ++ return 0xe1c8 << 15 // vdiv.bu ++ case AVDIVHU: ++ return 0xe1c9 << 15 // vdiv.hu ++ case AVDIVWU: ++ return 0xe1ca << 15 // vdiv.wu ++ case AVDIVVU: ++ return 0xe1cb << 15 // vdiv.du ++ case AVMODBU: ++ return 0xe1cc << 15 // vmod.bu ++ case AVMODHU: ++ return 0xe1cd << 15 // vmod.hu ++ case AVMODWU: ++ return 0xe1ce << 15 // vmod.wu ++ case AVMODVU: ++ return 0xe1cf << 15 // vmod.du ++ case AXVDIVB: ++ return 0xe9c0 << 15 // xvdiv.b ++ case AXVDIVH: ++ return 0xe9c1 << 15 // xvdiv.h ++ case AXVDIVW: ++ return 0xe9c2 << 15 // xvdiv.w ++ case AXVDIVV: ++ return 0xe9c3 << 15 // xvdiv.d ++ case AXVMODB: ++ return 0xe9c4 << 15 // xvmod.b ++ case AXVMODH: ++ return 0xe9c5 << 15 // xvmod.h ++ case AXVMODW: ++ return 0xe9c6 << 15 // xvmod.w ++ case AXVMODV: ++ return 0xe9c7 << 15 // xvmod.d ++ case AXVDIVBU: ++ return 0xe9c8 << 15 // xvdiv.bu ++ case AXVDIVHU: ++ return 0xe9c9 << 15 // xvdiv.hu ++ case AXVDIVWU: ++ return 0xe9ca << 15 // xvdiv.wu ++ case AXVDIVVU: ++ return 0xe9cb << 15 // xvdiv.du ++ case AXVMODBU: ++ return 0xe9cc << 15 // xvmod.bu ++ case AXVMODHU: ++ return 0xe9cd << 15 // xvmod.hu ++ case AXVMODWU: ++ return 0xe9ce << 15 // xvmod.wu ++ case AXVMODVU: ++ return 0xe9cf << 15 // xvmod.du + } + + if a < 0 { +-- +2.38.1 + diff --git a/0034-cmd-internal-obj-loong64-add-V-XV-BITCLR-BITSET-BITR.patch b/0034-cmd-internal-obj-loong64-add-V-XV-BITCLR-BITSET-BITR.patch new file mode 100644 index 0000000..2a2d4b1 --- /dev/null +++ b/0034-cmd-internal-obj-loong64-add-V-XV-BITCLR-BITSET-BITR.patch @@ -0,0 +1,341 @@ +From 054df785d79675c02f6bd2ad3ace9f1ce5874e84 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Thu, 12 Dec 2024 10:54:00 +0800 +Subject: [PATCH 34/44] cmd/internal/obj/loong64: add + {V,XV}{BITCLR/BITSET/BITREV}[I].{B/H/W/D} instructions support + +Go asm syntax: + V{BITCLR/BITSET/BITREV}{B/H/W/V} $1, V2, V3 + XV{BITCLR/BITSET/BITREV}{B/H/W/V} $1, X2, X3 + V{BITCLR/BITSET/BITREV}{B/H/W/V} VK, VJ, VD + XV{BITCLR/BITSET/BITREV}{B/H/W/V} XK, XJ, XD + +Equivalent platform assembler syntax: + v{bitclr/bitset/bitrev}i.{b/h/w/d} v3, v2, $1 + xv{bitclr/bitset/bitrev}i.{b/h/w/d} x3, x2, $1 + v{bitclr/bitset/bitrev}.{b/h/w/d} vd, vj, vk + xv{bitclr/bitset/bitrev}.{b/h/w/d} xd, xj, xk + +Change-Id: Id44e6cb7c22d650bb6b4d9f6faee5dcda4edb24e +--- + .../asm/internal/asm/testdata/loong64enc1.s | 50 ++++++++ + src/cmd/internal/obj/loong64/a.out.go | 25 ++++ + src/cmd/internal/obj/loong64/anames.go | 24 ++++ + src/cmd/internal/obj/loong64/asm.go | 120 ++++++++++++++++++ + 4 files changed, 219 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index bbac6036cf..19070c89ef 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -809,6 +809,56 @@ lable2: + XVMODWU X3, X2, X1 // 410ce774 + XVMODVU X3, X2, X1 // 418ce774 + ++ // [X]{VBITCLR/VBITSET/VBITREV}{B,H,W,V} instructions ++ VBITCLRB V1, V2, V3 // 43040c71 ++ VBITCLRH V1, V2, V3 // 43840c71 ++ VBITCLRW V1, V2, V3 // 43040d71 ++ VBITCLRV V1, V2, V3 // 43840d71 ++ VBITSETB V1, V2, V3 // 43040e71 ++ VBITSETH V1, V2, V3 // 43840e71 ++ VBITSETW V1, V2, V3 // 43040f71 ++ VBITSETV V1, V2, V3 // 43840f71 ++ VBITREVB V1, V2, V3 // 43041071 ++ VBITREVH V1, V2, V3 // 43841071 ++ VBITREVW V1, V2, V3 // 43041171 ++ VBITREVV V1, V2, V3 // 43841171 ++ XVBITCLRB X3, X2, X1 // 410c0c75 ++ XVBITCLRH X3, X2, X1 // 418c0c75 ++ XVBITCLRW X3, X2, X1 // 410c0d75 ++ XVBITCLRV X3, X2, X1 // 418c0d75 ++ XVBITSETB X3, X2, X1 // 410c0e75 ++ XVBITSETH X3, X2, X1 // 418c0e75 ++ XVBITSETW X3, X2, X1 // 410c0f75 ++ XVBITSETV X3, X2, X1 // 418c0f75 ++ XVBITREVB X3, X2, X1 // 410c1075 ++ XVBITREVH X3, X2, X1 // 418c1075 ++ XVBITREVW X3, X2, X1 // 410c1175 ++ XVBITREVV X3, X2, X1 // 418c1175 ++ VBITCLRB $7, V2, V3 // 433c1073 ++ VBITCLRH $15, V2, V3 // 437c1073 ++ VBITCLRW $31, V2, V3 // 43fc1073 ++ VBITCLRV $63, V2, V3 // 43fc1173 ++ VBITSETB $7, V2, V3 // 433c1473 ++ VBITSETH $15, V2, V3 // 437c1473 ++ VBITSETW $31, V2, V3 // 43fc1473 ++ VBITSETV $63, V2, V3 // 43fc1573 ++ VBITREVB $7, V2, V3 // 433c1873 ++ VBITREVH $15, V2, V3 // 437c1873 ++ VBITREVW $31, V2, V3 // 43fc1873 ++ VBITREVV $63, V2, V3 // 43fc1973 ++ XVBITCLRB $7, X2, X1 // 413c1077 ++ XVBITCLRH $15, X2, X1 // 417c1077 ++ XVBITCLRW $31, X2, X1 // 41fc1077 ++ XVBITCLRV $63, X2, X1 // 41fc1177 ++ XVBITSETB $7, X2, X1 // 413c1477 ++ XVBITSETH $15, X2, X1 // 417c1477 ++ XVBITSETW $31, X2, X1 // 41fc1477 ++ XVBITSETV $63, X2, X1 // 41fc1577 ++ XVBITREVB $7, X2, X1 // 413c1877 ++ XVBITREVH $15, X2, X1 // 417c1877 ++ XVBITREVW $31, X2, X1 // 41fc1877 ++ XVBITREVV $63, X2, X1 // 41fc1977 ++ + // MOVV C_DCON12_0, r + MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03 + MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 9164e9d59f..1fadbc648a 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -881,6 +881,31 @@ const ( + AXVPCNTW + AXVPCNTV + ++ AVBITCLRB ++ AVBITCLRH ++ AVBITCLRW ++ AVBITCLRV ++ AVBITSETB ++ AVBITSETH ++ AVBITSETW ++ AVBITSETV ++ AVBITREVB ++ AVBITREVH ++ AVBITREVW ++ AVBITREVV ++ AXVBITCLRB ++ AXVBITCLRH ++ AXVBITCLRW ++ AXVBITCLRV ++ AXVBITSETB ++ AXVBITSETH ++ AXVBITSETW ++ AXVBITSETV ++ AXVBITREVB ++ AXVBITREVH ++ AXVBITREVW ++ AXVBITREVV ++ + // LSX and LASX floating point instructions + AVFSQRTF + AVFSQRTD +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index 15a264c8e2..aee0da0a6e 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -401,6 +401,30 @@ var Anames = []string{ + "XVPCNTH", + "XVPCNTW", + "XVPCNTV", ++ "VBITCLRB", ++ "VBITCLRH", ++ "VBITCLRW", ++ "VBITCLRV", ++ "VBITSETB", ++ "VBITSETH", ++ "VBITSETW", ++ "VBITSETV", ++ "VBITREVB", ++ "VBITREVH", ++ "VBITREVW", ++ "VBITREVV", ++ "XVBITCLRB", ++ "XVBITCLRH", ++ "XVBITCLRW", ++ "XVBITCLRV", ++ "XVBITSETB", ++ "XVBITSETH", ++ "XVBITSETW", ++ "XVBITSETV", ++ "XVBITREVB", ++ "XVBITREVH", ++ "XVBITREVW", ++ "XVBITREVV", + "VFSQRTF", + "VFSQRTD", + "VFRECIPF", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 7a14137374..657d32ae81 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1675,41 +1675,65 @@ func buildop(ctxt *obj.Link) { + opset(AVSRLB, r0) + opset(AVSRAB, r0) + opset(AVROTRB, r0) ++ opset(AVBITCLRB, r0) ++ opset(AVBITSETB, r0) ++ opset(AVBITREVB, r0) + + case AXVSLLB: + opset(AXVSRLB, r0) + opset(AXVSRAB, r0) + opset(AXVROTRB, r0) ++ opset(AXVBITCLRB, r0) ++ opset(AXVBITSETB, r0) ++ opset(AXVBITREVB, r0) + + case AVSLLH: + opset(AVSRLH, r0) + opset(AVSRAH, r0) + opset(AVROTRH, r0) ++ opset(AVBITCLRH, r0) ++ opset(AVBITSETH, r0) ++ opset(AVBITREVH, r0) + + case AXVSLLH: + opset(AXVSRLH, r0) + opset(AXVSRAH, r0) + opset(AXVROTRH, r0) ++ opset(AXVBITCLRH, r0) ++ opset(AXVBITSETH, r0) ++ opset(AXVBITREVH, r0) + + case AVSLLW: + opset(AVSRLW, r0) + opset(AVSRAW, r0) + opset(AVROTRW, r0) ++ opset(AVBITCLRW, r0) ++ opset(AVBITSETW, r0) ++ opset(AVBITREVW, r0) + + case AXVSLLW: + opset(AXVSRLW, r0) + opset(AXVSRAW, r0) + opset(AXVROTRW, r0) ++ opset(AXVBITCLRW, r0) ++ opset(AXVBITSETW, r0) ++ opset(AXVBITREVW, r0) + + case AVSLLV: + opset(AVSRLV, r0) + opset(AVSRAV, r0) + opset(AVROTRV, r0) ++ opset(AVBITCLRV, r0) ++ opset(AVBITSETV, r0) ++ opset(AVBITREVV, r0) + + case AXVSLLV: + opset(AXVSRLV, r0) + opset(AXVSRAV, r0) + opset(AXVROTRV, r0) ++ opset(AXVBITCLRV, r0) ++ opset(AXVBITSETV, r0) ++ opset(AXVBITREVV, r0) + + case AVPCNTB: + opset(AVPCNTH, r0) +@@ -3205,6 +3229,54 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0xe9ce << 15 // xvmod.wu + case AXVMODVU: + return 0xe9cf << 15 // xvmod.du ++ case AVBITCLRB: ++ return 0xe218 << 15 // vbitclr.b ++ case AVBITCLRH: ++ return 0xe219 << 15 // vbitclr.h ++ case AVBITCLRW: ++ return 0xe21a << 15 // vbitclr.w ++ case AVBITCLRV: ++ return 0xe21b << 15 // vbitclr.d ++ case AVBITSETB: ++ return 0xe21c << 15 // vbitset.b ++ case AVBITSETH: ++ return 0xe21d << 15 // vbitset.h ++ case AVBITSETW: ++ return 0xe21e << 15 // vbitset.w ++ case AVBITSETV: ++ return 0xe21f << 15 // vbitset.d ++ case AVBITREVB: ++ return 0xe220 << 15 // vbitrev.b ++ case AVBITREVH: ++ return 0xe221 << 15 // vbitrev.h ++ case AVBITREVW: ++ return 0xe222 << 15 // vbitrev.w ++ case AVBITREVV: ++ return 0xe223 << 15 // vbitrev.d ++ case AXVBITCLRB: ++ return 0xea18 << 15 // xvbitclr.b ++ case AXVBITCLRH: ++ return 0xea19 << 15 // xvbitclr.h ++ case AXVBITCLRW: ++ return 0xea1a << 15 // xvbitclr.w ++ case AXVBITCLRV: ++ return 0xea1b << 15 // xvbitclr.d ++ case AXVBITSETB: ++ return 0xea1c << 15 // xvbitset.b ++ case AXVBITSETH: ++ return 0xea1d << 15 // xvbitset.h ++ case AXVBITSETW: ++ return 0xea1e << 15 // xvbitset.w ++ case AXVBITSETV: ++ return 0xea1f << 15 // xvbitset.d ++ case AXVBITREVB: ++ return 0xea20 << 15 // xvbitrev.b ++ case AXVBITREVH: ++ return 0xea21 << 15 // xvbitrev.h ++ case AXVBITREVW: ++ return 0xea22 << 15 // xvbitrev.w ++ case AXVBITREVV: ++ return 0xea23 << 15 // xvbitrev.d + } + + if a < 0 { +@@ -3668,6 +3740,54 @@ func (c *ctxt0) opirr(a obj.As) uint32 { + return 0x1DCD<<18 | 0x1<<15 // xvsrai.w + case AXVSRAV: + return 0x1DCD<<18 | 0x1<<16 // xvsrai.d ++ case AVBITCLRB: ++ return 0x1CC4<<18 | 0x1<<13 // vbitclri.b ++ case AVBITCLRH: ++ return 0x1CC4<<18 | 0x1<<14 // vbitclri.h ++ case AVBITCLRW: ++ return 0x1CC4<<18 | 0x1<<15 // vbitclri.w ++ case AVBITCLRV: ++ return 0x1CC4<<18 | 0x1<<16 // vbitclri.d ++ case AVBITSETB: ++ return 0x1CC5<<18 | 0x1<<13 // vbitseti.b ++ case AVBITSETH: ++ return 0x1CC5<<18 | 0x1<<14 // vbitseti.h ++ case AVBITSETW: ++ return 0x1CC5<<18 | 0x1<<15 // vbitseti.w ++ case AVBITSETV: ++ return 0x1CC5<<18 | 0x1<<16 // vbitseti.d ++ case AVBITREVB: ++ return 0x1CC6<<18 | 0x1<<13 // vbitrevi.b ++ case AVBITREVH: ++ return 0x1CC6<<18 | 0x1<<14 // vbitrevi.h ++ case AVBITREVW: ++ return 0x1CC6<<18 | 0x1<<15 // vbitrevi.w ++ case AVBITREVV: ++ return 0x1CC6<<18 | 0x1<<16 // vbitrevi.d ++ case AXVBITCLRB: ++ return 0x1DC4<<18 | 0x1<<13 // xvbitclri.b ++ case AXVBITCLRH: ++ return 0x1DC4<<18 | 0x1<<14 // xvbitclri.h ++ case AXVBITCLRW: ++ return 0x1DC4<<18 | 0x1<<15 // xvbitclri.w ++ case AXVBITCLRV: ++ return 0x1DC4<<18 | 0x1<<16 // xvbitclri.d ++ case AXVBITSETB: ++ return 0x1DC5<<18 | 0x1<<13 // xvbitseti.b ++ case AXVBITSETH: ++ return 0x1DC5<<18 | 0x1<<14 // xvbitseti.h ++ case AXVBITSETW: ++ return 0x1DC5<<18 | 0x1<<15 // xvbitseti.w ++ case AXVBITSETV: ++ return 0x1DC5<<18 | 0x1<<16 // xvbitseti.d ++ case AXVBITREVB: ++ return 0x1DC6<<18 | 0x1<<13 // xvbitrevi.b ++ case AXVBITREVH: ++ return 0x1DC6<<18 | 0x1<<14 // xvbitrevi.h ++ case AXVBITREVW: ++ return 0x1DC6<<18 | 0x1<<15 // xvbitrevi.w ++ case AXVBITREVV: ++ return 0x1DC6<<18 | 0x1<<16 // xvbitrevi.d + } + + if a < 0 { +-- +2.38.1 + diff --git a/0035-crypto-chacha20-add-loong64-SIMD-implementation.patch b/0035-crypto-chacha20-add-loong64-SIMD-implementation.patch new file mode 100644 index 0000000..2c8eb2b --- /dev/null +++ b/0035-crypto-chacha20-add-loong64-SIMD-implementation.patch @@ -0,0 +1,490 @@ +From d6bdc012b1c105a007d0fb5d7d1642f1a5653b1d Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Fri, 13 Dec 2024 17:09:31 +0800 +Subject: [PATCH 35/44] crypto/chacha20: add loong64 SIMD implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The performance of chacha20 has been greatly improved. + +goos: linux +goarch: loong64 +pkg: golang.org/x/crypto/chacha20 +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +ChaCha20/64 171.9n ± 0% 159.3n ± 0% -7.33% (p=0.000 n=20) +ChaCha20/256 592.2n ± 0% 142.8n ± 0% -75.89% (p=0.000 n=20) +ChaCha20/10x25 981.5n ± 0% 518.8n ± 0% -47.14% (p=0.000 n=20) +ChaCha20/4096 8.991µ ± 0% 1.732µ ± 0% -80.74% (p=0.000 n=20) +ChaCha20/100x40 10.651µ ± 0% 5.135µ ± 0% -51.79% (p=0.000 n=20) +ChaCha20/65536 143.43µ ± 0% 28.76µ ± 0% -79.95% (p=0.000 n=20) +ChaCha20/1000x65 146.17µ ± 0% 37.13µ ± 0% -74.60% (p=0.000 n=20) +geomean 5.721µ 1.962µ -65.70% + + | bench.old | bench.new | + | B/s | B/s vs base | +ChaCha20/64 355.1Mi ± 0% 383.1Mi ± 0% +7.89% (p=0.000 n=20) +ChaCha20/256 412.2Mi ± 0% 1710.2Mi ± 0% +314.86% (p=0.000 n=20) +ChaCha20/10x25 242.9Mi ± 0% 459.6Mi ± 0% +89.19% (p=0.000 n=20) +ChaCha20/4096 434.5Mi ± 0% 2255.8Mi ± 0% +419.22% (p=0.000 n=20) +ChaCha20/100x40 358.1Mi ± 0% 742.9Mi ± 0% +107.44% (p=0.000 n=20) +ChaCha20/65536 435.8Mi ± 0% 2173.2Mi ± 0% +398.72% (p=0.000 n=20) +ChaCha20/1000x65 424.1Mi ± 0% 1669.4Mi ± 0% +293.64% (p=0.000 n=20) +geomean 373.9Mi 1.065Gi +191.55% + +goos: linux +goarch: loong64 +pkg: golang.org/x/crypto/chacha20 +cpu: Loongson-3A5000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +ChaCha20/64 234.5n ± 0% 295.8n ± 0% +26.14% (p=0.000 n=20) +ChaCha20/256 782.0n ± 0% 274.6n ± 0% -64.88% (p=0.000 n=20) +ChaCha20/10x25 1340.0n ± 0% 752.7n ± 0% -43.83% (p=0.000 n=20) +ChaCha20/4096 11.744µ ± 0% 3.455µ ± 0% -70.58% (p=0.000 n=20) +ChaCha20/100x40 14.151µ ± 0% 7.435µ ± 0% -47.46% (p=0.000 n=20) +ChaCha20/65536 188.05µ ± 0% 54.33µ ± 0% -71.11% (p=0.000 n=20) +ChaCha20/1000x65 191.44µ ± 0% 66.29µ ± 0% -65.37% (p=0.000 n=20) +geomean 7.604µ 3.436µ -54.81% + + | bench.old | bench.new | + | B/s | B/s vs base | +ChaCha20/64 260.3Mi ± 0% 206.3Mi ± 0% -20.73% (p=0.000 n=20) +ChaCha20/256 312.2Mi ± 0% 888.9Mi ± 0% +184.75% (p=0.000 n=20) +ChaCha20/10x25 177.9Mi ± 0% 316.8Mi ± 0% +78.08% (p=0.000 n=20) +ChaCha20/4096 332.6Mi ± 0% 1130.8Mi ± 0% +239.95% (p=0.000 n=20) +ChaCha20/100x40 269.6Mi ± 0% 513.1Mi ± 0% +90.34% (p=0.000 n=20) +ChaCha20/65536 332.4Mi ± 0% 1150.5Mi ± 0% +246.16% (p=0.000 n=20) +ChaCha20/1000x65 323.8Mi ± 0% 935.2Mi ± 0% +188.81% (p=0.000 n=20) +geomean 281.3Mi 622.6Mi +121.31% + +Change-Id: Iab4934d78b845e3b248bd5d0a9a62e4e9c516831 +--- + .../x/crypto/chacha20/chacha_loong64.go | 17 + + .../x/crypto/chacha20/chacha_loong64.s | 374 ++++++++++++++++++ + .../x/crypto/chacha20/chacha_noasm.go | 2 +- + 3 files changed, 392 insertions(+), 1 deletion(-) + create mode 100644 src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.go + create mode 100644 src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.s + +diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.go b/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.go +new file mode 100644 +index 0000000000..d0f5d909fc +--- /dev/null ++++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.go +@@ -0,0 +1,17 @@ ++// Copyright 2024 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. ++ ++//go:build gc && !purego ++ ++package chacha20 ++ ++const bufSize = 256 ++ ++//go:noescape ++func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) ++ ++func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { ++ // add cpu.Loong64.HasLSX check TODO ++ xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) ++} +diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.s b/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.s +new file mode 100644 +index 0000000000..883c8d992a +--- /dev/null ++++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_loong64.s +@@ -0,0 +1,374 @@ ++// Copyright 2024 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. ++ ++// derived from chacha_arm64.s ++ ++//go:build gc && !purego ++ ++#include "textflag.h" ++ ++DATA ·constants+0x00(SB)/4, $0x61707865 ++DATA ·constants+0x04(SB)/4, $0x3320646e ++DATA ·constants+0x08(SB)/4, $0x79622d32 ++DATA ·constants+0x0c(SB)/4, $0x6b206574 ++GLOBL ·constants(SB), NOPTR|RODATA, $32 ++ ++DATA ·incRotMatrix+0x00(SB)/4, $0x00000000 ++DATA ·incRotMatrix+0x04(SB)/4, $0x00000001 ++DATA ·incRotMatrix+0x08(SB)/4, $0x00000002 ++DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003 ++GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32 ++ ++#define NUM_ROUNDS 10 ++ ++// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) ++TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 ++ MOVV dst+0(FP), R4 ++ MOVV src+24(FP), R5 ++ MOVV src_len+32(FP), R6 ++ MOVV key+48(FP), R7 ++ MOVV nonce+56(FP), R8 ++ MOVV counter+64(FP), R9 ++ ++ MOVV $·constants(SB), R10 ++ MOVV $·incRotMatrix(SB), R11 ++ ++ MOVW (R9), R12 ++ ++loop: ++ MOVV $NUM_ROUNDS, R15 ++ // load 4-32bit data from incRotMatrix added to counter ++ VMOVQ (R11), V30 ++ ++ // load contants ++ // VLDREPL.W $0, R10, V0 ++ WORD $0x30200140 ++ // VLDREPL.W $1, R10, V1 ++ WORD $0x30200541 ++ // VLDREPL.W $2, R10, V2 ++ WORD $0x30200942 ++ // VLDREPL.W $3, R10, V3 ++ WORD $0x30200d43 ++ ++ // load keys ++ // VLDREPL.W $0, R7, V4 ++ WORD $0x302000e4 ++ // VLDREPL.W $1, R7, V5 ++ WORD $0x302004e5 ++ // VLDREPL.W $2, R7, V6 ++ WORD $0x302008e6 ++ // VLDREPL.W $3, R7, V7 ++ WORD $0x30200ce7 ++ // VLDREPL.W $4, R7, V8 ++ WORD $0x302010e8 ++ // VLDREPL.W $5, R7, V9 ++ WORD $0x302014e9 ++ // VLDREPL.W $6, R7, V10 ++ WORD $0x302018ea ++ // VLDREPL.W $7, R7, V11 ++ WORD $0x30201ceb ++ ++ // load counter + nonce ++ // VLDREPL.W $0, R9, V12 ++ WORD $0x3020012c ++ ++ // VLDREPL.W $0, R8, V13 ++ WORD $0x3020010d ++ // VLDREPL.W $1, R8, V14 ++ WORD $0x3020050e ++ // VLDREPL.W $2, R8, V15 ++ WORD $0x3020090f ++ ++ // update counter ++ VADDW V30, V12, V12 ++ ++chacha: ++ // V0..V3 += V4..V7 ++ // V12..V15 <<<= ((V12..V15 XOR V0..V3), 16) ++ VADDW V0, V4, V0 ++ VADDW V1, V5, V1 ++ VADDW V2, V6, V2 ++ VADDW V3, V7, V3 ++ VXORV V12, V0, V12 ++ VXORV V13, V1, V13 ++ VXORV V14, V2, V14 ++ VXORV V15, V3, V15 ++ VROTRW $16, V12, V12 ++ VROTRW $16, V13, V13 ++ VROTRW $16, V14, V14 ++ VROTRW $16, V15, V15 ++ ++ // V8..V11 += V12..V15 ++ // V4..V7 <<<= ((V4..V7 XOR V8..V11), 12) ++ VADDW V8, V12, V8 ++ VADDW V9, V13, V9 ++ VADDW V10, V14, V10 ++ VADDW V11, V15, V11 ++ VXORV V4, V8, V4 ++ VXORV V5, V9, V5 ++ VXORV V6, V10, V6 ++ VXORV V7, V11, V7 ++ VROTRW $20, V4, V4 ++ VROTRW $20, V5, V5 ++ VROTRW $20, V6, V6 ++ VROTRW $20, V7, V7 ++ ++ // V0..V3 += V4..V7 ++ // V12..V15 <<<= ((V12..V15 XOR V0..V3), 8) ++ VADDW V0, V4, V0 ++ VADDW V1, V5, V1 ++ VADDW V2, V6, V2 ++ VADDW V3, V7, V3 ++ VXORV V12, V0, V12 ++ VXORV V13, V1, V13 ++ VXORV V14, V2, V14 ++ VXORV V15, V3, V15 ++ VROTRW $24, V12, V12 ++ VROTRW $24, V13, V13 ++ VROTRW $24, V14, V14 ++ VROTRW $24, V15, V15 ++ ++ // V8..V11 += V12..V15 ++ // V4..V7 <<<= ((V4..V7 XOR V8..V11), 7) ++ VADDW V12, V8, V8 ++ VADDW V13, V9, V9 ++ VADDW V14, V10, V10 ++ VADDW V15, V11, V11 ++ VXORV V4, V8, V4 ++ VXORV V5, V9, V5 ++ VXORV V6, V10, V6 ++ VXORV V7, V11, V7 ++ VROTRW $25, V4, V4 ++ VROTRW $25, V5, V5 ++ VROTRW $25, V6, V6 ++ VROTRW $25, V7, V7 ++ ++ // V0..V3 += V5..V7, V4 ++ // V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16) ++ VADDW V0, V5, V0 ++ VADDW V1, V6, V1 ++ VADDW V2, V7, V2 ++ VADDW V3, V4, V3 ++ VXORV V15, V0, V15 ++ VXORV V12, V1, V12 ++ VXORV V13, V2, V13 ++ VXORV V14, V3, V14 ++ VROTRW $16, V15, V15 ++ VROTRW $16, V12, V12 ++ VROTRW $16, V13, V13 ++ VROTRW $16, V14, V14 ++ ++ // V10,V11,V8,V9 += V15,V12,V13,V14 ++ // V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 12) ++ VADDW V10, V15, V10 ++ VADDW V11, V12, V11 ++ VADDW V8, V13, V8 ++ VADDW V9, V14, V9 ++ VXORV V5, V10, V5 ++ VXORV V6, V11, V6 ++ VXORV V7, V8, V7 ++ VXORV V4, V9, V4 ++ VROTRW $20, V5, V5 ++ VROTRW $20, V6, V6 ++ VROTRW $20, V7, V7 ++ VROTRW $20, V4, V4 ++ ++ // V0..V3 += V5..V7, V4 ++ // V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 8) ++ VADDW V5, V0, V0 ++ VADDW V6, V1, V1 ++ VADDW V7, V2, V2 ++ VADDW V4, V3, V3 ++ VXORV V15, V0, V15 ++ VXORV V12, V1, V12 ++ VXORV V13, V2, V13 ++ VXORV V14, V3, V14 ++ VROTRW $24, V15, V15 ++ VROTRW $24, V12, V12 ++ VROTRW $24, V13, V13 ++ VROTRW $24, V14, V14 ++ ++ // V10,V11,V8,V9 += V15,V12,V13,V14 ++ // V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 7) ++ VADDW V15, V10, V10 ++ VADDW V12, V11, V11 ++ VADDW V13, V8, V8 ++ VADDW V14, V9, V9 ++ VXORV V5, V10, V5 ++ VXORV V6, V11, V6 ++ VXORV V7, V8, V7 ++ VXORV V4, V9, V4 ++ VROTRW $25, V5, V5 ++ VROTRW $25, V6, V6 ++ VROTRW $25, V7, V7 ++ VROTRW $25, V4, V4 ++ ++ SUBV $1, R15 ++ BNE R15, R0, chacha ++ ++ // load origin contants ++ // VLDREPL.W $0, R10, V16 ++ WORD $0x30200150 ++ // VLDREPL.W $1, R10, V17 ++ WORD $0x30200551 ++ // VLDREPL.W $2, R10, V18 ++ WORD $0x30200952 ++ // VLDREPL.W $3, R10, V19 ++ WORD $0x30200d53 ++ ++ // load origin keys ++ // VLDREPL.W $0, R7, V20 ++ WORD $0x302000f4 ++ // VLDREPL.W $1, R7, V21 ++ WORD $0x302004f5 ++ // VLDREPL.W $2, R7, V22 ++ WORD $0x302008f6 ++ // VLDREPL.W $3, R7, V23 ++ WORD $0x30200cf7 ++ // VLDREPL.W $4, R7, V24 ++ WORD $0x302010f8 ++ // VLDREPL.W $5, R7, V25 ++ WORD $0x302014f9 ++ // VLDREPL.W $6, R7, V26 ++ WORD $0x302018fa ++ // VLDREPL.W $7, R7, V27 ++ WORD $0x30201cfb ++ ++ // add back the initial state to generate the key stream ++ VADDW V30, V12, V12 // update counter in advance to prevent V30 from being overwritten ++ VADDW V16, V0, V0 ++ VADDW V17, V1, V1 ++ VADDW V18, V2, V2 ++ VADDW V19, V3, V3 ++ ++ // load origin counter + nonce ++ // VLDREPL.W $0, R9, V28 ++ WORD $0x3020013c ++ // VLDREPL.W $0, R8, V29 ++ WORD $0x3020011d ++ // VLDREPL.W $1, R8, V30 ++ WORD $0x3020051e ++ // VLDREPL.W $2, R8, V31 ++ WORD $0x3020091f ++ ++ VADDW V20, V4, V4 ++ VADDW V21, V5, V5 ++ VADDW V22, V6, V6 ++ VADDW V23, V7, V7 ++ VADDW V24, V8, V8 ++ VADDW V25, V9, V9 ++ VADDW V26, V10, V10 ++ VADDW V27, V11, V11 ++ VADDW V28, V12, V12 ++ VADDW V29, V13, V13 ++ VADDW V30, V14, V14 ++ VADDW V31, V15, V15 ++ ++ // shuffle ++ VILVLW V0, V1, V16 ++ VILVHW V0, V1, V17 ++ VILVLW V2, V3, V18 ++ VILVHW V2, V3, V19 ++ VILVLW V4, V5 ,V20 ++ VILVHW V4, V5, V21 ++ VILVLW V6, V7, V22 ++ VILVHW V6, V7, V23 ++ VILVLW V8, V9, V24 ++ VILVHW V8, V9, V25 ++ VILVLW V10, V11, V26 ++ VILVHW V10, V11, V27 ++ VILVLW V12, V13, V28 ++ VILVHW V12, V13, V29 ++ VILVLW V14, V15, V30 ++ VILVHW V14, V15, V31 ++ VILVLV V16, V18, V0 ++ VILVHV V16, V18, V4 ++ VILVLV V17, V19, V8 ++ VILVHV V17, V19, V12 ++ ++ // load src data from R5 ++ VMOVQ 0(R5), V16 ++ VMOVQ 16(R5), V17 ++ VMOVQ 32(R5), V18 ++ VMOVQ 48(R5), V19 ++ ++ VILVLV V20, V22, V1 ++ VILVHV V20, V22, V5 ++ VILVLV V21, V23, V9 ++ VILVHV V21, V23, V13 ++ ++ VMOVQ 64(R5), V20 ++ VMOVQ 80(R5), V21 ++ VMOVQ 96(R5), V22 ++ VMOVQ 112(R5), V23 ++ ++ VILVLV V24, V26, V2 ++ VILVHV V24, V26, V6 ++ VILVLV V25, V27, V10 ++ VILVHV V25, V27, V14 ++ ++ VMOVQ 128(R5), V24 ++ VMOVQ 144(R5), V25 ++ VMOVQ 160(R5), V26 ++ VMOVQ 176(R5), V27 ++ ++ VILVLV V28, V30, V3 ++ VILVHV V28, V30, V7 ++ VILVLV V29, V31, V11 ++ VILVHV V29, V31, V15 ++ ++ VMOVQ 192(R5), V28 ++ VMOVQ 208(R5), V29 ++ VMOVQ 224(R5), V30 ++ VMOVQ 240(R5), V31 ++ ++ VXORV V0, V16, V16 ++ VXORV V1, V17, V17 ++ VXORV V2, V18, V18 ++ VXORV V3, V19, V19 ++ ++ VMOVQ V16, 0(R4) ++ VMOVQ V17, 16(R4) ++ VMOVQ V18, 32(R4) ++ VMOVQ V19, 48(R4) ++ ++ VXORV V4, V20, V20 ++ VXORV V5, V21, V21 ++ VXORV V6, V22, V22 ++ VXORV V7, V23, V23 ++ ++ VMOVQ V20, 64(R4) ++ VMOVQ V21, 80(R4) ++ VMOVQ V22, 96(R4) ++ VMOVQ V23, 112(R4) ++ ++ VXORV V8, V24, V24 ++ VXORV V9, V25, V25 ++ VXORV V10, V26, V26 ++ VXORV V11, V27, V27 ++ ++ VMOVQ V24, 128(R4) ++ VMOVQ V25, 144(R4) ++ VMOVQ V26, 160(R4) ++ VMOVQ V27, 176(R4) ++ ++ VXORV V12, V28, V28 ++ VXORV V13, V29, V29 ++ VXORV V14, V30, V30 ++ VXORV V15, V31, V31 ++ ++ VMOVQ V28, 192(R4) ++ VMOVQ V29, 208(R4) ++ VMOVQ V30, 224(R4) ++ VMOVQ V31, 240(R4) ++ ++ ADD $4, R12, R12 ++ MOVW R12, (R9) // update counter ++ ++ ADDV $256, R4, R4 ++ ADDV $256, R5, R5 ++ SUBV $256, R6, R6 ++ BNE R6, R0, loop ++ ++ RET +diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/src/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go +index c709b72847..3853cc0e0b 100644 +--- a/src/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go ++++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego ++//go:build (!arm64 && !loong64 && !s390x && !ppc64 && !ppc64le) || !gc || purego + + package chacha20 + +-- +2.38.1 + diff --git a/0036-internal-bytealg-optimize-Count-String-in-loong64.patch b/0036-internal-bytealg-optimize-Count-String-in-loong64.patch new file mode 100644 index 0000000..3a513c2 --- /dev/null +++ b/0036-internal-bytealg-optimize-Count-String-in-loong64.patch @@ -0,0 +1,268 @@ +From 1698704d825764d2cbdbbf2718c582cf45d66fb0 Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Tue, 10 Dec 2024 21:06:28 +0800 +Subject: [PATCH 36/44] internal/bytealg: optimize Count{,String} in loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Benchmark on Loongson 3A6000 and 3A5000: + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +CountSingle/10 12.81n ± 0% 10.74n ± 0% -16.16% (p=0.000 n=10) +CountSingle/32 33.135n ± 0% 8.007n ± 0% -75.84% (p=0.000 n=10) +CountSingle/4K 4057.0n ± 0% 207.5n ± 0% -94.89% (p=0.000 n=10) +CountSingle/4M 4161.7µ ± 0% 217.1µ ± 0% -94.78% (p=0.000 n=10) +CountSingle/64M 68.722m ± 0% 3.717m ± 11% -94.59% (p=0.000 n=10) +geomean 13.76µ 1.705µ -87.61% + + | bench.old | bench.new | + | B/s | B/s vs base | +CountSingle/10 744.4Mi ± 0% 887.8Mi ± 0% +19.26% (p=0.000 n=10) +CountSingle/32 921.0Mi ± 0% 3811.5Mi ± 0% +313.84% (p=0.000 n=10) +CountSingle/4K 962.7Mi ± 0% 18825.3Mi ± 0% +1855.40% (p=0.000 n=10) +CountSingle/4M 961.2Mi ± 0% 18425.4Mi ± 0% +1817.02% (p=0.000 n=10) +CountSingle/64M 931.3Mi ± 0% 17216.0Mi ± 10% +1748.62% (p=0.000 n=10) +geomean 900.1Mi 7.092Gi +706.88% + +goos: linux +goarch: loong64 +pkg: bytes +cpu: Loongson-3A5000-HV @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +CountSingle/10 14.03n ± 1% 14.82n ± 0% +5.67% (p=0.000 n=10) +CountSingle/32 36.23n ± 0% 11.61n ± 0% -67.95% (p=0.000 n=10) +CountSingle/4K 4367.0n ± 0% 323.5n ± 0% -92.59% (p=0.000 n=10) +CountSingle/4M 4538.6µ ± 0% 381.2µ ± 0% -91.60% (p=0.000 n=10) +CountSingle/64M 76.575m ± 22% 7.971m ± 0% -89.59% (p=0.000 n=10) +geomean 15.05µ 2.790µ -81.46% + + | bench.old | bench.new | + | B/s | B/s vs base | +CountSingle/10 680.0Mi ± 1% 643.7Mi ± 0% -5.34% (p=0.000 n=10) +CountSingle/32 842.2Mi ± 0% 2628.4Mi ± 0% +212.07% (p=0.000 n=10) +CountSingle/4K 894.5Mi ± 0% 12075.4Mi ± 0% +1249.95% (p=0.000 n=10) +CountSingle/4M 881.3Mi ± 0% 10492.9Mi ± 0% +1090.57% (p=0.000 n=10) +CountSingle/64M 835.8Mi ± 18% 8028.7Mi ± 0% +860.61% (p=0.000 n=10) +geomean 822.9Mi 4.334Gi +439.27% + +Change-Id: I0a45139965b3e5eb09ab22be75145302f88a1915 +--- + src/internal/bytealg/bytealg.go | 3 + + src/internal/bytealg/count_loong64.s | 110 ++++++++++++++++++-------- + src/internal/cpu/cpu.go | 1 + + src/internal/cpu/cpu_loong64.go | 1 + + src/internal/cpu/cpu_loong64_hwcap.go | 2 + + 5 files changed, 85 insertions(+), 32 deletions(-) + +diff --git a/src/internal/bytealg/bytealg.go b/src/internal/bytealg/bytealg.go +index 6b79a2e1fa..a5f71ce342 100644 +--- a/src/internal/bytealg/bytealg.go ++++ b/src/internal/bytealg/bytealg.go +@@ -18,6 +18,9 @@ const ( + offsetS390xHasVX = unsafe.Offsetof(cpu.S390X.HasVX) + + offsetPPC64HasPOWER9 = unsafe.Offsetof(cpu.PPC64.IsPOWER9) ++ ++ offsetLOONG64HasLSX = unsafe.Offsetof(cpu.Loong64.HasLSX) ++ offsetLOONG64HasLASX = unsafe.Offsetof(cpu.Loong64.HasLASX) + ) + + // MaxLen is the maximum length of the string to be searched for (argument b) in Index. +diff --git a/src/internal/bytealg/count_loong64.s b/src/internal/bytealg/count_loong64.s +index db8ba2cb24..5c9dfeb0eb 100644 +--- a/src/internal/bytealg/count_loong64.s ++++ b/src/internal/bytealg/count_loong64.s +@@ -25,17 +25,81 @@ TEXT ·CountString(SB),NOSPLIT,$0-32 + // R5 = s_len + // R6 = byte to count + TEXT countbody<>(SB),NOSPLIT,$0 +- MOVV R0, R7 // count +- ADDV R4, R5 // end ++ MOVV R0, R7 // count ++ ++ // short path to handle 0-byte case ++ BEQ R5, done ++ ++ // jump directly to tail length < 4 ++ MOVV $4, R8 ++ BLT R5, R8, tail ++ ++ // jump directly to genericCountBody if length < 16 ++ MOVV $16, R8 ++ BLT R5, R8, genericCountBody ++ ++ // jump directly to lsxCountBody if length < 64 ++ MOVV $64, R8 ++ BLT R5, R8, lsxCountBody ++lasxCountBody: ++ MOVBU internal∕cpu·Loong64+const_offsetLOONG64HasLASX(SB), R8 ++ BEQ R8, lsxCountBody ++ MOVV $32, R9 ++ XVMOVQ R6, X0.B32 ++ ++ PCALIGN $16 ++lasxLoop: ++ XVMOVQ (R4), X1 ++ XVSEQB X0, X1, X2 ++ XVANDB $1, X2, X2 ++ XVPCNTV X2, X3 ++ XVMOVQ X3.V[0], R8 ++ ADDV R8, R7 ++ XVMOVQ X3.V[1], R8 ++ ADDV R8, R7 ++ XVMOVQ X3.V[2], R8 ++ ADDV R8, R7 ++ XVMOVQ X3.V[3], R8 ++ ADDV R8, R7 ++ ADDV $-32, R5 ++ ADDV $32, R4 ++ BGE R5, R9, lasxLoop ++ ++lsxCountBody: ++ MOVBU internal∕cpu·Loong64+const_offsetLOONG64HasLSX(SB), R8 ++ BEQ R8, genericCountBody ++ // jump directly to genericCountBody if length < 16 ++ MOVV $16, R9 ++ BLT R5, R9, genericCountBody ++ VMOVQ R6, V0.B16 ++ ++ PCALIGN $16 ++lsxLoop: ++ VMOVQ (R4), V1 ++ VSEQB V0, V1, V2 ++ VANDB $1, V2, V2 ++ VPCNTV V2, V3 ++ VMOVQ V3.V[0], R8 ++ ADDV R8, R7 ++ VMOVQ V3.V[1], R8 ++ ADDV R8, R7 ++ ADDV $-16, R5 ++ ADDV $16, R4 ++ BGE R5, R9, lsxLoop ++ ++ // Work with genericCountBody shorter than 16 bytes ++genericCountBody: ++ MOVV $4, R9 + MOVV $1, R17 + +-loop: +- ADDV $8, R4, R9 ++ PCALIGN $16 ++genericLoop: + BLT R5, R9, tail +- MOVV (R4), R8 ++ ADDV $-4, R5 ++ MOVWU (R4)(R5), R8 + + AND $0xff, R8, R10 +- WORD $0xcf210b // bstrpick.w r11, r8, 15, 8 ++ BSTRPICKW $15, R8, $8, R11 + XOR R6, R10, R10 + XOR R6, R11, R11 + MASKNEZ R10, R17, R12 +@@ -43,8 +107,8 @@ loop: + ADDV R7, R12, R7 + ADDV R7, R13, R7 + +- WORD $0xd7410a // bstrpick.w r10, r8, 23, 16 +- WORD $0xdf610b // bstrpick.w r11, r8, 31, 24 ++ BSTRPICKW $23, R8, $16, R10 ++ BSTRPICKW $31, R8, $24, R11 + XOR R6, R10, R10 + XOR R6, R11, R11 + MASKNEZ R10, R17, R12 +@@ -52,35 +116,17 @@ loop: + ADDV R7, R12, R7 + ADDV R7, R13, R7 + +- WORD $0xe7810a // bstrpick.w r10, r8, 39, 32 +- WORD $0xefa10b // bstrpick.w r11, r8, 47, 40 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 +- +- WORD $0xf7c10a // bstrpick.w r10, r8, 55, 48 +- WORD $0xffe10b // bstrpick.w r11, r8, 63, 56 +- XOR R6, R10, R10 +- XOR R6, R11, R11 +- MASKNEZ R10, R17, R12 +- MASKNEZ R11, R17, R13 +- ADDV R7, R12, R7 +- ADDV R7, R13, R7 +- +- MOVV R9, R4 +- JMP loop ++ JMP genericLoop + ++ // Work with tail shorter than 4 bytes ++ PCALIGN $16 + tail: +- BEQ R4, R5, done +- MOVBU (R4), R8 +- ADDV $1, R4 ++ BEQ R5, done ++ ADDV $-1, R5 ++ MOVBU (R4)(R5), R8 + BNE R6, R8, tail + ADDV $1, R7 + JMP tail +- + done: + MOVV R7, R4 + RET +diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go +index cd3db10523..2443b31fc8 100644 +--- a/src/internal/cpu/cpu.go ++++ b/src/internal/cpu/cpu.go +@@ -83,6 +83,7 @@ var ARM64 struct { + var Loong64 struct { + _ CacheLinePad + HasLSX bool // support 128-bit vector extension ++ HasLASX bool // support 256-bit vector extension + HasCRC32 bool // support CRC instruction + HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} + HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction +diff --git a/src/internal/cpu/cpu_loong64.go b/src/internal/cpu/cpu_loong64.go +index 92583d0bca..9a58ea251c 100644 +--- a/src/internal/cpu/cpu_loong64.go ++++ b/src/internal/cpu/cpu_loong64.go +@@ -27,6 +27,7 @@ func get_cpucfg(reg uint32) uint32 + func doinit() { + options = []option{ + {Name: "lsx", Feature: &Loong64.HasLSX}, ++ {Name: "lasx", Feature: &Loong64.HasLASX}, + {Name: "crc32", Feature: &Loong64.HasCRC32}, + {Name: "lamcas", Feature: &Loong64.HasLAMCAS}, + {Name: "lam_bh", Feature: &Loong64.HasLAM_BH}, +diff --git a/src/internal/cpu/cpu_loong64_hwcap.go b/src/internal/cpu/cpu_loong64_hwcap.go +index 58397adae8..6c6b8a81f2 100644 +--- a/src/internal/cpu/cpu_loong64_hwcap.go ++++ b/src/internal/cpu/cpu_loong64_hwcap.go +@@ -13,12 +13,14 @@ var HWCap uint + // HWCAP bits. These are exposed by the Linux kernel. + const ( + hwcap_LOONGARCH_LSX = 1 << 4 ++ hwcap_LOONGARCH_LASX = 1 << 5 + ) + + func hwcapInit() { + // TODO: Features that require kernel support like LSX and LASX can + // be detected here once needed in std library or by the compiler. + Loong64.HasLSX = hwcIsSet(HWCap, hwcap_LOONGARCH_LSX) ++ Loong64.HasLASX = hwcIsSet(HWCap, hwcap_LOONGARCH_LASX) + } + + func hwcIsSet(hwc uint, val uint) bool { +-- +2.38.1 + diff --git a/0037-cmd-internal-obj-cmd-asm-reclassify-32-bit-immediate.patch b/0037-cmd-internal-obj-cmd-asm-reclassify-32-bit-immediate.patch new file mode 100644 index 0000000..ccad194 --- /dev/null +++ b/0037-cmd-internal-obj-cmd-asm-reclassify-32-bit-immediate.patch @@ -0,0 +1,690 @@ +From a713105842cd7b88dbb573980731062c218a8310 Mon Sep 17 00:00:00 2001 +From: limeidan +Date: Mon, 16 Dec 2024 16:31:37 +0800 +Subject: [PATCH 37/44] cmd/internal/obj, cmd/asm: reclassify 32-bit immediate + value + +Change-Id: If9fd257ca0837a8c8597889c4f5ed3d4edc602c1 +--- + .../asm/internal/asm/testdata/loong64enc1.s | 4 +- + .../asm/internal/asm/testdata/loong64enc2.s | 2 +- + src/cmd/internal/obj/loong64/a.out.go | 31 +- + src/cmd/internal/obj/loong64/asm.go | 376 +++++++----------- + src/cmd/internal/obj/loong64/cnames.go | 25 +- + 5 files changed, 186 insertions(+), 252 deletions(-) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index 19070c89ef..b40d86e596 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -107,8 +107,8 @@ lable2: + MOVV $4(R4), R5 // 8510c002 + MOVW $-1, R4 // 04fcff02 + MOVV $-1, R4 // 04fcff02 +- MOVW $1, R4 // 0404c002 +- MOVV $1, R4 // 0404c002 ++ MOVW $1, R4 // 04048003 ++ MOVV $1, R4 // 04048003 + ADD $-1, R4, R5 // 85fcbf02 + ADD $-1, R4 // 84fcbf02 + ADDV $-1, R4, R5 // 85fcff02 +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc2.s b/src/cmd/asm/internal/asm/testdata/loong64enc2.s +index ee3bad74b1..91aed4e2c7 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc2.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc2.s +@@ -12,7 +12,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 + AND $-1, R4, R5 // 1efcbf0285f81400 + AND $-1, R4 // 1efcbf0284f81400 + MOVW $-1, F4 // 1efcbf02c4a71401 +- MOVW $1, F4 // 1e048002c4a71401 ++ MOVW $1, F4 // 1e048003c4a71401 + TEQ $4, R4, R5 // 8508005c04002a00 + TEQ $4, R4 // 0408005c04002a00 + TNE $4, R4, R5 // 8508005804002a00 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index 1fadbc648a..f2d4c41d68 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -325,19 +325,26 @@ const ( + C_XREG + C_ARNG // Vn. + C_ELEM // Vn.[index] ++ + C_ZCON +- C_SCON // 12 bit signed +- C_UCON // 32 bit signed, low 12 bits 0 +- +- // When the immediate value is SCON, it can choose either the ADDCON implementation +- // or the ANDCON implementation, using ADD0CON/AND0CON to distinguish them, so that +- // the program can choose the implementation with fewer instructions. +- C_ADD0CON +- C_AND0CON +- +- C_ADDCON // -0x800 <= v < 0 +- C_ANDCON // 0 < v <= 0xFFF +- C_LCON // other 32 ++ C_U1CON // 1 bit unsigned constant ++ C_U2CON // 2 bit unsigned constant ++ C_U3CON // 3 bit unsigned constant ++ C_U4CON // 4 bit unsigned constant ++ C_U5CON // 5 bit unsigned constant ++ C_U6CON // 6 bit unsigned constant ++ C_U7CON // 7 bit unsigned constant ++ C_U8CON // 8 bit unsigned constant ++ C_S5CON // 5 bit signed constant ++ C_US12CON // same as C_S12CON, increase the priority of C_S12CON in special cases. ++ C_UU12CON // same as C_U12CON, increase the priority of C_U12CON in special cases. ++ C_S12CON // 12 bit signed constant, -0x800 < v <= 0x7ff ++ C_U12CON // 12 bit unsigned constant, 0 < v <= 0xfff ++ C_12CON // 12 bit signed constant, or 12 bit unsigned constant ++ C_U15CON // 15 bit unsigned constant ++ C_15CON20_0 // 15 bit unsigned constant, low 12 bits 0 ++ C_32CON20_0 // 32 bit signed, low 12 bits 0 ++ C_32CON // other 32 bit signed + + // 64 bit signed, lo32 bits 0, hi20 bits are not 0, hi12 bits can + // be obtained by sign extension of the hi20 bits. +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 657d32ae81..2480cf9382 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -51,12 +51,6 @@ const ( + // branchLoopHead marks loop entry. + // Used to insert padding for under-aligned loops. + branchLoopHead +- immFiledSi5 // The encoding of the immediate field in the instruction is 5-bits +- immFiledUi3 // The encoding of the immediate field in the instruction is 3-bits +- immFiledUi4 // The encoding of the immediate field in the instruction is 4-bits +- immFiledUi5 // The encoding of the immediate field in the instruction is 5-bits +- immFiledUi6 // The encoding of the immediate field in the instruction is 6-bits +- immFiledUi8 // The encoding of the immediate field in the instruction is 8-bits + ) + + var optab = []Optab{ +@@ -94,45 +88,41 @@ var optab = []Optab{ + {ACMPEQF, C_FREG, C_FREG, C_NONE, C_FCCREG, C_NONE, 2, 4, 0, 0}, + {AVSEQB, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSEQB, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVSEQB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 13, 4, 0, immFiledSi5}, +- {AXVSEQB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 13, 4, 0, immFiledSi5}, +- {AVSEQB, C_ADDCON, C_VREG, C_NONE, C_VREG, C_NONE, 13, 4, 0, immFiledSi5}, +- {AXVSEQB, C_ADDCON, C_XREG, C_NONE, C_XREG, C_NONE, 13, 4, 0, immFiledSi5}, ++ {AVSEQB, C_S5CON, C_VREG, C_NONE, C_VREG, C_NONE, 13, 4, 0, 0}, ++ {AXVSEQB, C_S5CON, C_XREG, C_NONE, C_XREG, C_NONE, 13, 4, 0, 0}, + + {AVANDV, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVANDV, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVANDB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, immFiledUi8}, +- {AXVANDB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, immFiledUi8}, +- {AVANDB, C_ADDCON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, immFiledUi8}, +- {AXVANDB, C_ADDCON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, immFiledUi8}, ++ {AVANDB, C_U8CON, C_VREG, C_NONE, C_VREG, C_NONE, 14, 4, 0, 0}, ++ {AXVANDB, C_U8CON, C_XREG, C_NONE, C_XREG, C_NONE, 14, 4, 0, 0}, + + {AVSLLB, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSLLB, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVSLLB, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 29, 4, 0, immFiledUi3}, +- {AXVSLLB, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 29, 4, 0, immFiledUi3}, +- {AVSLLB, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 29, 4, 0, immFiledUi3}, +- {AXVSLLB, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 29, 4, 0, immFiledUi3}, ++ {AVSLLB, C_U3CON, C_VREG, C_NONE, C_VREG, C_NONE, 29, 4, 0, 0}, ++ {AXVSLLB, C_U3CON, C_XREG, C_NONE, C_XREG, C_NONE, 29, 4, 0, 0}, ++ {AVSLLB, C_U3CON, C_NONE, C_NONE, C_VREG, C_NONE, 29, 4, 0, 0}, ++ {AXVSLLB, C_U3CON, C_NONE, C_NONE, C_XREG, C_NONE, 29, 4, 0, 0}, + + {AVSLLH, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSLLH, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVSLLH, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 31, 4, 0, immFiledUi4}, +- {AXVSLLH, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 31, 4, 0, immFiledUi4}, +- {AVSLLH, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 31, 4, 0, immFiledUi4}, +- {AXVSLLH, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 31, 4, 0, immFiledUi4}, ++ {AVSLLH, C_U4CON, C_VREG, C_NONE, C_VREG, C_NONE, 31, 4, 0, 0}, ++ {AXVSLLH, C_U4CON, C_XREG, C_NONE, C_XREG, C_NONE, 31, 4, 0, 0}, ++ {AVSLLH, C_U4CON, C_NONE, C_NONE, C_VREG, C_NONE, 31, 4, 0, 0}, ++ {AXVSLLH, C_U4CON, C_NONE, C_NONE, C_XREG, C_NONE, 31, 4, 0, 0}, + + {AVSLLW, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSLLW, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVSLLW, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 32, 4, 0, immFiledUi5}, +- {AXVSLLW, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 32, 4, 0, immFiledUi5}, +- {AVSLLW, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 32, 4, 0, immFiledUi5}, +- {AXVSLLW, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 32, 4, 0, immFiledUi5}, ++ {AVSLLW, C_U5CON, C_VREG, C_NONE, C_VREG, C_NONE, 32, 4, 0, 0}, ++ {AXVSLLW, C_U5CON, C_XREG, C_NONE, C_XREG, C_NONE, 32, 4, 0, 0}, ++ {AVSLLW, C_U5CON, C_NONE, C_NONE, C_VREG, C_NONE, 32, 4, 0, 0}, ++ {AXVSLLW, C_U5CON, C_NONE, C_NONE, C_XREG, C_NONE, 32, 4, 0, 0}, + + {AVSLLV, C_VREG, C_VREG, C_NONE, C_VREG, C_NONE, 2, 4, 0, 0}, + {AXVSLLV, C_XREG, C_XREG, C_NONE, C_XREG, C_NONE, 2, 4, 0, 0}, +- {AVSLLV, C_SCON, C_VREG, C_NONE, C_VREG, C_NONE, 33, 4, 0, immFiledUi6}, +- {AXVSLLV, C_SCON, C_XREG, C_NONE, C_XREG, C_NONE, 33, 4, 0, immFiledUi6}, +- {AVSLLV, C_SCON, C_NONE, C_NONE, C_VREG, C_NONE, 33, 4, 0, immFiledUi6}, +- {AXVSLLV, C_SCON, C_NONE, C_NONE, C_XREG, C_NONE, 33, 4, 0, immFiledUi6}, ++ {AVSLLV, C_U6CON, C_VREG, C_NONE, C_VREG, C_NONE, 33, 4, 0, 0}, ++ {AXVSLLV, C_U6CON, C_XREG, C_NONE, C_XREG, C_NONE, 33, 4, 0, 0}, ++ {AVSLLV, C_U6CON, C_NONE, C_NONE, C_VREG, C_NONE, 33, 4, 0, 0}, ++ {AXVSLLV, C_U6CON, C_NONE, C_NONE, C_XREG, C_NONE, 33, 4, 0, 0}, + + {ACLOW, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 9, 4, 0, 0}, + {AABSF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0}, +@@ -229,48 +219,46 @@ var optab = []Optab{ + + {AMOVW, C_LACON, C_NONE, C_NONE, C_REG, C_NONE, 26, 12, REGSP, 0}, + {AMOVV, C_LACON, C_NONE, C_NONE, C_REG, C_NONE, 26, 12, REGSP, 0}, +- {AMOVW, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, +- {AMOVV, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, +- {AMOVW, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, +- {AMOVV, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, +- +- {AMOVW, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0}, +- {AMOVV, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0}, +- {AMOVW, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, +- {AMOVV, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, ++ {AMOVW, C_12CON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, ++ {AMOVV, C_12CON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0}, ++ ++ {AMOVW, C_32CON20_0, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0}, ++ {AMOVV, C_32CON20_0, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0}, ++ {AMOVW, C_32CON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, ++ {AMOVV, C_32CON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP}, + {AMOVV, C_DCON12_0, C_NONE, C_NONE, C_REG, C_NONE, 67, 4, 0, NOTUSETMP}, + {AMOVV, C_DCON12_20S, C_NONE, C_NONE, C_REG, C_NONE, 68, 8, 0, NOTUSETMP}, + {AMOVV, C_DCON32_12S, C_NONE, C_NONE, C_REG, C_NONE, 69, 12, 0, NOTUSETMP}, + {AMOVV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 59, 16, 0, NOTUSETMP}, + +- {AADD, C_ADD0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AADD, C_ADD0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AADD, C_ANDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- {AADD, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- +- {AADDV, C_ADD0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AADDV, C_ADD0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AADDV, C_ANDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- {AADDV, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- +- {AAND, C_AND0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AAND, C_AND0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, +- {AAND, C_ADDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- {AAND, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, +- +- {AADD, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- {AADD, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- {AADDV, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- {AADDV, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- {AAND, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- {AAND, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, +- +- {AADD, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, +- {AADDV, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, +- {AAND, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, +- {AADD, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, +- {AADDV, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, +- {AAND, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AADD, C_US12CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AADD, C_US12CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AADD, C_U12CON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ {AADD, C_U12CON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ ++ {AADDV, C_US12CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AADDV, C_US12CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AADDV, C_U12CON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ {AADDV, C_U12CON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ ++ {AAND, C_UU12CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AAND, C_UU12CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0}, ++ {AAND, C_S12CON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ {AAND, C_S12CON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0}, ++ ++ {AADD, C_32CON20_0, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ {AADD, C_32CON20_0, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ {AADDV, C_32CON20_0, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ {AADDV, C_32CON20_0, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ {AAND, C_32CON20_0, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ {AAND, C_32CON20_0, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0}, ++ ++ {AADD, C_32CON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AADDV, C_32CON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AAND, C_32CON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AADD, C_32CON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AADDV, C_32CON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, ++ {AAND, C_32CON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0}, + + {AADDV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, + {AADDV, C_DCON, C_REG, C_NONE, C_REG, C_NONE, 60, 20, 0, 0}, +@@ -289,18 +277,18 @@ var optab = []Optab{ + {AAND, C_DCON32_12S, C_NONE, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, + {AAND, C_DCON32_12S, C_REG, C_NONE, C_REG, C_NONE, 72, 16, 0, 0}, + +- {ASLL, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, +- {ASLL, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, ++ {ASLL, C_U5CON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, ++ {ASLL, C_U5CON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, + +- {ASLLV, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, +- {ASLLV, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, ++ {ASLLV, C_U6CON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, ++ {ASLLV, C_U6CON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0}, + +- {ABSTRPICKW, C_SCON, C_REG, C_SCON, C_REG, C_NONE, 17, 4, 0, 0}, +- {ABSTRPICKW, C_SCON, C_REG, C_ZCON, C_REG, C_NONE, 17, 4, 0, 0}, ++ {ABSTRPICKW, C_U6CON, C_REG, C_U6CON, C_REG, C_NONE, 17, 4, 0, 0}, ++ {ABSTRPICKW, C_U6CON, C_REG, C_ZCON, C_REG, C_NONE, 17, 4, 0, 0}, + {ABSTRPICKW, C_ZCON, C_REG, C_ZCON, C_REG, C_NONE, 17, 4, 0, 0}, + + {ASYSCALL, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0}, +- {ASYSCALL, C_ANDCON, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0}, ++ {ASYSCALL, C_U15CON, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0}, + + {ABEQ, C_REG, C_REG, C_NONE, C_BRAN, C_NONE, 6, 4, 0, 0}, + {ABEQ, C_REG, C_NONE, C_NONE, C_BRAN, C_NONE, 6, 4, 0, 0}, +@@ -348,8 +336,7 @@ var optab = []Optab{ + {AMOVV, C_FREG, C_NONE, C_NONE, C_FCCREG, C_NONE, 30, 4, 0, 0}, + {AMOVV, C_FCCREG, C_NONE, C_NONE, C_FREG, C_NONE, 30, 4, 0, 0}, + +- {AMOVW, C_ADDCON, C_NONE, C_NONE, C_FREG, C_NONE, 34, 8, 0, 0}, +- {AMOVW, C_ANDCON, C_NONE, C_NONE, C_FREG, C_NONE, 34, 8, 0, 0}, ++ {AMOVW, C_12CON, C_NONE, C_NONE, C_FREG, C_NONE, 34, 8, 0, 0}, + + {AMOVB, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0}, + {AMOVW, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0}, +@@ -363,13 +350,13 @@ var optab = []Optab{ + {AMOVBU, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0}, + {AMOVWU, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0}, + +- {AWORD, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0}, ++ {AWORD, C_32CON, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0}, + {AWORD, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 61, 4, 0, 0}, + + {AMOVV, C_GOTADDR, C_NONE, C_NONE, C_REG, C_NONE, 65, 8, 0, 0}, + +- {ATEQ, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, +- {ATEQ, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, ++ {ATEQ, C_US12CON, C_REG, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, ++ {ATEQ, C_US12CON, C_NONE, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, + + {ARDTIMELW, C_NONE, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0}, + {AAMSWAPW, C_REG, C_NONE, C_NONE, C_ZOREG, C_REG, 66, 4, 0, 0}, +@@ -409,12 +396,12 @@ var optab = []Optab{ + + {AVMOVQ, C_ELEM, C_NONE, C_NONE, C_ARNG, C_NONE, 45, 4, 0, 0}, + +- {obj.APCALIGN, C_SCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, +- {obj.APCDATA, C_LCON, C_NONE, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0}, ++ {obj.APCALIGN, C_U12CON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, ++ {obj.APCDATA, C_32CON, C_NONE, C_NONE, C_32CON, C_NONE, 0, 0, 0, 0}, + {obj.APCDATA, C_DCON, C_NONE, C_NONE, C_DCON, C_NONE, 0, 0, 0, 0}, +- {obj.AFUNCDATA, C_SCON, C_NONE, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0}, ++ {obj.AFUNCDATA, C_U12CON, C_NONE, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0}, + {obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, +- {obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, // nop variants, see #40689 ++ {obj.ANOP, C_32CON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, // nop variants, see #40689 + {obj.ANOP, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, // nop variants, see #40689 + {obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, + {obj.ANOP, C_FREG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, +@@ -857,34 +844,35 @@ func (c *ctxt0) aclass(a *obj.Addr) int { + } + + if c.instoffset >= 0 { +- if c.instoffset == 0 { +- return C_ZCON +- } +- if c.instoffset <= 0x7ff { +- return C_SCON +- } +- if c.instoffset <= 0xfff { +- return C_ANDCON +- } +- if c.instoffset&0xfff == 0 && isuint32(uint64(c.instoffset)) { // && ((instoffset & (1<<31)) == 0) +- return C_UCON ++ sbits := bits.Len64(uint64(c.instoffset)) ++ switch { ++ case sbits <=8: ++ return C_ZCON + sbits ++ case sbits <= 12: ++ if c.instoffset <= 0x7ff { ++ return C_US12CON ++ } ++ return C_U12CON ++ case sbits <= 15: ++ if c.instoffset & 0xfff == 0 { ++ return C_15CON20_0 ++ } ++ return C_U15CON + } +- if isint32(c.instoffset) || isuint32(uint64(c.instoffset)) { +- return C_LCON ++ } else { ++ sbits := bits.Len64(uint64(^c.instoffset)) ++ switch { ++ case sbits < 5: ++ return C_S5CON ++ case sbits < 12: ++ return C_S12CON + } +- return C_LCON + } + +- if c.instoffset >= -0x800 { +- return C_ADDCON ++ if c.instoffset&0xfff == 0 { ++ return C_32CON20_0 + } +- if c.instoffset&0xfff == 0 && isint32(c.instoffset) { +- return C_UCON +- } +- if isint32(c.instoffset) { +- return C_LCON +- } +- return C_LCON ++ return C_32CON + + case obj.TYPE_BRANCH: + return C_BRAN +@@ -1130,10 +1118,11 @@ func (c *ctxt0) oplook(p *obj.Prog) *Optab { + + ops := oprange[p.As&obj.AMask] + c1 := &xcmp[a1] ++ c3 := &xcmp[a3] + c4 := &xcmp[a4] + for i := range ops { + op := &ops[i] +- if (int(op.reg) == a2) && int(op.from3) == a3 && c1[op.from1] && c4[op.to1] && (int(op.to2) == a5) { ++ if (int(op.reg) == a2) && c3[op.from3] && c1[op.from1] && c4[op.to1] && (int(op.to2) == a5) { + p.Optab = uint16(cap(optab) - cap(ops) + i + 1) + return op + } +@@ -1151,21 +1140,41 @@ func cmp(a int, b int) bool { + } + switch a { + case C_DCON: +- if b == C_LCON || b == C_DCON32_0 || +- b == C_DCON12_0 || b == C_DCON20S_0 || +- b == C_DCON12_20S || b == C_DCON12_12S || +- b == C_DCON20S_20 || b == C_DCON32_20 || +- b == C_DCON20S_12S || b == C_DCON32_12S || +- b == C_DCON12_32S || b == C_DCON20S_32 || +- b == C_DCON12_12U || b == C_DCON20S_12U || +- b == C_DCON32_12U { +- return true +- } +- fallthrough +- case C_LCON: +- if b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON { +- return true +- } ++ return cmp(C_32CON, b) || cmp(C_DCON12_20S, b) || cmp(C_DCON32_12S, b) || b == C_DCON12_0 ++ case C_32CON: ++ return cmp(C_32CON20_0, b) || cmp(C_U15CON, b) || cmp(C_S12CON, b) ++ case C_32CON20_0: ++ return b == C_15CON20_0 || b == C_ZCON ++ case C_U15CON: ++ return cmp(C_U12CON, b) || b == C_15CON20_0 ++ case C_12CON: ++ return cmp(C_U12CON, b) || cmp(C_S12CON, b) ++ case C_UU12CON: ++ return cmp(C_U12CON, b) ++ case C_U12CON: ++ return cmp(C_U8CON, b) || b == C_US12CON ++ case C_U8CON: ++ return cmp(C_U7CON, b) ++ case C_U7CON: ++ return cmp(C_U6CON, b) ++ case C_U6CON: ++ return cmp(C_U5CON, b) ++ case C_U5CON: ++ return cmp(C_U4CON, b) ++ case C_U4CON: ++ return cmp(C_U3CON, b) ++ case C_U3CON: ++ return cmp(C_U2CON, b) ++ case C_U2CON: ++ return cmp(C_U1CON, b) ++ case C_U1CON: ++ return cmp(C_ZCON, b) ++ case C_US12CON: ++ return cmp(C_S12CON, b) ++ case C_S12CON: ++ return cmp(C_S5CON, b) || cmp(C_U8CON, b) || b == C_US12CON ++ case C_S5CON: ++ return cmp(C_ZCON, b) || cmp(C_U4CON, b) + + case C_DCON12_0: + +@@ -1183,62 +1192,20 @@ func cmp(a int, b int) bool { + return true + } + +- case C_ADD0CON: +- if b == C_ADDCON { +- return true +- } +- fallthrough +- +- case C_ADDCON: +- if b == C_ZCON || b == C_SCON { +- return true +- } +- +- case C_AND0CON: +- if b == C_ANDCON { +- return true +- } +- fallthrough +- +- case C_ANDCON: +- if b == C_ZCON || b == C_SCON { +- return true +- } +- +- case C_UCON: +- if b == C_ZCON { +- return true +- } +- +- case C_SCON: +- if b == C_ZCON { +- return true +- } +- + case C_LACON: +- if b == C_SACON { +- return true +- } ++ return b == C_SACON + + case C_LAUTO: +- if b == C_SAUTO { +- return true +- } ++ return b == C_SAUTO + + case C_REG: +- if b == C_ZCON { +- return true +- } ++ return b == C_ZCON + + case C_LOREG: +- if b == C_ZOREG || b == C_SOREG { +- return true +- } ++ return b == C_ZOREG || b == C_SOREG + + case C_SOREG: +- if b == C_ZOREG { +- return true +- } ++ return b == C_ZOREG + } + + return false +@@ -1881,7 +1848,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(o.param) + } + a := add +- if o.from1 == C_ANDCON { ++ if o.from1 == C_12CON && v > 0 { + a = AOR + } + +@@ -2008,15 +1975,9 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + if r == 0 { + r = int(p.To.Reg) + } +- +- switch o.flag { +- case immFiledSi5: +- c.checkimmFiled(p, v, 5, true) +- o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } +- ++ ++ o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) ++ + case 14: // add $ui8,[r1],r2 + v := c.regoff(&p.From) + r := int(p.Reg) +@@ -2024,13 +1985,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(p.To.Reg) + } + +- switch o.flag { +- case immFiledUi8: +- c.checkimmFiled(p, v, 8, false) +- o1 = OP_8IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } ++ o1 = OP_8IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 15: // teq $c r,r + v := c.regoff(&p.From) +@@ -2185,13 +2140,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(p.To.Reg) + } + +- switch o.flag { +- case immFiledUi3: +- c.checkimmFiled(p, v, 3, false) +- o1 = OP_3IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } ++ o1 = OP_3IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 30: // mov gr/fr/fcc/fcsr, fr/fcc/fcsr/gr + a := c.specialFpMovInst(p.As, oclass(&p.From), oclass(&p.To)) +@@ -2204,13 +2153,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(p.To.Reg) + } + +- switch o.flag { +- case immFiledUi4: +- c.checkimmFiled(p, v, 4, false) +- o1 = OP_4IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } ++ o1 = OP_4IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 32: // add $ui5,[r1],r2 + v := c.regoff(&p.From) +@@ -2219,13 +2162,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(p.To.Reg) + } + +- switch o.flag { +- case immFiledUi5: +- c.checkimmFiled(p, v, 5, false) +- o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } ++ o1 = OP_5IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 33: // add $ui6,[r1],r2 + v := c.regoff(&p.From) +@@ -2234,18 +2171,12 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { + r = int(p.To.Reg) + } + +- switch o.flag { +- case immFiledUi6: +- c.checkimmFiled(p, v, 6, false) +- o1 = OP_6IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) +- default: +- c.ctxt.Diag("Invalid immediate value type\n%v", p) +- } ++ o1 = OP_6IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg)) + + case 34: // mov $con,fr + v := c.regoff(&p.From) + a := AADDU +- if o.from1 == C_ANDCON { ++ if v > 0 { + a = AOR + } + a2 := c.specialFpMovInst(p.As, C_REG, oclass(&p.To)) +@@ -2702,21 +2633,6 @@ func (c *ctxt0) checkindex(p *obj.Prog, index uint32, mask uint32) { + } + } + +-// checkimmFiled checks whether the immediate value exceeds the valid encoding range +-func (c *ctxt0) checkimmFiled(p *obj.Prog, imm int32, bits uint8, isSigned bool) { +- if isSigned { +- bound := int32(1 << (bits - 1)) +- if imm < -bound || imm > bound { +- c.ctxt.Diag("signed immediate %v exceeds the %d-bit range: %v", imm, bits, p) +- } +- } else { +- mask := uint32(0xffffffff) << bits +- if uint32(imm) != (uint32(imm) & ^mask) { +- c.ctxt.Diag("unsigned immediate %v exceeds the %d-bit range: %v", imm, bits, p) +- } +- } +-} +- + func (c *ctxt0) vregoff(a *obj.Addr) int64 { + c.instoffset = 0 + c.aclass(a) +diff --git a/src/cmd/internal/obj/loong64/cnames.go b/src/cmd/internal/obj/loong64/cnames.go +index a2f04a22ee..1d38f1ee36 100644 +--- a/src/cmd/internal/obj/loong64/cnames.go ++++ b/src/cmd/internal/obj/loong64/cnames.go +@@ -14,13 +14,24 @@ var cnames0 = []string{ + "ARNG", + "ELEM", + "ZCON", +- "SCON", +- "UCON", +- "ADD0CON", +- "AND0CON", +- "ADDCON", +- "ANDCON", +- "LCON", ++ "U1CON", ++ "U2CON", ++ "U3CON", ++ "U4CON", ++ "U5CON", ++ "U6CON", ++ "U7CON", ++ "U8CON", ++ "S5CON", ++ "US12CON", ++ "UU12CON", ++ "S12CON", ++ "U12CON", ++ "12CON", ++ "U15CON", ++ "15CON20_0", ++ "32CON20_0", ++ "32CON", + "DCON20S_0", + "DCON12_0", + "DCON32_0", +-- +2.38.1 + diff --git a/0038-crypto-internal-poly1305-implement-function-update-i.patch b/0038-crypto-internal-poly1305-implement-function-update-i.patch new file mode 100644 index 0000000..e18caf2 --- /dev/null +++ b/0038-crypto-internal-poly1305-implement-function-update-i.patch @@ -0,0 +1,298 @@ +From 9e01e315f3ea08fc01854bf8beb2cdeb9ff6dddc Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Thu, 19 Dec 2024 15:38:48 +0800 +Subject: [PATCH 38/44] crypto/internal/poly1305: implement function update in + assembly on loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is some improvement in performance on Loongson 3A5000 and 3A6000. + +goos: linux +goarch: loong64 +pkg: golang.org/x/crypto/internal/poly1305 +cpu: Loongson-3A5000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +64 122.8n ± 0% 101.2n ± 0% -17.59% (p=0.000 n=10) +1K 1152.0n ± 0% 779.4n ± 0% -32.34% (p=0.000 n=10) +2M 2.356m ± 0% 1.556m ± 0% -33.94% (p=0.000 n=10) +64Unaligned 122.7n ± 0% 102.5n ± 0% -16.46% (p=0.000 n=10) +1KUnaligned 1152.0n ± 0% 802.4n ± 0% -30.35% (p=0.000 n=10) +2MUnaligned 2.336m ± 0% 1.582m ± 0% -32.26% (p=0.000 n=10) +Write64 77.92n ± 0% 57.45n ± 0% -26.27% (p=0.000 n=10) +Write1K 1106.0n ± 0% 736.2n ± 0% -33.44% (p=0.000 n=10) +Write2M 2.356m ± 0% 1.562m ± 0% -33.69% (p=0.000 n=10) +Write64Unaligned 77.87n ± 0% 59.71n ± 0% -23.33% (p=0.000 n=10) +Write1KUnaligned 1106.0n ± 0% 749.5n ± 0% -32.23% (p=0.000 n=10) +Write2MUnaligned 2.335m ± 0% 1.580m ± 0% -32.34% (p=0.000 n=10) +geomean 6.373µ 4.530µ -28.93% + + | bench.old | bench.new | + | B/s | B/s vs base | +64 497.1Mi ± 0% 603.3Mi ± 0% +21.37% (p=0.000 n=10) +1K 847.6Mi ± 0% 1252.9Mi ± 0% +47.82% (p=0.000 n=10) +2M 849.0Mi ± 0% 1285.3Mi ± 0% +51.39% (p=0.000 n=10) +64Unaligned 497.4Mi ± 0% 595.5Mi ± 0% +19.73% (p=0.000 n=10) +1KUnaligned 847.6Mi ± 0% 1217.1Mi ± 0% +43.59% (p=0.000 n=10) +2MUnaligned 856.3Mi ± 0% 1264.0Mi ± 0% +47.61% (p=0.000 n=10) +Write64 783.3Mi ± 0% 1062.4Mi ± 0% +35.64% (p=0.000 n=10) +Write1K 882.8Mi ± 0% 1326.5Mi ± 0% +50.25% (p=0.000 n=10) +Write2M 849.0Mi ± 0% 1280.3Mi ± 0% +50.80% (p=0.000 n=10) +Write64Unaligned 783.8Mi ± 0% 1022.3Mi ± 0% +30.43% (p=0.000 n=10) +Write1KUnaligned 882.8Mi ± 0% 1303.0Mi ± 0% +47.59% (p=0.000 n=10) +Write2MUnaligned 856.5Mi ± 0% 1266.0Mi ± 0% +47.81% (p=0.000 n=10) +geomean 772.2Mi 1.061Gi +40.72% + +goos: linux +goarch: loong64 +pkg: golang.org/x/crypto/internal/poly1305 +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +64 92.06n ± 0% 77.56n ± 0% -15.75% (p=0.000 n=10) +1K 998.4n ± 0% 683.0n ± 0% -31.59% (p=0.000 n=10) +2M 1.978m ± 0% 1.323m ± 0% -33.11% (p=0.000 n=10) +64Unaligned 92.06n ± 0% 77.56n ± 0% -15.75% (p=0.000 n=10) +1KUnaligned 998.4n ± 0% 683.0n ± 0% -31.59% (p=0.000 n=10) +2MUnaligned 1.979m ± 0% 1.369m ± 0% -30.82% (p=0.000 n=10) +Write64 65.25n ± 0% 50.39n ± 0% -22.77% (p=0.000 n=10) +Write1K 970.7n ± 0% 656.8n ± 0% -32.34% (p=0.000 n=10) +Write2M 1.966m ± 0% 1.323m ± 0% -32.73% (p=0.000 n=10) +Write64Unaligned 65.24n ± 0% 50.37n ± 0% -22.79% (p=0.000 n=10) +Write1KUnaligned 970.8n ± 0% 656.8n ± 0% -32.34% (p=0.000 n=10) +Write2MUnaligned 1.966m ± 0% 1.368m ± 0% -30.42% (p=0.000 n=10) +geomean 5.319µ 3.834µ -27.93% + + | bench.old | bench.new | + | B/s | B/s vs base | +64 663.0Mi ± 0% 786.9Mi ± 0% +18.69% (p=0.000 n=10) +1K 978.1Mi ± 0% 1429.8Mi ± 0% +46.18% (p=0.000 n=10) +2M 1011.0Mi ± 0% 1511.4Mi ± 0% +49.50% (p=0.000 n=10) +64Unaligned 663.0Mi ± 0% 786.9Mi ± 0% +18.69% (p=0.000 n=10) +1KUnaligned 978.1Mi ± 0% 1429.8Mi ± 0% +46.18% (p=0.000 n=10) +2MUnaligned 1010.6Mi ± 0% 1460.9Mi ± 0% +44.56% (p=0.000 n=10) +Write64 935.4Mi ± 0% 1211.3Mi ± 0% +29.49% (p=0.000 n=10) +Write1K 1006.0Mi ± 0% 1486.9Mi ± 0% +47.81% (p=0.000 n=10) +Write2M 1017.3Mi ± 0% 1512.1Mi ± 0% +48.64% (p=0.000 n=10) +Write64Unaligned 935.5Mi ± 0% 1211.7Mi ± 0% +29.53% (p=0.000 n=10) +Write1KUnaligned 1005.9Mi ± 0% 1486.9Mi ± 0% +47.81% (p=0.000 n=10) +Write2MUnaligned 1017.1Mi ± 0% 1461.8Mi ± 0% +43.71% (p=0.000 n=10) +geomean 925.3Mi 1.254Gi +38.75% + +Change-Id: Iec990384a7be9a89a019c2b3b546d9fc59a2d58e +--- + .../x/crypto/internal/poly1305/mac_noasm.go | 2 +- + .../x/crypto/internal/poly1305/sum_loong64.go | 47 +++++++ + .../x/crypto/internal/poly1305/sum_loong64.s | 131 ++++++++++++++++++ + 3 files changed, 179 insertions(+), 1 deletion(-) + create mode 100644 src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.go + create mode 100644 src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s + +diff --git a/src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +index bd896bdc76..8d99551fee 100644 +--- a/src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go ++++ b/src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build (!amd64 && !ppc64le && !ppc64 && !s390x) || !gc || purego ++//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego + + package poly1305 + +diff --git a/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.go b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.go +new file mode 100644 +index 0000000000..d4dc8f91ec +--- /dev/null ++++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.go +@@ -0,0 +1,47 @@ ++// Copyright 2024 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. ++ ++//go:build gc && !purego ++ ++package poly1305 ++ ++//go:noescape ++func update(state *macState, msg []byte) ++ ++// mac is a wrapper for macGeneric that redirects calls that would have gone to ++// updateGeneric to update. ++// ++// Its Write and Sum methods are otherwise identical to the macGeneric ones, but ++// using function pointers would carry a major performance cost. ++type mac struct{ macGeneric } ++ ++func (h *mac) Write(p []byte) (int, error) { ++ nn := len(p) ++ if h.offset > 0 { ++ n := copy(h.buffer[h.offset:], p) ++ if h.offset+n < TagSize { ++ h.offset += n ++ return nn, nil ++ } ++ p = p[n:] ++ h.offset = 0 ++ update(&h.macState, h.buffer[:]) ++ } ++ if n := len(p) - (len(p) % TagSize); n > 0 { ++ update(&h.macState, p[:n]) ++ p = p[n:] ++ } ++ if len(p) > 0 { ++ h.offset += copy(h.buffer[h.offset:], p) ++ } ++ return nn, nil ++} ++ ++func (h *mac) Sum(out *[16]byte) { ++ state := h.macState ++ if h.offset > 0 { ++ update(&state, h.buffer[:h.offset]) ++ } ++ finalize(out, &state.h, &state.s) ++} +diff --git a/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s +new file mode 100644 +index 0000000000..baf0c95333 +--- /dev/null ++++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s +@@ -0,0 +1,131 @@ ++// Copyright 2024 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. ++ ++//go:build gc && !purego ++ ++// func update(state *macState, msg []byte) ++TEXT ·update(SB), $0-32 ++ MOVV state+0(FP), R4 ++ MOVV msg_base+8(FP), R5 ++ MOVV msg_len+16(FP), R6 ++ MOVV $16, R7 ++ MOVV (R4), R8 // h0 ++ MOVV 8(R4), R9 // h1 ++ MOVV 16(R4), R10 // h2 ++ MOVV 24(R4), R11 // r0 ++ MOVV 32(R4), R12 // r1 ++ ++ BLT R6, R7, bytes_between_0_and_15 ++ ++loop: ++ MOVV (R5), R14 // msg[0:8] ++ MOVV 8(R5), R16 // msg[8:16] ++ ADDV R14, R8, R8 // h0 ++ ADDV R9, R16, R27 ++ SGTU R14, R8, R24 // h0.carry ++ SGTU R9, R27, R28 ++ ADDV R27, R24, R9 // h1 ++ SGTU R27, R9, R24 ++ OR R24, R28, R24 // h1.carry ++ ADDV $1, R24, R24 ++ ADDV R10, R24, R10 // h2 ++ ++ ADDV $16, R5, R5 // msg = msg[16:] ++ ++multiply: ++ MULV R8, R11, R13 // h0r0.lo ++ MULHVU R8, R11, R16 // h0r0.hi ++ MOVV R13, R14 ++ MOVV R16, R15 ++ MULV R9, R11, R13 // h1r0.lo ++ MULHVU R9, R11, R16 // h1r0.hi ++ ADDV R13, R15, R15 ++ SGTU R13, R15, R24 ++ ADDV R24, R16, R16 ++ MULV R10, R11, R25 ++ ADDV R16, R25, R25 ++ MULV R8, R12, R13 // h0r1.lo ++ MULHVU R8, R12, R16 // h0r1.hi ++ ADDV R13, R15, R15 ++ SGTU R13, R15, R24 ++ ADDV R24, R16, R16 ++ MOVV R16, R8 ++ MULV R10, R12, R26 // h2r1 ++ MULV R9, R12, R13 // h1r1.lo ++ MULHVU R9, R12, R16 // h1r1.hi ++ ADDV R13, R25, R25 ++ ADDV R16, R26, R27 ++ SGTU R13, R25, R24 ++ SGTU R16, R27, R28 ++ ADDV R27, R24, R26 ++ SGTU R27, R26, R24 ++ OR R24, R28, R24 ++ ADDV R8, R25, R25 ++ SGTU R8, R25, R24 ++ ADDV R24, R26, R26 ++ MOVV R14, R8 ++ MOVV R15, R9 ++ MOVV R25, R10 ++ MOVV R25, R14 ++ AND $3, R10, R10 ++ AND $-4, R14, R14 ++ ADDV R14, R8, R8 ++ ADDV R26, R9, R27 ++ SGTU R14, R8, R24 ++ SGTU R26, R27, R28 ++ ADDV R27, R24, R9 ++ SGTU R27, R9, R24 ++ OR R24, R28, R24 ++ ADDV R24, R10, R10 ++ SLLV $62, R26, R27 ++ SRLV $2, R25, R28 ++ SRLV $2, R26, R26 ++ OR R27, R28, R25 ++ ADDV R25, R8, R8 ++ ADDV R26, R9, R27 ++ SGTU R25, R8, R24 ++ SGTU R26, R27, R28 ++ ADDV R27, R24, R9 ++ SGTU R27, R9, R24 ++ OR R24, R28, R24 ++ ADDV R24, R10, R10 ++ ++ SUBV $16, R6, R6 ++ BGE R6, R7, loop ++ ++bytes_between_0_and_15: ++ BEQ R6, R0, done ++ MOVV $1, R14 ++ XOR R15, R15 ++ XOR R25, R25 ++ ADDV R6, R5, R5 ++ ++flush_buffer: ++ SRLV $56, R14, R24 ++ SLLV $8, R15, R28 ++ OR R24, R28, R15 ++ SLLV $8, R14, R14 ++ MOVBU -1(R5), R25 ++ XOR R25, R14, R14 ++ SUBV $1, R5, R5 ++ SUBV $1, R6, R6 ++ BNE R6, R0, flush_buffer ++ ++ ADDV R14, R8, R8 ++ SGTU R14, R8, R24 ++ ADDV R15, R9, R27 ++ SGTU R15, R27, R28 ++ ADDV R27, R24, R9 ++ SGTU R27, R9, R24 ++ OR R24, R28, R24 ++ ADDV R10, R24, R10 ++ ++ MOVV $16, R6 ++ JMP multiply ++ ++done: ++ MOVV R8, (R4) ++ MOVV R9, 8(R4) ++ MOVV R10, 16(R4) ++ RET +-- +2.38.1 + diff --git a/0039-runtime-optimize-the-implementation-of-memclrNoHeapP.patch b/0039-runtime-optimize-the-implementation-of-memclrNoHeapP.patch new file mode 100644 index 0000000..289a1f5 --- /dev/null +++ b/0039-runtime-optimize-the-implementation-of-memclrNoHeapP.patch @@ -0,0 +1,374 @@ +From 0e94e34886a3632315e444c5fd0ba448239c500e Mon Sep 17 00:00:00 2001 +From: chenguoqi +Date: Tue, 31 Dec 2024 18:31:50 +0800 +Subject: [PATCH 39/44] runtime: optimize the implementation of + memclrNoHeapPointers on loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: runtime +cpu: Loongson-3A6000 @ 2500.00MHz + │ bench.old │ bench.new │ + │ sec/op │ sec/op vs base │ +Memclr/5 2.456n ± 0% 3.202n ± 0% +30.37% (p=0.000 n=10) +Memclr/16 2.806n ± 0% 2.810n ± 1% +0.14% (p=0.002 n=10) +Memclr/64 5.053n ± 1% 5.045n ± 1% ~ (p=0.591 n=10) +Memclr/256 10.240n ± 0% 6.027n ± 0% -41.14% (p=0.000 n=10) +Memclr/4096 107.00n ± 0% 30.46n ± 0% -71.53% (p=0.000 n=10) +Memclr/65536 1676.0n ± 0% 431.3n ± 0% -74.26% (p=0.000 n=10) +Memclr/1M 52.52µ ± 0% 32.81µ ± 0% -37.54% (p=0.000 n=10) +Memclr/4M 210.0µ ± 0% 131.3µ ± 0% -37.48% (p=0.000 n=10) +Memclr/8M 420.0µ ± 0% 262.8µ ± 1% -37.43% (p=0.000 n=10) +Memclr/16M 846.7µ ± 0% 528.8µ ± 0% -37.55% (p=0.000 n=10) +Memclr/64M 3.388m ± 0% 2.180m ± 1% -35.66% (p=0.000 n=10) +MemclrUnaligned/0_5 4.382n ± 0% 4.006n ± 0% -8.59% (p=0.000 n=10) +MemclrUnaligned/0_16 4.600n ± 0% 4.204n ± 0% -8.60% (p=0.000 n=10) +MemclrUnaligned/0_64 5.604n ± 0% 5.005n ± 0% -10.69% (p=0.000 n=10) +MemclrUnaligned/0_256 10.340n ± 0% 6.808n ± 0% -34.16% (p=0.000 n=10) +MemclrUnaligned/0_4096 107.10n ± 0% 33.81n ± 0% -68.43% (p=0.000 n=10) +MemclrUnaligned/0_65536 1701.0n ± 0% 441.6n ± 0% -74.04% (p=0.000 n=10) +MemclrUnaligned/1_5 4.386n ± 0% 4.004n ± 0% -8.71% (p=0.000 n=10) +MemclrUnaligned/1_16 4.597n ± 0% 4.203n ± 0% -8.56% (p=0.000 n=10) +MemclrUnaligned/1_64 7.204n ± 0% 7.106n ± 0% -1.36% (p=0.000 n=10) +MemclrUnaligned/1_256 12.580n ± 0% 9.796n ± 0% -22.13% (p=0.000 n=10) +MemclrUnaligned/1_4096 115.60n ± 0% 38.63n ± 0% -66.58% (p=0.000 n=10) +MemclrUnaligned/1_65536 1709.0n ± 0% 446.5n ± 0% -73.87% (p=0.000 n=10) +MemclrUnaligned/4_5 4.386n ± 0% 4.005n ± 0% -8.69% (p=0.000 n=10) +MemclrUnaligned/4_16 4.597n ± 0% 4.203n ± 0% -8.57% (p=0.000 n=10) +MemclrUnaligned/4_64 7.204n ± 0% 7.104n ± 0% -1.39% (p=0.000 n=10) +MemclrUnaligned/4_256 12.58n ± 0% 10.66n ± 0% -15.22% (p=0.000 n=10) +MemclrUnaligned/4_4096 114.30n ± 0% 39.99n ± 0% -65.01% (p=0.000 n=10) +MemclrUnaligned/4_65536 1709.0n ± 0% 449.8n ± 0% -73.68% (p=0.000 n=10) +MemclrUnaligned/7_5 4.381n ± 0% 4.002n ± 0% -8.64% (p=0.000 n=10) +MemclrUnaligned/7_16 4.597n ± 0% 4.202n ± 0% -8.59% (p=0.000 n=10) +MemclrUnaligned/7_64 7.204n ± 0% 7.104n ± 0% -1.39% (p=0.000 n=10) +MemclrUnaligned/7_256 12.58n ± 0% 10.60n ± 0% -15.74% (p=0.000 n=10) +MemclrUnaligned/7_4096 115.50n ± 0% 39.75n ± 0% -65.58% (p=0.000 n=10) +MemclrUnaligned/7_65536 1709.0n ± 0% 447.1n ± 0% -73.84% (p=0.000 n=10) +MemclrUnaligned/0_1M 52.52µ ± 0% 32.80µ ± 0% -37.56% (p=0.000 n=10) +MemclrUnaligned/0_4M 210.0µ ± 0% 131.2µ ± 0% -37.53% (p=0.000 n=10) +MemclrUnaligned/0_8M 419.9µ ± 0% 262.5µ ± 0% -37.48% (p=0.000 n=10) +MemclrUnaligned/0_16M 845.0µ ± 0% 528.1µ ± 0% -37.51% (p=0.000 n=10) +MemclrUnaligned/0_64M 3.406m ± 0% 2.165m ± 1% -36.44% (p=0.000 n=10) +MemclrUnaligned/1_1M 52.53µ ± 0% 32.80µ ± 0% -37.55% (p=0.000 n=10) +MemclrUnaligned/1_4M 210.2µ ± 0% 131.3µ ± 0% -37.55% (p=0.000 n=10) +MemclrUnaligned/1_8M 419.9µ ± 0% 262.4µ ± 0% -37.50% (p=0.000 n=10) +MemclrUnaligned/1_16M 844.2µ ± 0% 528.0µ ± 0% -37.46% (p=0.000 n=10) +MemclrUnaligned/1_64M 3.369m ± 0% 2.161m ± 5% -35.84% (p=0.000 n=10) +MemclrUnaligned/4_1M 52.53µ ± 0% 32.80µ ± 0% -37.55% (p=0.000 n=10) +MemclrUnaligned/4_4M 210.2µ ± 0% 131.2µ ± 0% -37.59% (p=0.000 n=10) +MemclrUnaligned/4_8M 419.9µ ± 0% 262.4µ ± 0% -37.52% (p=0.000 n=10) +MemclrUnaligned/4_16M 844.5µ ± 0% 527.9µ ± 0% -37.49% (p=0.000 n=10) +MemclrUnaligned/4_64M 3.366m ± 0% 2.173m ± 0% -35.46% (p=0.000 n=10) +MemclrUnaligned/7_1M 52.52µ ± 0% 32.80µ ± 0% -37.55% (p=0.000 n=10) +MemclrUnaligned/7_4M 210.2µ ± 0% 131.5µ ± 0% -37.45% (p=0.000 n=10) +MemclrUnaligned/7_8M 419.9µ ± 0% 262.6µ ± 0% -37.47% (p=0.000 n=10) +MemclrUnaligned/7_16M 844.4µ ± 0% 529.0µ ± 0% -37.36% (p=0.000 n=10) +MemclrUnaligned/7_64M 3.372m ± 1% 2.201m ± 0% -34.72% (p=0.000 n=10) +MemclrRange/1K_2K 2703.0n ± 0% 948.1n ± 0% -64.93% (p=0.000 n=10) +MemclrRange/2K_8K 8.826µ ± 0% 2.458µ ± 0% -72.15% (p=0.000 n=10) +MemclrRange/4K_16K 8.325µ ± 0% 2.210µ ± 0% -73.45% (p=0.000 n=10) +MemclrRange/160K_228K 83.40µ ± 0% 31.27µ ± 0% -62.50% (p=0.000 n=10) +MemclrKnownSize1 0.4003n ± 0% 0.4002n ± 0% -0.02% (p=0.027 n=10) +MemclrKnownSize2 0.4003n ± 0% 0.4002n ± 0% -0.02% (p=0.000 n=10) +MemclrKnownSize4 0.4003n ± 0% 0.4002n ± 0% -0.02% (p=0.000 n=10) +MemclrKnownSize8 0.4003n ± 0% 0.4002n ± 0% -0.02% (p=0.000 n=10) +MemclrKnownSize16 0.4213n ± 1% 0.8007n ± 0% +90.03% (p=0.000 n=10) +MemclrKnownSize32 2.001n ± 0% 1.602n ± 0% -19.94% (p=0.000 n=10) +MemclrKnownSize64 2.010n ± 0% 2.402n ± 0% +19.47% (p=0.000 n=10) +MemclrKnownSize112 3.202n ± 0% 2.803n ± 0% -12.46% (p=0.000 n=10) +MemclrKnownSize128 3.442n ± 0% 3.236n ± 0% -6.00% (p=0.000 n=10) +MemclrKnownSize192 5.204n ± 0% 5.205n ± 0% ~ (p=0.279 n=10) +MemclrKnownSize248 6.301n ± 0% 6.299n ± 0% -0.03% (p=0.000 n=10) +MemclrKnownSize256 6.707n ± 0% 6.704n ± 0% -0.04% (p=0.018 n=10) +MemclrKnownSize512 13.610n ± 0% 6.989n ± 0% -48.65% (p=0.000 n=10) +MemclrKnownSize1024 26.420n ± 0% 8.458n ± 0% -67.99% (p=0.000 n=10) +MemclrKnownSize4096 103.30n ± 0% 28.02n ± 0% -72.88% (p=0.000 n=10) +MemclrKnownSize512KiB 26.28µ ± 0% 16.41µ ± 0% -37.53% (p=0.000 n=10) +geomean 624.0n 397.1n -36.37% + +Change-Id: I702b9c1991cf13f9338c189c5ef59cb2c6f279de +--- + src/runtime/cpuflags.go | 3 +- + src/runtime/memclr_loong64.s | 214 ++++++++++++++++++++++++----------- + 2 files changed, 152 insertions(+), 65 deletions(-) + +diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go +index e81e50f5df..06424642c7 100644 +--- a/src/runtime/cpuflags.go ++++ b/src/runtime/cpuflags.go +@@ -20,7 +20,8 @@ const ( + + offsetMIPS64XHasMSA = unsafe.Offsetof(cpu.MIPS64X.HasMSA) + +- offsetLOONG64HasLSX = unsafe.Offsetof(cpu.Loong64.HasLSX) ++ offsetLOONG64HasLSX = unsafe.Offsetof(cpu.Loong64.HasLSX) ++ offsetLOONG64HasLASX = unsafe.Offsetof(cpu.Loong64.HasLASX) + ) + + var ( +diff --git a/src/runtime/memclr_loong64.s b/src/runtime/memclr_loong64.s +index 346b210c8d..0d0d9f0cbb 100644 +--- a/src/runtime/memclr_loong64.s ++++ b/src/runtime/memclr_loong64.s +@@ -11,6 +11,7 @@ + // R5: n + // R6: ptrend + // R7: tmp ++// R8: tmp + + // Algorithm: + // +@@ -38,44 +39,129 @@ + + // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) + TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 +- BEQ R5, clr_0 ++ // <=64 bytes, clear directly, not check aligned ++generic_small: + ADDV R4, R5, R6 ++ BEQ R4, R6, clr_0 ++ MOVV $2, R7 ++ BLT R5, R7, clr_1 ++ MOVV $3, R7 ++ BLT R5, R7, clr_2 ++ MOVV $4, R7 ++ BLT R5, R7, clr_3 ++ MOVV $5, R7 ++ BLT R5, R7, clr_4 ++ MOVV $8, R7 ++ BLT R5, R7, clr_5_7 ++ MOVV $9, R7 ++ BLT R5, R7, clr_8 ++ MOVV $17, R7 ++ BLT R5, R7, clr_9_16 ++ MOVV $33, R7 ++ BLT R5, R7, clr_17_32 ++ MOVV $65, R7 ++ BLT R5, R7, clr_33_64 + +-tail: +- // <=64 bytes, clear directly, not check aligned +- SGTU $2, R5, R7 +- BNE R7, clr_1 +- SGTU $3, R5, R7 +- BNE R7, clr_2 +- SGTU $4, R5, R7 +- BNE R7, clr_3 +- SGTU $5, R5, R7 +- BNE R7, clr_4 +- SGTU $8, R5, R7 +- BNE R7, clr_5through7 +- SGTU $9, R5, R7 +- BNE R7, clr_8 +- SGTU $17, R5, R7 +- BNE R7, clr_9through16 +- SGTU $33, R5, R7 +- BNE R7, clr_17through32 +- SGTU $65, R5, R7 +- BNE R7, clr_33through64 ++lasx_large: ++ MOVBU internal∕cpu·Loong64+const_offsetLOONG64HasLASX(SB), R7 ++ BEQ R7, lsx_large ++ ++ // X0 = 0 ++ XVMOVQ R0, X0.V4 ++ ++ // check 32-byte alignment ++ AND $31, R4, R7 ++ BEQ R7, lasx_large_aligned ++ XVMOVQ X0, (R4) ++ SUBV R7, R4 ++ ADDV R7, R5 ++ SUBV $32, R5 // newn = n - (32 - (ptr & 31)) ++ ADDV $32, R4 // newptr = ptr + (32 - (ptr & 31)) ++ ++lasx_large_aligned: ++ MOVV $256, R8 ++ BLT R5, R8, lasx_small ++lasx_large_body: ++ XVMOVQ X0, 0(R4) ++ XVMOVQ X0, 32(R4) ++ XVMOVQ X0, 64(R4) ++ XVMOVQ X0, 96(R4) ++ XVMOVQ X0, 128(R4) ++ XVMOVQ X0, 160(R4) ++ XVMOVQ X0, 192(R4) ++ XVMOVQ X0, 224(R4) ++ SUBV $256, R5 ++ ADDV $256, R4 ++ BGE R5, R8, lasx_large_body ++ ++lasx_small: ++ MOVV $32, R8 ++ BLT R5, R8, generic_small ++lasx_small_body: ++ XVMOVQ X0, (R4) ++ SUBV $32, R5 ++ ADDV $32, R4 ++ BGE R5, R8, lasx_small_body ++lasx_tail: ++ JMP generic_small ++ ++lsx_large: ++ MOVBU internal∕cpu·Loong64+const_offsetLOONG64HasLSX(SB), R7 ++ BEQ R7, generic_large ++ ++ // V0 = 0 ++ VMOVQ R0, V0.V2 + ++ // check 16-byte alignment ++ AND $15, R4, R7 ++ BEQ R7, lsx_large_aligned ++ VMOVQ V0, (R4) ++ SUBV R7, R4 ++ ADDV R7, R5 ++ SUBV $16, R5 // newn = n - (16 - (ptr & 15)) ++ ADDV $16, R4 // newptr = ptr + (16 - (ptr & 15)) ++ ++lsx_large_aligned: ++ MOVV $128, R8 ++ BLT R5, R8, lsx_small ++lsx_large_body: ++ VMOVQ V0, 0(R4) ++ VMOVQ V0, 16(R4) ++ VMOVQ V0, 32(R4) ++ VMOVQ V0, 48(R4) ++ VMOVQ V0, 64(R4) ++ VMOVQ V0, 80(R4) ++ VMOVQ V0, 96(R4) ++ VMOVQ V0, 112(R4) ++ SUBV $128, R5 ++ ADDV $128, R4 ++ BGE R5, R8, lsx_large_body ++ ++lsx_small: ++ MOVV $16, R8 ++ BLT R5, R8, generic_small ++lsx_small_body: ++ VMOVQ V0, (R4) ++ SUBV $16, R5 ++ ADDV $16, R4 ++ BGE R5, R8, lsx_small_body ++lsx_tail: ++ JMP generic_small ++ ++generic_large: + // n > 64 bytes, check aligned + AND $7, R4, R7 +- BEQ R7, body +- +-head: ++ BEQ R7, generic_large_aligned + MOVV R0, (R4) +- SUBV R7, R4 + ADDV R7, R5 +- ADDV $8, R4 // newptr = ptr + (8 - (ptr & 7)) ++ SUBV R7, R4 + SUBV $8, R5 // newn = n - (8 - (ptr & 7)) +- SGTU $65, R5, R7 +- BNE R7, clr_33through64 ++ ADDV $8, R4 // newptr = ptr + (8 - (ptr & 7)) + +-body: ++generic_large_aligned: ++ MOVV $65, R7 ++ BLT R5, R7, generic_small ++generic_large_body: + MOVV R0, (R4) + MOVV R0, 8(R4) + MOVV R0, 16(R4) +@@ -84,52 +170,52 @@ body: + MOVV R0, 40(R4) + MOVV R0, 48(R4) + MOVV R0, 56(R4) +- ADDV $-64, R5 ++ SUBV $64, R5 + ADDV $64, R4 +- SGTU $65, R5, R7 +- BEQ R7, body +- BEQ R5, clr_0 +- JMP tail ++ BGE R5, R7, generic_large_body ++generic_tail: ++ JMP generic_small + +-clr_0: ++clr_33_64: ++ MOVV R0, (R4) ++ MOVV R0, 8(R4) ++ MOVV R0, 16(R4) ++ MOVV R0, 24(R4) ++ MOVV R0, -32(R6) ++ MOVV R0, -24(R6) ++ MOVV R0, -16(R6) ++ MOVV R0, -8(R6) + RET +-clr_1: +- MOVB R0, (R4) ++ ++clr_17_32: ++ MOVV R0, (R4) ++ MOVV R0, 8(R4) ++ MOVV R0, -16(R6) ++ MOVV R0, -8(R6) + RET +-clr_2: +- MOVH R0, (R4) ++clr_9_16: ++ MOVV R0, (R4) ++ MOVV R0, -8(R6) + RET +-clr_3: +- MOVH R0, (R4) +- MOVB R0, 2(R4) ++clr_8: ++ MOVV R0, (R4) + RET +-clr_4: ++clr_5_7: + MOVW R0, (R4) ++ MOVW R0, -4(R6) + RET +-clr_5through7: ++clr_4: + MOVW R0, (R4) +- MOVW R0, -4(R6) + RET +-clr_8: +- MOVV R0, (R4) ++clr_3: ++ MOVH R0, (R4) ++ MOVB R0, 2(R4) + RET +-clr_9through16: +- MOVV R0, (R4) +- MOVV R0, -8(R6) ++clr_2: ++ MOVH R0, (R4) + RET +-clr_17through32: +- MOVV R0, (R4) +- MOVV R0, 8(R4) +- MOVV R0, -16(R6) +- MOVV R0, -8(R6) ++clr_1: ++ MOVB R0, (R4) + RET +-clr_33through64: +- MOVV R0, (R4) +- MOVV R0, 8(R4) +- MOVV R0, 16(R4) +- MOVV R0, 24(R4) +- MOVV R0, -32(R6) +- MOVV R0, -24(R6) +- MOVV R0, -16(R6) +- MOVV R0, -8(R6) ++clr_0: + RET +-- +2.38.1 + diff --git a/0040-runtime-race-add-the-implementation-of-atomic.-Or-An.patch b/0040-runtime-race-add-the-implementation-of-atomic.-Or-An.patch new file mode 100644 index 0000000..39a261b --- /dev/null +++ b/0040-runtime-race-add-the-implementation-of-atomic.-Or-An.patch @@ -0,0 +1,75 @@ +From 88b165cf7d4cb6a77f47d3c291d3ee7e1f13695e Mon Sep 17 00:00:00 2001 +From: Guoqi Chen +Date: Fri, 10 Jan 2025 10:31:47 +0800 +Subject: [PATCH 40/44] runtime/race: add the implementation of atomic.{Or,And} + on loong64 + +Change-Id: Ia4298a4d92fce210e3c743b2d5ce2b28b82d4971 +--- + src/runtime/race_loong64.s | 50 +++++++++++++++++++++++ + 2 files changed, 50 insertions(+) + +diff --git a/src/runtime/race_loong64.s b/src/runtime/race_loong64.s +index 04f264b21b..e6c11d44f7 100644 +--- a/src/runtime/race_loong64.s ++++ b/src/runtime/race_loong64.s +@@ -308,6 +308,56 @@ TEXT sync∕atomic·AddUintptr(SB), NOSPLIT, $0-24 + GO_ARGS + JMP sync∕atomic·AddInt64(SB) + ++// And ++TEXT sync∕atomic·AndInt32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_fetch_and(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·AndInt64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_fetch_and(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·AndUint32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ JMP sync∕atomic·AndInt32(SB) ++ ++TEXT sync∕atomic·AndUint64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·AndInt64(SB) ++ ++TEXT sync∕atomic·AndUintptr(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·AndInt64(SB) ++ ++// Or ++TEXT sync∕atomic·OrInt32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ MOVV $__tsan_go_atomic32_fetch_or(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·OrInt64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ MOVV $__tsan_go_atomic64_fetch_or(SB), RCALL ++ JAL racecallatomic<>(SB) ++ RET ++ ++TEXT sync∕atomic·OrUint32(SB), NOSPLIT, $0-20 ++ GO_ARGS ++ JMP sync∕atomic·OrInt32(SB) ++ ++TEXT sync∕atomic·OrUint64(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·OrInt64(SB) ++ ++TEXT sync∕atomic·OrUintptr(SB), NOSPLIT, $0-24 ++ GO_ARGS ++ JMP sync∕atomic·OrInt64(SB) ++ + // CompareAndSwap + TEXT sync∕atomic·CompareAndSwapInt32(SB), NOSPLIT, $0-17 + GO_ARGS +-- +2.38.1 + diff --git a/0041-cmd-internal-obj-loong64-add-F-MAXA-MINA-.-S-D-instr.patch b/0041-cmd-internal-obj-loong64-add-F-MAXA-MINA-.-S-D-instr.patch new file mode 100644 index 0000000..847b4b1 --- /dev/null +++ b/0041-cmd-internal-obj-loong64-add-F-MAXA-MINA-.-S-D-instr.patch @@ -0,0 +1,107 @@ +From e652e32e37bfd898af333a32b73cfde6ab2116fa Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Mon, 30 Dec 2024 10:08:58 +0800 +Subject: [PATCH 41/44] cmd/internal/obj/loong64: add F{MAXA/MINA}.{S/D} + instructions + +Go asm syntax: + F{MAXA/MINA}{F/D} FK, FJ, FD + +Equivalent platform assembler syntax: + f{maxa/mina}.{s/d} fd, fj, fk + +Ref: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html + +Change-Id: I6790657d2f36bdf5e6818b6c0aaa48117e782b8d +--- + src/cmd/asm/internal/asm/testdata/loong64enc1.s | 9 +++++++++ + src/cmd/internal/obj/loong64/a.out.go | 6 ++++++ + src/cmd/internal/obj/loong64/anames.go | 4 ++++ + src/cmd/internal/obj/loong64/asm.go | 12 ++++++++++++ + 4 files changed, 31 insertions(+) + +diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +index b40d86e596..32d3b3f0a2 100644 +--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s ++++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s +@@ -346,6 +346,15 @@ lable2: + FTINTVF F0, F1 // 01241b01 + FTINTVD F0, F1 // 01281b01 + ++ FMAXAF F4, F5, F6 // a6900c01 ++ FMAXAF F4, F5 // a5900c01 ++ FMAXAD F4, F5, F6 // a6100d01 ++ FMAXAD F4, F5 // a5100d01 ++ FMINAF F4, F5, F6 // a6900e01 ++ FMINAF F4, F5 // a5900e01 ++ FMINAD F4, F5, F6 // a6100f01 ++ FMINAD F4, F5 // a5100f01 ++ + FTINTRMWF F0, F2 // 02041a01 + FTINTRMWD F0, F2 // 02081a01 + FTINTRMVF F0, F2 // 02241a01 +diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go +index f2d4c41d68..857ea649e7 100644 +--- a/src/cmd/internal/obj/loong64/a.out.go ++++ b/src/cmd/internal/obj/loong64/a.out.go +@@ -688,6 +688,12 @@ const ( + AFMAXF + AFMAXD + ++ // 3.2.1.4 ++ AFMAXAF ++ AFMAXAD ++ AFMINAF ++ AFMINAD ++ + // 3.2.1.7 + AFCOPYSGF + AFCOPYSGD +diff --git a/src/cmd/internal/obj/loong64/anames.go b/src/cmd/internal/obj/loong64/anames.go +index aee0da0a6e..d2acdf7042 100644 +--- a/src/cmd/internal/obj/loong64/anames.go ++++ b/src/cmd/internal/obj/loong64/anames.go +@@ -223,6 +223,10 @@ var Anames = []string{ + "FMIND", + "FMAXF", + "FMAXD", ++ "FMAXAF", ++ "FMAXAD", ++ "FMINAF", ++ "FMINAD", + "FCOPYSGF", + "FCOPYSGD", + "FSCALEBF", +diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go +index 2480cf9382..31f5376f8e 100644 +--- a/src/cmd/internal/obj/loong64/asm.go ++++ b/src/cmd/internal/obj/loong64/asm.go +@@ -1347,6 +1347,10 @@ func buildop(ctxt *obj.Link) { + opset(AFCOPYSGD, r0) + opset(AFSCALEBF, r0) + opset(AFSCALEBD, r0) ++ opset(AFMAXAF, r0) ++ opset(AFMAXAD, r0) ++ opset(AFMINAF, r0) ++ opset(AFMINAD, r0) + + case AFMADDF: + opset(AFMADDD, r0) +@@ -2811,6 +2815,14 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { + return 0x211 << 15 // fmax.s + case AFMAXD: + return 0x212 << 15 // fmax.d ++ case AFMAXAF: ++ return 0x219 << 15 // fmaxa.s ++ case AFMAXAD: ++ return 0x21a << 15 // fmaxa.d ++ case AFMINAF: ++ return 0x21d << 15 // fmina.s ++ case AFMINAD: ++ return 0x21e << 15 // fmina.d + case AFSCALEBF: + return 0x221 << 15 // fscaleb.s + case AFSCALEBD: +-- +2.38.1 + diff --git a/0042-math-implement-func-archExp-and-archExp2-in-assembly.patch b/0042-math-implement-func-archExp-and-archExp2-in-assembly.patch new file mode 100644 index 0000000..a9303c2 --- /dev/null +++ b/0042-math-implement-func-archExp-and-archExp2-in-assembly.patch @@ -0,0 +1,358 @@ +From f463c4a1db9ac0e4be9d67bc53f4ddb8515232d3 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Tue, 31 Dec 2024 21:02:47 +0800 +Subject: [PATCH 42/44] math: implement func archExp and archExp2 in assembly + on loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: math +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Exp 26.30n ± 0% 12.93n ± 0% -50.85% (p=0.000 n=10) +ExpGo 26.86n ± 0% 26.92n ± 0% +0.22% (p=0.000 n=10) +Expm1 16.76n ± 0% 16.75n ± 0% ~ (p=0.060 n=10) +Exp2 23.05n ± 0% 12.12n ± 0% -47.42% (p=0.000 n=10) +Exp2Go 23.41n ± 0% 23.47n ± 0% +0.28% (p=0.000 n=10) +geomean 22.97n 17.54n -23.64% + +goos: linux +goarch: loong64 +pkg: math/cmplx +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Exp 51.32n ± 0% 35.41n ± 0% -30.99% (p=0.000 n=10) + +goos: linux +goarch: loong64 +pkg: math +cpu: Loongson-3A5000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Exp 50.27n ± 0% 48.75n ± 1% -3.01% (p=0.000 n=10) +ExpGo 50.72n ± 0% 50.44n ± 0% -0.55% (p=0.000 n=10) +Expm1 28.40n ± 0% 28.32n ± 0% ~ (p=0.360 n=10) +Exp2 50.09n ± 0% 21.49n ± 1% -57.10% (p=0.000 n=10) +Exp2Go 50.05n ± 0% 49.69n ± 0% -0.72% (p=0.000 n=10) +geomean 44.85n 37.52n -16.35% + +goos: linux +goarch: loong64 +pkg: math/cmplx +cpu: Loongson-3A5000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Exp 88.56n ± 0% 67.29n ± 0% -24.03% (p=0.000 n=10) + +Change-Id: I89e456d26fc075d83335ee4a31227d2aface5714 +--- + src/math/exp2_asm.go | 2 +- + src/math/exp2_noasm.go | 2 +- + src/math/exp_asm.go | 2 +- + src/math/exp_loong64.s | 236 +++++++++++++++++++++++++++++++++++++++++ + src/math/exp_noasm.go | 2 +- + 5 files changed, 240 insertions(+), 4 deletions(-) + create mode 100644 src/math/exp_loong64.s + +diff --git a/src/math/exp2_asm.go b/src/math/exp2_asm.go +index c26b2c3fab..1e78759374 100644 +--- a/src/math/exp2_asm.go ++++ b/src/math/exp2_asm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build arm64 ++//go:build arm64 || loong64 + + package math + +diff --git a/src/math/exp2_noasm.go b/src/math/exp2_noasm.go +index c2b409329f..847138b622 100644 +--- a/src/math/exp2_noasm.go ++++ b/src/math/exp2_noasm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !arm64 ++//go:build !arm64 && !loong64 + + package math + +diff --git a/src/math/exp_asm.go b/src/math/exp_asm.go +index 424442845b..125529fca3 100644 +--- a/src/math/exp_asm.go ++++ b/src/math/exp_asm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build amd64 || arm64 || s390x ++//go:build amd64 || arm64 || loong64 || s390x + + package math + +diff --git a/src/math/exp_loong64.s b/src/math/exp_loong64.s +new file mode 100644 +index 0000000000..3d24214289 +--- /dev/null ++++ b/src/math/exp_loong64.s +@@ -0,0 +1,236 @@ ++// Copyright 2024 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. ++ ++#include "textflag.h" ++ ++#define NearZero 0x3e30000000000000 // 2**-28 ++#define PosInf 0x7ff0000000000000 ++#define FracMask 0x000fffffffffffff ++#define C1 0x3cb0000000000000 // 2**-52 ++ ++DATA exprodata<>+0(SB)/8, $0.0 ++DATA exprodata<>+8(SB)/8, $0.5 ++DATA exprodata<>+16(SB)/8, $1.0 ++DATA exprodata<>+24(SB)/8, $2.0 ++DATA exprodata<>+32(SB)/8, $6.93147180369123816490e-01 // Ln2Hi ++DATA exprodata<>+40(SB)/8, $1.90821492927058770002e-10 // Ln2Lo ++DATA exprodata<>+48(SB)/8, $1.44269504088896338700e+00 // Log2e ++DATA exprodata<>+56(SB)/8, $7.09782712893383973096e+02 // Overflow ++DATA exprodata<>+64(SB)/8, $-7.45133219101941108420e+02 // Underflow ++DATA exprodata<>+72(SB)/8, $1.0239999999999999e+03 // Overflow2 ++DATA exprodata<>+80(SB)/8, $-1.0740e+03 // Underflow2 ++DATA exprodata<>+88(SB)/8, $3.7252902984619141e-09 // NearZero ++GLOBL exprodata<>+0(SB), NOPTR|RODATA, $96 ++ ++DATA expmultirodata<>+0(SB)/8, $1.66666666666666657415e-01 // P1 ++DATA expmultirodata<>+8(SB)/8, $-2.77777777770155933842e-03 // P2 ++DATA expmultirodata<>+16(SB)/8, $6.61375632143793436117e-05 // P3 ++DATA expmultirodata<>+24(SB)/8, $-1.65339022054652515390e-06 // P4 ++DATA expmultirodata<>+32(SB)/8, $4.13813679705723846039e-08 // P5 ++GLOBL expmultirodata<>+0(SB), NOPTR|RODATA, $40 ++ ++// Exp returns e**x, the base-e exponential of x. ++// This is an assembly implementation of the method used for function Exp in file exp.go. ++// ++// func Exp(x float64) float64 ++TEXT ·archExp(SB),$0-16 ++ MOVD x+0(FP), F0 // F0 = x ++ ++ MOVV $exprodata<>+0(SB), R10 ++ MOVD 56(R10), F1 // Overflow ++ MOVD 64(R10), F2 // Underflow ++ MOVD 88(R10), F3 // NearZero ++ MOVD 16(R10), F17 // 1.0 ++ ++ CMPEQD F0, F0, FCC0 ++ BFPF isNaN // x = NaN, return NaN ++ ++ CMPGTD F0, F1, FCC0 ++ BFPT overflow // x > Overflow, return PosInf ++ ++ CMPGTD F2, F0, FCC0 ++ BFPT underflow // x < Underflow, return 0 ++ ++ ABSD F0, F5 ++ CMPGTD F3, F5, FCC0 ++ BFPT nearzero // fabs(x) < NearZero, return 1 + x ++ ++ // argument reduction, x = k*ln2 + r, |r| <= 0.5*ln2 ++ // computed as r = hi - lo for extra precision. ++ MOVD 0(R10), F5 // 0.0 ++ MOVD 8(R10), F3 // 0.5 ++ MOVD 48(R10), F2 // Log2e ++ CMPGTD F0, F5, FCC0 ++ BFPT add // x > 0 ++sub: ++ FMSUBD F3, F2, F0, F3 // Log2e*x - 0.5 ++ JMP 2(PC) ++add: ++ FMADDD F3, F2, F0, F3 // Log2e*x + 0.5 ++ ++ FTINTRZVD F3, F4 // float64 -> int64 ++ MOVV F4, R5 // R5 = int(k) ++ FFINTDV F4, F3 // int64 -> float64 ++ ++ MOVD 32(R10), F4 // F4 = Ln2Hi ++ MOVD 40(R10), F5 // F5 = Ln2Lo ++ FNMSUBD F0, F3, F4, F4 // F4 = hi = x - float64(int(k))*Ln2Hi ++ MULD F3, F5, F5 // F5 = lo = float64(int(k)) * Ln2Lo ++ SUBD F5, F4, F6 // F6 = r = hi - lo ++ MULD F6, F6, F7 // F7 = t = r * r ++ ++ // compute c ++ MOVV $expmultirodata<>+0(SB), R11 ++ MOVD 32(R11), F8 // F8 = P5 ++ MOVD 24(R11), F9 // F9 = P4 ++ FMADDD F9, F8, F7, F13 // P4+t*P5 ++ MOVD 16(R11), F10 // F10 = P3 ++ FMADDD F10, F13, F7, F13 // P3+t*(P4+t*P5) ++ MOVD 8(R11), F11 // F11 = P2 ++ FMADDD F11, F13, F7, F13 // P2+t*(P3+t*(P4+t*P5)) ++ MOVD 0(R11), F12 // F12 = P1 ++ FMADDD F12, F13, F7, F13 // P1+t*(P2+t*(P3+t*(P4+t*P5))) ++ FNMSUBD F6, F13, F7, F13 // F13 = c = r - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))) ++ ++ // compute y ++ MOVD 24(R10), F14 // F14 = 2.0 ++ SUBD F13, F14, F14 // F14 = 2 - c ++ MULD F6, F13, F15 // F15 = r*c ++ DIVD F14, F15, F15 // F15 = (r*c)/(2-c) ++ SUBD F15, F5, F15 // F15 = lo-(r*c)/(2-c) ++ SUBD F4, F15, F15 // F15 = (lo-(r*c)/(2-c))-hi ++ SUBD F15, F17, F16 // F16 = y = 1-((lo-(r*c)/(2-c))-hi) ++ ++ // inline Ldexp(y, k), benefit: ++ // 1, no parameter pass overhead. ++ // 2, skip unnecessary checks for Inf/NaN/Zero ++ MOVV F16, R4 ++ MOVV $FracMask, R9 ++ AND R9, R4, R6 // fraction ++ SRLV $52, R4, R7 // exponent ++ ADDV R5, R7 // R5 = int(k) ++ MOVV $1, R12 ++ BGE R7, R12, normal ++ ADDV $52, R7 // denormal ++ MOVV $C1, R8 ++ MOVV R8, F17 // m = 2**-52 ++normal: ++ SLLV $52, R7 ++ OR R7, R6, R4 ++ MOVV R4, F0 ++ MULD F17, F0 // return m * x ++ MOVD F0, ret+8(FP) ++ RET ++nearzero: ++ ADDD F17, F0, F0 ++isNaN: ++ MOVD F0, ret+8(FP) ++ RET ++underflow: ++ MOVV R0, ret+8(FP) ++ RET ++overflow: ++ MOVV $PosInf, R4 ++ MOVV R4, ret+8(FP) ++ RET ++ ++ ++// Exp2 returns 2**x, the base-2 exponential of x. ++// This is an assembly implementation of the method used for function Exp2 in file exp.go. ++// ++// func Exp2(x float64) float64 ++TEXT ·archExp2(SB),$0-16 ++ MOVD x+0(FP), F0 // F0 = x ++ ++ MOVV $exprodata<>+0(SB), R10 ++ MOVD 72(R10), F1 // Overflow2 ++ MOVD 80(R10), F2 // Underflow2 ++ MOVD 88(R10), F3 // NearZero ++ ++ CMPEQD F0, F0, FCC0 ++ BFPF isNaN // x = NaN, return NaN ++ ++ CMPGTD F0, F1, FCC0 ++ BFPT overflow // x > Overflow, return PosInf ++ ++ CMPGTD F2, F0, FCC0 ++ BFPT underflow // x < Underflow, return 0 ++ ++ // argument reduction; x = r*lg(e) + k with |r| <= ln(2)/2 ++ // computed as r = hi - lo for extra precision. ++ MOVD 0(R10), F10 // 0.0 ++ MOVD 8(R10), F2 // 0.5 ++ CMPGTD F0, F10, FCC0 ++ BFPT add ++sub: ++ SUBD F2, F0, F3 // x - 0.5 ++ JMP 2(PC) ++add: ++ ADDD F2, F0, F3 // x + 0.5 ++ ++ FTINTRZVD F3, F4 // float64 -> int64 ++ MOVV F4, R5 // R5 = int(k) ++ FFINTDV F4, F3 // F3 = float64(int(k)) ++ ++ MOVD 32(R10), F4 // F4 = Ln2Hi ++ MOVD 40(R10), F5 // F5 = Ln2Lo ++ SUBD F3, F0, F3 // t = x - float64(int(k)) ++ MULD F3, F4 // F4 = hi = t * Ln2Hi ++ FNMSUBD F10, F3, F5, F5 // F5 = lo = -t * Ln2Lo ++ SUBD F5, F4, F6 // F6 = r = hi - lo ++ MULD F6, F6, F7 // F7 = t = r * r ++ ++ // compute c ++ MOVV $expmultirodata<>+0(SB), R11 ++ MOVD 32(R11), F8 // F8 = P5 ++ MOVD 24(R11), F9 // F9 = P4 ++ FMADDD F9, F8, F7, F13 // P4+t*P5 ++ MOVD 16(R11), F10 // F10 = P3 ++ FMADDD F10, F13, F7, F13 // P3+t*(P4+t*P5) ++ MOVD 8(R11), F11 // F11 = P2 ++ FMADDD F11, F13, F7, F13 // P2+t*(P3+t*(P4+t*P5)) ++ MOVD 0(R11), F12 // F12 = P1 ++ FMADDD F12, F13, F7, F13 // P1+t*(P2+t*(P3+t*(P4+t*P5))) ++ FNMSUBD F6, F13, F7, F13 // F13 = c = r - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))) ++ ++ // compute y ++ MOVD 24(R10), F14 // F14 = 2.0 ++ SUBD F13, F14, F14 // F14 = 2 - c ++ MULD F6, F13, F15 // F15 = r*c ++ DIVD F14, F15 // F15 = (r*c)/(2-c) ++ ++ MOVD 16(R10), F17 // 1.0 ++ SUBD F15, F5, F15 // lo-(r*c)/(2-c) ++ SUBD F4, F15, F15 // (lo-(r*c)/(2-c))-hi ++ SUBD F15, F17, F16 // F16 = y = 1-((lo-(r*c)/(2-c))-hi) ++ ++ // inline Ldexp(y, k), benefit: ++ // 1, no parameter pass overhead. ++ // 2, skip unnecessary checks for Inf/NaN/Zero ++ MOVV F16, R4 ++ MOVV $FracMask, R9 ++ SRLV $52, R4, R7 // exponent ++ AND R9, R4, R6 // fraction ++ ADDV R5, R7 // R5 = int(k) ++ MOVV $1, R12 ++ BGE R7, R12, normal ++ ++ ADDV $52, R7 // denormal ++ MOVV $C1, R8 ++ MOVV R8, F17 // m = 2**-52 ++normal: ++ SLLV $52, R7 ++ OR R7, R6, R4 ++ MOVV R4, F0 ++ MULD F17, F0 // return m * x ++isNaN: ++ MOVD F0, ret+8(FP) ++ RET ++underflow: ++ MOVV R0, ret+8(FP) ++ RET ++overflow: ++ MOVV $PosInf, R4 ++ MOVV R4, ret+8(FP) ++ RET +diff --git a/src/math/exp_noasm.go b/src/math/exp_noasm.go +index bd3f02412a..bf5e84b736 100644 +--- a/src/math/exp_noasm.go ++++ b/src/math/exp_noasm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !amd64 && !arm64 && !s390x ++//go:build !amd64 && !arm64 && !loong64 && !s390x + + package math + +-- +2.38.1 + diff --git a/0043-math-implement-func-archLog-in-assembly-on-loong64.patch b/0043-math-implement-func-archLog-in-assembly-on-loong64.patch new file mode 100644 index 0000000..f01c831 --- /dev/null +++ b/0043-math-implement-func-archLog-in-assembly-on-loong64.patch @@ -0,0 +1,217 @@ +From 066bd3bf1a03e21cc27b463164461a56ce107d59 Mon Sep 17 00:00:00 2001 +From: Xiaolin Zhao +Date: Mon, 6 Jan 2025 15:40:06 +0800 +Subject: [PATCH 43/44] math: implement func archLog in assembly on loong64 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +goos: linux +goarch: loong64 +pkg: math +cpu: Loongson-3A6000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Log 18.87n ± 0% 12.85n ± 0% -31.90% (p=0.000 n=10) +Logb 5.203n ± 0% 5.604n ± 0% +7.71% (p=0.000 n=10) +Log1p 16.78n ± 0% 16.78n ± 0% ~ (p=0.450 n=10) +Log10 20.47n ± 0% 13.59n ± 0% -33.61% (p=0.000 n=10) +Log2 6.804n ± 0% 8.805n ± 0% +29.40% (p=0.000 n=10) +geomean 11.81n 10.77n -8.82% + +goos: linux +goarch: loong64 +pkg: math +cpu: Loongson-3A5000 @ 2500.00MHz + | bench.old | bench.new | + | sec/op | sec/op vs base | +Log 28.28n ± 0% 24.95n ± 1% -11.78% (p=0.000 n=10) +Logb 7.609n ± 0% 7.207n ± 0% -5.29% (p=0.000 n=10) +Log1p 27.27n ± 0% 27.18n ± 1% ~ (p=0.078 n=10) +Log10 29.56n ± 0% 26.56n ± 0% -10.16% (p=0.000 n=10) +Log2 11.43n ± 0% 10.41n ± 0% -8.92% (p=0.000 n=10) +geomean 18.17n 16.83n -7.38% + +Change-Id: I42a17280874c28b31a3b5c75fc19ddac90c92f32 +--- + src/math/log_asm.go | 2 +- + src/math/log_loong64.s | 140 +++++++++++++++++++++++++++++++++++++++++ + src/math/log_stub.go | 2 +- + 3 files changed, 142 insertions(+), 2 deletions(-) + create mode 100644 src/math/log_loong64.s + +diff --git a/src/math/log_asm.go b/src/math/log_asm.go +index 848cce13b2..82372d1e64 100644 +--- a/src/math/log_asm.go ++++ b/src/math/log_asm.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build amd64 || s390x ++//go:build amd64 || loong64 || s390x + + package math + +diff --git a/src/math/log_loong64.s b/src/math/log_loong64.s +new file mode 100644 +index 0000000000..534295cb53 +--- /dev/null ++++ b/src/math/log_loong64.s +@@ -0,0 +1,140 @@ ++// 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. ++ ++#include "textflag.h" ++ ++DATA logrodata<>+0(SB)/8, $0.5 ++DATA logrodata<>+8(SB)/8, $1.0 ++DATA logrodata<>+16(SB)/8, $2.0 ++DATA logrodata<>+24(SB)/8, $7.07106781186547524401e-01 // sqrt(2)/2 ++DATA logrodata<>+32(SB)/8, $6.93147180369123816490e-01 // Ln2Hi ++DATA logrodata<>+40(SB)/8, $1.90821492927058770002e-10 // Ln2Lo ++DATA logrodata<>+48(SB)/8, $6.666666666666735130e-01 // L1 ++DATA logrodata<>+56(SB)/8, $3.999999999940941908e-01 // L2 ++DATA logrodata<>+64(SB)/8, $2.857142874366239149e-01 // L3 ++DATA logrodata<>+72(SB)/8, $2.222219843214978396e-01 // L4 ++DATA logrodata<>+80(SB)/8, $1.818357216161805012e-01 // L5 ++DATA logrodata<>+88(SB)/8, $1.531383769920937332e-01 // L6 ++DATA logrodata<>+96(SB)/8, $1.479819860511658591e-01 // L7 ++DATA logrodata<>+104(SB)/8, $2.2250738585072014e-308 // 2**-1022 ++GLOBL logrodata<>+0(SB), NOPTR|RODATA, $112 ++ ++#define NaN 0x7FF8000000000001 ++#define NegInf 0xFFF0000000000000 ++#define PosInf 0x7FF0000000000000 ++ ++// func Log(x float64) float64 ++TEXT ·archLog(SB),NOSPLIT,$0 ++ // test bits for special cases ++ MOVD x+0(FP), F0 ++ MOVV x+0(FP), R4 ++ MOVV $logrodata<>+0(SB), R10 ++ FCLASSD F0, F4 ++ MOVV F4, R5 ++ AND $67, R5, R6 // NaN or +Inf ++ AND $544, R5, R7 // +0 or -0 ++ AND $28, R5, R8 // <0 ++ BNE R6, R0, isInfOrNaN ++ BNE R7, R0, isZero ++ BNE R8, R0, isNegative ++ ++ // reduce ++ // f1, ki := Frexp(x) FIXME ++ MOVD 104(R10), F4 ++ ABSD F0, F1 ++ CMPGED F1, F4, FCC0 ++ BFPT direct_return ++ MOVV $0x10000000000000, R5 // 1 << 52 ++ MULV R4, R5, R4 // R4 = y ++ MOVV $-52, R15 // R15 = ki (exp) ++ JMP 2(PC) ++direct_return: ++ MOVV $0, R15 // R15 = ki (exp) F0 = y ++ ++ MOVV $0x000FFFFFFFFFFFFF, R5 ++ AND R4, R5, R7 // x &^= mask << shift ++ MOVV $0x3FE0000000000000, R6 // (-1 + bias) << shift ++ OR R6, R7 // x |= (-1 + bias) << shift ++ MOVV R7, F2 // F2 = f1 ++ SRLV $52, R4 // x >> shift ++ AND $0x7FF, R4 // (x>>shift)&mask ++ SUBV $0x3FE, R4 // int((x>>shift)&mask) - bias + 1 ++ ADDV R4, R15, R4 // R4 = exp ++ ++ // if f1 < math.Sqrt2/2 { k -= 1; f1 *= 2 } ++ MOVD 0(R10), F10 // 0.5 ++ MOVD 8(R10), F3 // 1.0 ++ MOVD 16(R10), F4 // 2.0 ++ MOVD 24(R10), F0 // sqrt(2)/2 ++ CMPGED F2, F0, FCC0 // if f1 >= Sqrt2/2 ++ BFPT next ++ MULD F4, F2, F2 // f1 *= 2 ++ SUBV $1, R4, R4 ++next: ++ MOVV R4, F1 // k-- ++ FFINTDV F1, F1 // F1 = k ++ // f := f1 - 1 ++ SUBD F3, F2, F2 ++ ++ // compute ++ MOVD 96(R10), F17 // L7 ++ MOVD 80(R10), F15 // L5 ++ MOVD 64(R10), F13 // L3 ++ MOVD 48(R10), F11 // L1 ++ ADDD F4, F2, F3 // 2 + f ++ DIVD F3, F2, F4 // s := f / (2 + f) ++ MULD F4, F4, F5 // s2 := s * s ++ MULD F5, F5, F6 // s4 := s2 * s2 ++ // t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7))) ++ MULD F17, F6, F7 // s4*L7 ++ ADDD F15, F7 // L5+s4*L7 ++ MULD F6, F7 // s4*(L5+s4*L7) ++ ADDD F13, F7 // L3+s4*(L5+s4*L7) ++ MULD F6, F7 // s4*(L3+s4*(L5+s4*L7)) ++ ADDD F11, F7 // L1 + s4*(L3+s4*(L5+s4*L7)) ++ MULD F5, F7 // s2 * (L1 + s4*(L3+s4*(L5+s4*L7))) ++ ++ MOVD 88(R10), F16 // L6 ++ MOVD 72(R10), F14 // L4 ++ MOVD 56(R10), F12 // L2 ++ // t2 := s4 * (L2 + s4*(L4+s4*L6)) ++ MULD F6, F16, F8 // s4*L6 ++ ADDD F14, F8 // L4+s4*L6 ++ MULD F6, F8 // s4*(L4+s4*L6) ++ ADDD F12, F8 // L2 + s4*(L4+s4*L6) ++ MULD F6, F8 // s4 * (L2 + s4*(L4+s4*L6)) ++ ++ // R := t1 + t2 ++ ADDD F7, F8 ++ ++ // hfsq := 0.5 * f * f ++ MULD F2, F2, F12 // f * f ++ MULD F10, F12, F9 // 0.5 * f * f ++ ++ // return k*Ln2Hi - ((hfsq - (s*(hfsq+R) + k*Ln2Lo)) - f) ++ MOVD 40(R10), F19 // Ln2Lo ++ MOVD 32(R10), F18 // Ln2Hi ++ // f9=hfsq, f1=k, f4=s, f8=R, f2=f ++ ADDD F9, F8, F10 // F10 = hfsq+R ++ MULD F1, F19, F11 // F11 = k*Ln2Lo ++ MULD F10, F4, F12 // F12 = s*(hfsq+R) ++ MULD F1, F18, F15 // F15 = k*Ln2Hi ++ ADDD F12, F11, F13 // F13 = s*(hfsq+R) + k*Ln2Lo ++ SUBD F13, F9, F14 // F14 = hfsq - (s*(hfsq+R) + k*Ln2Lo) ++ SUBD F2, F14, F14 // F14 = (hfsq - (s*(hfsq+R) + k*Ln2Lo)) - f ++ SUBD F14, F15, F0 ++ MOVD F0, ret+8(FP) ++ RET ++ ++isInfOrNaN: ++ MOVV R4, ret+8(FP) // +Inf or NaN, return x ++ RET ++isNegative: ++ MOVV $NaN, R4 ++ MOVV R4, ret+8(FP) // return NaN ++ RET ++isZero: ++ MOVV $NegInf, R4 ++ MOVV R4, ret+8(FP) // return -Inf ++ RET +diff --git a/src/math/log_stub.go b/src/math/log_stub.go +index d35992bf37..1dd4058435 100644 +--- a/src/math/log_stub.go ++++ b/src/math/log_stub.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !amd64 && !s390x ++//go:build !amd64 && !loong64 && !s390x + + package math + +-- +2.38.1 + diff --git a/0044-cmd-go-internal-work-allow-a-bunch-of-loong64-specif.patch b/0044-cmd-go-internal-work-allow-a-bunch-of-loong64-specif.patch new file mode 100644 index 0000000..40422df --- /dev/null +++ b/0044-cmd-go-internal-work-allow-a-bunch-of-loong64-specif.patch @@ -0,0 +1,126 @@ +From fc3470aafbb3facc619e4813eaf0ea10d5c7eda9 Mon Sep 17 00:00:00 2001 +From: WANG Xuerui +Date: Sun, 9 Feb 2025 18:57:49 +0800 +Subject: [PATCH 44/44] cmd/go/internal/work: allow a bunch of loong64-specific + flags + +Recognize and allow all LoongArch-specific CFLAGS as standardized +in the LoongArch Toolchain Conventions v1.1, and implemented in current +versions of GCC and Clang, to enable advanced cgo use cases on loong64. +These flags are also allowed for linker invocations in case of possible +LTO. + +See: https://github.com/loongson/la-toolchain-conventions/blob/releases/v1.1/LoongArch-toolchain-conventions-EN.adoc#list + +While at it, also add support for -mtls-dialect as some C programs +may benefit performance-wise from the optional TLSDESC usage. This flag +is not specific to loong64 though; it is available for amd64, arm, +arm64, loong64, riscv64 and x86. + +Fixes #71597. + +Change-Id: I35d2507edb71fa324ae429a3ae3c739644a9cac1 +--- + src/cmd/go/internal/work/security.go | 13 ++++++++-- + src/cmd/go/internal/work/security_test.go | 31 +++++++++++++++++++++++ + 2 files changed, 42 insertions(+), 2 deletions(-) + +diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go +index 50bfd0ab70..c3d62ddc23 100644 +--- a/src/cmd/go/internal/work/security.go ++++ b/src/cmd/go/internal/work/security.go +@@ -96,17 +96,21 @@ var validCompilerFlags = []*lazyregexp.Regexp{ + re(`-g([^@\-].*)?`), + re(`-m32`), + re(`-m64`), +- re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), ++ re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`), + re(`-m(no-)?v?aes`), + re(`-marm`), + re(`-m(no-)?avx[0-9a-z]*`), + re(`-mcmodel=[0-9a-z-]+`), + re(`-mfloat-abi=([^@\-].*)`), ++ re(`-m(soft|single|double)-float`), + re(`-mfpmath=[0-9a-z,+]*`), + re(`-m(no-)?avx[0-9a-z.]*`), + re(`-m(no-)?ms-bitfields`), + re(`-m(no-)?stack-(.+)`), + re(`-mmacosx-(.+)`), ++ re(`-m(no-)?relax`), ++ re(`-m(no-)?strict-align`), ++ re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`), + re(`-mios-simulator-version-min=(.+)`), + re(`-miphoneos-version-min=(.+)`), + re(`-mlarge-data-threshold=[0-9]+`), +@@ -166,8 +170,13 @@ var validLinkerFlags = []*lazyregexp.Regexp{ + re(`-flat_namespace`), + re(`-g([^@\-].*)?`), + re(`-headerpad_max_install_names`), +- re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), ++ re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`), ++ re(`-mcmodel=[0-9a-z-]+`), + re(`-mfloat-abi=([^@\-].*)`), ++ re(`-m(soft|single|double)-float`), ++ re(`-m(no-)?relax`), ++ re(`-m(no-)?strict-align`), ++ re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`), + re(`-mmacosx-(.+)`), + re(`-mios-simulator-version-min=(.+)`), + re(`-miphoneos-version-min=(.+)`), +diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go +index 35af621764..48f98100a5 100644 +--- a/src/cmd/go/internal/work/security_test.go ++++ b/src/cmd/go/internal/work/security_test.go +@@ -50,10 +50,35 @@ var goodCompilerFlags = [][]string{ + {"-ftls-model=local-dynamic"}, + {"-g"}, + {"-ggdb"}, ++ {"-mabi=lp64d"}, + {"-march=souza"}, + {"-mcmodel=medium"}, + {"-mcpu=123"}, + {"-mfpu=123"}, ++ {"-mtls-dialect=gnu"}, ++ {"-mtls-dialect=gnu2"}, ++ {"-mtls-dialect=trad"}, ++ {"-mtls-dialect=desc"}, ++ {"-mtls-dialect=xyz"}, ++ {"-msimd=lasx"}, ++ {"-msimd=xyz"}, ++ {"-mdouble-float"}, ++ {"-mrelax"}, ++ {"-mstrict-align"}, ++ {"-mlsx"}, ++ {"-mlasx"}, ++ {"-mfrecipe"}, ++ {"-mlam-bh"}, ++ {"-mlamcas"}, ++ {"-mld-seq-sa"}, ++ {"-mno-relax"}, ++ {"-mno-strict-align"}, ++ {"-mno-lsx"}, ++ {"-mno-lasx"}, ++ {"-mno-frecipe"}, ++ {"-mno-lam-bh"}, ++ {"-mno-lamcas"}, ++ {"-mno-ld-seq-sa"}, + {"-mlarge-data-threshold=16"}, + {"-mtune=happybirthday"}, + {"-mstack-overflow"}, +@@ -96,7 +121,13 @@ var badCompilerFlags = [][]string{ + {"-march=@dawn"}, + {"-march=-dawn"}, + {"-mcmodel=@model"}, ++ {"-mfpu=@0"}, ++ {"-mfpu=-0"}, + {"-mlarge-data-threshold=@12"}, ++ {"-mtls-dialect=@gnu"}, ++ {"-mtls-dialect=-gnu"}, ++ {"-msimd=@none"}, ++ {"-msimd=-none"}, + {"-std=@c99"}, + {"-std=-c99"}, + {"-x@c"}, +-- +2.38.1 + diff --git a/golang.spec b/golang.spec index b3d2c41..7bc84e3 100644 --- a/golang.spec +++ b/golang.spec @@ -1,4 +1,4 @@ -%define anolis_release 1 +%define anolis_release 2 # Disable debuginfo packages %global debug_package %{nil} @@ -42,7 +42,11 @@ %endif # Build golang shared objects for stdlib +%ifarch loongarch64 +%bcond_with shared +%else %bcond_without shared +%endif # Pre build std lib with -race enabled # Disabled due to 1.20 new cache usage, see 1.20 upstream release notes @@ -73,8 +77,50 @@ Source0: https://go.dev/dl/go%{go_api}%{?go_patch:.%{go_patch}}.src.tar.g # make possible to override default traceback level at build time by setting build tag rpm_crashtraceback Source1: anolis.go -# Exclude for temporary -ExcludeArch: loongarch64 +Patch1: 0001-cmd-link-internal-add-support-for-internal-linking-o.patch +Patch2: 0002-cmd-dist-internal-platform-enable-internal-linking-f.patch +Patch3: 0003-cmd-runtime-enable-race-detector-on-loong64.patch +Patch4: 0004-runtime-delete-on-register-ABI-fallback-path-for-rac.patch +Patch5: 0005-cmd-internal-obj-loong64-remove-unused-register-alia.patch +Patch6: 0006-internal-bytealg-optimize-IndexByte-and-IndexByteStr.patch +Patch7: 0007-internal-bytealg-optimize-memequal-and-memequal_varl.patch +Patch8: 0008-internal-bytealg-optimize-Index-and-IndexString-func.patch +Patch9: 0009-internal-bytealg-optimize-Count-and-CountString-func.patch +Patch10: 0010-internal-bytealg-adjust-the-format-of-assembly-files.patch +Patch11: 0011-cmd-internal-obj-loong64-optimize-immediate-loading.patch +Patch12: 0012-math-big-optimize-addVV-function-for-loong64.patch +Patch13: 0013-math-big-optimize-addVW-function-for-loong64.patch +Patch14: 0014-math-big-optimize-subVV-function-for-loong64.patch +Patch15: 0015-math-big-optimize-subVW-function-for-loong64.patch +Patch16: 0016-math-big-optimize-shlVU-function-for-loong64.patch +Patch17: 0017-math-big-optimize-shrVU-function-for-loong64.patch +Patch18: 0018-math-big-optimize-mulAddVWW-function-for-loong64.patch +Patch19: 0019-math-big-optimize-addMulVVW-function-for-loong64.patch +Patch20: 0020-cmd-compile-fold-constant-shift-with-extension-on-lo.patch +Patch21: 0021-test-codegen-fix-the-matching-instructions-inside-pl.patch +Patch22: 0022-cmd-compile-optimize-shifts-of-int32-and-uint32-on-l.patch +Patch23: 0023-cmd-compile-simplify-bounded-shift-on-loong64.patch +Patch24: 0024-runtime-use-ABIInternal-on-syscall-and-other-sys.stu.patch +Patch25: 0025-runtime-use-correct-memory-barrier-in-exitThread-fun.patch +Patch26: 0026-cmd-internal-obj-loong64-add-V-XV-SEQI-V-XV-.-AND-OR.patch +Patch27: 0027-cmd-internal-obj-loong64-add-V-XV-ADD-SUB-.-B-H-W-D-.patch +Patch28: 0028-cmd-internal-obj-loong64-add-V-XV-ILV-L-H-.-B-H-W-D-.patch +Patch29: 0029-cmd-internal-obj-loong64-add-V-XV-SLL-SRL-SRA-ROTR-I.patch +Patch30: 0030-cmd-internal-obj-loong64-add-V-XV-FSQRT-FRECIP-FRSQR.patch +Patch31: 0031-cmd-internal-obj-loong64-add-V-XV-NEG-B-H-W-V-instru.patch +Patch32: 0032-cmd-internal-obj-loong64-add-V-XV-MUL-B-H-W-V-and-V-.patch +Patch33: 0033-cmd-internal-obj-loong64-add-V-XV-DIV-B-H-W-V-U-and-.patch +Patch34: 0034-cmd-internal-obj-loong64-add-V-XV-BITCLR-BITSET-BITR.patch +Patch35: 0035-crypto-chacha20-add-loong64-SIMD-implementation.patch +Patch36: 0036-internal-bytealg-optimize-Count-String-in-loong64.patch +Patch37: 0037-cmd-internal-obj-cmd-asm-reclassify-32-bit-immediate.patch +Patch38: 0038-crypto-internal-poly1305-implement-function-update-i.patch +Patch39: 0039-runtime-optimize-the-implementation-of-memclrNoHeapP.patch +Patch40: 0040-runtime-race-add-the-implementation-of-atomic.-Or-An.patch +Patch41: 0041-cmd-internal-obj-loong64-add-F-MAXA-MINA-.-S-D-instr.patch +Patch42: 0042-math-implement-func-archExp-and-archExp2-in-assembly.patch +Patch43: 0043-math-implement-func-archLog-in-assembly-on-loong64.patch +Patch44: 0044-cmd-go-internal-work-allow-a-bunch-of-loong64-specif.patch # The compiler is written in Go. Needs go(1.4+) compiler for build. %if %{with bootstrap} @@ -545,6 +591,13 @@ fi %files docs -f go-docs.list %changelog +* Mon Feb 24 2025 limeidan - 1.24.0-2 +- add internal linker support on loong64 +- optimize the internal/bytealg package on loong64 +- optimize the math/big package on loong64 +- add new instructions support on loong64 +- optimize memory operation function of runtime on loong64 + * Tue Feb 18 2025 gaochang - 1.24.0-1 - update to 1.24.0 @@ -554,7 +607,7 @@ fi * Wed Jul 10 2024 yangxinyu - 1.21.11-1 - update to 1.21.11 fix cve-2024-24789 -* Thu Mon 13 2024 chenguoqi - 1.21.10-2 +* Thu Jun 13 2024 chenguoqi - 1.21.10-2 - add buildmode={plugin,shared} support on linux/loong64 - asan and msan support on linux/loong64 - loong64 disassembler support diff --git a/race_linux_loong64.syso b/race_linux_loong64.syso index 6fdb3bad77751956e4c1ee6c0732ddcc3a7fc3dc..0d2b4946fbf31abc042ea4ee852785cb13cce5a6 100644 GIT binary patch literal 1412368 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 xeKpX;b2*e=}hd>+xaR|gA5Qjh<0&xh$ zArOZ^90G9 xeKpX;b2*e=}hd>+xaR|gA5Qjh<0&xh$ArOZ^90G9 xeKpX;b z2*e=}hd>+xaR|gA5Qjh<0&xh$ArOZ^90G9 xeKpX;b2*e=}hd>+xaR|gA5Qjh< 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 c>I_TKRy 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
)eT?n4555sH^ayN<#!DeffNfpT`><2QFc&7b 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�LiTaVv$iz;9I+jBL#(Op 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