mirror of
https://github.com/golang/go
synced 2024-11-23 22:30:05 -07:00
[dev.cc] cmd/asm: add ppc64
Fairly straightforward. A couple of unusual addressing tricks. Also added the ability to write R(10) to mean R10. PPC64 uses this for a couple of large register spaces. It appears for ARM now as well, since I saw some uses of that before, although I rewrote them in our source. I could put it in for 386 and amd64 but it's not worth it. Change-Id: I3ffd7ffa62d511b95b92c3c75b9f1d621f5393b6 Reviewed-on: https://go-review.googlesource.com/5282 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
6acd5a65b2
commit
e559c5cce2
@ -8,6 +8,7 @@ import (
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/arm"
|
||||
"cmd/internal/obj/i386" // == 386
|
||||
"cmd/internal/obj/ppc64"
|
||||
"cmd/internal/obj/x86" // == amd64
|
||||
"fmt"
|
||||
)
|
||||
@ -26,7 +27,11 @@ type Arch struct {
|
||||
// Map of instruction names to enumeration.
|
||||
Instructions map[string]int
|
||||
// Map of register names to enumeration.
|
||||
Registers map[string]int16
|
||||
Register map[string]int16
|
||||
// Table of register prefix names. These are things like R for R(0) and SPR for SPR(268).
|
||||
RegisterPrefix map[string]bool
|
||||
// RegisterNumber converts R(10) into arm.REG_R10.
|
||||
RegisterNumber func(string, int16) (int16, bool)
|
||||
// Instructions that take one operand whose result is a destination.
|
||||
UnaryDestination map[int]bool
|
||||
// Instruction is a jump.
|
||||
@ -37,6 +42,12 @@ type Arch struct {
|
||||
Dconv func(p *obj.Prog, flag int, a *obj.Addr) string
|
||||
}
|
||||
|
||||
// nilRegisterNumber is the register number function for architectures
|
||||
// that do not accept the R(N) notation. It always returns failure.
|
||||
func nilRegisterNumber(name string, n int16) (int16, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
var Pseudos = map[string]int{
|
||||
"DATA": obj.ADATA,
|
||||
"FUNCDATA": obj.AFUNCDATA,
|
||||
@ -60,6 +71,10 @@ func Set(GOARCH string) *Arch {
|
||||
return a
|
||||
case "arm":
|
||||
return archArm()
|
||||
case "ppc64":
|
||||
a := archPPC64()
|
||||
a.LinkArch = &ppc64.Linkppc64
|
||||
return a
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -69,16 +84,17 @@ func jump386(word string) bool {
|
||||
}
|
||||
|
||||
func arch386() *Arch {
|
||||
registers := make(map[string]int16)
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// TODO: Should this be done in obj for us?
|
||||
for i, s := range i386.Register {
|
||||
registers[s] = int16(i + i386.REG_AL)
|
||||
register[s] = int16(i + i386.REG_AL)
|
||||
}
|
||||
// Pseudo-registers.
|
||||
registers["SB"] = RSB
|
||||
registers["FP"] = RFP
|
||||
registers["PC"] = RPC
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Prefixes not used on this architecture.
|
||||
|
||||
instructions := make(map[string]int)
|
||||
for i, s := range i386.Anames {
|
||||
@ -162,7 +178,9 @@ func arch386() *Arch {
|
||||
return &Arch{
|
||||
LinkArch: &i386.Link386,
|
||||
Instructions: instructions,
|
||||
Registers: registers,
|
||||
Register: register,
|
||||
RegisterPrefix: nil,
|
||||
RegisterNumber: nilRegisterNumber,
|
||||
UnaryDestination: unaryDestination,
|
||||
IsJump: jump386,
|
||||
Aconv: i386.Aconv,
|
||||
@ -171,16 +189,17 @@ func arch386() *Arch {
|
||||
}
|
||||
|
||||
func archAmd64() *Arch {
|
||||
registers := make(map[string]int16)
|
||||
register := make(map[string]int16)
|
||||
// Create maps for easy lookup of instruction names etc.
|
||||
// TODO: Should this be done in obj for us?
|
||||
for i, s := range x86.Register {
|
||||
registers[s] = int16(i + x86.REG_AL)
|
||||
register[s] = int16(i + x86.REG_AL)
|
||||
}
|
||||
// Pseudo-registers.
|
||||
registers["SB"] = RSB
|
||||
registers["FP"] = RFP
|
||||
registers["PC"] = RPC
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Register prefix not used on this architecture.
|
||||
|
||||
instructions := make(map[string]int)
|
||||
for i, s := range x86.Anames {
|
||||
@ -271,7 +290,9 @@ func archAmd64() *Arch {
|
||||
return &Arch{
|
||||
LinkArch: &x86.Linkamd64,
|
||||
Instructions: instructions,
|
||||
Registers: registers,
|
||||
Register: register,
|
||||
RegisterPrefix: nil,
|
||||
RegisterNumber: nilRegisterNumber,
|
||||
UnaryDestination: unaryDestination,
|
||||
IsJump: jump386,
|
||||
Aconv: x86.Aconv,
|
||||
@ -280,26 +301,30 @@ func archAmd64() *Arch {
|
||||
}
|
||||
|
||||
func archArm() *Arch {
|
||||
registers := make(map[string]int16)
|
||||
register := 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)
|
||||
register[arm.Rconv(i)] = int16(i)
|
||||
}
|
||||
// Avoid unintentionally clobbering g using R10.
|
||||
delete(registers, "R10")
|
||||
registers["g"] = arm.REG_R10
|
||||
delete(register, "R10")
|
||||
register["g"] = arm.REG_R10
|
||||
for i := 0; i < 16; i++ {
|
||||
registers[fmt.Sprintf("C%d", i)] = int16(i)
|
||||
register[fmt.Sprintf("C%d", i)] = int16(i)
|
||||
}
|
||||
|
||||
// Pseudo-registers.
|
||||
registers["SB"] = RSB
|
||||
registers["FP"] = RFP
|
||||
registers["PC"] = RPC
|
||||
registers["SP"] = RSP
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
register["SP"] = RSP
|
||||
registerPrefix := map[string]bool{
|
||||
"F": true,
|
||||
"R": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]int)
|
||||
for i, s := range arm.Anames {
|
||||
@ -318,10 +343,72 @@ func archArm() *Arch {
|
||||
return &Arch{
|
||||
LinkArch: &arm.Linkarm,
|
||||
Instructions: instructions,
|
||||
Registers: registers,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: armRegisterNumber,
|
||||
UnaryDestination: unaryDestination,
|
||||
IsJump: jumpArm,
|
||||
Aconv: arm.Aconv,
|
||||
Dconv: arm.Dconv,
|
||||
}
|
||||
}
|
||||
|
||||
func archPPC64() *Arch {
|
||||
register := 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.
|
||||
for i := ppc64.REG_R0; i <= ppc64.REG_R31; i++ {
|
||||
register[ppc64.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_F0; i <= ppc64.REG_F31; i++ {
|
||||
register[ppc64.Rconv(i)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_C0; i <= ppc64.REG_C7; i++ {
|
||||
// TODO: Rconv prints these as C7 but the input syntax requires CR7.
|
||||
register[fmt.Sprintf("CR%d", i-ppc64.REG_C0)] = int16(i)
|
||||
}
|
||||
for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ {
|
||||
register[ppc64.Rconv(i)] = int16(i)
|
||||
}
|
||||
register["CR"] = ppc64.REG_CR
|
||||
register["XER"] = ppc64.REG_XER
|
||||
register["LR"] = ppc64.REG_LR
|
||||
register["CTR"] = ppc64.REG_CTR
|
||||
register["FPSCR"] = ppc64.REG_FPSCR
|
||||
register["MSR"] = ppc64.REG_MSR
|
||||
// Pseudo-registers.
|
||||
register["SB"] = RSB
|
||||
register["FP"] = RFP
|
||||
register["PC"] = RPC
|
||||
// Avoid unintentionally clobbering g using R30.
|
||||
delete(register, "R30")
|
||||
register["g"] = ppc64.REG_R30
|
||||
registerPrefix := map[string]bool{
|
||||
"CR": true,
|
||||
"F": true,
|
||||
"R": true,
|
||||
"SPR": true,
|
||||
}
|
||||
|
||||
instructions := make(map[string]int)
|
||||
for i, s := range ppc64.Anames {
|
||||
instructions[s] = i
|
||||
}
|
||||
// Annoying aliases.
|
||||
instructions["BR"] = ppc64.ABR
|
||||
instructions["BL"] = ppc64.ABL
|
||||
instructions["RETURN"] = ppc64.ARETURN
|
||||
|
||||
return &Arch{
|
||||
LinkArch: &ppc64.Linkppc64,
|
||||
Instructions: instructions,
|
||||
Register: register,
|
||||
RegisterPrefix: registerPrefix,
|
||||
RegisterNumber: ppc64RegisterNumber,
|
||||
UnaryDestination: nil,
|
||||
IsJump: jumpPPC64,
|
||||
Aconv: ppc64.Aconv,
|
||||
Dconv: ppc64.Dconv,
|
||||
}
|
||||
}
|
||||
|
@ -188,3 +188,16 @@ func parseARMCondition(cond string) (uint8, bool) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
63
src/cmd/asm/internal/arch/ppc64.go
Normal file
63
src/cmd/asm/internal/arch/ppc64.go
Normal file
@ -0,0 +1,63 @@
|
||||
// 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 "cmd/internal/obj/ppc64"
|
||||
|
||||
func jumpPPC64(word string) bool {
|
||||
switch word {
|
||||
case "BC", "BCL", "BEQ", "BGE", "BGT", "BL", "BLE", "BLT", "BNE", "BR", "BVC", "BVS", "CALL":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPPC64RLD reports whether the op (as defined by an ppc64.A* constant) is
|
||||
// one of the RLD-like instructions that require special handling.
|
||||
func IsPPC64RLD(op int) bool {
|
||||
switch op {
|
||||
case ppc64.ARLDC, ppc64.ARLDCCC, ppc64.ARLDCL, ppc64.ARLDCLCC,
|
||||
ppc64.ARLDCR, ppc64.ARLDCRCC, ppc64.ARLDMI, ppc64.ARLDMICC,
|
||||
ppc64.ARLWMI, ppc64.ARLWMICC, ppc64.ARLWNM, ppc64.ARLWNMCC:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPPC64CMP reports whether the op (as defined by an ppc64.A* constant) is
|
||||
// one of the CMP instructions that require special handling.
|
||||
func IsPPC64CMP(op int) bool {
|
||||
switch op {
|
||||
case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ppc64RegisterNumber(name string, n int16) (int16, bool) {
|
||||
switch name {
|
||||
case "CR":
|
||||
if 0 <= n && n <= 7 {
|
||||
return ppc64.REG_C0 + n, true
|
||||
}
|
||||
case "F":
|
||||
if 0 <= n && n <= 31 {
|
||||
return ppc64.REG_F0 + n, true
|
||||
}
|
||||
case "R":
|
||||
if 0 <= n && n <= 31 {
|
||||
return ppc64.REG_R0 + n, true
|
||||
}
|
||||
case "SPR":
|
||||
if 0 <= n && n <= 1024 {
|
||||
return ppc64.REG_SPR0 + n, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
@ -13,6 +13,7 @@ import (
|
||||
"cmd/asm/internal/lex"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/arm"
|
||||
"cmd/internal/obj/ppc64"
|
||||
)
|
||||
|
||||
// TODO: configure the architecture
|
||||
@ -292,23 +293,37 @@ func (p *Parser) asmFuncData(word string, operands [][]lex.Token) {
|
||||
// JMP 3(PC)
|
||||
func (p *Parser) asmJump(op int, cond string, a []obj.Addr) {
|
||||
var target *obj.Addr
|
||||
switch len(a) {
|
||||
case 1:
|
||||
target = &a[0]
|
||||
default:
|
||||
p.errorf("wrong number of arguments to %s instruction", p.arch.Aconv(op))
|
||||
}
|
||||
prog := &obj.Prog{
|
||||
Ctxt: p.linkCtxt,
|
||||
Lineno: p.histLineNum,
|
||||
As: int16(op),
|
||||
}
|
||||
switch len(a) {
|
||||
case 1:
|
||||
target = &a[0]
|
||||
case 3:
|
||||
if p.arch.Thechar == '9' {
|
||||
target = &a[2]
|
||||
// Special 3-operand jumps.
|
||||
// First two must be constants.
|
||||
prog.From = obj.Addr{
|
||||
Type: obj.TYPE_CONST,
|
||||
Offset: p.getConstant(prog, op, &a[0]),
|
||||
}
|
||||
prog.Reg = int16(ppc64.REG_R0 + p.getConstant(prog, op, &a[1]))
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
p.errorf("wrong number of arguments to %s instruction", p.arch.Aconv(op))
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case target.Type == obj.TYPE_BRANCH:
|
||||
// JMP 4(PC)
|
||||
prog.To = obj.Addr{
|
||||
Type: obj.TYPE_BRANCH,
|
||||
Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in link, below.
|
||||
Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in append, below.
|
||||
}
|
||||
case target.Type == obj.TYPE_REG:
|
||||
// JMP R1
|
||||
@ -322,6 +337,10 @@ func (p *Parser) asmJump(op int, cond string, a []obj.Addr) {
|
||||
prog.To.Type = obj.TYPE_INDIR
|
||||
case target.Type == obj.TYPE_MEM && target.Reg == 0 && target.Offset == 0:
|
||||
// JMP exit
|
||||
if target.Sym == nil {
|
||||
// Parse error left name unset.
|
||||
return
|
||||
}
|
||||
targetProg := p.labels[target.Sym.Name]
|
||||
if targetProg == nil {
|
||||
p.toPatch = append(p.toPatch, Patch{prog, target.Sym.Name})
|
||||
@ -329,8 +348,12 @@ func (p *Parser) asmJump(op int, cond string, a []obj.Addr) {
|
||||
p.branch(prog, targetProg)
|
||||
}
|
||||
case target.Type == obj.TYPE_MEM && target.Name == obj.NAME_NONE:
|
||||
// JMP 4(PC)
|
||||
// JMP 4(R0)
|
||||
prog.To = *target
|
||||
// On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same.
|
||||
if p.arch.Thechar == '9' && target.Offset == 0 {
|
||||
prog.To.Type = obj.TYPE_REG
|
||||
}
|
||||
default:
|
||||
p.errorf("cannot assemble jump %+v", target)
|
||||
}
|
||||
@ -452,6 +475,31 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
|
||||
default:
|
||||
p.errorf("expected offset or register for 3rd operand")
|
||||
}
|
||||
case '9':
|
||||
if arch.IsPPC64CMP(op) {
|
||||
// CMPW etc.; third argument is a CR register that goes into prog.Reg.
|
||||
prog.From = a[0]
|
||||
prog.Reg = p.getRegister(prog, op, &a[2])
|
||||
prog.To = a[1]
|
||||
break
|
||||
}
|
||||
// Arithmetic. Choices are:
|
||||
// reg reg reg
|
||||
// imm reg reg
|
||||
// reg imm reg
|
||||
// If the immediate is the middle argument, use From3.
|
||||
switch a[1].Type {
|
||||
case obj.TYPE_REG:
|
||||
prog.From = a[0]
|
||||
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||
prog.To = a[2]
|
||||
case obj.TYPE_CONST:
|
||||
prog.From = a[0]
|
||||
prog.From3 = a[1]
|
||||
prog.To = a[2]
|
||||
default:
|
||||
p.errorf("invalid addressing modes for %s instruction", p.arch.Aconv(op))
|
||||
}
|
||||
default:
|
||||
p.errorf("TODO: implement three-operand instructions for this architecture")
|
||||
}
|
||||
@ -469,6 +517,14 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
|
||||
prog.Reg = r1
|
||||
break
|
||||
}
|
||||
if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) {
|
||||
// 2nd operand is always a register.
|
||||
prog.From = a[0]
|
||||
prog.Reg = p.getRegister(prog, op, &a[1])
|
||||
prog.From3 = a[2]
|
||||
prog.To = a[3]
|
||||
break
|
||||
}
|
||||
p.errorf("can't handle %s instruction with 4 operands", p.arch.Aconv(op))
|
||||
case 6:
|
||||
// MCR and MRC on ARM
|
||||
|
@ -5,6 +5,7 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cmd/asm/internal/arch"
|
||||
@ -15,10 +16,10 @@ import (
|
||||
// A simple in-out test: Do we print what we parse?
|
||||
|
||||
func newParser(goarch string) *Parser {
|
||||
os.Setenv("GOOS", "linux") // obj can handle this OS for all architectures.
|
||||
architecture := arch.Set(goarch)
|
||||
ctxt := obj.Linknew(architecture.LinkArch)
|
||||
parser := NewParser(ctxt, architecture, nil)
|
||||
return parser
|
||||
return NewParser(ctxt, architecture, nil)
|
||||
}
|
||||
|
||||
func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) {
|
||||
@ -34,15 +35,15 @@ func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) {
|
||||
}
|
||||
|
||||
func testX86RegisterPair(t *testing.T, parser *Parser) {
|
||||
// Special case for AX:DX, which is really two operands so isn't print correcctly
|
||||
// Special case for AX:DX, which is really two operands so isn't printed correcctly
|
||||
// by Aconv, but is OK by the -S output.
|
||||
parser.start(lex.Tokenize("AX:BX)"))
|
||||
addr := obj.Addr{}
|
||||
parser.operand(&addr)
|
||||
want := obj.Addr{
|
||||
Type: obj.TYPE_REG,
|
||||
Reg: parser.arch.Registers["AX"],
|
||||
Class: int8(parser.arch.Registers["BX"]), // TODO: clean up how this is encoded in parse.go
|
||||
Reg: parser.arch.Register["AX"],
|
||||
Class: int8(parser.arch.Register["BX"]), // TODO: clean up how this is encoded in parse.go
|
||||
}
|
||||
if want != addr {
|
||||
t.Errorf("AX:DX: expected %+v got %+v", want, addr)
|
||||
@ -66,6 +67,11 @@ func TestARMOperandParser(t *testing.T) {
|
||||
testOperandParser(t, parser, armOperandTests)
|
||||
}
|
||||
|
||||
func TestPPC64OperandParser(t *testing.T) {
|
||||
parser := newParser("ppc64")
|
||||
testOperandParser(t, parser, ppc64OperandTests)
|
||||
}
|
||||
|
||||
type operandTest struct {
|
||||
input, output string
|
||||
}
|
||||
@ -251,12 +257,12 @@ var armOperandTests = []operandTest{
|
||||
{"-12(R4)", "-12(R4)"},
|
||||
{"0(PC)", "0(PC)"},
|
||||
{"1024", "1024"},
|
||||
{"12(R1)", "12(R1)"},
|
||||
{"12(R(1))", "12(R1)"},
|
||||
{"12(R13)", "12(R13)"},
|
||||
{"R0", "R0"},
|
||||
{"R0->(32-1)", "R0->31"},
|
||||
{"R0<<R1", "R0<<R1"},
|
||||
{"R0>>R1", "R0>>R1"},
|
||||
{"R0>>R(1)", "R0>>R1"},
|
||||
{"R0@>(32-1)", "R0@>31"},
|
||||
{"R1", "R1"},
|
||||
{"R11", "R11"},
|
||||
@ -268,6 +274,7 @@ var armOperandTests = []operandTest{
|
||||
{"R2", "R2"},
|
||||
{"R3", "R3"},
|
||||
{"R4", "R4"},
|
||||
{"R(4)", "R4"},
|
||||
{"R5", "R5"},
|
||||
{"R6", "R6"},
|
||||
{"R7", "R7"},
|
||||
@ -275,6 +282,7 @@ var armOperandTests = []operandTest{
|
||||
// TODO: Fix Dconv to handle these. MOVM print shows the registers.
|
||||
{"[R0,R1,g,R15]", "$33795"},
|
||||
{"[R0-R7]", "$255"},
|
||||
{"[R(0)-R(7)]", "$255"},
|
||||
{"[R0]", "$1"},
|
||||
{"[R1-R12]", "$8190"},
|
||||
{"armCAS64(SB)", "armCAS64+0(SB)"},
|
||||
@ -286,3 +294,90 @@ var armOperandTests = []operandTest{
|
||||
{"runtime·_sfloat2(SB)", "runtime._sfloat2+0(SB)"},
|
||||
{"·AddUint32(SB)", "\"\".AddUint32+0(SB)"},
|
||||
}
|
||||
|
||||
var ppc64OperandTests = []operandTest{
|
||||
{"$((1<<63)-1)", "$0x7fffffffffffffff"},
|
||||
{"$(-64*1024)", "$-65536"},
|
||||
{"$(1024 * 8)", "$8192"},
|
||||
{"$-1", "$-1"},
|
||||
{"$-24(R4)", "$-24(R4)"},
|
||||
{"$0", "$0"},
|
||||
{"$0(R1)", "$0(R1)"},
|
||||
{"$0.5", "$0.5"},
|
||||
{"$0x7000", "$28672"},
|
||||
{"$0x88888eef", "$0x88888eef"},
|
||||
{"$1", "$1"},
|
||||
{"$_main<>(SB)", "$_main<>+0(SB)"},
|
||||
{"$argframe+0(FP)", "$argframe+0(FP)"},
|
||||
{"$runtime·tlsg(SB)", "$runtime.tlsg(SB)"},
|
||||
{"$~3", "$-4"},
|
||||
{"(-288-3*8)(R1)", "-312(R1)"},
|
||||
{"(16)(R7)", "16(R7)"},
|
||||
{"(8)(g)", "8(R30)"}, // TODO: Should print 8(g)
|
||||
{"(CTR)", "0(CTR)"},
|
||||
{"(R0)", "0(R0)"},
|
||||
{"(R3)", "0(R3)"},
|
||||
{"(R4)", "0(R4)"},
|
||||
{"(R5)", "0(R5)"},
|
||||
{"-1(R4)", "-1(R4)"},
|
||||
{"-1(R5)", "-1(R5)"},
|
||||
{"6(PC)", "6(APC)"}, // TODO: Should print 6(PC).
|
||||
{"CR7", "C7"}, // TODO: Should print CR7.
|
||||
{"CTR", "CTR"},
|
||||
{"F14", "F14"},
|
||||
{"F15", "F15"},
|
||||
{"F16", "F16"},
|
||||
{"F17", "F17"},
|
||||
{"F18", "F18"},
|
||||
{"F19", "F19"},
|
||||
{"F20", "F20"},
|
||||
{"F21", "F21"},
|
||||
{"F22", "F22"},
|
||||
{"F23", "F23"},
|
||||
{"F24", "F24"},
|
||||
{"F25", "F25"},
|
||||
{"F26", "F26"},
|
||||
{"F27", "F27"},
|
||||
{"F28", "F28"},
|
||||
{"F29", "F29"},
|
||||
{"F30", "F30"},
|
||||
{"F31", "F31"},
|
||||
{"LR", "LR"},
|
||||
{"R0", "R0"},
|
||||
{"R1", "R1"},
|
||||
{"R11", "R11"},
|
||||
{"R12", "R12"},
|
||||
{"R13", "R13"},
|
||||
{"R14", "R14"},
|
||||
{"R15", "R15"},
|
||||
{"R16", "R16"},
|
||||
{"R17", "R17"},
|
||||
{"R18", "R18"},
|
||||
{"R19", "R19"},
|
||||
{"R2", "R2"},
|
||||
{"R20", "R20"},
|
||||
{"R21", "R21"},
|
||||
{"R22", "R22"},
|
||||
{"R23", "R23"},
|
||||
{"R24", "R24"},
|
||||
{"R25", "R25"},
|
||||
{"R26", "R26"},
|
||||
{"R27", "R27"},
|
||||
{"R28", "R28"},
|
||||
{"R29", "R29"},
|
||||
{"R3", "R3"},
|
||||
{"R31", "R31"},
|
||||
{"R4", "R4"},
|
||||
{"R5", "R5"},
|
||||
{"R6", "R6"},
|
||||
{"R7", "R7"},
|
||||
{"R8", "R8"},
|
||||
{"R9", "R9"},
|
||||
{"SPR(269)", "SPR(269)"},
|
||||
{"a+0(FP)", "a+0(FP)"},
|
||||
{"g", "R30"}, // TODO: Should print g.
|
||||
{"ret+8(FP)", "ret+8(FP)"},
|
||||
{"runtime·abort(SB)", "runtime.abort(SB)"},
|
||||
{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
|
||||
{"·trunc(SB)", "\"\".trunc(SB)"},
|
||||
}
|
||||
|
@ -226,6 +226,7 @@ func (p *Parser) parseScale(s string) int8 {
|
||||
|
||||
// operand parses a general operand and stores the result in *a.
|
||||
func (p *Parser) operand(a *obj.Addr) bool {
|
||||
// fmt.Printf("Operand: %v\n", p.input)
|
||||
if len(p.input) == 0 {
|
||||
p.errorf("empty operand: cannot happen")
|
||||
return false
|
||||
@ -250,9 +251,10 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
|
||||
// Symbol: sym±offset(SB)
|
||||
tok := p.next()
|
||||
if tok.ScanToken == scanner.Ident && !p.isRegister(tok.String()) {
|
||||
name := tok.String()
|
||||
if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) {
|
||||
// We have a symbol. Parse $sym±offset(symkind)
|
||||
p.symbolReference(a, tok.String(), prefix)
|
||||
p.symbolReference(a, name, prefix)
|
||||
// fmt.Printf("SYM %s\n", p.arch.Dconv(&emptyProg, 0, a))
|
||||
if p.peek() == scanner.EOF {
|
||||
return true
|
||||
@ -270,7 +272,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
}
|
||||
|
||||
// Register: R1
|
||||
if tok.ScanToken == scanner.Ident && p.isRegister(tok.String()) {
|
||||
if tok.ScanToken == scanner.Ident && p.atStartOfRegister(name) {
|
||||
if lex.IsRegisterShift(p.peek()) {
|
||||
// ARM shifted register such as R1<<R2 or R1>>2.
|
||||
a.Type = obj.TYPE_SHIFT
|
||||
@ -280,10 +282,10 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
p.next()
|
||||
tok := p.next()
|
||||
name := tok.String()
|
||||
if !p.isRegister(name) {
|
||||
if !p.atStartOfRegister(name) {
|
||||
p.errorf("expected register; found %s", name)
|
||||
}
|
||||
a.Reg = p.arch.Registers[name]
|
||||
a.Reg, _ = p.registerReference(name)
|
||||
p.get(')')
|
||||
}
|
||||
} else if r1, r2, scale, ok := p.register(tok.String(), prefix); ok {
|
||||
@ -312,7 +314,7 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
// Could be parenthesized expression or (R).
|
||||
rname := p.next().String()
|
||||
p.back()
|
||||
haveConstant = !p.isRegister(rname)
|
||||
haveConstant = !p.atStartOfRegister(rname)
|
||||
if !haveConstant {
|
||||
p.back() // Put back the '('.
|
||||
}
|
||||
@ -368,18 +370,49 @@ func (p *Parser) operand(a *obj.Addr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// isRegister reports whether the token is a register.
|
||||
func (p *Parser) isRegister(name string) bool {
|
||||
_, present := p.arch.Registers[name]
|
||||
return present
|
||||
// atStartOfRegister reports whether the parser is at the start of a register definition.
|
||||
func (p *Parser) atStartOfRegister(name string) bool {
|
||||
// Simple register: R10.
|
||||
_, present := p.arch.Register[name]
|
||||
if present {
|
||||
return true
|
||||
}
|
||||
// Parenthesized register: R(10).
|
||||
return p.arch.RegisterPrefix[name] && p.peek() == '('
|
||||
}
|
||||
|
||||
// register parses a register reference where there is no symbol present (as in 4(R0) not sym(SB)).
|
||||
// registerReference parses a register given either the name, R10, or a parenthesized form, SPR(10).
|
||||
func (p *Parser) registerReference(name string) (int16, bool) {
|
||||
r, present := p.arch.Register[name]
|
||||
if present {
|
||||
return r, true
|
||||
}
|
||||
if !p.arch.RegisterPrefix[name] {
|
||||
p.errorf("expected register; found %s", name)
|
||||
return 0, false
|
||||
}
|
||||
p.get('(')
|
||||
tok := p.get(scanner.Int)
|
||||
num, err := strconv.ParseInt(tok.String(), 10, 16)
|
||||
p.get(')')
|
||||
if err != nil {
|
||||
p.errorf("parsing register list: %s", err)
|
||||
return 0, false
|
||||
}
|
||||
r, ok := p.arch.RegisterNumber(name, int16(num))
|
||||
if !ok {
|
||||
p.errorf("illegal register %s(%d)", name, r)
|
||||
return 0, false
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
|
||||
// register parses a full register reference where there is no symbol present (as in 4(R0) or R(10) but not sym(SB))
|
||||
// including forms involving multiple registers such as R1:R2.
|
||||
func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, ok bool) {
|
||||
// R1 or R1:R2 R1,R2 or R1*scale.
|
||||
var present bool
|
||||
r1, present = p.arch.Registers[name]
|
||||
if !present {
|
||||
// R1 or R(1) R1:R2 R1,R2 or R1*scale.
|
||||
r1, ok = p.registerReference(name)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if prefix != 0 {
|
||||
@ -392,16 +425,18 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o
|
||||
case ':':
|
||||
if char != '6' && char != '8' {
|
||||
p.errorf("illegal register pair syntax")
|
||||
return
|
||||
}
|
||||
case ',':
|
||||
if char != '5' {
|
||||
p.errorf("illegal register pair syntax")
|
||||
return
|
||||
}
|
||||
}
|
||||
name := p.next().String()
|
||||
r2, present = p.arch.Registers[name]
|
||||
if !present {
|
||||
p.errorf("%s not a register", name)
|
||||
r2, ok = p.registerReference(name)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.peek() == '*' {
|
||||
@ -415,18 +450,18 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o
|
||||
// registerShift parses an ARM shifted register reference and returns the encoded representation.
|
||||
// There is known to be a register (current token) and a shift operator (peeked token).
|
||||
func (p *Parser) registerShift(name string, prefix rune) int64 {
|
||||
if prefix != 0 {
|
||||
p.errorf("prefix %c not allowed for shifted register: $%s", prefix, name)
|
||||
}
|
||||
// R1 op R2 or r1 op constant.
|
||||
// op is:
|
||||
// "<<" == 0
|
||||
// ">>" == 1
|
||||
// "->" == 2
|
||||
// "@>" == 3
|
||||
r1, present := p.arch.Registers[name]
|
||||
if !present {
|
||||
p.errorf("shift of non-register %s", name)
|
||||
}
|
||||
if prefix != 0 {
|
||||
p.errorf("prefix %c not allowed for shifted register: $%s", prefix, name)
|
||||
r1, ok := p.registerReference(name)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
var op int16
|
||||
switch p.next().ScanToken {
|
||||
@ -444,8 +479,8 @@ func (p *Parser) registerShift(name string, prefix rune) int64 {
|
||||
var count int16
|
||||
switch tok.ScanToken {
|
||||
case scanner.Ident:
|
||||
r2, present := p.arch.Registers[str]
|
||||
if !present {
|
||||
r2, ok := p.registerReference(str)
|
||||
if !ok {
|
||||
p.errorf("rhs of shift must be register or integer: %s", str)
|
||||
}
|
||||
count = (r2&15)<<8 | 1<<4
|
||||
@ -632,26 +667,19 @@ func (p *Parser) registerList(a *obj.Addr) {
|
||||
a.Offset = int64(bits)
|
||||
}
|
||||
|
||||
// register number is ARM-specific. It returns the number of the specified register.
|
||||
func (p *Parser) registerNumber(name string) uint16 {
|
||||
if !p.isRegister(name) {
|
||||
p.errorf("expected register; found %s", name)
|
||||
}
|
||||
// Register must be of the form R0 through R15.
|
||||
// On ARM, g is register 10.
|
||||
if p.arch.Thechar == '5' && name == "g" {
|
||||
return 10
|
||||
}
|
||||
if name[0] != 'R' {
|
||||
p.errorf("expected g or R0 through R15; found %s", name)
|
||||
}
|
||||
num, err := strconv.ParseUint(name[1:], 10, 8)
|
||||
if err != nil {
|
||||
p.errorf("parsing register list: %s", err)
|
||||
r, ok := p.registerReference(name)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
if num > 15 {
|
||||
p.errorf("illegal register %s in register list", name)
|
||||
}
|
||||
return uint16(num)
|
||||
return uint16(r - p.arch.Register["R0"])
|
||||
}
|
||||
|
||||
// Note: There are two changes in the expression handling here
|
||||
|
Loading…
Reference in New Issue
Block a user