Home | History | Annotate | Download | only in arch
      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 	"cmd/internal/obj"
     15 	"cmd/internal/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 	if strings.HasPrefix(cond, ".") {
    227 		cond = cond[1:]
    228 	}
    229 	if cond == "" {
    230 		return arm.C_SCOND_NONE, true
    231 	}
    232 	names := strings.Split(cond, ".")
    233 	bits := uint8(0)
    234 	for _, name := range names {
    235 		if b, present := ls[name]; present {
    236 			bits |= b
    237 			continue
    238 		}
    239 		if b, present := scond[name]; present {
    240 			bits = (bits &^ arm.C_SCOND) | b
    241 			continue
    242 		}
    243 		return 0, false
    244 	}
    245 	return bits, true
    246 }
    247 
    248 func armRegisterNumber(name string, n int16) (int16, bool) {
    249 	if n < 0 || 15 < n {
    250 		return 0, false
    251 	}
    252 	switch name {
    253 	case "R":
    254 		return arm.REG_R0 + n, true
    255 	case "F":
    256 		return arm.REG_F0 + n, true
    257 	}
    258 	return 0, false
    259 }
    260