github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/asm/internal/arch/arm64.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file encapsulates some of the odd characteristics of the ARM64
     6  // instruction set, to minimize its interaction with the core of the
     7  // assembler.
     8  
     9  package arch
    10  
    11  import (
    12  	"github.com/gagliardetto/golang-go/cmd/internal/obj"
    13  	"github.com/gagliardetto/golang-go/cmd/internal/obj/arm64"
    14  	"errors"
    15  )
    16  
    17  var arm64LS = map[string]uint8{
    18  	"P": arm64.C_XPOST,
    19  	"W": arm64.C_XPRE,
    20  }
    21  
    22  var arm64Jump = map[string]bool{
    23  	"B":     true,
    24  	"BL":    true,
    25  	"BEQ":   true,
    26  	"BNE":   true,
    27  	"BCS":   true,
    28  	"BHS":   true,
    29  	"BCC":   true,
    30  	"BLO":   true,
    31  	"BMI":   true,
    32  	"BPL":   true,
    33  	"BVS":   true,
    34  	"BVC":   true,
    35  	"BHI":   true,
    36  	"BLS":   true,
    37  	"BGE":   true,
    38  	"BLT":   true,
    39  	"BGT":   true,
    40  	"BLE":   true,
    41  	"CALL":  true,
    42  	"CBZ":   true,
    43  	"CBZW":  true,
    44  	"CBNZ":  true,
    45  	"CBNZW": true,
    46  	"JMP":   true,
    47  	"TBNZ":  true,
    48  	"TBZ":   true,
    49  }
    50  
    51  func jumpArm64(word string) bool {
    52  	return arm64Jump[word]
    53  }
    54  
    55  // IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
    56  // one of the comparison instructions that require special handling.
    57  func IsARM64CMP(op obj.As) bool {
    58  	switch op {
    59  	case arm64.ACMN, arm64.ACMP, arm64.ATST,
    60  		arm64.ACMNW, arm64.ACMPW, arm64.ATSTW,
    61  		arm64.AFCMPS, arm64.AFCMPD,
    62  		arm64.AFCMPES, arm64.AFCMPED:
    63  		return true
    64  	}
    65  	return false
    66  }
    67  
    68  // IsARM64STLXR reports whether the op (as defined by an arm64.A*
    69  // constant) is one of the STLXR-like instructions that require special
    70  // handling.
    71  func IsARM64STLXR(op obj.As) bool {
    72  	switch op {
    73  	case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR,
    74  		arm64.ASTXRB, arm64.ASTXRH, arm64.ASTXRW, arm64.ASTXR,
    75  		arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
    76  		return true
    77  	}
    78  	// atomic instructions
    79  	if arm64.IsAtomicInstruction(op) {
    80  		return true
    81  	}
    82  	return false
    83  }
    84  
    85  // ARM64Suffix handles the special suffix for the ARM64.
    86  // It returns a boolean to indicate success; failure means
    87  // cond was unrecognized.
    88  func ARM64Suffix(prog *obj.Prog, cond string) bool {
    89  	if cond == "" {
    90  		return true
    91  	}
    92  	bits, ok := parseARM64Suffix(cond)
    93  	if !ok {
    94  		return false
    95  	}
    96  	prog.Scond = bits
    97  	return true
    98  }
    99  
   100  // parseARM64Suffix parses the suffix attached to an ARM64 instruction.
   101  // The input is a single string consisting of period-separated condition
   102  // codes, such as ".P.W". An initial period is ignored.
   103  func parseARM64Suffix(cond string) (uint8, bool) {
   104  	if cond == "" {
   105  		return 0, true
   106  	}
   107  	return parseARMCondition(cond, arm64LS, nil)
   108  }
   109  
   110  func arm64RegisterNumber(name string, n int16) (int16, bool) {
   111  	switch name {
   112  	case "F":
   113  		if 0 <= n && n <= 31 {
   114  			return arm64.REG_F0 + n, true
   115  		}
   116  	case "R":
   117  		if 0 <= n && n <= 30 { // not 31
   118  			return arm64.REG_R0 + n, true
   119  		}
   120  	case "V":
   121  		if 0 <= n && n <= 31 {
   122  			return arm64.REG_V0 + n, true
   123  		}
   124  	}
   125  	return 0, false
   126  }
   127  
   128  // IsARM64TBL reports whether the op (as defined by an arm64.A*
   129  // constant) is one of the table lookup instructions that require special
   130  // handling.
   131  func IsARM64TBL(op obj.As) bool {
   132  	return op == arm64.AVTBL
   133  }
   134  
   135  // ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
   136  func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
   137  	Rnum := (reg & 31) + int16(num<<5)
   138  	if isAmount {
   139  		if num < 0 || num > 7 {
   140  			return errors.New("index shift amount is out of range")
   141  		}
   142  	}
   143  	switch ext {
   144  	case "UXTB":
   145  		if !isAmount {
   146  			return errors.New("invalid register extension")
   147  		}
   148  		if a.Type == obj.TYPE_MEM {
   149  			return errors.New("invalid shift for the register offset addressing mode")
   150  		}
   151  		a.Reg = arm64.REG_UXTB + Rnum
   152  	case "UXTH":
   153  		if !isAmount {
   154  			return errors.New("invalid register extension")
   155  		}
   156  		if a.Type == obj.TYPE_MEM {
   157  			return errors.New("invalid shift for the register offset addressing mode")
   158  		}
   159  		a.Reg = arm64.REG_UXTH + Rnum
   160  	case "UXTW":
   161  		if !isAmount {
   162  			return errors.New("invalid register extension")
   163  		}
   164  		// effective address of memory is a base register value and an offset register value.
   165  		if a.Type == obj.TYPE_MEM {
   166  			a.Index = arm64.REG_UXTW + Rnum
   167  		} else {
   168  			a.Reg = arm64.REG_UXTW + Rnum
   169  		}
   170  	case "UXTX":
   171  		if !isAmount {
   172  			return errors.New("invalid register extension")
   173  		}
   174  		if a.Type == obj.TYPE_MEM {
   175  			return errors.New("invalid shift for the register offset addressing mode")
   176  		}
   177  		a.Reg = arm64.REG_UXTX + Rnum
   178  	case "SXTB":
   179  		if !isAmount {
   180  			return errors.New("invalid register extension")
   181  		}
   182  		a.Reg = arm64.REG_SXTB + Rnum
   183  	case "SXTH":
   184  		if !isAmount {
   185  			return errors.New("invalid register extension")
   186  		}
   187  		if a.Type == obj.TYPE_MEM {
   188  			return errors.New("invalid shift for the register offset addressing mode")
   189  		}
   190  		a.Reg = arm64.REG_SXTH + Rnum
   191  	case "SXTW":
   192  		if !isAmount {
   193  			return errors.New("invalid register extension")
   194  		}
   195  		if a.Type == obj.TYPE_MEM {
   196  			a.Index = arm64.REG_SXTW + Rnum
   197  		} else {
   198  			a.Reg = arm64.REG_SXTW + Rnum
   199  		}
   200  	case "SXTX":
   201  		if !isAmount {
   202  			return errors.New("invalid register extension")
   203  		}
   204  		if a.Type == obj.TYPE_MEM {
   205  			a.Index = arm64.REG_SXTX + Rnum
   206  		} else {
   207  			a.Reg = arm64.REG_SXTX + Rnum
   208  		}
   209  	case "LSL":
   210  		if !isAmount {
   211  			return errors.New("invalid register extension")
   212  		}
   213  		a.Index = arm64.REG_LSL + Rnum
   214  	case "B8":
   215  		if isIndex {
   216  			return errors.New("invalid register extension")
   217  		}
   218  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
   219  	case "B16":
   220  		if isIndex {
   221  			return errors.New("invalid register extension")
   222  		}
   223  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
   224  	case "H4":
   225  		if isIndex {
   226  			return errors.New("invalid register extension")
   227  		}
   228  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
   229  	case "H8":
   230  		if isIndex {
   231  			return errors.New("invalid register extension")
   232  		}
   233  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
   234  	case "S2":
   235  		if isIndex {
   236  			return errors.New("invalid register extension")
   237  		}
   238  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
   239  	case "S4":
   240  		if isIndex {
   241  			return errors.New("invalid register extension")
   242  		}
   243  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
   244  	case "D1":
   245  		if isIndex {
   246  			return errors.New("invalid register extension")
   247  		}
   248  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
   249  	case "D2":
   250  		if isIndex {
   251  			return errors.New("invalid register extension")
   252  		}
   253  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
   254  	case "Q1":
   255  		if isIndex {
   256  			return errors.New("invalid register extension")
   257  		}
   258  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
   259  	case "B":
   260  		if !isIndex {
   261  			return nil
   262  		}
   263  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
   264  		a.Index = num
   265  	case "H":
   266  		if !isIndex {
   267  			return nil
   268  		}
   269  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
   270  		a.Index = num
   271  	case "S":
   272  		if !isIndex {
   273  			return nil
   274  		}
   275  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
   276  		a.Index = num
   277  	case "D":
   278  		if !isIndex {
   279  			return nil
   280  		}
   281  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
   282  		a.Index = num
   283  	default:
   284  		return errors.New("unsupported register extension type: " + ext)
   285  	}
   286  
   287  	return nil
   288  }
   289  
   290  // ARM64RegisterArrangement parses an ARM64 vector register arrangement.
   291  func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
   292  	var curQ, curSize uint16
   293  	if name[0] != 'V' {
   294  		return 0, errors.New("expect V0 through V31; found: " + name)
   295  	}
   296  	if reg < 0 {
   297  		return 0, errors.New("invalid register number: " + name)
   298  	}
   299  	switch arng {
   300  	case "B8":
   301  		curSize = 0
   302  		curQ = 0
   303  	case "B16":
   304  		curSize = 0
   305  		curQ = 1
   306  	case "H4":
   307  		curSize = 1
   308  		curQ = 0
   309  	case "H8":
   310  		curSize = 1
   311  		curQ = 1
   312  	case "S2":
   313  		curSize = 2
   314  		curQ = 0
   315  	case "S4":
   316  		curSize = 2
   317  		curQ = 1
   318  	case "D1":
   319  		curSize = 3
   320  		curQ = 0
   321  	case "D2":
   322  		curSize = 3
   323  		curQ = 1
   324  	default:
   325  		return 0, errors.New("invalid arrangement in ARM64 register list")
   326  	}
   327  	return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
   328  }
   329  
   330  // ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
   331  func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
   332  	offset := int64(firstReg)
   333  	switch regCnt {
   334  	case 1:
   335  		offset |= 0x7 << 12
   336  	case 2:
   337  		offset |= 0xa << 12
   338  	case 3:
   339  		offset |= 0x6 << 12
   340  	case 4:
   341  		offset |= 0x2 << 12
   342  	default:
   343  		return 0, errors.New("invalid register numbers in ARM64 register list")
   344  	}
   345  	offset |= arrangement
   346  	// arm64 uses the 60th bit to differentiate from other archs
   347  	// For more details, refer to: obj/arm64/list7.go
   348  	offset |= 1 << 60
   349  	return offset, nil
   350  }