Commit c497349a authored by Rob Pike's avatar Rob Pike

[dev.cc] cmd/asm: support ARM

There are many peculiarites of the ARM architecture that require work:
condition codes, new instructions, new instruction arg counts, and more.

Rewrite the parser to do a cleaner job, flowing left to right through the
sequence of elements of an operand.

Add ARM to arch.
Add ARM-specific details to the arch in a new file, internal/arch/arm.
These are probably better kept away from the "portable" asm. However
there are some pieces, like MRC, that are hard to disentangle. They
can be cleaned up later.

Change-Id: I8c06aedcf61f8a3960a406c094e168182d21b972
Reviewed-on: https://go-review.googlesource.com/4923Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
parent ae2b145d
......@@ -6,8 +6,10 @@ package arch
import (
"cmd/internal/obj"
"cmd/internal/obj/arm"
"cmd/internal/obj/i386" // == 386
"cmd/internal/obj/x86" // == amd64
"fmt"
)
// Pseudo-registers whose names are the constant name without the leading R.
......@@ -27,6 +29,12 @@ type Arch struct {
Registers map[string]int16
// Instructions that take one operand whose result is a destination.
UnaryDestination map[int]bool
// Instruction is a jump.
IsJump func(word string) bool
// Aconv pretty-prints an instruction opcode for this architecture.
Aconv func(int) string
// Dconv pretty-prints an address for this architecture.
Dconv func(p *obj.Prog, flag int, a *obj.Addr) string
}
var Pseudos = map[string]int{
......@@ -46,12 +54,21 @@ func Set(GOARCH string) *Arch {
return arch386()
case "amd64":
return archAmd64()
case "amd64p32":
a := archAmd64()
a.LinkArch = &x86.Linkamd64p32
return a
case "arm":
return archArm()
}
return nil
}
func arch386() *Arch {
func jump386(word string) bool {
return word[0] == 'J' || word == "CALL"
}
func arch386() *Arch {
registers := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// TODO: Should this be done in obj for us?
......@@ -147,11 +164,13 @@ func arch386() *Arch {
Instructions: instructions,
Registers: registers,
UnaryDestination: unaryDestination,
IsJump: jump386,
Aconv: i386.Aconv,
Dconv: i386.Dconv,
}
}
func archAmd64() *Arch {
registers := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// TODO: Should this be done in obj for us?
......@@ -254,5 +273,55 @@ func archAmd64() *Arch {
Instructions: instructions,
Registers: registers,
UnaryDestination: unaryDestination,
IsJump: jump386,
Aconv: x86.Aconv,
Dconv: x86.Dconv,
}
}
func archArm() *Arch {
registers := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// TODO: Should this be done in obj for us?
// Note that there is no list of names as there is for 386 and amd64.
// TODO: Are there aliases we need to add?
for i := arm.REG_R0; i < arm.REG_SPSR; i++ {
registers[arm.Rconv(i)] = int16(i)
}
// Avoid unintentionally clobbering g using R10.
delete(registers, "R10")
registers["g"] = arm.REG_R10
for i := 0; i < 16; i++ {
registers[fmt.Sprintf("C%d", i)] = int16(i)
}
// Pseudo-registers.
registers["SB"] = RSB
registers["FP"] = RFP
registers["PC"] = RPC
registers["SP"] = RSP
instructions := make(map[string]int)
for i, s := range arm.Anames {
instructions[s] = i
}
// Annoying aliases.
instructions["B"] = obj.AJMP
instructions["BL"] = obj.ACALL
unaryDestination := make(map[int]bool) // Instruction takes one operand and result is a destination.
// These instructions write to prog.To.
// TODO: These are silly. Fix once C assembler is gone.
unaryDestination[arm.ASWI] = true
unaryDestination[arm.AWORD] = true
return &Arch{
LinkArch: &arm.Linkarm,
Instructions: instructions,
Registers: registers,
UnaryDestination: unaryDestination,
IsJump: jumpArm,
Aconv: arm.Aconv,
Dconv: arm.Dconv,
}
}
// 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,
"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,
}
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 int) 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 int) bool {
switch op {
case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
return true
}
return false
}
// IsARMMRC reports whether the op (as defined by an arm.A* constant) is
// MRC or MCR
func IsARMMRC(op int) bool {
switch op {
case arm.AMRC /*, arm.AMCR*/ :
return true
}
return false
}
// IsARMMULA reports whether the op (as defined by an arm.A* constant) is
// MULA, MULAWT or MULAWB, the 4-operand instructions.
func IsARMMULA(op int) bool {
switch op {
case arm.AMULA, arm.AMULAWB, arm.AMULAWT:
return true
}
return false
}
var bcode = []int{
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 = int16(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) {
if strings.HasPrefix(cond, ".") {
cond = cond[1:]
}
if cond == "" {
return arm.C_SCOND_NONE, true
}
names := strings.Split(cond, ".")
bits := uint8(0)
for _, name := range names {
if b, present := armLS[name]; present {
bits |= b
continue
}
if b, present := armSCOND[name]; present {
bits = (bits &^ arm.C_SCOND) | b
continue
}
return 0, false
}
return bits, true
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment