summaryrefslogtreecommitdiffstats
path: root/src/cmd/asm/internal/arch/arm.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/asm/internal/arch/arm.go
parentInitial commit. (diff)
downloadgolang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz
golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/asm/internal/arch/arm.go')
-rw-r--r--src/cmd/asm/internal/arch/arm.go257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/cmd/asm/internal/arch/arm.go b/src/cmd/asm/internal/arch/arm.go
new file mode 100644
index 0000000..22ac483
--- /dev/null
+++ b/src/cmd/asm/internal/arch/arm.go
@@ -0,0 +1,257 @@
+// 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.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
+}