github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/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  	"cmd/internal/obj"
    13  	"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  		return true
    76  	}
    77  	return false
    78  }
    79  
    80  // ARM64Suffix handles the special suffix for the ARM64.
    81  // It returns a boolean to indicate success; failure means
    82  // cond was unrecognized.
    83  func ARM64Suffix(prog *obj.Prog, cond string) bool {
    84  	if cond == "" {
    85  		return true
    86  	}
    87  	bits, ok := ParseARM64Suffix(cond)
    88  	if !ok {
    89  		return false
    90  	}
    91  	prog.Scond = bits
    92  	return true
    93  }
    94  
    95  // ParseARM64Suffix parses the suffix attached to an ARM64 instruction.
    96  // The input is a single string consisting of period-separated condition
    97  // codes, such as ".P.W". An initial period is ignored.
    98  func ParseARM64Suffix(cond string) (uint8, bool) {
    99  	if cond == "" {
   100  		return 0, true
   101  	}
   102  	return parseARMCondition(cond, arm64LS, nil)
   103  }
   104  
   105  func arm64RegisterNumber(name string, n int16) (int16, bool) {
   106  	switch name {
   107  	case "F":
   108  		if 0 <= n && n <= 31 {
   109  			return arm64.REG_F0 + n, true
   110  		}
   111  	case "R":
   112  		if 0 <= n && n <= 30 { // not 31
   113  			return arm64.REG_R0 + n, true
   114  		}
   115  	case "V":
   116  		if 0 <= n && n <= 31 {
   117  			return arm64.REG_V0 + n, true
   118  		}
   119  	}
   120  	return 0, false
   121  }
   122  
   123  // ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
   124  func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
   125  	rm := uint32(reg)
   126  	if isAmount {
   127  		if num < 0 || num > 7 {
   128  			return errors.New("shift amount out of range")
   129  		}
   130  	}
   131  	switch ext {
   132  	case "UXTB":
   133  		if !isAmount {
   134  			return errors.New("invalid register extension")
   135  		}
   136  		a.Reg = arm64.REG_UXTB + (reg & 31) + int16(num<<5)
   137  		a.Offset = int64(((rm & 31) << 16) | (uint32(num) << 10))
   138  	case "UXTH":
   139  		if !isAmount {
   140  			return errors.New("invalid register extension")
   141  		}
   142  		a.Reg = arm64.REG_UXTH + (reg & 31) + int16(num<<5)
   143  		a.Offset = int64(((rm & 31) << 16) | (1 << 13) | (uint32(num) << 10))
   144  	case "UXTW":
   145  		if !isAmount {
   146  			return errors.New("invalid register extension")
   147  		}
   148  		a.Reg = arm64.REG_UXTW + (reg & 31) + int16(num<<5)
   149  		a.Offset = int64(((rm & 31) << 16) | (2 << 13) | (uint32(num) << 10))
   150  	case "UXTX":
   151  		if !isAmount {
   152  			return errors.New("invalid register extension")
   153  		}
   154  		a.Reg = arm64.REG_UXTX + (reg & 31) + int16(num<<5)
   155  		a.Offset = int64(((rm & 31) << 16) | (3 << 13) | (uint32(num) << 10))
   156  	case "SXTB":
   157  		if !isAmount {
   158  			return errors.New("invalid register extension")
   159  		}
   160  		a.Reg = arm64.REG_SXTB + (reg & 31) + int16(num<<5)
   161  		a.Offset = int64(((rm & 31) << 16) | (4 << 13) | (uint32(num) << 10))
   162  	case "SXTH":
   163  		if !isAmount {
   164  			return errors.New("invalid register extension")
   165  		}
   166  		a.Reg = arm64.REG_SXTH + (reg & 31) + int16(num<<5)
   167  		a.Offset = int64(((rm & 31) << 16) | (5 << 13) | (uint32(num) << 10))
   168  	case "SXTW":
   169  		if !isAmount {
   170  			return errors.New("invalid register extension")
   171  		}
   172  		a.Reg = arm64.REG_SXTW + (reg & 31) + int16(num<<5)
   173  		a.Offset = int64(((rm & 31) << 16) | (6 << 13) | (uint32(num) << 10))
   174  	case "SXTX":
   175  		if !isAmount {
   176  			return errors.New("invalid register extension")
   177  		}
   178  		a.Reg = arm64.REG_SXTX + (reg & 31) + int16(num<<5)
   179  		a.Offset = int64(((rm & 31) << 16) | (7 << 13) | (uint32(num) << 10))
   180  	case "B8":
   181  		if isIndex {
   182  			return errors.New("invalid register extension")
   183  		}
   184  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
   185  	case "B16":
   186  		if isIndex {
   187  			return errors.New("invalid register extension")
   188  		}
   189  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
   190  	case "H4":
   191  		if isIndex {
   192  			return errors.New("invalid register extension")
   193  		}
   194  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
   195  	case "H8":
   196  		if isIndex {
   197  			return errors.New("invalid register extension")
   198  		}
   199  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
   200  	case "S2":
   201  		if isIndex {
   202  			return errors.New("invalid register extension")
   203  		}
   204  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
   205  	case "S4":
   206  		if isIndex {
   207  			return errors.New("invalid register extension")
   208  		}
   209  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
   210  	case "D2":
   211  		if isIndex {
   212  			return errors.New("invalid register extension")
   213  		}
   214  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
   215  	case "B":
   216  		if !isIndex {
   217  			return nil
   218  		}
   219  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
   220  		a.Index = num
   221  	case "H":
   222  		if !isIndex {
   223  			return nil
   224  		}
   225  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
   226  		a.Index = num
   227  	case "S":
   228  		if !isIndex {
   229  			return nil
   230  		}
   231  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
   232  		a.Index = num
   233  	case "D":
   234  		if !isIndex {
   235  			return nil
   236  		}
   237  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
   238  		a.Index = num
   239  	default:
   240  		return errors.New("unsupported register extension type: " + ext)
   241  	}
   242  	a.Type = obj.TYPE_REG
   243  	return nil
   244  }
   245  
   246  // ARM64RegisterArrangement parses an ARM64 vector register arrangement.
   247  func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
   248  	var curQ, curSize uint16
   249  	if name[0] != 'V' {
   250  		return 0, errors.New("expect V0 through V31; found: " + name)
   251  	}
   252  	if reg < 0 {
   253  		return 0, errors.New("invalid register number: " + name)
   254  	}
   255  	switch arng {
   256  	case "B8":
   257  		curSize = 0
   258  		curQ = 0
   259  	case "B16":
   260  		curSize = 0
   261  		curQ = 1
   262  	case "H4":
   263  		curSize = 1
   264  		curQ = 0
   265  	case "H8":
   266  		curSize = 1
   267  		curQ = 1
   268  	case "S2":
   269  		curSize = 1
   270  		curQ = 0
   271  	case "S4":
   272  		curSize = 2
   273  		curQ = 1
   274  	case "D1":
   275  		curSize = 3
   276  		curQ = 0
   277  	case "D2":
   278  		curSize = 3
   279  		curQ = 1
   280  	default:
   281  		return 0, errors.New("invalid arrangement in ARM64 register list")
   282  	}
   283  	return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
   284  }
   285  
   286  // ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
   287  func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
   288  	offset := int64(firstReg)
   289  	switch regCnt {
   290  	case 1:
   291  		offset |= 0x7 << 12
   292  	case 2:
   293  		offset |= 0xa << 12
   294  	case 3:
   295  		offset |= 0x6 << 12
   296  	case 4:
   297  		offset |= 0x2 << 12
   298  	default:
   299  		return 0, errors.New("invalid register numbers in ARM64 register list")
   300  	}
   301  	offset |= arrangement
   302  	// arm64 uses the 60th bit to differentiate from other archs
   303  	// For more details, refer to: obj/arm64/list7.go
   304  	offset |= 1 << 60
   305  	return offset, nil
   306  }