mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[chore] Update gin to v1.9.0 (#1553)
This commit is contained in:
716
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arch.go
generated
vendored
Normal file
716
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arch.go
generated
vendored
Normal file
@ -0,0 +1,716 @@
|
||||
// Copyright 2015 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 arch defines architecture-specific information and support functions.
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/arm"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/arm64"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/mips"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/ppc64"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/riscv"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/s390x"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/wasm"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/x86"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pseudo-registers whose names are the constant name without the leading R.
|
||||
const (
|
||||
RFP = -(iota + 1)
|
||||
RSB
|
||||
RSP
|
||||
RPC
|
||||
)
|
||||
|
||||
// Arch wraps the link architecture object with more architecture-specific information.
|
||||
type Arch struct {
|
||||
*obj.LinkArch
|
||||
// Map of instruction names to enumeration.
|
||||
Instructions map[string]obj.As
|
||||
// Map of register names to enumeration.
|
||||
Register map[string]int16
|
||||
// Table of register prefix names. These are things like R for R(0) and SPR for SPR(268).
|
||||
RegisterPrefix map[string]bool
|
||||
// RegisterNumber converts R(10) into arm.REG_R10.
|
||||
RegisterNumber func(string, int16) (int16, bool)
|
||||
// Instruction is a jump.
|
||||
IsJump func(word string) bool
|
||||
}
|
||||
|
||||
// nilRegisterNumber is the register number function for architectures
|
||||
// that do not accept the R(N) notation. It always returns failure.
|
||||
func nilRegisterNumber(name string, n int16) (int16, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Set configures the architecture specified by GOARCH and returns its representation.
|
||||
// It returns nil if GOARCH is not recognized.
|
||||
func Set(GOARCH string) *Arch {
|
||||
switch GOARCH {
|
||||
case "386":
|
||||
return archX86(&x86.Link386)
|
||||
case "amd64":
|
||||
return archX86(&x86.Linkamd64)
|
||||
case "arm":
|
||||
return archArm()
|
||||
case "arm64":
|
||||
return archArm64()
|
||||
case "mips":
|
||||
return archMips(&mips.Linkmips)
|
||||
case "mipsle":
|
||||
return archMips(&mips.Linkmipsle)
|
||||
case "mips64":
|
||||
return archMips64(&mips.Linkmips64)
|
||||
case "mips64le":
|
||||
return archMips64(&mips.Linkmips64le)
|
||||
case "ppc64":
|
||||
return archPPC64(&ppc64.Linkppc64)
|
||||
case "ppc64le":
|
||||
return archPPC64(&ppc64.Linkppc64le)
|
||||
case "riscv64":
|
||||
return archRISCV64()
|
||||
case "s390x":
|
||||
return archS390x()
|
||||
case "wasm":
|
||||
return archWasm()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func jumpX86(word string) bool {
|
||||
return word[0] == 'J' || word == "CALL" || strings.HasPrefix(word, "LOOP") || word == "XBEGIN"
|
||||
}
|
||||
|
||||
func jumpRISCV(word string) bool {
|
||||
switch word {
|
||||
case "BEQ", "BEQZ", "BGE", "BGEU", "BGEZ", "BGT", "BGTU", "BGTZ", "BLE", "BLEU", "BLEZ",
|
||||
"BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "JAL", "JALR", "JMP":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func jumpWasm(word string) bool {
|
||||
return word == "JMP" || word == "CALL" || word == "Call" || word == "Br" || word == "BrIf"
|
||||
}
|
||||
|
||||
func archX86(linkArch *obj.LinkArch) *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
for i, s := range x86.Register {
|
||||
register[s] = int16(i + x86.REG_AL)
|
||||
}
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Register prefix not used on this architecture.
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range x86.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseAMD64
|
||||
}
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["JA"] = x86.AJHI /* alternate */
|
||||
instructions["JAE"] = x86.AJCC /* alternate */
|
||||
instructions["JB"] = x86.AJCS /* alternate */
|
||||
instructions["JBE"] = x86.AJLS /* alternate */
|
||||
instructions["JC"] = x86.AJCS /* alternate */
|
||||
instructions["JCC"] = x86.AJCC /* carry clear (CF = 0) */
|
||||
instructions["JCS"] = x86.AJCS /* carry set (CF = 1) */
|
||||
instructions["JE"] = x86.AJEQ /* alternate */
|
||||
instructions["JEQ"] = x86.AJEQ /* equal (ZF = 1) */
|
||||
instructions["JG"] = x86.AJGT /* alternate */
|
||||
instructions["JGE"] = x86.AJGE /* greater than or equal (signed) (SF = OF) */
|
||||
instructions["JGT"] = x86.AJGT /* greater than (signed) (ZF = 0 && SF = OF) */
|
||||
instructions["JHI"] = x86.AJHI /* higher (unsigned) (CF = 0 && ZF = 0) */
|
||||
instructions["JHS"] = x86.AJCC /* alternate */
|
||||
instructions["JL"] = x86.AJLT /* alternate */
|
||||
instructions["JLE"] = x86.AJLE /* less than or equal (signed) (ZF = 1 || SF != OF) */
|
||||
instructions["JLO"] = x86.AJCS /* alternate */
|
||||
instructions["JLS"] = x86.AJLS /* lower or same (unsigned) (CF = 1 || ZF = 1) */
|
||||
instructions["JLT"] = x86.AJLT /* less than (signed) (SF != OF) */
|
||||
instructions["JMI"] = x86.AJMI /* negative (minus) (SF = 1) */
|
||||
instructions["JNA"] = x86.AJLS /* alternate */
|
||||
instructions["JNAE"] = x86.AJCS /* alternate */
|
||||
instructions["JNB"] = x86.AJCC /* alternate */
|
||||
instructions["JNBE"] = x86.AJHI /* alternate */
|
||||
instructions["JNC"] = x86.AJCC /* alternate */
|
||||
instructions["JNE"] = x86.AJNE /* not equal (ZF = 0) */
|
||||
instructions["JNG"] = x86.AJLE /* alternate */
|
||||
instructions["JNGE"] = x86.AJLT /* alternate */
|
||||
instructions["JNL"] = x86.AJGE /* alternate */
|
||||
instructions["JNLE"] = x86.AJGT /* alternate */
|
||||
instructions["JNO"] = x86.AJOC /* alternate */
|
||||
instructions["JNP"] = x86.AJPC /* alternate */
|
||||
instructions["JNS"] = x86.AJPL /* alternate */
|
||||
instructions["JNZ"] = x86.AJNE /* alternate */
|
||||
instructions["JO"] = x86.AJOS /* alternate */
|
||||
instructions["JOC"] = x86.AJOC /* overflow clear (OF = 0) */
|
||||
instructions["JOS"] = x86.AJOS /* overflow set (OF = 1) */
|
||||
instructions["JP"] = x86.AJPS /* alternate */
|
||||
instructions["JPC"] = x86.AJPC /* parity clear (PF = 0) */
|
||||
instructions["JPE"] = x86.AJPS /* alternate */
|
||||
instructions["JPL"] = x86.AJPL /* non-negative (plus) (SF = 0) */
|
||||
instructions["JPO"] = x86.AJPC /* alternate */
|
||||
instructions["JPS"] = x86.AJPS /* parity set (PF = 1) */
|
||||
instructions["JS"] = x86.AJMI /* alternate */
|
||||
instructions["JZ"] = x86.AJEQ /* alternate */
|
||||
instructions["MASKMOVDQU"] = x86.AMASKMOVOU
|
||||
instructions["MOVD"] = x86.AMOVQ
|
||||
instructions["MOVDQ2Q"] = x86.AMOVQ
|
||||
instructions["MOVNTDQ"] = x86.AMOVNTO
|
||||
instructions["MOVOA"] = x86.AMOVO
|
||||
instructions["PSLLDQ"] = x86.APSLLO
|
||||
instructions["PSRLDQ"] = x86.APSRLO
|
||||
instructions["PADDD"] = x86.APADDL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: linkArch,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: nil,
|
||||
RegisterNumber: nilRegisterNumber,
|
||||
IsJump: jumpX86,
|
||||
}
|
||||
}
|
||||
|
||||
func archArm() *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for x86.
|
||||
for i := arm.REG_R0; i < arm.REG_SPSR; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
// Avoid unintentionally clobbering g using R10.
|
||||
delete(register, "R10")
|
||||
register["g"] = arm.REG_R10
|
||||
for i := 0; i < 16; i++ {
|
||||
register[fmt.Sprintf("C%d", i)] = int16(i)
|
||||
}
|
||||
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
register["SP"] = RSP
|
||||
registerPrefix := map[string]bool{
|
||||
"F": true,
|
||||
"R": true,
|
||||
}
|
||||
|
||||
// special operands for DMB/DSB instructions
|
||||
register["MB_SY"] = arm.REG_MB_SY
|
||||
register["MB_ST"] = arm.REG_MB_ST
|
||||
register["MB_ISH"] = arm.REG_MB_ISH
|
||||
register["MB_ISHST"] = arm.REG_MB_ISHST
|
||||
register["MB_NSH"] = arm.REG_MB_NSH
|
||||
register["MB_NSHST"] = arm.REG_MB_NSHST
|
||||
register["MB_OSH"] = arm.REG_MB_OSH
|
||||
register["MB_OSHST"] = arm.REG_MB_OSHST
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range arm.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseARM
|
||||
}
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["B"] = obj.AJMP
|
||||
instructions["BL"] = obj.ACALL
|
||||
// MCR differs from MRC by the way fields of the word are encoded.
|
||||
// (Details in arm.go). Here we add the instruction so parse will find
|
||||
// it, but give it an opcode number known only to us.
|
||||
instructions["MCR"] = aMCR
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &arm.Linkarm,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: armRegisterNumber,
|
||||
IsJump: jumpArm,
|
||||
}
|
||||
}
|
||||
|
||||
func archArm64() *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for 386 and amd64.
|
||||
register[obj.Rconv(arm64.REGSP)] = int16(arm64.REGSP)
|
||||
for i := arm64.REG_R0; i <= arm64.REG_R31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
// Rename R18 to R18_PLATFORM to avoid accidental use.
|
||||
register["R18_PLATFORM"] = register["R18"]
|
||||
delete(register, "R18")
|
||||
for i := arm64.REG_F0; i <= arm64.REG_F31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := arm64.REG_V0; i <= arm64.REG_V31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
|
||||
// System registers.
|
||||
for i := 0; i < len(arm64.SystemReg); i++ {
|
||||
register[arm64.SystemReg[i].Name] = arm64.SystemReg[i].Reg
|
||||
}
|
||||
|
||||
register["LR"] = arm64.REGLINK
|
||||
register["DAIFSet"] = arm64.REG_DAIFSet
|
||||
register["DAIFClr"] = arm64.REG_DAIFClr
|
||||
register["PLDL1KEEP"] = arm64.REG_PLDL1KEEP
|
||||
register["PLDL1STRM"] = arm64.REG_PLDL1STRM
|
||||
register["PLDL2KEEP"] = arm64.REG_PLDL2KEEP
|
||||
register["PLDL2STRM"] = arm64.REG_PLDL2STRM
|
||||
register["PLDL3KEEP"] = arm64.REG_PLDL3KEEP
|
||||
register["PLDL3STRM"] = arm64.REG_PLDL3STRM
|
||||
register["PLIL1KEEP"] = arm64.REG_PLIL1KEEP
|
||||
register["PLIL1STRM"] = arm64.REG_PLIL1STRM
|
||||
register["PLIL2KEEP"] = arm64.REG_PLIL2KEEP
|
||||
register["PLIL2STRM"] = arm64.REG_PLIL2STRM
|
||||
register["PLIL3KEEP"] = arm64.REG_PLIL3KEEP
|
||||
register["PLIL3STRM"] = arm64.REG_PLIL3STRM
|
||||
register["PSTL1KEEP"] = arm64.REG_PSTL1KEEP
|
||||
register["PSTL1STRM"] = arm64.REG_PSTL1STRM
|
||||
register["PSTL2KEEP"] = arm64.REG_PSTL2KEEP
|
||||
register["PSTL2STRM"] = arm64.REG_PSTL2STRM
|
||||
register["PSTL3KEEP"] = arm64.REG_PSTL3KEEP
|
||||
register["PSTL3STRM"] = arm64.REG_PSTL3STRM
|
||||
|
||||
// Conditional operators, like EQ, NE, etc.
|
||||
register["EQ"] = arm64.COND_EQ
|
||||
register["NE"] = arm64.COND_NE
|
||||
register["HS"] = arm64.COND_HS
|
||||
register["CS"] = arm64.COND_HS
|
||||
register["LO"] = arm64.COND_LO
|
||||
register["CC"] = arm64.COND_LO
|
||||
register["MI"] = arm64.COND_MI
|
||||
register["PL"] = arm64.COND_PL
|
||||
register["VS"] = arm64.COND_VS
|
||||
register["VC"] = arm64.COND_VC
|
||||
register["HI"] = arm64.COND_HI
|
||||
register["LS"] = arm64.COND_LS
|
||||
register["GE"] = arm64.COND_GE
|
||||
register["LT"] = arm64.COND_LT
|
||||
register["GT"] = arm64.COND_GT
|
||||
register["LE"] = arm64.COND_LE
|
||||
register["AL"] = arm64.COND_AL
|
||||
register["NV"] = arm64.COND_NV
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
register["SP"] = RSP
|
||||
// Avoid unintentionally clobbering g using R28.
|
||||
delete(register, "R28")
|
||||
register["g"] = arm64.REG_R28
|
||||
registerPrefix := map[string]bool{
|
||||
"F": true,
|
||||
"R": true,
|
||||
"V": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range arm64.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseARM64
|
||||
}
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["B"] = arm64.AB
|
||||
instructions["BL"] = arm64.ABL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &arm64.Linkarm64,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: arm64RegisterNumber,
|
||||
IsJump: jumpArm64,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func archPPC64(linkArch *obj.LinkArch) *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for x86.
|
||||
for i := ppc64.REG_R0; i <= ppc64.REG_R31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_F0; i <= ppc64.REG_F31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_V0; i <= ppc64.REG_V31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_VS0; i <= ppc64.REG_VS63; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_CR0; i <= ppc64.REG_CR7; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
register["CR"] = ppc64.REG_CR
|
||||
register["XER"] = ppc64.REG_XER
|
||||
register["LR"] = ppc64.REG_LR
|
||||
register["CTR"] = ppc64.REG_CTR
|
||||
register["FPSCR"] = ppc64.REG_FPSCR
|
||||
register["MSR"] = ppc64.REG_MSR
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Avoid unintentionally clobbering g using R30.
|
||||
delete(register, "R30")
|
||||
register["g"] = ppc64.REG_R30
|
||||
registerPrefix := map[string]bool{
|
||||
"CR": true,
|
||||
"F": true,
|
||||
"R": true,
|
||||
"SPR": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range ppc64.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABasePPC64
|
||||
}
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["BR"] = ppc64.ABR
|
||||
instructions["BL"] = ppc64.ABL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: linkArch,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: ppc64RegisterNumber,
|
||||
IsJump: jumpPPC64,
|
||||
}
|
||||
}
|
||||
|
||||
func archMips(linkArch *obj.LinkArch) *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for x86.
|
||||
for i := mips.REG_R0; i <= mips.REG_R31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
|
||||
for i := mips.REG_F0; i <= mips.REG_F31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_M0; i <= mips.REG_M31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
register["HI"] = mips.REG_HI
|
||||
register["LO"] = mips.REG_LO
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Avoid unintentionally clobbering g using R30.
|
||||
delete(register, "R30")
|
||||
register["g"] = mips.REG_R30
|
||||
|
||||
registerPrefix := map[string]bool{
|
||||
"F": true,
|
||||
"FCR": true,
|
||||
"M": true,
|
||||
"R": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range mips.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseMIPS
|
||||
}
|
||||
}
|
||||
// Annoying alias.
|
||||
instructions["JAL"] = mips.AJAL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: linkArch,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: mipsRegisterNumber,
|
||||
IsJump: jumpMIPS,
|
||||
}
|
||||
}
|
||||
|
||||
func archMips64(linkArch *obj.LinkArch) *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for x86.
|
||||
for i := mips.REG_R0; i <= mips.REG_R31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_F0; i <= mips.REG_F31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_M0; i <= mips.REG_M31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := mips.REG_W0; i <= mips.REG_W31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
register["HI"] = mips.REG_HI
|
||||
register["LO"] = mips.REG_LO
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Avoid unintentionally clobbering g using R30.
|
||||
delete(register, "R30")
|
||||
register["g"] = mips.REG_R30
|
||||
// Avoid unintentionally clobbering RSB using R28.
|
||||
delete(register, "R28")
|
||||
register["RSB"] = mips.REG_R28
|
||||
registerPrefix := map[string]bool{
|
||||
"F": true,
|
||||
"FCR": true,
|
||||
"M": true,
|
||||
"R": true,
|
||||
"W": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range mips.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseMIPS
|
||||
}
|
||||
}
|
||||
// Annoying alias.
|
||||
instructions["JAL"] = mips.AJAL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: linkArch,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: mipsRegisterNumber,
|
||||
IsJump: jumpMIPS,
|
||||
}
|
||||
}
|
||||
|
||||
func archRISCV64() *Arch {
|
||||
register := make(map[string]int16)
|
||||
|
||||
// Standard register names.
|
||||
for i := riscv.REG_X0; i <= riscv.REG_X31; i++ {
|
||||
name := fmt.Sprintf("X%d", i-riscv.REG_X0)
|
||||
register[name] = int16(i)
|
||||
}
|
||||
for i := riscv.REG_F0; i <= riscv.REG_F31; i++ {
|
||||
name := fmt.Sprintf("F%d", i-riscv.REG_F0)
|
||||
register[name] = int16(i)
|
||||
}
|
||||
|
||||
// General registers with ABI names.
|
||||
register["ZERO"] = riscv.REG_ZERO
|
||||
register["RA"] = riscv.REG_RA
|
||||
register["SP"] = riscv.REG_SP
|
||||
register["GP"] = riscv.REG_GP
|
||||
register["TP"] = riscv.REG_TP
|
||||
register["T0"] = riscv.REG_T0
|
||||
register["T1"] = riscv.REG_T1
|
||||
register["T2"] = riscv.REG_T2
|
||||
register["S0"] = riscv.REG_S0
|
||||
register["S1"] = riscv.REG_S1
|
||||
register["A0"] = riscv.REG_A0
|
||||
register["A1"] = riscv.REG_A1
|
||||
register["A2"] = riscv.REG_A2
|
||||
register["A3"] = riscv.REG_A3
|
||||
register["A4"] = riscv.REG_A4
|
||||
register["A5"] = riscv.REG_A5
|
||||
register["A6"] = riscv.REG_A6
|
||||
register["A7"] = riscv.REG_A7
|
||||
register["S2"] = riscv.REG_S2
|
||||
register["S3"] = riscv.REG_S3
|
||||
register["S4"] = riscv.REG_S4
|
||||
register["S5"] = riscv.REG_S5
|
||||
register["S6"] = riscv.REG_S6
|
||||
register["S7"] = riscv.REG_S7
|
||||
register["S8"] = riscv.REG_S8
|
||||
register["S9"] = riscv.REG_S9
|
||||
register["S10"] = riscv.REG_S10
|
||||
register["S11"] = riscv.REG_S11
|
||||
register["T3"] = riscv.REG_T3
|
||||
register["T4"] = riscv.REG_T4
|
||||
register["T5"] = riscv.REG_T5
|
||||
register["T6"] = riscv.REG_T6
|
||||
|
||||
// Go runtime register names.
|
||||
register["g"] = riscv.REG_G
|
||||
register["CTXT"] = riscv.REG_CTXT
|
||||
register["TMP"] = riscv.REG_TMP
|
||||
|
||||
// ABI names for floating point register.
|
||||
register["FT0"] = riscv.REG_FT0
|
||||
register["FT1"] = riscv.REG_FT1
|
||||
register["FT2"] = riscv.REG_FT2
|
||||
register["FT3"] = riscv.REG_FT3
|
||||
register["FT4"] = riscv.REG_FT4
|
||||
register["FT5"] = riscv.REG_FT5
|
||||
register["FT6"] = riscv.REG_FT6
|
||||
register["FT7"] = riscv.REG_FT7
|
||||
register["FS0"] = riscv.REG_FS0
|
||||
register["FS1"] = riscv.REG_FS1
|
||||
register["FA0"] = riscv.REG_FA0
|
||||
register["FA1"] = riscv.REG_FA1
|
||||
register["FA2"] = riscv.REG_FA2
|
||||
register["FA3"] = riscv.REG_FA3
|
||||
register["FA4"] = riscv.REG_FA4
|
||||
register["FA5"] = riscv.REG_FA5
|
||||
register["FA6"] = riscv.REG_FA6
|
||||
register["FA7"] = riscv.REG_FA7
|
||||
register["FS2"] = riscv.REG_FS2
|
||||
register["FS3"] = riscv.REG_FS3
|
||||
register["FS4"] = riscv.REG_FS4
|
||||
register["FS5"] = riscv.REG_FS5
|
||||
register["FS6"] = riscv.REG_FS6
|
||||
register["FS7"] = riscv.REG_FS7
|
||||
register["FS8"] = riscv.REG_FS8
|
||||
register["FS9"] = riscv.REG_FS9
|
||||
register["FS10"] = riscv.REG_FS10
|
||||
register["FS11"] = riscv.REG_FS11
|
||||
register["FT8"] = riscv.REG_FT8
|
||||
register["FT9"] = riscv.REG_FT9
|
||||
register["FT10"] = riscv.REG_FT10
|
||||
register["FT11"] = riscv.REG_FT11
|
||||
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range riscv.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseRISCV
|
||||
}
|
||||
}
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &riscv.LinkRISCV64,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: nil,
|
||||
RegisterNumber: nilRegisterNumber,
|
||||
IsJump: jumpRISCV,
|
||||
}
|
||||
}
|
||||
|
||||
func archS390x() *Arch {
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// Note that there is no list of names as there is for x86.
|
||||
for i := s390x.REG_R0; i <= s390x.REG_R15; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := s390x.REG_F0; i <= s390x.REG_F15; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := s390x.REG_V0; i <= s390x.REG_V31; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := s390x.REG_AR0; i <= s390x.REG_AR15; i++ {
|
||||
register[obj.Rconv(i)] = int16(i)
|
||||
}
|
||||
register["LR"] = s390x.REG_LR
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Avoid unintentionally clobbering g using R13.
|
||||
delete(register, "R13")
|
||||
register["g"] = s390x.REG_R13
|
||||
registerPrefix := map[string]bool{
|
||||
"AR": true,
|
||||
"F": true,
|
||||
"R": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range s390x.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseS390X
|
||||
}
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["BR"] = s390x.ABR
|
||||
instructions["BL"] = s390x.ABL
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &s390x.Links390x,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: s390xRegisterNumber,
|
||||
IsJump: jumpS390x,
|
||||
}
|
||||
}
|
||||
|
||||
func archWasm() *Arch {
|
||||
instructions := make(map[string]obj.As)
|
||||
for i, s := range obj.Anames {
|
||||
instructions[s] = obj.As(i)
|
||||
}
|
||||
for i, s := range wasm.Anames {
|
||||
if obj.As(i) >= obj.A_ARCHSPECIFIC {
|
||||
instructions[s] = obj.As(i) + obj.ABaseWasm
|
||||
}
|
||||
}
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &wasm.Linkwasm,
|
||||
Instructions: instructions,
|
||||
Register: wasm.Register,
|
||||
RegisterPrefix: nil,
|
||||
RegisterNumber: nilRegisterNumber,
|
||||
IsJump: jumpWasm,
|
||||
}
|
||||
}
|
257
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arm.go
generated
vendored
Normal file
257
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arm.go
generated
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the ARM
|
||||
// instruction set, to minimize its interaction with the core of the
|
||||
// assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/arm"
|
||||
)
|
||||
|
||||
var armLS = map[string]uint8{
|
||||
"U": arm.C_UBIT,
|
||||
"S": arm.C_SBIT,
|
||||
"W": arm.C_WBIT,
|
||||
"P": arm.C_PBIT,
|
||||
"PW": arm.C_WBIT | arm.C_PBIT,
|
||||
"WP": arm.C_WBIT | arm.C_PBIT,
|
||||
}
|
||||
|
||||
var armSCOND = map[string]uint8{
|
||||
"EQ": arm.C_SCOND_EQ,
|
||||
"NE": arm.C_SCOND_NE,
|
||||
"CS": arm.C_SCOND_HS,
|
||||
"HS": arm.C_SCOND_HS,
|
||||
"CC": arm.C_SCOND_LO,
|
||||
"LO": arm.C_SCOND_LO,
|
||||
"MI": arm.C_SCOND_MI,
|
||||
"PL": arm.C_SCOND_PL,
|
||||
"VS": arm.C_SCOND_VS,
|
||||
"VC": arm.C_SCOND_VC,
|
||||
"HI": arm.C_SCOND_HI,
|
||||
"LS": arm.C_SCOND_LS,
|
||||
"GE": arm.C_SCOND_GE,
|
||||
"LT": arm.C_SCOND_LT,
|
||||
"GT": arm.C_SCOND_GT,
|
||||
"LE": arm.C_SCOND_LE,
|
||||
"AL": arm.C_SCOND_NONE,
|
||||
"U": arm.C_UBIT,
|
||||
"S": arm.C_SBIT,
|
||||
"W": arm.C_WBIT,
|
||||
"P": arm.C_PBIT,
|
||||
"PW": arm.C_WBIT | arm.C_PBIT,
|
||||
"WP": arm.C_WBIT | arm.C_PBIT,
|
||||
"F": arm.C_FBIT,
|
||||
"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
|
||||
"IAW": arm.C_WBIT | arm.C_UBIT,
|
||||
"DBW": arm.C_WBIT | arm.C_PBIT,
|
||||
"DAW": arm.C_WBIT,
|
||||
"IB": arm.C_PBIT | arm.C_UBIT,
|
||||
"IA": arm.C_UBIT,
|
||||
"DB": arm.C_PBIT,
|
||||
"DA": 0,
|
||||
}
|
||||
|
||||
var armJump = map[string]bool{
|
||||
"B": true,
|
||||
"BL": true,
|
||||
"BX": true,
|
||||
"BEQ": true,
|
||||
"BNE": true,
|
||||
"BCS": true,
|
||||
"BHS": true,
|
||||
"BCC": true,
|
||||
"BLO": true,
|
||||
"BMI": true,
|
||||
"BPL": true,
|
||||
"BVS": true,
|
||||
"BVC": true,
|
||||
"BHI": true,
|
||||
"BLS": true,
|
||||
"BGE": true,
|
||||
"BLT": true,
|
||||
"BGT": true,
|
||||
"BLE": true,
|
||||
"CALL": true,
|
||||
"JMP": true,
|
||||
}
|
||||
|
||||
func jumpArm(word string) bool {
|
||||
return armJump[word]
|
||||
}
|
||||
|
||||
// IsARMCMP reports whether the op (as defined by an arm.A* constant) is
|
||||
// one of the comparison instructions that require special handling.
|
||||
func IsARMCMP(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
|
||||
// one of the STREX-like instructions that require special handling.
|
||||
func IsARMSTREX(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MCR is not defined by the obj/arm; instead we define it privately here.
|
||||
// It is encoded as an MRC with a bit inside the instruction word,
|
||||
// passed to arch.ARMMRCOffset.
|
||||
const aMCR = arm.ALAST + 1
|
||||
|
||||
// IsARMMRC reports whether the op (as defined by an arm.A* constant) is
|
||||
// MRC or MCR
|
||||
func IsARMMRC(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
|
||||
// BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
|
||||
func IsARMBFX(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsARMFloatCmp reports whether the op is a floating comparison instruction.
|
||||
func IsARMFloatCmp(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.ACMPF, arm.ACMPD:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
|
||||
// The difference between MRC and MCR is represented by a bit high in the word, not
|
||||
// in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
|
||||
// we return the opcode for MRC so that asm doesn't need to import obj/arm.
|
||||
func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
|
||||
op1 := int64(0)
|
||||
if op == arm.AMRC {
|
||||
op1 = 1
|
||||
}
|
||||
bits, ok := ParseARMCondition(cond)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
offset = (0xe << 24) | // opcode
|
||||
(op1 << 20) | // MCR/MRC
|
||||
((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
|
||||
((x0 & 15) << 8) | //coprocessor number
|
||||
((x1 & 7) << 21) | // coprocessor operation
|
||||
((x2 & 15) << 12) | // ARM register
|
||||
((x3 & 15) << 16) | // Crn
|
||||
((x4 & 15) << 0) | // Crm
|
||||
((x5 & 7) << 5) | // coprocessor information
|
||||
(1 << 4) /* must be set */
|
||||
return offset, arm.AMRC, true
|
||||
}
|
||||
|
||||
// IsARMMULA reports whether the op (as defined by an arm.A* constant) is
|
||||
// MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
|
||||
func IsARMMULA(op obj.As) bool {
|
||||
switch op {
|
||||
case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var bcode = []obj.As{
|
||||
arm.ABEQ,
|
||||
arm.ABNE,
|
||||
arm.ABCS,
|
||||
arm.ABCC,
|
||||
arm.ABMI,
|
||||
arm.ABPL,
|
||||
arm.ABVS,
|
||||
arm.ABVC,
|
||||
arm.ABHI,
|
||||
arm.ABLS,
|
||||
arm.ABGE,
|
||||
arm.ABLT,
|
||||
arm.ABGT,
|
||||
arm.ABLE,
|
||||
arm.AB,
|
||||
obj.ANOP,
|
||||
}
|
||||
|
||||
// ARMConditionCodes handles the special condition code situation for the ARM.
|
||||
// It returns a boolean to indicate success; failure means cond was unrecognized.
|
||||
func ARMConditionCodes(prog *obj.Prog, cond string) bool {
|
||||
if cond == "" {
|
||||
return true
|
||||
}
|
||||
bits, ok := ParseARMCondition(cond)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
/* hack to make B.NE etc. work: turn it into the corresponding conditional */
|
||||
if prog.As == arm.AB {
|
||||
prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
|
||||
bits = (bits &^ 0xf) | arm.C_SCOND_NONE
|
||||
}
|
||||
prog.Scond = bits
|
||||
return true
|
||||
}
|
||||
|
||||
// ParseARMCondition parses the conditions attached to an ARM instruction.
|
||||
// The input is a single string consisting of period-separated condition
|
||||
// codes, such as ".P.W". An initial period is ignored.
|
||||
func ParseARMCondition(cond string) (uint8, bool) {
|
||||
return parseARMCondition(cond, armLS, armSCOND)
|
||||
}
|
||||
|
||||
func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
|
||||
cond = strings.TrimPrefix(cond, ".")
|
||||
if cond == "" {
|
||||
return arm.C_SCOND_NONE, true
|
||||
}
|
||||
names := strings.Split(cond, ".")
|
||||
bits := uint8(0)
|
||||
for _, name := range names {
|
||||
if b, present := ls[name]; present {
|
||||
bits |= b
|
||||
continue
|
||||
}
|
||||
if b, present := scond[name]; present {
|
||||
bits = (bits &^ arm.C_SCOND) | b
|
||||
continue
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
return bits, true
|
||||
}
|
||||
|
||||
func armRegisterNumber(name string, n int16) (int16, bool) {
|
||||
if n < 0 || 15 < n {
|
||||
return 0, false
|
||||
}
|
||||
switch name {
|
||||
case "R":
|
||||
return arm.REG_R0 + n, true
|
||||
case "F":
|
||||
return arm.REG_F0 + n, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
350
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arm64.go
generated
vendored
Normal file
350
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/arm64.go
generated
vendored
Normal file
@ -0,0 +1,350 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the ARM64
|
||||
// instruction set, to minimize its interaction with the core of the
|
||||
// assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/arm64"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var arm64LS = map[string]uint8{
|
||||
"P": arm64.C_XPOST,
|
||||
"W": arm64.C_XPRE,
|
||||
}
|
||||
|
||||
var arm64Jump = map[string]bool{
|
||||
"B": true,
|
||||
"BL": true,
|
||||
"BEQ": true,
|
||||
"BNE": true,
|
||||
"BCS": true,
|
||||
"BHS": true,
|
||||
"BCC": true,
|
||||
"BLO": true,
|
||||
"BMI": true,
|
||||
"BPL": true,
|
||||
"BVS": true,
|
||||
"BVC": true,
|
||||
"BHI": true,
|
||||
"BLS": true,
|
||||
"BGE": true,
|
||||
"BLT": true,
|
||||
"BGT": true,
|
||||
"BLE": true,
|
||||
"CALL": true,
|
||||
"CBZ": true,
|
||||
"CBZW": true,
|
||||
"CBNZ": true,
|
||||
"CBNZW": true,
|
||||
"JMP": true,
|
||||
"TBNZ": true,
|
||||
"TBZ": true,
|
||||
}
|
||||
|
||||
func jumpArm64(word string) bool {
|
||||
return arm64Jump[word]
|
||||
}
|
||||
|
||||
// IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
|
||||
// one of the comparison instructions that require special handling.
|
||||
func IsARM64CMP(op obj.As) bool {
|
||||
switch op {
|
||||
case arm64.ACMN, arm64.ACMP, arm64.ATST,
|
||||
arm64.ACMNW, arm64.ACMPW, arm64.ATSTW,
|
||||
arm64.AFCMPS, arm64.AFCMPD,
|
||||
arm64.AFCMPES, arm64.AFCMPED:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsARM64STLXR reports whether the op (as defined by an arm64.A*
|
||||
// constant) is one of the STLXR-like instructions that require special
|
||||
// handling.
|
||||
func IsARM64STLXR(op obj.As) bool {
|
||||
switch op {
|
||||
case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR,
|
||||
arm64.ASTXRB, arm64.ASTXRH, arm64.ASTXRW, arm64.ASTXR,
|
||||
arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
|
||||
return true
|
||||
}
|
||||
// atomic instructions
|
||||
if arm64.IsAtomicInstruction(op) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ARM64Suffix handles the special suffix for the ARM64.
|
||||
// It returns a boolean to indicate success; failure means
|
||||
// cond was unrecognized.
|
||||
func ARM64Suffix(prog *obj.Prog, cond string) bool {
|
||||
if cond == "" {
|
||||
return true
|
||||
}
|
||||
bits, ok := parseARM64Suffix(cond)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
prog.Scond = bits
|
||||
return true
|
||||
}
|
||||
|
||||
// parseARM64Suffix parses the suffix attached to an ARM64 instruction.
|
||||
// The input is a single string consisting of period-separated condition
|
||||
// codes, such as ".P.W". An initial period is ignored.
|
||||
func parseARM64Suffix(cond string) (uint8, bool) {
|
||||
if cond == "" {
|
||||
return 0, true
|
||||
}
|
||||
return parseARMCondition(cond, arm64LS, nil)
|
||||
}
|
||||
|
||||
func arm64RegisterNumber(name string, n int16) (int16, bool) {
|
||||
switch name {
|
||||
case "F":
|
||||
if 0 <= n && n <= 31 {
|
||||
return arm64.REG_F0 + n, true
|
||||
}
|
||||
case "R":
|
||||
if 0 <= n && n <= 30 { // not 31
|
||||
return arm64.REG_R0 + n, true
|
||||
}
|
||||
case "V":
|
||||
if 0 <= n && n <= 31 {
|
||||
return arm64.REG_V0 + n, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// IsARM64TBL reports whether the op (as defined by an arm64.A*
|
||||
// constant) is one of the table lookup instructions that require special
|
||||
// handling.
|
||||
func IsARM64TBL(op obj.As) bool {
|
||||
return op == arm64.AVTBL
|
||||
}
|
||||
|
||||
// ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
|
||||
func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
|
||||
Rnum := (reg & 31) + int16(num<<5)
|
||||
if isAmount {
|
||||
if num < 0 || num > 7 {
|
||||
return errors.New("index shift amount is out of range")
|
||||
}
|
||||
}
|
||||
switch ext {
|
||||
case "UXTB":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
return errors.New("invalid shift for the register offset addressing mode")
|
||||
}
|
||||
a.Reg = arm64.REG_UXTB + Rnum
|
||||
case "UXTH":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
return errors.New("invalid shift for the register offset addressing mode")
|
||||
}
|
||||
a.Reg = arm64.REG_UXTH + Rnum
|
||||
case "UXTW":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
// effective address of memory is a base register value and an offset register value.
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
a.Index = arm64.REG_UXTW + Rnum
|
||||
} else {
|
||||
a.Reg = arm64.REG_UXTW + Rnum
|
||||
}
|
||||
case "UXTX":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
return errors.New("invalid shift for the register offset addressing mode")
|
||||
}
|
||||
a.Reg = arm64.REG_UXTX + Rnum
|
||||
case "SXTB":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_SXTB + Rnum
|
||||
case "SXTH":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
return errors.New("invalid shift for the register offset addressing mode")
|
||||
}
|
||||
a.Reg = arm64.REG_SXTH + Rnum
|
||||
case "SXTW":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
a.Index = arm64.REG_SXTW + Rnum
|
||||
} else {
|
||||
a.Reg = arm64.REG_SXTW + Rnum
|
||||
}
|
||||
case "SXTX":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
if a.Type == obj.TYPE_MEM {
|
||||
a.Index = arm64.REG_SXTX + Rnum
|
||||
} else {
|
||||
a.Reg = arm64.REG_SXTX + Rnum
|
||||
}
|
||||
case "LSL":
|
||||
if !isAmount {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Index = arm64.REG_LSL + Rnum
|
||||
case "B8":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
|
||||
case "B16":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
|
||||
case "H4":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
|
||||
case "H8":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
|
||||
case "S2":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
|
||||
case "S4":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
|
||||
case "D1":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
|
||||
case "D2":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
|
||||
case "Q1":
|
||||
if isIndex {
|
||||
return errors.New("invalid register extension")
|
||||
}
|
||||
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
|
||||
case "B":
|
||||
if !isIndex {
|
||||
return nil
|
||||
}
|
||||
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
|
||||
a.Index = num
|
||||
case "H":
|
||||
if !isIndex {
|
||||
return nil
|
||||
}
|
||||
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
|
||||
a.Index = num
|
||||
case "S":
|
||||
if !isIndex {
|
||||
return nil
|
||||
}
|
||||
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
|
||||
a.Index = num
|
||||
case "D":
|
||||
if !isIndex {
|
||||
return nil
|
||||
}
|
||||
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
|
||||
a.Index = num
|
||||
default:
|
||||
return errors.New("unsupported register extension type: " + ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ARM64RegisterArrangement parses an ARM64 vector register arrangement.
|
||||
func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
|
||||
var curQ, curSize uint16
|
||||
if name[0] != 'V' {
|
||||
return 0, errors.New("expect V0 through V31; found: " + name)
|
||||
}
|
||||
if reg < 0 {
|
||||
return 0, errors.New("invalid register number: " + name)
|
||||
}
|
||||
switch arng {
|
||||
case "B8":
|
||||
curSize = 0
|
||||
curQ = 0
|
||||
case "B16":
|
||||
curSize = 0
|
||||
curQ = 1
|
||||
case "H4":
|
||||
curSize = 1
|
||||
curQ = 0
|
||||
case "H8":
|
||||
curSize = 1
|
||||
curQ = 1
|
||||
case "S2":
|
||||
curSize = 2
|
||||
curQ = 0
|
||||
case "S4":
|
||||
curSize = 2
|
||||
curQ = 1
|
||||
case "D1":
|
||||
curSize = 3
|
||||
curQ = 0
|
||||
case "D2":
|
||||
curSize = 3
|
||||
curQ = 1
|
||||
default:
|
||||
return 0, errors.New("invalid arrangement in ARM64 register list")
|
||||
}
|
||||
return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
|
||||
}
|
||||
|
||||
// ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
|
||||
func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
|
||||
offset := int64(firstReg)
|
||||
switch regCnt {
|
||||
case 1:
|
||||
offset |= 0x7 << 12
|
||||
case 2:
|
||||
offset |= 0xa << 12
|
||||
case 3:
|
||||
offset |= 0x6 << 12
|
||||
case 4:
|
||||
offset |= 0x2 << 12
|
||||
default:
|
||||
return 0, errors.New("invalid register numbers in ARM64 register list")
|
||||
}
|
||||
offset |= arrangement
|
||||
// arm64 uses the 60th bit to differentiate from other archs
|
||||
// For more details, refer to: obj/arm64/list7.go
|
||||
offset |= 1 << 60
|
||||
return offset, nil
|
||||
}
|
72
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/mips.go
generated
vendored
Normal file
72
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/mips.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the
|
||||
// MIPS (MIPS64) instruction set, to minimize its interaction
|
||||
// with the core of the assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/mips"
|
||||
)
|
||||
|
||||
func jumpMIPS(word string) bool {
|
||||
switch word {
|
||||
case "BEQ", "BFPF", "BFPT", "BGEZ", "BGEZAL", "BGTZ", "BLEZ", "BLTZ", "BLTZAL", "BNE", "JMP", "JAL", "CALL":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMIPSCMP reports whether the op (as defined by an mips.A* constant) is
|
||||
// one of the CMP instructions that require special handling.
|
||||
func IsMIPSCMP(op obj.As) bool {
|
||||
switch op {
|
||||
case mips.ACMPEQF, mips.ACMPEQD, mips.ACMPGEF, mips.ACMPGED,
|
||||
mips.ACMPGTF, mips.ACMPGTD:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMIPSMUL reports whether the op (as defined by an mips.A* constant) is
|
||||
// one of the MUL/DIV/REM/MADD/MSUB instructions that require special handling.
|
||||
func IsMIPSMUL(op obj.As) bool {
|
||||
switch op {
|
||||
case mips.AMUL, mips.AMULU, mips.AMULV, mips.AMULVU,
|
||||
mips.ADIV, mips.ADIVU, mips.ADIVV, mips.ADIVVU,
|
||||
mips.AREM, mips.AREMU, mips.AREMV, mips.AREMVU,
|
||||
mips.AMADD, mips.AMSUB:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mipsRegisterNumber(name string, n int16) (int16, bool) {
|
||||
switch name {
|
||||
case "F":
|
||||
if 0 <= n && n <= 31 {
|
||||
return mips.REG_F0 + n, true
|
||||
}
|
||||
case "FCR":
|
||||
if 0 <= n && n <= 31 {
|
||||
return mips.REG_FCR0 + n, true
|
||||
}
|
||||
case "M":
|
||||
if 0 <= n && n <= 31 {
|
||||
return mips.REG_M0 + n, true
|
||||
}
|
||||
case "R":
|
||||
if 0 <= n && n <= 31 {
|
||||
return mips.REG_R0 + n, true
|
||||
}
|
||||
case "W":
|
||||
if 0 <= n && n <= 31 {
|
||||
return mips.REG_W0 + n, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
102
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/ppc64.go
generated
vendored
Normal file
102
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/ppc64.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the
|
||||
// 64-bit PowerPC (PPC64) instruction set, to minimize its interaction
|
||||
// with the core of the assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/ppc64"
|
||||
)
|
||||
|
||||
func jumpPPC64(word string) bool {
|
||||
switch word {
|
||||
case "BC", "BCL", "BEQ", "BGE", "BGT", "BL", "BLE", "BLT", "BNE", "BR", "BVC", "BVS", "CALL", "JMP":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPPC64RLD reports whether the op (as defined by an ppc64.A* constant) is
|
||||
// one of the RLD-like instructions that require special handling.
|
||||
// The FMADD-like instructions behave similarly.
|
||||
func IsPPC64RLD(op obj.As) bool {
|
||||
switch op {
|
||||
case ppc64.ARLDC, ppc64.ARLDCCC, ppc64.ARLDCL, ppc64.ARLDCLCC,
|
||||
ppc64.ARLDCR, ppc64.ARLDCRCC, ppc64.ARLDMI, ppc64.ARLDMICC,
|
||||
ppc64.ARLWMI, ppc64.ARLWMICC, ppc64.ARLWNM, ppc64.ARLWNMCC:
|
||||
return true
|
||||
case ppc64.AFMADD, ppc64.AFMADDCC, ppc64.AFMADDS, ppc64.AFMADDSCC,
|
||||
ppc64.AFMSUB, ppc64.AFMSUBCC, ppc64.AFMSUBS, ppc64.AFMSUBSCC,
|
||||
ppc64.AFNMADD, ppc64.AFNMADDCC, ppc64.AFNMADDS, ppc64.AFNMADDSCC,
|
||||
ppc64.AFNMSUB, ppc64.AFNMSUBCC, ppc64.AFNMSUBS, ppc64.AFNMSUBSCC:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsPPC64ISEL(op obj.As) bool {
|
||||
return op == ppc64.AISEL
|
||||
}
|
||||
|
||||
// IsPPC64CMP reports whether the op (as defined by an ppc64.A* constant) is
|
||||
// one of the CMP instructions that require special handling.
|
||||
func IsPPC64CMP(op obj.As) bool {
|
||||
switch op {
|
||||
case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU, ppc64.AFCMPU:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPPC64NEG reports whether the op (as defined by an ppc64.A* constant) is
|
||||
// one of the NEG-like instructions that require special handling.
|
||||
func IsPPC64NEG(op obj.As) bool {
|
||||
switch op {
|
||||
case ppc64.AADDMECC, ppc64.AADDMEVCC, ppc64.AADDMEV, ppc64.AADDME,
|
||||
ppc64.AADDZECC, ppc64.AADDZEVCC, ppc64.AADDZEV, ppc64.AADDZE,
|
||||
ppc64.ACNTLZDCC, ppc64.ACNTLZD, ppc64.ACNTLZWCC, ppc64.ACNTLZW,
|
||||
ppc64.AEXTSBCC, ppc64.AEXTSB, ppc64.AEXTSHCC, ppc64.AEXTSH,
|
||||
ppc64.AEXTSWCC, ppc64.AEXTSW, ppc64.ANEGCC, ppc64.ANEGVCC,
|
||||
ppc64.ANEGV, ppc64.ANEG, ppc64.ASLBMFEE, ppc64.ASLBMFEV,
|
||||
ppc64.ASLBMTE, ppc64.ASUBMECC, ppc64.ASUBMEVCC, ppc64.ASUBMEV,
|
||||
ppc64.ASUBME, ppc64.ASUBZECC, ppc64.ASUBZEVCC, ppc64.ASUBZEV,
|
||||
ppc64.ASUBZE:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ppc64RegisterNumber(name string, n int16) (int16, bool) {
|
||||
switch name {
|
||||
case "CR":
|
||||
if 0 <= n && n <= 7 {
|
||||
return ppc64.REG_CR0 + n, true
|
||||
}
|
||||
case "VS":
|
||||
if 0 <= n && n <= 63 {
|
||||
return ppc64.REG_VS0 + n, true
|
||||
}
|
||||
case "V":
|
||||
if 0 <= n && n <= 31 {
|
||||
return ppc64.REG_V0 + n, true
|
||||
}
|
||||
case "F":
|
||||
if 0 <= n && n <= 31 {
|
||||
return ppc64.REG_F0 + n, true
|
||||
}
|
||||
case "R":
|
||||
if 0 <= n && n <= 31 {
|
||||
return ppc64.REG_R0 + n, true
|
||||
}
|
||||
case "SPR":
|
||||
if 0 <= n && n <= 1024 {
|
||||
return ppc64.REG_SPR0 + n, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
28
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/riscv64.go
generated
vendored
Normal file
28
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/riscv64.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the RISCV64
|
||||
// instruction set, to minimize its interaction with the core of the
|
||||
// assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj"
|
||||
"github.com/twitchyliquid64/golang-asm/obj/riscv"
|
||||
)
|
||||
|
||||
// IsRISCV64AMO reports whether the op (as defined by a riscv.A*
|
||||
// constant) is one of the AMO instructions that requires special
|
||||
// handling.
|
||||
func IsRISCV64AMO(op obj.As) bool {
|
||||
switch op {
|
||||
case riscv.ASCW, riscv.ASCD, riscv.AAMOSWAPW, riscv.AAMOSWAPD, riscv.AAMOADDW, riscv.AAMOADDD,
|
||||
riscv.AAMOANDW, riscv.AAMOANDD, riscv.AAMOORW, riscv.AAMOORD, riscv.AAMOXORW, riscv.AAMOXORD,
|
||||
riscv.AAMOMINW, riscv.AAMOMIND, riscv.AAMOMINUW, riscv.AAMOMINUD,
|
||||
riscv.AAMOMAXW, riscv.AAMOMAXD, riscv.AAMOMAXUW, riscv.AAMOMAXUD:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
81
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/s390x.go
generated
vendored
Normal file
81
vendor/github.com/twitchyliquid64/golang-asm/asm/arch/s390x.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// This file encapsulates some of the odd characteristics of the
|
||||
// s390x instruction set, to minimize its interaction
|
||||
// with the core of the assembler.
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"github.com/twitchyliquid64/golang-asm/obj/s390x"
|
||||
)
|
||||
|
||||
func jumpS390x(word string) bool {
|
||||
switch word {
|
||||
case "BRC",
|
||||
"BC",
|
||||
"BCL",
|
||||
"BEQ",
|
||||
"BGE",
|
||||
"BGT",
|
||||
"BL",
|
||||
"BLE",
|
||||
"BLEU",
|
||||
"BLT",
|
||||
"BLTU",
|
||||
"BNE",
|
||||
"BR",
|
||||
"BVC",
|
||||
"BVS",
|
||||
"BRCT",
|
||||
"BRCTG",
|
||||
"CMPBEQ",
|
||||
"CMPBGE",
|
||||
"CMPBGT",
|
||||
"CMPBLE",
|
||||
"CMPBLT",
|
||||
"CMPBNE",
|
||||
"CMPUBEQ",
|
||||
"CMPUBGE",
|
||||
"CMPUBGT",
|
||||
"CMPUBLE",
|
||||
"CMPUBLT",
|
||||
"CMPUBNE",
|
||||
"CRJ",
|
||||
"CGRJ",
|
||||
"CLRJ",
|
||||
"CLGRJ",
|
||||
"CIJ",
|
||||
"CGIJ",
|
||||
"CLIJ",
|
||||
"CLGIJ",
|
||||
"CALL",
|
||||
"JMP":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func s390xRegisterNumber(name string, n int16) (int16, bool) {
|
||||
switch name {
|
||||
case "AR":
|
||||
if 0 <= n && n <= 15 {
|
||||
return s390x.REG_AR0 + n, true
|
||||
}
|
||||
case "F":
|
||||
if 0 <= n && n <= 15 {
|
||||
return s390x.REG_F0 + n, true
|
||||
}
|
||||
case "R":
|
||||
if 0 <= n && n <= 15 {
|
||||
return s390x.REG_R0 + n, true
|
||||
}
|
||||
case "V":
|
||||
if 0 <= n && n <= 31 {
|
||||
return s390x.REG_V0 + n, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
Reference in New Issue
Block a user