Commit 5b9429d1 authored by Rob Pike's avatar Rob Pike

[dev.cc] cmd/asm: bring asm on ppc64 in sync with 9a

I created a .s file that covered every instruction and operand production
in 9a/a.y and made sure that 9a and asm give bit-identical results for it.
I found a few things, including one addressing mode (R1+R2) that was
not present in the source we use. Fixed those

I also found quite a few things where 9a's grammar accepts the instruction
but liblink rejects it. These need to be sorted out, and I will do that separately.
Once that's done, I'll turn my test file into a proper test.

Change-Id: Ib093271b0f7ffd64ffed164ed2a820ebf2420e34
Reviewed-on: https://go-review.googlesource.com/5361Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
parent c21f1d5e
......@@ -40,6 +40,24 @@ func IsPPC64CMP(op int) bool {
return false
}
// IsPPC64NEG reports whether the op (as defined by an ppc64.A* constant) is
// one of the NEG-like instructions that require special handling.
func IsPPC64NEG(op int) bool {
switch op {
case ppc64.AADDMECC, ppc64.AADDMEVCC, ppc64.AADDMEV, ppc64.AADDME,
ppc64.AADDZECC, ppc64.AADDZEVCC, ppc64.AADDZEV, ppc64.AADDZE,
ppc64.ACNTLZDCC, ppc64.ACNTLZD, ppc64.ACNTLZWCC, ppc64.ACNTLZW,
ppc64.AEXTSBCC, ppc64.AEXTSB, ppc64.AEXTSHCC, ppc64.AEXTSH,
ppc64.AEXTSWCC, ppc64.AEXTSW, ppc64.ANEGCC, ppc64.ANEGVCC,
ppc64.ANEGV, ppc64.ANEG, ppc64.ASLBMFEE, ppc64.ASLBMFEV,
ppc64.ASLBMTE, ppc64.ASUBMECC, ppc64.ASUBMEVCC, ppc64.ASUBMEV,
ppc64.ASUBME, ppc64.ASUBZECC, ppc64.ASUBZEVCC, ppc64.ASUBZEV,
ppc64.ASUBZE:
return true
}
return false
}
func ppc64RegisterNumber(name string, n int16) (int16, bool) {
switch name {
case "CR":
......
......@@ -301,11 +301,20 @@ func (p *Parser) asmJump(op int, cond string, a []obj.Addr) {
switch len(a) {
case 1:
target = &a[0]
case 2:
if p.arch.Thechar == '9' {
// Special 2-operand jumps.
target = &a[1]
prog.From = a[0]
break
}
p.errorf("wrong number of arguments to %s instruction", p.arch.Aconv(op))
return
case 3:
if p.arch.Thechar == '9' {
target = &a[2]
// Special 3-operand jumps.
// First two must be constants.
// First two must be constants; a[1] is a register number.
target = &a[2]
prog.From = obj.Addr{
Type: obj.TYPE_CONST,
Offset: p.getConstant(prog, op, &a[0]),
......@@ -384,7 +393,7 @@ func (p *Parser) branch(jmp, target *obj.Prog) {
// asmInstruction assembles an instruction.
// MOVW R9, (R10)
func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
// fmt.Printf("%+v\n", a)
// fmt.Printf("%s %+v\n", p.arch.Aconv(op), a)
prog := &obj.Prog{
Ctxt: p.linkCtxt,
Lineno: p.histLineNum,
......@@ -401,6 +410,12 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
prog.From = a[0]
// prog.To is no address.
}
if p.arch.Thechar == '9' && arch.IsPPC64NEG(op) {
// NEG: From and To are both a[0].
prog.To = a[0]
prog.From = a[0]
break
}
case 2:
if p.arch.Thechar == '5' {
if arch.IsARMCMP(op) {
......@@ -432,15 +447,31 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
}
prog.From = a[0]
prog.To = a[1]
// DX:AX as a register pair can only appear on the RHS.
// Bizarrely, to obj it's specified by setting index on the LHS.
// TODO: can we fix this?
if a[1].Class != 0 {
if a[0].Class != 0 {
p.errorf("register pair must be on LHS")
switch p.arch.Thechar {
case '6', '8':
// DX:AX as a register pair can only appear on the RHS.
// Bizarrely, to obj it's specified by setting index on the LHS.
// TODO: can we fix this?
if a[1].Class != 0 {
if a[0].Class != 0 {
p.errorf("register pair must be on LHS")
}
prog.From.Index = int16(a[1].Class)
prog.To.Class = 0
}
case '9':
var reg0, reg1 int16
// Handle (R1+R2)
if a[0].Scale != 0 {
reg0 = int16(a[0].Scale)
prog.Reg = reg0
} else if a[1].Scale != 0 {
reg1 = int16(a[1].Scale)
prog.Reg = reg1
}
if reg0 != 0 && reg1 != 0 {
p.errorf("register pair cannot be both left and right operands")
}
prog.From.Index = int16(a[1].Class)
prog.To.Class = 0
}
case 3:
switch p.arch.Thechar {
......@@ -526,6 +557,27 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) {
break
}
p.errorf("can't handle %s instruction with 4 operands", p.arch.Aconv(op))
case 5:
if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) {
// Always reg, reg, con, con, reg. (con, con is a 'mask').
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
mask1 := p.getConstant(prog, op, &a[2])
mask2 := p.getConstant(prog, op, &a[3])
var mask uint32
if mask1 < mask2 {
mask = (^uint32(0) >> uint(mask1)) & (^uint32(0) << uint(31-mask2))
} else {
mask = (^uint32(0) >> uint(mask2+1)) & (^uint32(0) << uint(31-(mask1-1)))
}
prog.From3 = obj.Addr{
Type: obj.TYPE_CONST,
Offset: int64(mask),
}
prog.To = a[4]
break
}
p.errorf("can't handle %s instruction with 5 operands", p.arch.Aconv(op))
case 6:
// MCR and MRC on ARM
if p.arch.Thechar == '5' && arch.IsARMMRC(op) {
......
......@@ -70,6 +70,18 @@ func TestARMOperandParser(t *testing.T) {
func TestPPC64OperandParser(t *testing.T) {
parser := newParser("ppc64")
testOperandParser(t, parser, ppc64OperandTests)
// Special encoding for (R1+R2).
parser.start(lex.Tokenize("(R1+R2)"))
addr := obj.Addr{}
parser.operand(&addr)
want := obj.Addr{
Type: obj.TYPE_MEM,
Reg: parser.arch.Register["R1"],
Scale: int8(parser.arch.Register["R2"]), // TODO: clean up how this is encoded in parse.go
}
if want != addr {
t.Errorf("(R1+R2): expected %+v got %+v", want, addr)
}
}
type operandTest struct {
......
......@@ -410,7 +410,7 @@ func (p *Parser) registerReference(name string) (int16, bool) {
// 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 R(1) R1:R2 R1,R2 or R1*scale.
// R1 or R(1) R1:R2 R1,R2 R1+R2, or R1*scale.
r1, ok = p.registerReference(name)
if !ok {
return
......@@ -418,8 +418,10 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o
if prefix != 0 {
p.errorf("prefix %c not allowed for register: $%s", prefix, name)
}
if p.peek() == ':' || p.peek() == ',' {
// 2nd register; syntax (R1:R2). Check the architectures match.
c := p.peek()
if c == ':' || c == ',' || c == '+' {
// 2nd register; syntax (R1:R2) etc. No two architectures agree.
// Check the architectures match the syntax.
char := p.arch.Thechar
switch p.next().ScanToken {
case ':':
......@@ -432,6 +434,11 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o
p.errorf("illegal register pair syntax")
return
}
case '+':
if char != '9' {
p.errorf("illegal register pair syntax")
return
}
}
name := p.next().String()
r2, ok = p.registerReference(name)
......@@ -589,16 +596,31 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
return
}
a.Reg = r1
if r2 != 0 && p.arch.Thechar == '5' {
// Special form for ARM: destination register pair (R1, R2).
if prefix != 0 || scale != 0 {
p.errorf("illegal address mode for register pair")
if r2 != 0 {
// TODO: Consistency in the encoding would be nice here.
if p.arch.Thechar == '5' {
// Special form for ARM: destination register pair (R1, R2).
if prefix != 0 || scale != 0 {
p.errorf("illegal address mode for register pair")
return
}
a.Type = obj.TYPE_REGREG
a.Offset = int64(r2)
// Nothing may follow; this is always a pure destination.
return
}
if p.arch.Thechar == '9' {
// Special form for PPC64: register pair (R1+R2).
if prefix != 0 || scale != 0 {
p.errorf("illegal address mode for register pair")
return
}
// TODO: This is rewritten in asm. Clumsy.
a.Type = obj.TYPE_MEM
a.Scale = int8(r2)
// Nothing may follow.
return
}
a.Type = obj.TYPE_REGREG
a.Offset = int64(r2)
// Nothing may follow; this is always a pure destination.
return
}
if r2 != 0 {
p.errorf("indirect through register pair")
......
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