github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/asm/arch/arm.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 ARM
     6  // instruction set, to minimize its interaction with the core of the
     7  // assembler.
     8  
     9  package arch
    10  
    11  import (
    12  	"strings"
    13  
    14  	"github.com/go-asm/go/cmd/obj"
    15  	"github.com/go-asm/go/cmd/obj/arm"
    16  )
    17  
    18  var armLS = map[string]uint8{
    19  	"U":  arm.C_UBIT,
    20  	"S":  arm.C_SBIT,
    21  	"W":  arm.C_WBIT,
    22  	"P":  arm.C_PBIT,
    23  	"PW": arm.C_WBIT | arm.C_PBIT,
    24  	"WP": arm.C_WBIT | arm.C_PBIT,
    25  }
    26  
    27  var armSCOND = map[string]uint8{
    28  	"EQ":  arm.C_SCOND_EQ,
    29  	"NE":  arm.C_SCOND_NE,
    30  	"CS":  arm.C_SCOND_HS,
    31  	"HS":  arm.C_SCOND_HS,
    32  	"CC":  arm.C_SCOND_LO,
    33  	"LO":  arm.C_SCOND_LO,
    34  	"MI":  arm.C_SCOND_MI,
    35  	"PL":  arm.C_SCOND_PL,
    36  	"VS":  arm.C_SCOND_VS,
    37  	"VC":  arm.C_SCOND_VC,
    38  	"HI":  arm.C_SCOND_HI,
    39  	"LS":  arm.C_SCOND_LS,
    40  	"GE":  arm.C_SCOND_GE,
    41  	"LT":  arm.C_SCOND_LT,
    42  	"GT":  arm.C_SCOND_GT,
    43  	"LE":  arm.C_SCOND_LE,
    44  	"AL":  arm.C_SCOND_NONE,
    45  	"U":   arm.C_UBIT,
    46  	"S":   arm.C_SBIT,
    47  	"W":   arm.C_WBIT,
    48  	"P":   arm.C_PBIT,
    49  	"PW":  arm.C_WBIT | arm.C_PBIT,
    50  	"WP":  arm.C_WBIT | arm.C_PBIT,
    51  	"F":   arm.C_FBIT,
    52  	"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
    53  	"IAW": arm.C_WBIT | arm.C_UBIT,
    54  	"DBW": arm.C_WBIT | arm.C_PBIT,
    55  	"DAW": arm.C_WBIT,
    56  	"IB":  arm.C_PBIT | arm.C_UBIT,
    57  	"IA":  arm.C_UBIT,
    58  	"DB":  arm.C_PBIT,
    59  	"DA":  0,
    60  }
    61  
    62  var armJump = map[string]bool{
    63  	"B":    true,
    64  	"BL":   true,
    65  	"BX":   true,
    66  	"BEQ":  true,
    67  	"BNE":  true,
    68  	"BCS":  true,
    69  	"BHS":  true,
    70  	"BCC":  true,
    71  	"BLO":  true,
    72  	"BMI":  true,
    73  	"BPL":  true,
    74  	"BVS":  true,
    75  	"BVC":  true,
    76  	"BHI":  true,
    77  	"BLS":  true,
    78  	"BGE":  true,
    79  	"BLT":  true,
    80  	"BGT":  true,
    81  	"BLE":  true,
    82  	"CALL": true,
    83  	"JMP":  true,
    84  }
    85  
    86  func jumpArm(word string) bool {
    87  	return armJump[word]
    88  }
    89  
    90  // IsARMCMP reports whether the op (as defined by an arm.A* constant) is
    91  // one of the comparison instructions that require special handling.
    92  func IsARMCMP(op obj.As) bool {
    93  	switch op {
    94  	case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
    95  		return true
    96  	}
    97  	return false
    98  }
    99  
   100  // IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
   101  // one of the STREX-like instructions that require special handling.
   102  func IsARMSTREX(op obj.As) bool {
   103  	switch op {
   104  	case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
   105  		return true
   106  	}
   107  	return false
   108  }
   109  
   110  // MCR is not defined by the obj/arm; instead we define it privately here.
   111  // It is encoded as an MRC with a bit inside the instruction word,
   112  // passed to arch.ARMMRCOffset.
   113  const aMCR = arm.ALAST + 1
   114  
   115  // IsARMMRC reports whether the op (as defined by an arm.A* constant) is
   116  // MRC or MCR.
   117  func IsARMMRC(op obj.As) bool {
   118  	switch op {
   119  	case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
   120  		return true
   121  	}
   122  	return false
   123  }
   124  
   125  // IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
   126  // BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
   127  func IsARMBFX(op obj.As) bool {
   128  	switch op {
   129  	case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
   130  		return true
   131  	}
   132  	return false
   133  }
   134  
   135  // IsARMFloatCmp reports whether the op is a floating comparison instruction.
   136  func IsARMFloatCmp(op obj.As) bool {
   137  	switch op {
   138  	case arm.ACMPF, arm.ACMPD:
   139  		return true
   140  	}
   141  	return false
   142  }
   143  
   144  // ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
   145  // The difference between MRC and MCR is represented by a bit high in the word, not
   146  // in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
   147  // we return the opcode for MRC so that asm doesn't need to import obj/arm.
   148  func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
   149  	op1 := int64(0)
   150  	if op == arm.AMRC {
   151  		op1 = 1
   152  	}
   153  	bits, ok := ParseARMCondition(cond)
   154  	if !ok {
   155  		return
   156  	}
   157  	offset = (0xe << 24) | // opcode
   158  		(op1 << 20) | // MCR/MRC
   159  		((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
   160  		((x0 & 15) << 8) | //coprocessor number
   161  		((x1 & 7) << 21) | // coprocessor operation
   162  		((x2 & 15) << 12) | // ARM register
   163  		((x3 & 15) << 16) | // Crn
   164  		((x4 & 15) << 0) | // Crm
   165  		((x5 & 7) << 5) | // coprocessor information
   166  		(1 << 4) /* must be set */
   167  	return offset, arm.AMRC, true
   168  }
   169  
   170  // IsARMMULA reports whether the op (as defined by an arm.A* constant) is
   171  // MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
   172  func IsARMMULA(op obj.As) bool {
   173  	switch op {
   174  	case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
   175  		return true
   176  	}
   177  	return false
   178  }
   179  
   180  var bcode = []obj.As{
   181  	arm.ABEQ,
   182  	arm.ABNE,
   183  	arm.ABCS,
   184  	arm.ABCC,
   185  	arm.ABMI,
   186  	arm.ABPL,
   187  	arm.ABVS,
   188  	arm.ABVC,
   189  	arm.ABHI,
   190  	arm.ABLS,
   191  	arm.ABGE,
   192  	arm.ABLT,
   193  	arm.ABGT,
   194  	arm.ABLE,
   195  	arm.AB,
   196  	obj.ANOP,
   197  }
   198  
   199  // ARMConditionCodes handles the special condition code situation for the ARM.
   200  // It returns a boolean to indicate success; failure means cond was unrecognized.
   201  func ARMConditionCodes(prog *obj.Prog, cond string) bool {
   202  	if cond == "" {
   203  		return true
   204  	}
   205  	bits, ok := ParseARMCondition(cond)
   206  	if !ok {
   207  		return false
   208  	}
   209  	/* hack to make B.NE etc. work: turn it into the corresponding conditional */
   210  	if prog.As == arm.AB {
   211  		prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
   212  		bits = (bits &^ 0xf) | arm.C_SCOND_NONE
   213  	}
   214  	prog.Scond = bits
   215  	return true
   216  }
   217  
   218  // ParseARMCondition parses the conditions attached to an ARM instruction.
   219  // The input is a single string consisting of period-separated condition
   220  // codes, such as ".P.W". An initial period is ignored.
   221  func ParseARMCondition(cond string) (uint8, bool) {
   222  	return parseARMCondition(cond, armLS, armSCOND)
   223  }
   224  
   225  func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
   226  	cond = strings.TrimPrefix(cond, ".")
   227  	if cond == "" {
   228  		return arm.C_SCOND_NONE, true
   229  	}
   230  	names := strings.Split(cond, ".")
   231  	bits := uint8(0)
   232  	for _, name := range names {
   233  		if b, present := ls[name]; present {
   234  			bits |= b
   235  			continue
   236  		}
   237  		if b, present := scond[name]; present {
   238  			bits = (bits &^ arm.C_SCOND) | b
   239  			continue
   240  		}
   241  		return 0, false
   242  	}
   243  	return bits, true
   244  }
   245  
   246  func armRegisterNumber(name string, n int16) (int16, bool) {
   247  	if n < 0 || 15 < n {
   248  		return 0, false
   249  	}
   250  	switch name {
   251  	case "R":
   252  		return arm.REG_R0 + n, true
   253  	case "F":
   254  		return arm.REG_F0 + n, true
   255  	}
   256  	return 0, false
   257  }