cmd/internal/obj/ppc64: rework argument classifications

This introduces a number of new classifications which will make it
easier to generate functions to assemble the new instructions of
ISA 3.1, and potentially earlier versions.

No code generation changes should occur as a result of these. These
allow finer control over how an opcode is matched to an optab entry.

Literal values are now classified based on the smallest number of bits
needed to encode, and matching rules will accept a literal if it
can be zero/sign extended to fit a larger literal class.

Likewise, support classifying even register numbers for GPR, VSX, and
FPR instructions. Some instructions require and even/odd register pair,
and these are usually represented by specifying the even register, and
similarly encoded.

Likewise, add a unit test for the argument classifier function (aclass).
This caught an off-by-one bug in aclass which is also fixed.

Updates #44549

Change-Id: Ia03013aea8b56c4d59b7c3812cdd67ddb3b720b9
Reviewed-on: https://go-review.googlesource.com/c/go/+/350152
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go
index dd6d903..fe18296 100644
--- a/src/cmd/internal/obj/ppc64/a.out.go
+++ b/src/cmd/internal/obj/ppc64/a.out.go
@@ -350,41 +350,64 @@
 )
 
 const (
-	C_NONE = iota
-	C_REG
-	C_FREG
-	C_VREG
-	C_VSREG
-	C_CREG
-	C_SPR /* special processor register */
-	C_ZCON
-	C_SCON   /* 16 bit signed */
-	C_UCON   /* 32 bit signed, low 16 bits 0 */
-	C_ADDCON /* -0x8000 <= v < 0 */
-	C_ANDCON /* 0 < v <= 0xFFFF */
-	C_LCON   /* other 32 */
-	C_DCON   /* other 64 (could subdivide further) */
-	C_SACON  /* $n(REG) where n <= int16 */
-	C_LACON  /* $n(REG) where int16 < n <= int32 */
-	C_DACON  /* $n(REG) where int32 < n */
-	C_SBRA
-	C_LBRA
-	C_LBRAPIC
-	C_ZOREG // conjecture: either (1) register + zeroed offset, or (2) "R0" implies zero or C_REG
-	C_SOREG // D/DS form memory operation
-	C_LOREG // 32 bit addis + D/DS-form memory operation
-	C_FPSCR
-	C_XER
-	C_LR
-	C_CTR
-	C_ANY
-	C_GOK
-	C_ADDR
-	C_TLS_LE
-	C_TLS_IE
-	C_TEXTSIZE
+	C_NONE     = iota
+	C_REGP     /* An even numbered gpr which can be used a gpr pair argument */
+	C_REG      /* Any gpr register */
+	C_FREGP    /* An even numbered fpr which can be used a fpr pair argument */
+	C_FREG     /* Any fpr register */
+	C_VREG     /* Any vector register */
+	C_VSREGP   /* An even numbered vsx register which can be used as a vsx register pair argument */
+	C_VSREG    /* Any vector-scalar register */
+	C_CREG     /* The condition registor (CR) or a condition register field (CRx) */
+	C_SPR      /* special processor register */
+	C_ZCON     /* The constant zero */
+	C_U1CON    /* 1 bit unsigned constant */
+	C_U2CON    /* 2 bit unsigned constant */
+	C_U3CON    /* 3 bit unsigned constant */
+	C_U4CON    /* 4 bit unsigned constant */
+	C_U5CON    /* 5 bit unsigned constant */
+	C_U8CON    /* 8 bit unsigned constant */
+	C_U15CON   /* 15 bit unsigned constant */
+	C_S16CON   /* 16 bit signed constant */
+	C_U16CON   /* 16 bit unsigned constant */
+	C_32S16CON /* Any 32 bit constant of the form 0x....0000, signed or unsigned */
+	C_32CON    /* Any constant which fits into 32 bits. Can be signed or unsigned */
+	C_S34CON   /* 34 bit signed constant */
+	C_64CON    /* Any constant which fits into 64 bits. Can be signed or unsigned */
+	C_SACON    /* $n(REG) where n <= int16 */
+	C_LACON    /* $n(REG) where n <= int32 */
+	C_DACON    /* $n(REG) where n <= int64 */
+	C_SBRA     /* A short offset argument to a branching instruction */
+	C_LBRA     /* A long offset argument to a branching instruction */
+	C_LBRAPIC  /* Like C_LBRA, but requires an extra NOP for potential TOC restore by the linker. */
+	C_ZOREG    /* An reg+reg memory arg, or a $0+reg memory op */
+	C_SOREG    /* An $n+reg memory arg where n is a 16 bit signed offset */
+	C_LOREG    /* An $n+reg memory arg where n is a 32 bit signed offset */
+	C_FPSCR    /* The fpscr register */
+	C_XER      /* The xer, holds the carry bit */
+	C_LR       /* The link register */
+	C_CTR      /* The count register */
+	C_ANY      /* Any argument */
+	C_GOK      /* A non-matched argument */
+	C_ADDR     /* A symbolic memory location */
+	C_TLS_LE   /* A thread local, local-exec, type memory arg */
+	C_TLS_IE   /* A thread local, initial-exec, type memory arg */
+	C_TEXTSIZE /* An argument with Type obj.TYPE_TEXTSIZE */
 
 	C_NCLASS /* must be the last */
+
+	/* Aliased names which should be cleaned up, or integrated. */
+	C_SCON   = C_U15CON
+	C_UCON   = C_32S16CON
+	C_ADDCON = C_S16CON
+	C_ANDCON = C_U16CON
+	C_LCON   = C_32CON
+
+	/* Aliased names which may be generated by ppc64map for the optab. */
+	C_S3216CON = C_32S16CON // TODO: these should be treated differently (e.g xoris vs addis)
+	C_U3216CON = C_32S16CON
+	C_S32CON   = C_32CON
+	C_U32CON   = C_32CON
 )
 
 const (
diff --git a/src/cmd/internal/obj/ppc64/anames9.go b/src/cmd/internal/obj/ppc64/anames9.go
index b2632aa..3b2aba7 100644
--- a/src/cmd/internal/obj/ppc64/anames9.go
+++ b/src/cmd/internal/obj/ppc64/anames9.go
@@ -6,19 +6,29 @@
 
 var cnames9 = []string{
 	"NONE",
+	"REGP",
 	"REG",
+	"FREGP",
 	"FREG",
 	"VREG",
+	"VSREGP",
 	"VSREG",
 	"CREG",
 	"SPR",
 	"ZCON",
-	"SCON",
-	"UCON",
-	"ADDCON",
-	"ANDCON",
-	"LCON",
-	"DCON",
+	"U1CON",
+	"U2CON",
+	"U3CON",
+	"U4CON",
+	"U5CON",
+	"U8CON",
+	"U15CON",
+	"S16CON",
+	"U16CON",
+	"32S16CON",
+	"32CON",
+	"S34CON",
+	"64CON",
 	"SACON",
 	"LACON",
 	"DACON",
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 6ed11b3..08fb511 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -307,8 +307,7 @@
 	{as: ABC, a6: C_ZOREG, type_: 15, size: 8},
 	{as: ASYNC, type_: 46, size: 4},
 	{as: AWORD, a1: C_LCON, type_: 40, size: 4},
-	{as: ADWORD, a1: C_LCON, type_: 31, size: 8},
-	{as: ADWORD, a1: C_DCON, type_: 31, size: 8},
+	{as: ADWORD, a1: C_64CON, type_: 31, size: 8},
 	{as: ADWORD, a1: C_LACON, type_: 31, size: 8},
 	{as: AADDME, a1: C_REG, a6: C_REG, type_: 47, size: 4},
 	{as: AEXTSB, a1: C_REG, a6: C_REG, type_: 48, size: 4},
@@ -336,7 +335,7 @@
 	{as: ALDMX, a1: C_SOREG, a6: C_REG, type_: 45, size: 4},                       /* load doubleword monitored, x-form */
 	{as: AMADDHD, a1: C_REG, a2: C_REG, a3: C_REG, a6: C_REG, type_: 83, size: 4}, /* multiply-add high/low doubleword, va-form */
 	{as: AADDEX, a1: C_REG, a2: C_REG, a3: C_SCON, a6: C_REG, type_: 94, size: 4}, /* add extended using alternate carry, z23-form */
-	{as: ACRAND, a1: C_CREG, a6: C_CREG, type_: 2, size: 4},                       /* logical ops for condition registers xl-form */
+	{as: ACRAND, a1: C_CREG, a2: C_CREG, a6: C_CREG, type_: 2, size: 4},           /* logical ops for condition register bits xl-form */
 
 	/* Vector instructions */
 
@@ -485,15 +484,15 @@
 	{as: AXVCVSXDDP, a1: C_VSREG, a6: C_VSREG, type_: 89, size: 4}, /* vsx vector integer-fp conversion, xx2-form */
 
 	{as: ACMP, a1: C_REG, a6: C_REG, type_: 70, size: 4},
-	{as: ACMP, a1: C_REG, a2: C_REG, a6: C_REG, type_: 70, size: 4},
+	{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
 	{as: ACMP, a1: C_REG, a6: C_ADDCON, type_: 71, size: 4},
-	{as: ACMP, a1: C_REG, a2: C_REG, a6: C_ADDCON, type_: 71, size: 4},
+	{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_ADDCON, type_: 71, size: 4},
 	{as: ACMPU, a1: C_REG, a6: C_REG, type_: 70, size: 4},
-	{as: ACMPU, a1: C_REG, a2: C_REG, a6: C_REG, type_: 70, size: 4},
+	{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
 	{as: ACMPU, a1: C_REG, a6: C_ANDCON, type_: 71, size: 4},
-	{as: ACMPU, a1: C_REG, a2: C_REG, a6: C_ANDCON, type_: 71, size: 4},
+	{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_ANDCON, type_: 71, size: 4},
 	{as: AFCMPO, a1: C_FREG, a6: C_FREG, type_: 70, size: 4},
-	{as: AFCMPO, a1: C_FREG, a2: C_REG, a6: C_FREG, type_: 70, size: 4},
+	{as: AFCMPO, a1: C_FREG, a2: C_CREG, a6: C_FREG, type_: 70, size: 4},
 	{as: ATW, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 60, size: 4},
 	{as: ATW, a1: C_LCON, a2: C_REG, a6: C_ADDCON, type_: 61, size: 4},
 	{as: ADCBF, a1: C_ZOREG, type_: 43, size: 4},
@@ -584,9 +583,11 @@
 // or "MOVD R5, foo+10(SP) or pseudo-register is used.  The other common case is when
 // generating constants in register like "MOVD $constant, Rx".
 func (c *ctxt9) getimpliedreg(a *obj.Addr, p *obj.Prog) int {
-	switch oclass(a) {
-	case C_ADDCON, C_ANDCON, C_UCON, C_LCON, C_SCON, C_ZCON:
+	class := oclass(a)
+	if class >= C_ZCON && class <= C_64CON {
 		return REGZERO
+	}
+	switch class {
 	case C_SACON, C_LACON:
 		return REGSP
 	case C_LOREG, C_SOREG, C_ZOREG:
@@ -839,45 +840,49 @@
 	return uint64(uint32(v)) == v
 }
 
+func (c *ctxt9) aclassreg(reg int16) int {
+	if REG_R0 <= reg && reg <= REG_R31 {
+		return C_REGP + int(reg&1)
+	}
+	if REG_F0 <= reg && reg <= REG_F31 {
+		return C_FREGP + int(reg&1)
+	}
+	if REG_V0 <= reg && reg <= REG_V31 {
+		return C_VREG
+	}
+	if REG_VS0 <= reg && reg <= REG_VS63 {
+		return C_VSREGP + int(reg&1)
+	}
+	if REG_CR0 <= reg && reg <= REG_CR7 || reg == REG_CR {
+		return C_CREG
+	}
+	if REG_SPR0 <= reg && reg <= REG_SPR0+1023 {
+		switch reg {
+		case REG_LR:
+			return C_LR
+
+		case REG_XER:
+			return C_XER
+
+		case REG_CTR:
+			return C_CTR
+		}
+
+		return C_SPR
+	}
+	if reg == REG_FPSCR {
+		return C_FPSCR
+	}
+	return C_GOK
+}
+
 func (c *ctxt9) aclass(a *obj.Addr) int {
 	switch a.Type {
 	case obj.TYPE_NONE:
 		return C_NONE
 
 	case obj.TYPE_REG:
-		if REG_R0 <= a.Reg && a.Reg <= REG_R31 {
-			return C_REG
-		}
-		if REG_F0 <= a.Reg && a.Reg <= REG_F31 {
-			return C_FREG
-		}
-		if REG_V0 <= a.Reg && a.Reg <= REG_V31 {
-			return C_VREG
-		}
-		if REG_VS0 <= a.Reg && a.Reg <= REG_VS63 {
-			return C_VSREG
-		}
-		if REG_CR0 <= a.Reg && a.Reg <= REG_CR7 || a.Reg == REG_CR {
-			return C_CREG
-		}
-		if REG_SPR0 <= a.Reg && a.Reg <= REG_SPR0+1023 {
-			switch a.Reg {
-			case REG_LR:
-				return C_LR
-
-			case REG_XER:
-				return C_XER
-
-			case REG_CTR:
-				return C_CTR
-			}
-
-			return C_SPR
-		}
-		if a.Reg == REG_FPSCR {
-			return C_FPSCR
-		}
-		return C_GOK
+		return c.aclassreg(a.Reg)
 
 	case obj.TYPE_MEM:
 		switch a.Name {
@@ -948,7 +953,7 @@
 		case obj.NAME_NONE:
 			c.instoffset = a.Offset
 			if a.Reg != 0 {
-				if -BIG <= c.instoffset && c.instoffset <= BIG {
+				if -BIG <= c.instoffset && c.instoffset < BIG {
 					return C_SACON
 				}
 				if isint32(c.instoffset) {
@@ -985,35 +990,47 @@
 		}
 
 		if c.instoffset >= 0 {
-			if c.instoffset == 0 {
-				return C_ZCON
+			sbits := bits.Len64(uint64(c.instoffset))
+			switch {
+			case sbits <= 5:
+				return C_ZCON + sbits
+			case sbits <= 8:
+				return C_U8CON
+			case sbits <= 15:
+				return C_U15CON
+			case sbits <= 16:
+				return C_U16CON
+			case sbits <= 31:
+				// Special case, a positive int32 value which is a multiple of 2^16
+				if c.instoffset&0xFFFF == 0 {
+					return C_U3216CON
+				}
+				return C_U32CON
+			case sbits <= 32:
+				return C_U32CON
+			case sbits <= 33:
+				return C_S34CON
+			default:
+				return C_64CON
 			}
-			if c.instoffset <= 0x7fff {
-				return C_SCON
+		} else {
+			sbits := bits.Len64(uint64(^c.instoffset))
+			switch {
+			case sbits <= 15:
+				return C_S16CON
+			case sbits <= 31:
+				// Special case, a negative int32 value which is a multiple of 2^16
+				if c.instoffset&0xFFFF == 0 {
+					return C_S3216CON
+				}
+				return C_S32CON
+			case sbits <= 33:
+				return C_S34CON
+			default:
+				return C_64CON
 			}
-			if c.instoffset <= 0xffff {
-				return C_ANDCON
-			}
-			if c.instoffset&0xffff == 0 && isuint32(uint64(c.instoffset)) { /* && (instoffset & (1<<31)) == 0) */
-				return C_UCON
-			}
-			if isint32(c.instoffset) || isuint32(uint64(c.instoffset)) {
-				return C_LCON
-			}
-			return C_DCON
 		}
 
-		if c.instoffset >= -0x8000 {
-			return C_ADDCON
-		}
-		if c.instoffset&0xffff == 0 && isint32(c.instoffset) {
-			return C_UCON
-		}
-		if isint32(c.instoffset) {
-			return C_LCON
-		}
-		return C_DCON
-
 	case obj.TYPE_BRANCH:
 		if a.Sym != nil && c.ctxt.Flag_dynlink {
 			return C_LBRAPIC
@@ -1062,15 +1079,7 @@
 
 	a2 := C_NONE
 	if p.Reg != 0 {
-		if REG_R0 <= p.Reg && p.Reg <= REG_R31 {
-			a2 = C_REG
-		} else if REG_V0 <= p.Reg && p.Reg <= REG_V31 {
-			a2 = C_VREG
-		} else if REG_VS0 <= p.Reg && p.Reg <= REG_VS63 {
-			a2 = C_VSREG
-		} else if REG_F0 <= p.Reg && p.Reg <= REG_F31 {
-			a2 = C_FREG
-		}
+		a2 = c.aclassreg(p.Reg)
 	}
 
 	// c.ctxt.Logf("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4, a5, a6)
@@ -1097,71 +1106,72 @@
 	return &ops[0]
 }
 
+// Compare two operand types (ex C_REG, or C_SCON)
+// and return true if b is compatible with a.
+//
+// Argument comparison isn't reflexitive, so care must be taken.
+// a is the argument type as found in optab, b is the argument as
+// fitted by aclass.
 func cmp(a int, b int) bool {
 	if a == b {
 		return true
 	}
 	switch a {
-	case C_LCON:
-		if b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON {
-			return true
-		}
-
-	case C_ADDCON:
-		if b == C_ZCON || b == C_SCON {
-			return true
-		}
-
-	case C_ANDCON:
-		if b == C_ZCON || b == C_SCON {
-			return true
-		}
 
 	case C_SPR:
 		if b == C_LR || b == C_XER || b == C_CTR {
 			return true
 		}
 
-	case C_UCON:
-		if b == C_ZCON {
-			return true
-		}
+	case C_U1CON:
+		return cmp(C_ZCON, b)
+	case C_U2CON:
+		return cmp(C_U1CON, b)
+	case C_U3CON:
+		return cmp(C_U2CON, b)
+	case C_U4CON:
+		return cmp(C_U3CON, b)
+	case C_U5CON:
+		return cmp(C_U4CON, b)
+	case C_U8CON:
+		return cmp(C_U5CON, b)
+	case C_U15CON:
+		return cmp(C_U8CON, b)
+	case C_U16CON:
+		return cmp(C_U15CON, b)
 
-	case C_SCON:
-		if b == C_ZCON {
-			return true
-		}
+	case C_S16CON:
+		return cmp(C_U15CON, b)
+	case C_32CON:
+		return cmp(C_S16CON, b) || cmp(C_U16CON, b) || cmp(C_32S16CON, b)
+	case C_S34CON:
+		return cmp(C_32CON, b)
+	case C_64CON:
+		return cmp(C_S34CON, b)
+
+	case C_32S16CON:
+		return cmp(C_ZCON, b)
 
 	case C_LACON:
-		if b == C_SACON {
-			return true
-		}
+		return cmp(C_SACON, b)
 
 	case C_LBRA:
-		if b == C_SBRA {
-			return true
-		}
+		return cmp(C_SBRA, b)
 
 	case C_SOREG:
-		if b == C_ZOREG {
-			return true
-		}
+		return cmp(C_ZOREG, b)
 
 	case C_LOREG:
-		if b == C_SOREG || b == C_ZOREG {
-			return true
-		}
+		return cmp(C_SOREG, b)
 
+	// An even/odd register input always matches the regular register types.
 	case C_REG:
-		if b == C_ZCON {
-			return r0iszero != 0 /*TypeKind(100016)*/
-		}
-
+		return cmp(C_REGP, b) || (b == C_ZCON && r0iszero != 0)
+	case C_FREG:
+		return cmp(C_FREGP, b)
 	case C_VSREG:
 		/* Allow any VR argument as a VSR operand. */
-		if b == C_VREG {
-			return true
-		}
+		return cmp(C_VSREGP, b) || cmp(C_VREG, b)
 
 	case C_ANY:
 		return true
diff --git a/src/cmd/internal/obj/ppc64/asm_test.go b/src/cmd/internal/obj/ppc64/asm_test.go
index ec0ba19..3a9b873 100644
--- a/src/cmd/internal/obj/ppc64/asm_test.go
+++ b/src/cmd/internal/obj/ppc64/asm_test.go
@@ -6,9 +6,12 @@
 
 import (
 	"bytes"
+	"cmd/internal/obj"
+	"cmd/internal/objabi"
 	"fmt"
 	"internal/testenv"
 	"io/ioutil"
+	"math"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -437,3 +440,114 @@
 		tstFunc(t.rstart, t.rend, t.msk, t.rout)
 	}
 }
+
+// Verify interesting obj.Addr arguments are classified correctly.
+func TestAddrClassifier(t *testing.T) {
+	type cmplx struct {
+		pic     int
+		pic_dyn int
+		dyn     int
+		nonpic  int
+	}
+	tsts := [...]struct {
+		arg    obj.Addr
+		output interface{}
+	}{
+		// Supported register type args
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_R1}, C_REG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_R2}, C_REGP},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_F1}, C_FREG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_F2}, C_FREGP},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_V2}, C_VREG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS1}, C_VSREG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS2}, C_VSREGP},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR}, C_CREG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1}, C_CREG},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0}, C_SPR},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 1}, C_XER},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 8}, C_LR},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 9}, C_CTR},
+		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_FPSCR}, C_FPSCR},
+
+		// Memory type arguments.
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_GOTREF}, C_ADDR},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_TOCREF}, C_ADDR},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.STLSBSS}}, cmplx{C_TLS_IE, C_TLS_IE, C_TLS_LE, C_TLS_LE}},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_ADDR},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO}, C_SOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: BIG}, C_LOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM}, C_SOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: BIG}, C_LOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LOREG}, // 33 is FixedFrameSize-1
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE}, C_ZOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: 1}, C_SOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: BIG}, C_LOREG},
+		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: -BIG - 33}, C_LOREG},
+
+		// Misc (golang initializes -0.0 to 0.0, hence the obfuscation below)
+		{obj.Addr{Type: obj.TYPE_TEXTSIZE}, C_TEXTSIZE},
+		{obj.Addr{Type: obj.TYPE_FCONST, Val: 0.0}, C_ZCON},
+		{obj.Addr{Type: obj.TYPE_FCONST, Val: math.Float64frombits(0x8000000000000000)}, C_S16CON},
+
+		// Address type arguments
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1}, C_SACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: BIG}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: -BIG - 1}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1 << 32}, C_DACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_STATIC, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: 1}, C_SACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: BIG}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: 1}, C_SACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: BIG}, C_LACON},
+		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LACON}, // 33 is FixedFrameSize-1
+
+		// Constant type arguments
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0}, C_ZCON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1}, C_U1CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 2}, C_U2CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 4}, C_U3CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 8}, C_U4CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 16}, C_U5CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 16}, C_U3216CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U32CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10000}, C_S3216CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
+		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
+
+		// Branch like arguments
+		{obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_SBRA, C_LBRAPIC, C_LBRAPIC, C_SBRA}},
+		{obj.Addr{Type: obj.TYPE_BRANCH}, C_SBRA},
+	}
+
+	pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
+	pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
+	dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
+	nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
+	ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
+	name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
+	for _, tst := range tsts {
+		var expect []int
+		switch tst.output.(type) {
+		case cmplx:
+			v := tst.output.(cmplx)
+			expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
+		case int:
+			expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
+		}
+		for i, _ := range ctxts {
+			if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
+				t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
+			}
+		}
+	}
+}