]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.cc] cmd/asm: bring asm on ppc64 in sync with 9a
authorRob Pike <r@golang.org>
Fri, 20 Feb 2015 00:34:51 +0000 (16:34 -0800)
committerRob Pike <r@golang.org>
Fri, 20 Feb 2015 00:42:09 +0000 (00:42 +0000)
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/5361
Reviewed-by: Russ Cox <rsc@golang.org>
src/cmd/asm/internal/arch/ppc64.go
src/cmd/asm/internal/asm/asm.go
src/cmd/asm/internal/asm/operand_test.go
src/cmd/asm/internal/asm/parse.go

index 7fb9f7dd2ebc2ba97568b9bf923fbb084cae8a45..f2b32f5c9d069a413fcda543f515d39edaa71d14 100644 (file)
@@ -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":
index 2cb8f9737f9ea71a3f5d9142d45755efedbf816f..af7366dba59ee66d35b02b5112fd7641c07bda16 100644 (file)
@@ -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) {
index 58b927442681e3b58a9caf4cd428801adf9b79c7..0e3d844954208d6d060f6331b5a05bc21721820e 100644 (file)
@@ -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 {
index 3ed7b2819116d17cb1785e2e511d0ba24d111526..05db95b420890911b604c04514b13a4aeeefab50 100644 (file)
@@ -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")