mirror of
https://github.com/golang/go.git
synced 2025-05-28 19:02:22 +00:00
cmd/asm: add support for ARM64
Pre/post-index addressing modes with writeback use .W and .P instruction suffixes, like on ARM. Complex addressing modes are not supported yet. Change-Id: I537a1c3fe5b057c0812662677d0010bc8c468ffb Reviewed-on: https://go-review.googlesource.com/7047 Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
26bbe7ac9b
commit
18d9ddc35c
@ -7,6 +7,7 @@ package arch
|
|||||||
import (
|
import (
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"cmd/internal/obj/arm"
|
"cmd/internal/obj/arm"
|
||||||
|
"cmd/internal/obj/arm64"
|
||||||
"cmd/internal/obj/ppc64"
|
"cmd/internal/obj/ppc64"
|
||||||
"cmd/internal/obj/x86"
|
"cmd/internal/obj/x86"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -62,6 +63,8 @@ func Set(GOARCH string) *Arch {
|
|||||||
return archX86(&x86.Linkamd64p32)
|
return archX86(&x86.Linkamd64p32)
|
||||||
case "arm":
|
case "arm":
|
||||||
return archArm()
|
return archArm()
|
||||||
|
case "arm64":
|
||||||
|
return archArm64()
|
||||||
case "ppc64":
|
case "ppc64":
|
||||||
a := archPPC64()
|
a := archPPC64()
|
||||||
a.LinkArch = &ppc64.Linkppc64
|
a.LinkArch = &ppc64.Linkppc64
|
||||||
@ -201,6 +204,89 @@ func archArm() *Arch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func archArm64() *Arch {
|
||||||
|
register := make(map[string]int16)
|
||||||
|
// Create maps for easy lookup of instruction names etc.
|
||||||
|
// Note that there is no list of names as there is for 386 and amd64.
|
||||||
|
register[arm64.Rconv(arm64.REGSP)] = int16(arm64.REGSP)
|
||||||
|
for i := arm64.REG_R0; i <= arm64.REG_R31; i++ {
|
||||||
|
register[arm64.Rconv(i)] = int16(i)
|
||||||
|
}
|
||||||
|
for i := arm64.REG_F0; i <= arm64.REG_F31; i++ {
|
||||||
|
register[arm64.Rconv(i)] = int16(i)
|
||||||
|
}
|
||||||
|
for i := arm64.REG_V0; i <= arm64.REG_V31; i++ {
|
||||||
|
register[arm64.Rconv(i)] = int16(i)
|
||||||
|
}
|
||||||
|
register["LR"] = arm64.REGLINK
|
||||||
|
register["DAIF"] = arm64.REG_DAIF
|
||||||
|
register["NZCV"] = arm64.REG_NZCV
|
||||||
|
register["FPSR"] = arm64.REG_FPSR
|
||||||
|
register["FPCR"] = arm64.REG_FPCR
|
||||||
|
register["SPSR_EL1"] = arm64.REG_SPSR_EL1
|
||||||
|
register["ELR_EL1"] = arm64.REG_ELR_EL1
|
||||||
|
register["SPSR_EL2"] = arm64.REG_SPSR_EL2
|
||||||
|
register["ELR_EL2"] = arm64.REG_ELR_EL2
|
||||||
|
register["CurrentEL"] = arm64.REG_CurrentEL
|
||||||
|
register["SP_EL0"] = arm64.REG_SP_EL0
|
||||||
|
register["SPSel"] = arm64.REG_SPSel
|
||||||
|
register["DAIFSet"] = arm64.REG_DAIFSet
|
||||||
|
register["DAIFClr"] = arm64.REG_DAIFClr
|
||||||
|
// Conditional operators, like EQ, NE, etc.
|
||||||
|
register["EQ"] = arm64.COND_EQ
|
||||||
|
register["NE"] = arm64.COND_NE
|
||||||
|
register["HS"] = arm64.COND_HS
|
||||||
|
register["LO"] = arm64.COND_LO
|
||||||
|
register["MI"] = arm64.COND_MI
|
||||||
|
register["PL"] = arm64.COND_PL
|
||||||
|
register["VS"] = arm64.COND_VS
|
||||||
|
register["VC"] = arm64.COND_VC
|
||||||
|
register["HI"] = arm64.COND_HI
|
||||||
|
register["LS"] = arm64.COND_LS
|
||||||
|
register["GE"] = arm64.COND_GE
|
||||||
|
register["LT"] = arm64.COND_LT
|
||||||
|
register["GT"] = arm64.COND_GT
|
||||||
|
register["LE"] = arm64.COND_LE
|
||||||
|
register["AL"] = arm64.COND_AL
|
||||||
|
register["NV"] = arm64.COND_NV
|
||||||
|
// Pseudo-registers.
|
||||||
|
register["SB"] = RSB
|
||||||
|
register["FP"] = RFP
|
||||||
|
register["PC"] = RPC
|
||||||
|
register["SP"] = RSP
|
||||||
|
// Avoid unintentionally clobbering g using R28.
|
||||||
|
delete(register, "R28")
|
||||||
|
register["g"] = arm64.REG_R28
|
||||||
|
registerPrefix := map[string]bool{
|
||||||
|
"F": true,
|
||||||
|
"R": true,
|
||||||
|
"V": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions := make(map[string]int)
|
||||||
|
for i, s := range obj.Anames {
|
||||||
|
instructions[s] = i
|
||||||
|
}
|
||||||
|
for i, s := range arm64.Anames {
|
||||||
|
if i >= obj.A_ARCHSPECIFIC {
|
||||||
|
instructions[s] = i + obj.ABaseARM64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Annoying aliases.
|
||||||
|
instructions["B"] = arm64.AB
|
||||||
|
instructions["BL"] = arm64.ABL
|
||||||
|
|
||||||
|
return &Arch{
|
||||||
|
LinkArch: &arm64.Linkarm64,
|
||||||
|
Instructions: instructions,
|
||||||
|
Register: register,
|
||||||
|
RegisterPrefix: registerPrefix,
|
||||||
|
RegisterNumber: arm64RegisterNumber,
|
||||||
|
IsJump: jumpArm64,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func archPPC64() *Arch {
|
func archPPC64() *Arch {
|
||||||
register := make(map[string]int16)
|
register := make(map[string]int16)
|
||||||
// Create maps for easy lookup of instruction names etc.
|
// Create maps for easy lookup of instruction names etc.
|
||||||
|
@ -198,6 +198,10 @@ func ARMConditionCodes(prog *obj.Prog, cond string) bool {
|
|||||||
// The input is a single string consisting of period-separated condition
|
// The input is a single string consisting of period-separated condition
|
||||||
// codes, such as ".P.W". An initial period is ignored.
|
// codes, such as ".P.W". An initial period is ignored.
|
||||||
func ParseARMCondition(cond string) (uint8, bool) {
|
func ParseARMCondition(cond string) (uint8, bool) {
|
||||||
|
return parseARMCondition(cond, armLS, armSCOND)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
|
||||||
if strings.HasPrefix(cond, ".") {
|
if strings.HasPrefix(cond, ".") {
|
||||||
cond = cond[1:]
|
cond = cond[1:]
|
||||||
}
|
}
|
||||||
@ -207,11 +211,11 @@ func ParseARMCondition(cond string) (uint8, bool) {
|
|||||||
names := strings.Split(cond, ".")
|
names := strings.Split(cond, ".")
|
||||||
bits := uint8(0)
|
bits := uint8(0)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if b, present := armLS[name]; present {
|
if b, present := ls[name]; present {
|
||||||
bits |= b
|
bits |= b
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if b, present := armSCOND[name]; present {
|
if b, present := scond[name]; present {
|
||||||
bits = (bits &^ arm.C_SCOND) | b
|
bits = (bits &^ arm.C_SCOND) | b
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
114
src/cmd/asm/internal/arch/arm64.go
Normal file
114
src/cmd/asm/internal/arch/arm64.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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 ARM64
|
||||||
|
// instruction set, to minimize its interaction with the core of the
|
||||||
|
// assembler.
|
||||||
|
|
||||||
|
package arch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/internal/obj"
|
||||||
|
"cmd/internal/obj/arm64"
|
||||||
|
)
|
||||||
|
|
||||||
|
var arm64LS = map[string]uint8{
|
||||||
|
"P": arm64.C_XPOST,
|
||||||
|
"W": arm64.C_XPRE,
|
||||||
|
}
|
||||||
|
|
||||||
|
var arm64Jump = map[string]bool{
|
||||||
|
"B": true,
|
||||||
|
"BL": 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,
|
||||||
|
"CBZ": true,
|
||||||
|
"CBZW": true,
|
||||||
|
"CBNZ": true,
|
||||||
|
"CBNZW": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func jumpArm64(word string) bool {
|
||||||
|
return arm64Jump[word]
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
|
||||||
|
// one of the comparison instructions that require special handling.
|
||||||
|
func IsARM64CMP(op int) bool {
|
||||||
|
switch op {
|
||||||
|
case arm64.ACMN, arm64.ACMP, arm64.ATST,
|
||||||
|
arm64.ACMNW, arm64.ACMPW, arm64.ATSTW:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsARM64STLXR reports whether the op (as defined by an arm64.A*
|
||||||
|
// constant) is one of the STLXR-like instructions that require special
|
||||||
|
// handling.
|
||||||
|
func IsARM64STLXR(op int) bool {
|
||||||
|
switch op {
|
||||||
|
case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARM64Suffix handles the special suffix for the ARM64.
|
||||||
|
// It returns a boolean to indicate success; failure means
|
||||||
|
// cond was unrecognized.
|
||||||
|
func ARM64Suffix(prog *obj.Prog, cond string) bool {
|
||||||
|
if cond == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
bits, ok := ParseARM64Suffix(cond)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prog.Scond = bits
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseARM64Suffix parses the suffix attached to an ARM64 instruction.
|
||||||
|
// The input is a single string consisting of period-separated condition
|
||||||
|
// codes, such as ".P.W". An initial period is ignored.
|
||||||
|
func ParseARM64Suffix(cond string) (uint8, bool) {
|
||||||
|
if cond == "" {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return parseARMCondition(cond, arm64LS, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func arm64RegisterNumber(name string, n int16) (int16, bool) {
|
||||||
|
switch name {
|
||||||
|
case "F":
|
||||||
|
if 0 <= n && n <= 31 {
|
||||||
|
return arm64.REG_F0 + n, true
|
||||||
|
}
|
||||||
|
case "R":
|
||||||
|
if 0 <= n && n <= 30 { // not 31
|
||||||
|
return arm64.REG_R0 + n, true
|
||||||
|
}
|
||||||
|
case "V":
|
||||||
|
if 0 <= n && n <= 31 {
|
||||||
|
return arm64.REG_V0 + n, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
@ -22,9 +22,20 @@ var testOut *bytes.Buffer // Gathers output when testing.
|
|||||||
// append adds the Prog to the end of the program-thus-far.
|
// append adds the Prog to the end of the program-thus-far.
|
||||||
// If doLabel is set, it also defines the labels collect for this Prog.
|
// If doLabel is set, it also defines the labels collect for this Prog.
|
||||||
func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {
|
func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {
|
||||||
if p.arch.Thechar == '5' {
|
if cond != "" {
|
||||||
if !arch.ARMConditionCodes(prog, cond) {
|
switch p.arch.Thechar {
|
||||||
p.errorf("unrecognized condition code .%q", cond)
|
case '5':
|
||||||
|
if !arch.ARMConditionCodes(prog, cond) {
|
||||||
|
p.errorf("unrecognized condition code .%q", cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
case '7':
|
||||||
|
if !arch.ARM64Suffix(prog, cond) {
|
||||||
|
p.errorf("unrecognized suffix .%q", cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
p.errorf("unrecognized suffix .%q", cond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.firstProg == nil {
|
if p.firstProg == nil {
|
||||||
@ -307,14 +318,9 @@ func (p *Parser) asmJump(op int, cond string, a []obj.Addr) {
|
|||||||
case 1:
|
case 1:
|
||||||
target = &a[0]
|
target = &a[0]
|
||||||
case 2:
|
case 2:
|
||||||
if p.arch.Thechar == '9' {
|
// Special 2-operand jumps.
|
||||||
// Special 2-operand jumps.
|
target = &a[1]
|
||||||
target = &a[1]
|
prog.From = a[0]
|
||||||
prog.From = a[0]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.errorf("wrong number of arguments to %s instruction", obj.Aconv(op))
|
|
||||||
return
|
|
||||||
case 3:
|
case 3:
|
||||||
if p.arch.Thechar == '9' {
|
if p.arch.Thechar == '9' {
|
||||||
// Special 3-operand jumps.
|
// Special 3-operand jumps.
|
||||||
@ -457,6 +463,10 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
|
|||||||
}
|
}
|
||||||
p.errorf("unrecognized addressing for %s", obj.Aconv(op))
|
p.errorf("unrecognized addressing for %s", obj.Aconv(op))
|
||||||
}
|
}
|
||||||
|
} else if p.arch.Thechar == '7' && arch.IsARM64CMP(op) {
|
||||||
|
prog.From = a[0]
|
||||||
|
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||||
|
break
|
||||||
}
|
}
|
||||||
prog.From = a[0]
|
prog.From = a[0]
|
||||||
prog.To = a[1]
|
prog.To = a[1]
|
||||||
@ -478,6 +488,17 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
|
|||||||
prog.From = a[0]
|
prog.From = a[0]
|
||||||
prog.Reg = p.getRegister(prog, op, &a[1])
|
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||||
prog.To = a[2]
|
prog.To = a[2]
|
||||||
|
case '7':
|
||||||
|
// ARM64 instructions with one input and two outputs.
|
||||||
|
if arch.IsARM64STLXR(op) {
|
||||||
|
prog.From = a[0]
|
||||||
|
prog.To = a[1]
|
||||||
|
prog.To2 = a[2]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
prog.From = a[0]
|
||||||
|
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||||
|
prog.To = a[2]
|
||||||
case '6', '8':
|
case '6', '8':
|
||||||
prog.From = a[0]
|
prog.From = a[0]
|
||||||
prog.From3 = a[1]
|
prog.From3 = a[1]
|
||||||
|
@ -55,6 +55,10 @@ func TestARMOperandParser(t *testing.T) {
|
|||||||
parser := newParser("arm")
|
parser := newParser("arm")
|
||||||
testOperandParser(t, parser, armOperandTests)
|
testOperandParser(t, parser, armOperandTests)
|
||||||
}
|
}
|
||||||
|
func TestARM64OperandParser(t *testing.T) {
|
||||||
|
parser := newParser("arm64")
|
||||||
|
testOperandParser(t, parser, arm64OperandTests)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPPC64OperandParser(t *testing.T) {
|
func TestPPC64OperandParser(t *testing.T) {
|
||||||
parser := newParser("ppc64")
|
parser := newParser("ppc64")
|
||||||
@ -373,3 +377,51 @@ var ppc64OperandTests = []operandTest{
|
|||||||
{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
|
{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
|
||||||
{"·trunc(SB)", "\"\".trunc(SB)"},
|
{"·trunc(SB)", "\"\".trunc(SB)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var arm64OperandTests = []operandTest{
|
||||||
|
{"$0", "$0"},
|
||||||
|
{"$0.5", "$(0.5)"},
|
||||||
|
{"0(R26)", "(R26)"},
|
||||||
|
{"0(RSP)", "(RSP)"},
|
||||||
|
{"$1", "$1"},
|
||||||
|
{"$-1", "$-1"},
|
||||||
|
{"$1000", "$1000"},
|
||||||
|
{"$1000000000", "$1000000000"},
|
||||||
|
{"$0x7fff3c000", "$34358935552"},
|
||||||
|
{"$1234", "$1234"},
|
||||||
|
{"$~15", "$-16"},
|
||||||
|
{"$16", "$16"},
|
||||||
|
{"-16(RSP)", "-16(RSP)"},
|
||||||
|
{"16(RSP)", "16(RSP)"},
|
||||||
|
{"1(R1)", "1(R1)"},
|
||||||
|
{"-1(R4)", "-1(R4)"},
|
||||||
|
{"18740(R5)", "18740(R5)"},
|
||||||
|
{"$2", "$2"},
|
||||||
|
{"$-24(R4)", "$-24(R4)"},
|
||||||
|
{"-24(RSP)", "-24(RSP)"},
|
||||||
|
{"$24(RSP)", "$24(RSP)"},
|
||||||
|
{"-32(RSP)", "-32(RSP)"},
|
||||||
|
{"$48", "$48"},
|
||||||
|
{"$(-64*1024)(R7)", "$-65536(R7)"},
|
||||||
|
{"$(8-1)", "$7"},
|
||||||
|
{"a+0(FP)", "a(FP)"},
|
||||||
|
{"a1+8(FP)", "a1+8(FP)"},
|
||||||
|
{"·AddInt32(SB)", `"".AddInt32(SB)`},
|
||||||
|
{"runtime·divWVW(SB)", "runtime.divWVW(SB)"},
|
||||||
|
{"$argframe+0(FP)", "$argframe(FP)"},
|
||||||
|
{"$asmcgocall<>(SB)", "$asmcgocall<>(SB)"},
|
||||||
|
{"EQ", "EQ"},
|
||||||
|
{"F29", "F29"},
|
||||||
|
{"F3", "F3"},
|
||||||
|
{"F30", "F30"},
|
||||||
|
{"g", "g"},
|
||||||
|
{"LR", "R30"},
|
||||||
|
{"(LR)", "(R30)"},
|
||||||
|
{"R0", "R0"},
|
||||||
|
{"R10", "R10"},
|
||||||
|
{"R11", "R11"},
|
||||||
|
{"$4503601774854144.0", "$(4503601774854144.0)"},
|
||||||
|
{"$runtime·badsystemstack(SB)", "$runtime.badsystemstack(SB)"},
|
||||||
|
{"ZR", "ZR"},
|
||||||
|
{"(ZR)", "(ZR)"},
|
||||||
|
}
|
||||||
|
@ -116,7 +116,7 @@ func (p *Parser) line() bool {
|
|||||||
for {
|
for {
|
||||||
tok = p.lex.Next()
|
tok = p.lex.Next()
|
||||||
if len(operands) == 0 && len(items) == 0 {
|
if len(operands) == 0 && len(items) == 0 {
|
||||||
if p.arch.Thechar == '5' && tok == '.' {
|
if (p.arch.Thechar == '5' || p.arch.Thechar == '7') && tok == '.' {
|
||||||
// ARM conditionals.
|
// ARM conditionals.
|
||||||
tok = p.lex.Next()
|
tok = p.lex.Next()
|
||||||
str := p.lex.Text()
|
str := p.lex.Text()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user