mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 03:52:24 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			350 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
}
 |