// 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 ARM // instruction set, to minimize its interaction with the core of the // assembler. package arch import ( "strings" "cmd/internal/obj" "cmd/internal/obj/arm" ) var armLS = map[string]uint8{ "U": arm.C_UBIT, "S": arm.C_SBIT, "W": arm.C_WBIT, "P": arm.C_PBIT, "PW": arm.C_WBIT | arm.C_PBIT, "WP": arm.C_WBIT | arm.C_PBIT, } var armSCOND = map[string]uint8{ "EQ": arm.C_SCOND_EQ, "NE": arm.C_SCOND_NE, "CS": arm.C_SCOND_HS, "HS": arm.C_SCOND_HS, "CC": arm.C_SCOND_LO, "LO": arm.C_SCOND_LO, "MI": arm.C_SCOND_MI, "PL": arm.C_SCOND_PL, "VS": arm.C_SCOND_VS, "VC": arm.C_SCOND_VC, "HI": arm.C_SCOND_HI, "LS": arm.C_SCOND_LS, "GE": arm.C_SCOND_GE, "LT": arm.C_SCOND_LT, "GT": arm.C_SCOND_GT, "LE": arm.C_SCOND_LE, "AL": arm.C_SCOND_NONE, "U": arm.C_UBIT, "S": arm.C_SBIT, "W": arm.C_WBIT, "P": arm.C_PBIT, "PW": arm.C_WBIT | arm.C_PBIT, "WP": arm.C_WBIT | arm.C_PBIT, "F": arm.C_FBIT, "IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT, "IAW": arm.C_WBIT | arm.C_UBIT, "DBW": arm.C_WBIT | arm.C_PBIT, "DAW": arm.C_WBIT, "IB": arm.C_PBIT | arm.C_UBIT, "IA": arm.C_UBIT, "DB": arm.C_PBIT, "DA": 0, } var armJump = map[string]bool{ "B": true, "BL": true, "BX": 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, "JMP": true, } func jumpArm(word string) bool { return armJump[word] } // IsARMCMP reports whether the op (as defined by an arm.A* constant) is // one of the comparison instructions that require special handling. func IsARMCMP(op obj.As) bool { switch op { case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST: return true } return false } // IsARMSTREX reports whether the op (as defined by an arm.A* constant) is // one of the STREX-like instructions that require special handling. func IsARMSTREX(op obj.As) bool { switch op { case arm.ASTREX, arm.ASTREXD, arm.ASTREXB, arm.ASWPW, arm.ASWPBU: return true } return false } // MCR is not defined by the obj/arm; instead we define it privately here. // It is encoded as an MRC with a bit inside the instruction word, // passed to arch.ARMMRCOffset. const aMCR = arm.ALAST + 1 // IsARMMRC reports whether the op (as defined by an arm.A* constant) is // MRC or MCR. func IsARMMRC(op obj.As) bool { switch op { case arm.AMRC, aMCR: // Note: aMCR is defined in this package. return true } return false } // IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the // BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg". func IsARMBFX(op obj.As) bool { switch op { case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI: return true } return false } // IsARMFloatCmp reports whether the op is a floating comparison instruction. func IsARMFloatCmp(op obj.As) bool { switch op { case arm.ACMPF, arm.ACMPD: return true } return false } // ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions. // The difference between MRC and MCR is represented by a bit high in the word, not // in the usual way by the opcode itself. Asm must use AMRC for both instructions, so // we return the opcode for MRC so that asm doesn't need to import obj/arm. func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) { op1 := int64(0) if op == arm.AMRC { op1 = 1 } bits, ok := ParseARMCondition(cond) if !ok { return } offset = (0xe << 24) | // opcode (op1 << 20) | // MCR/MRC ((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond ((x0 & 15) << 8) | //coprocessor number ((x1 & 7) << 21) | // coprocessor operation ((x2 & 15) << 12) | // ARM register ((x3 & 15) << 16) | // Crn ((x4 & 15) << 0) | // Crm ((x5 & 7) << 5) | // coprocessor information (1 << 4) /* must be set */ return offset, arm.AMRC, true } // IsARMMULA reports whether the op (as defined by an arm.A* constant) is // MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions. func IsARMMULA(op obj.As) bool { switch op { case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT: return true } return false } var bcode = []obj.As{ arm.ABEQ, arm.ABNE, arm.ABCS, arm.ABCC, arm.ABMI, arm.ABPL, arm.ABVS, arm.ABVC, arm.ABHI, arm.ABLS, arm.ABGE, arm.ABLT, arm.ABGT, arm.ABLE, arm.AB, obj.ANOP, } // ARMConditionCodes handles the special condition code situation for the ARM. // It returns a boolean to indicate success; failure means cond was unrecognized. func ARMConditionCodes(prog *obj.Prog, cond string) bool { if cond == "" { return true } bits, ok := ParseARMCondition(cond) if !ok { return false } /* hack to make B.NE etc. work: turn it into the corresponding conditional */ if prog.As == arm.AB { prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf] bits = (bits &^ 0xf) | arm.C_SCOND_NONE } prog.Scond = bits return true } // ParseARMCondition parses the conditions attached to an ARM instruction. // The input is a single string consisting of period-separated condition // codes, such as ".P.W". An initial period is ignored. func ParseARMCondition(cond string) (uint8, bool) { return parseARMCondition(cond, armLS, armSCOND) } func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) { cond = strings.TrimPrefix(cond, ".") if cond == "" { return arm.C_SCOND_NONE, true } names := strings.Split(cond, ".") bits := uint8(0) for _, name := range names { if b, present := ls[name]; present { bits |= b continue } if b, present := scond[name]; present { bits = (bits &^ arm.C_SCOND) | b continue } return 0, false } return bits, true } func armRegisterNumber(name string, n int16) (int16, bool) { if n < 0 || 15 < n { return 0, false } switch name { case "R": return arm.REG_R0 + n, true case "F": return arm.REG_F0 + n, true } return 0, false }