This patch adds support for CASx and CASPx atomic instructions.
go syntax gnu syntax
CASD Rs, (Rn|RSP), Rt => cas Xs, Xt, (Xn|SP)
CASALW Rs, (Rn|RSP), Rt => casal Ws, Wt, (Xn|SP)
CASPD (Rs, Rs+1), (Rn|RSP), (Rt, Rt+1) => casp Xs, Xs+1, Xt, Xt+1, (Xn|SP)
CASPW (Rs, Rs+1), (Rn|RSP), (Rt, Rt+1) => casp Ws, Ws+1, Wt, Wt+1, (Xn|SP)
This patch changes the type of prog.RestArgs from "[]Addr" to
"[]struct{Addr, Pos}", Pos is a enum, indicating the position of
the operand.
This patch also adds test cases.
Change-Id: Ib971cfda7890b7aa895d17bab22dea326c7fcaa4
Reviewed-on: https://go-review.googlesource.com/c/go/+/233277
Trust: fannie zhang <Fannie.Zhang@arm.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
return true
}
- // atomic instructions
+ // LDADDx/SWPx/CASx atomic instructions
if arm64.IsAtomicInstruction(op) {
return true
}
return false
}
+// IsARM64CASP reports whether the op (as defined by an arm64.A*
+// constant) is one of the CASP-like instructions, and its 2nd
+// destination is a register pair that require special handling.
+func IsARM64CASP(op obj.As) bool {
+ switch op {
+ case arm64.ACASPD, arm64.ACASPW:
+ return true
+ }
+ return false
+}
+
// ARM64Suffix handles the special suffix for the ARM64.
// It returns a boolean to indicate success; failure means
// cond was unrecognized.
prog.From = a[0]
prog.SetFrom3(a[1])
prog.To = a[2]
+ case arch.IsARM64CASP(op):
+ prog.From = a[0]
+ prog.To = a[1]
+ // both 1st operand and 3rd operand are (Rs, Rs+1) register pair.
+ // And the register pair must be contiguous.
+ if (a[0].Type != obj.TYPE_REGREG) || (a[2].Type != obj.TYPE_REGREG) {
+ p.errorf("invalid addressing modes for 1st or 3rd operand to %s instruction, must be register pair", op)
+ return
+ }
+ // For ARM64 CASP-like instructions, its 2nd destination operand is register pair(Rt, Rt+1) that can
+ // not fit into prog.RegTo2, so save it to the prog.RestArgs.
+ prog.SetTo2(a[2])
default:
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
}
if p.arch.Family == sys.AMD64 {
prog.From = a[0]
- prog.RestArgs = []obj.Addr{a[1], a[2]}
+ prog.SetRestArgs([]obj.Addr{a[1], a[2]})
prog.To = a[3]
break
}
}
if p.arch.Family == sys.AMD64 {
prog.From = a[0]
- prog.RestArgs = []obj.Addr{a[1], a[2], a[3]}
+ prog.SetRestArgs([]obj.Addr{a[1], a[2], a[3]})
prog.To = a[4]
break
}
if p.arch.Family == sys.S390X {
prog.From = a[0]
- prog.RestArgs = []obj.Addr{a[1], a[2], a[3]}
+ prog.SetRestArgs([]obj.Addr{a[1], a[2], a[3]})
prog.To = a[4]
break
}
LDORLH R5, (RSP), R7 // e7336578
LDORLB R5, (R6), R7 // c7306538
LDORLB R5, (RSP), R7 // e7336538
+ CASD R1, (R2), ZR // 5f7ca1c8
+ CASW R1, (RSP), ZR // ff7fa188
+ CASB ZR, (R5), R3 // a37cbf08
+ CASH R3, (RSP), ZR // ff7fa348
+ CASW R5, (R7), R6 // e67ca588
+ CASLD ZR, (RSP), R8 // e8ffbfc8
+ CASLW R9, (R10), ZR // 5ffda988
+ CASAD R7, (R11), R15 // 6f7de7c8
+ CASAW R10, (RSP), R19 // f37fea88
+ CASALD R5, (R6), R7 // c7fce5c8
+ CASALD R5, (RSP), R7 // e7ffe5c8
+ CASALW R5, (R6), R7 // c7fce588
+ CASALW R5, (RSP), R7 // e7ffe588
+ CASALH ZR, (R5), R8 // a8fcff48
+ CASALB R8, (R9), ZR // 3ffde808
+ CASPD (R30, ZR), (RSP), (R8, R9) // e87f3e48
+ CASPW (R6, R7), (R8), (R4, R5) // 047d2608
+ CASPD (R2, R3), (R2), (R8, R9) // 487c2248
// RET
RET
VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch"
VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range"
VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range"
+ CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register"
+ CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register"
+ CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous"
+ CASPD (R2, R3), (R2), (R8, R10) // ERROR "destination register pair must be contiguous"
RET
i := v.Aux.(s390x.RotateParams)
p := s.Prog(v.Op.Asm())
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(i.Start)}
- p.RestArgs = []obj.Addr{
+ p.SetRestArgs([]obj.Addr{
{Type: obj.TYPE_CONST, Offset: int64(i.End)},
{Type: obj.TYPE_CONST, Offset: int64(i.Amount)},
{Type: obj.TYPE_REG, Reg: r2},
- }
+ })
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1}
case ssa.OpS390XADD, ssa.OpS390XADDW,
ssa.OpS390XSUB, ssa.OpS390XSUBW,
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(s390x.NotEqual & s390x.NotUnordered) // unordered is not possible
p.Reg = s390x.REG_R3
- p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: 0}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: 0})
if b.Succs[0].Block() != next {
s.Br(s390x.ABR, b.Succs[0].Block())
}
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
p.Reg = b.Controls[0].Reg()
- p.RestArgs = []obj.Addr{{Type: obj.TYPE_REG, Reg: b.Controls[1].Reg()}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: b.Controls[1].Reg()})
case ssa.BlockS390XCGIJ, ssa.BlockS390XCIJ:
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
p.Reg = b.Controls[0].Reg()
- p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: int64(int8(b.AuxInt))}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: int64(int8(b.AuxInt))})
case ssa.BlockS390XCLGIJ, ssa.BlockS390XCLIJ:
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible
p.Reg = b.Controls[0].Reg()
- p.RestArgs = []obj.Addr{{Type: obj.TYPE_CONST, Offset: int64(uint8(b.AuxInt))}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: int64(uint8(b.AuxInt))})
default:
b.Fatalf("branch not implemented: %s", b.LongString())
}
ASWPLW
ASWPLH
ASWPLB
+ ACASD
+ ACASW
+ ACASH
+ ACASB
+ ACASAD
+ ACASAW
+ ACASLD
+ ACASLW
+ ACASALD
+ ACASALW
+ ACASALH
+ ACASALB
+ ACASPD
+ ACASPW
ABEQ
ABNE
ABCS
"SWPLW",
"SWPLH",
"SWPLB",
+ "CASD",
+ "CASW",
+ "CASH",
+ "CASB",
+ "CASAD",
+ "CASAW",
+ "CASLD",
+ "CASLW",
+ "CASALD",
+ "CASALW",
+ "CASALH",
+ "CASALB",
+ "CASPD",
+ "CASPW",
"BEQ",
"BNE",
"BCS",
}
func IsAtomicInstruction(as obj.As) bool {
- _, ok := atomicInstructions[as]
- return ok
+ if _, ok := atomicLDADD[as]; ok {
+ return true
+ }
+ if _, ok := atomicSWP[as]; ok {
+ return true
+ }
+ return false
}
// known field values of an instruction.
-var atomicInstructions = map[obj.As]uint32{
+var atomicLDADD = map[obj.As]uint32{
ALDADDAD: 3<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDAW: 2<<30 | 0x1c5<<21 | 0x00<<10,
ALDADDAH: 1<<30 | 0x1c5<<21 | 0x00<<10,
ALDORLW: 2<<30 | 0x1c3<<21 | 0x0c<<10,
ALDORLH: 1<<30 | 0x1c3<<21 | 0x0c<<10,
ALDORLB: 0<<30 | 0x1c3<<21 | 0x0c<<10,
- ASWPAD: 3<<30 | 0x1c5<<21 | 0x20<<10,
- ASWPAW: 2<<30 | 0x1c5<<21 | 0x20<<10,
- ASWPAH: 1<<30 | 0x1c5<<21 | 0x20<<10,
- ASWPAB: 0<<30 | 0x1c5<<21 | 0x20<<10,
- ASWPALD: 3<<30 | 0x1c7<<21 | 0x20<<10,
- ASWPALW: 2<<30 | 0x1c7<<21 | 0x20<<10,
- ASWPALH: 1<<30 | 0x1c7<<21 | 0x20<<10,
- ASWPALB: 0<<30 | 0x1c7<<21 | 0x20<<10,
- ASWPD: 3<<30 | 0x1c1<<21 | 0x20<<10,
- ASWPW: 2<<30 | 0x1c1<<21 | 0x20<<10,
- ASWPH: 1<<30 | 0x1c1<<21 | 0x20<<10,
- ASWPB: 0<<30 | 0x1c1<<21 | 0x20<<10,
- ASWPLD: 3<<30 | 0x1c3<<21 | 0x20<<10,
- ASWPLW: 2<<30 | 0x1c3<<21 | 0x20<<10,
- ASWPLH: 1<<30 | 0x1c3<<21 | 0x20<<10,
- ASWPLB: 0<<30 | 0x1c3<<21 | 0x20<<10,
+}
+
+var atomicSWP = map[obj.As]uint32{
+ ASWPAD: 3<<30 | 0x1c5<<21 | 0x20<<10,
+ ASWPAW: 2<<30 | 0x1c5<<21 | 0x20<<10,
+ ASWPAH: 1<<30 | 0x1c5<<21 | 0x20<<10,
+ ASWPAB: 0<<30 | 0x1c5<<21 | 0x20<<10,
+ ASWPALD: 3<<30 | 0x1c7<<21 | 0x20<<10,
+ ASWPALW: 2<<30 | 0x1c7<<21 | 0x20<<10,
+ ASWPALH: 1<<30 | 0x1c7<<21 | 0x20<<10,
+ ASWPALB: 0<<30 | 0x1c7<<21 | 0x20<<10,
+ ASWPD: 3<<30 | 0x1c1<<21 | 0x20<<10,
+ ASWPW: 2<<30 | 0x1c1<<21 | 0x20<<10,
+ ASWPH: 1<<30 | 0x1c1<<21 | 0x20<<10,
+ ASWPB: 0<<30 | 0x1c1<<21 | 0x20<<10,
+ ASWPLD: 3<<30 | 0x1c3<<21 | 0x20<<10,
+ ASWPLW: 2<<30 | 0x1c3<<21 | 0x20<<10,
+ ASWPLH: 1<<30 | 0x1c3<<21 | 0x20<<10,
+ ASWPLB: 0<<30 | 0x1c3<<21 | 0x20<<10,
+ ACASD: 3<<30 | 0x45<<21 | 0x1f<<10,
+ ACASW: 2<<30 | 0x45<<21 | 0x1f<<10,
+ ACASH: 1<<30 | 0x45<<21 | 0x1f<<10,
+ ACASB: 0<<30 | 0x45<<21 | 0x1f<<10,
+ ACASAD: 3<<30 | 0x47<<21 | 0x1f<<10,
+ ACASAW: 2<<30 | 0x47<<21 | 0x1f<<10,
+ ACASLD: 3<<30 | 0x45<<21 | 0x3f<<10,
+ ACASLW: 2<<30 | 0x45<<21 | 0x3f<<10,
+ ACASALD: 3<<30 | 0x47<<21 | 0x3f<<10,
+ ACASALW: 2<<30 | 0x47<<21 | 0x3f<<10,
+ ACASALH: 1<<30 | 0x47<<21 | 0x3f<<10,
+ ACASALB: 0<<30 | 0x47<<21 | 0x3f<<10,
+}
+var atomicCASP = map[obj.As]uint32{
+ ACASPD: 1<<30 | 0x41<<21 | 0x1f<<10,
+ ACASPW: 0<<30 | 0x41<<21 | 0x1f<<10,
}
var oprange [ALAST & obj.AMask][]Optab
{ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPOST},
{ASTPW, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0},
- {ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG
- {ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG
+ {ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG
+ {ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG
+ {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZOREG, 106, 4, 0, 0, 0}, // RegTo2=C_REGREG
+ {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZAUTO, 106, 4, REGSP, 0, 0}, // RegTo2=C_REGREG
{ALDAR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
{ALDXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
{ALDAXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0},
a1--
a3 := C_NONE + 1
- if p.GetFrom3() != nil {
+ if p.GetFrom3() != nil && p.RestArgs[0].Pos == 0 {
a3 = int(p.GetFrom3().Class)
if a3 == 0 {
a3 = c.aclass(p.GetFrom3()) + 1
oprangeset(AMOVZW, t)
case ASWPD:
- for i := range atomicInstructions {
+ for i := range atomicLDADD {
+ oprangeset(i, t)
+ }
+ for i := range atomicSWP {
+ if i == ASWPD {
+ continue
+ }
oprangeset(i, t)
}
+ case ACASPD:
+ oprangeset(ACASPW, t)
case ABEQ:
oprangeset(ABNE, t)
oprangeset(ABCS, t)
o1 |= uint32(p.From.Reg&31) << 5
o1 |= uint32(p.To.Reg & 31)
- case 47: /* SWPx/LDADDx/LDANDx/LDEORx/LDORx Rs, (Rb), Rt */
+ case 47: // SWPx/LDADDx/LDANDx/LDEORx/LDORx/CASx Rs, (Rb), Rt
rs := p.From.Reg
rt := p.RegTo2
rb := p.To.Reg
- fields := atomicInstructions[p.As]
- // rt can't be sp. rt can't be r31 when field A is 0, A bit is the 23rd bit.
- if rt == REG_RSP || (rt == REGZERO && (fields&(1<<23) == 0)) {
+ // rt can't be sp.
+ if rt == REG_RSP {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
- o1 |= fields | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31)
+ if enc, ok := atomicLDADD[p.As]; ok {
+ // for LDADDx-like instructions, rt can't be r31 when field.enc A is 0, A bit is the 23rd bit.
+ if (rt == REGZERO) && (enc&(1<<23) == 0) {
+ c.ctxt.Diag("illegal destination register: %v\n", p)
+ }
+ o1 |= enc
+ } else if enc, ok := atomicSWP[p.As]; ok {
+ o1 |= enc
+ } else {
+ c.ctxt.Diag("invalid atomic instructions: %v\n", p)
+ }
+ o1 |= uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31)
case 48: /* ADD $C_ADDCON2, Rm, Rd */
// NOTE: this case does not use REGTMP. If it ever does,
rt := int((p.To.Reg) & 31)
r := int((p.Reg) & 31)
o1 |= ((Q & 1) << 30) | ((size & 3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
+
+ case 106: // CASPx (Rs, Rs+1), (Rb), (Rt, Rt+1)
+ rs := p.From.Reg
+ rt := p.GetTo2().Reg
+ rb := p.To.Reg
+ rs1 := int16(p.From.Offset)
+ rt1 := int16(p.GetTo2().Offset)
+
+ enc, ok := atomicCASP[p.As]
+ if !ok {
+ c.ctxt.Diag("invalid CASP-like atomic instructions: %v\n", p)
+ }
+ // for CASPx-like instructions, Rs<0> != 1 && Rt<0> != 1
+ switch {
+ case rs&1 != 0:
+ c.ctxt.Diag("source register pair must start from even register: %v\n", p)
+ break
+ case rt&1 != 0:
+ c.ctxt.Diag("destination register pair must start from even register: %v\n", p)
+ break
+ case rs != rs1-1:
+ c.ctxt.Diag("source register pair must be contiguous: %v\n", p)
+ break
+ case rt != rt1-1:
+ c.ctxt.Diag("destination register pair must be contiguous: %v\n", p)
+ break
+ }
+ // rt can't be sp.
+ if rt == REG_RSP {
+ c.ctxt.Diag("illegal destination register: %v\n", p)
+ }
+ o1 |= enc | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31)
}
out[0] = o1
out[1] = o2
// The other fields not yet mentioned are for use by the back ends and should
// be left zeroed by creators of Prog lists.
type Prog struct {
- Ctxt *Link // linker context
- Link *Prog // next Prog in linked list
- From Addr // first source operand
- RestArgs []Addr // can pack any operands that not fit into {Prog.From, Prog.To}
- To Addr // destination operand (second is RegTo2 below)
- Pool *Prog // constant pool entry, for arm,arm64 back ends
- Forwd *Prog // for x86 back end
- Rel *Prog // for x86, arm back ends
- Pc int64 // for back ends or assembler: virtual or actual program counter, depending on phase
- Pos src.XPos // source position of this instruction
- Spadj int32 // effect of instruction on stack pointer (increment or decrement amount)
- As As // assembler opcode
- Reg int16 // 2nd source operand
- RegTo2 int16 // 2nd destination operand
- Mark uint16 // bitmask of arch-specific items
- Optab uint16 // arch-specific opcode index
- Scond uint8 // bits that describe instruction suffixes (e.g. ARM conditions)
- Back uint8 // for x86 back end: backwards branch state
- Ft uint8 // for x86 back end: type index of Prog.From
- Tt uint8 // for x86 back end: type index of Prog.To
- Isize uint8 // for x86 back end: size of the instruction in bytes
-}
+ Ctxt *Link // linker context
+ Link *Prog // next Prog in linked list
+ From Addr // first source operand
+ RestArgs []AddrPos // can pack any operands that not fit into {Prog.From, Prog.To}
+ To Addr // destination operand (second is RegTo2 below)
+ Pool *Prog // constant pool entry, for arm,arm64 back ends
+ Forwd *Prog // for x86 back end
+ Rel *Prog // for x86, arm back ends
+ Pc int64 // for back ends or assembler: virtual or actual program counter, depending on phase
+ Pos src.XPos // source position of this instruction
+ Spadj int32 // effect of instruction on stack pointer (increment or decrement amount)
+ As As // assembler opcode
+ Reg int16 // 2nd source operand
+ RegTo2 int16 // 2nd destination operand
+ Mark uint16 // bitmask of arch-specific items
+ Optab uint16 // arch-specific opcode index
+ Scond uint8 // bits that describe instruction suffixes (e.g. ARM conditions)
+ Back uint8 // for x86 back end: backwards branch state
+ Ft uint8 // for x86 back end: type index of Prog.From
+ Tt uint8 // for x86 back end: type index of Prog.To
+ Isize uint8 // for x86 back end: size of the instruction in bytes
+}
+
+// Pos indicates whether the oprand is the source or the destination.
+type AddrPos struct {
+ Addr
+ Pos OperandPos
+}
+
+type OperandPos int8
+
+const (
+ Source OperandPos = iota
+ Destination
+)
// From3Type returns p.GetFrom3().Type, or TYPE_NONE when
// p.GetFrom3() returns nil.
if p.RestArgs == nil {
return nil
}
- return &p.RestArgs[0]
+ return &p.RestArgs[0].Addr
}
-// SetFrom3 assigns []Addr{a} to p.RestArgs.
+// SetFrom3 assigns []Args{{a, 0}} to p.RestArgs.
// In pair with Prog.GetFrom3 it can help in emulation of Prog.From3.
//
// Deprecated: for the same reasons as Prog.GetFrom3.
func (p *Prog) SetFrom3(a Addr) {
- p.RestArgs = []Addr{a}
+ p.RestArgs = []AddrPos{{a, Source}}
+}
+
+// SetTo2 assings []Args{{a, 1}} to p.RestArgs when the second destination
+// operand does not fit into prog.RegTo2.
+func (p *Prog) SetTo2(a Addr) {
+ p.RestArgs = []AddrPos{{a, Destination}}
+}
+
+// GetTo2 returns the second destination operand.
+func (p *Prog) GetTo2() *Addr {
+ if p.RestArgs == nil {
+ return nil
+ }
+ return &p.RestArgs[0].Addr
+}
+
+// SetRestArgs assigns more than one source operands to p.RestArgs.
+func (p *Prog) SetRestArgs(args []Addr) {
+ for i := range args {
+ p.RestArgs = append(p.RestArgs, AddrPos{args[i], Source})
+ }
}
// An As denotes an assembler opcode.
p.As = AAUIPC
p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym})
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
p.Reg = 0
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
p.As = AAUIPC
p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym})
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
p.Reg = 0
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: to.Reg}
p.As = AAUIPC
p.Mark |= NEED_PCREL_STYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym})
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
p.Reg = 0
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
p.As = AAUIPC
p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym})
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
p.Reg = 0
p.To = to
p.From.Class = int8(c.aclass(&p.From) + 1)
p.To.Class = int8(c.aclass(&p.To) + 1)
for i := range p.RestArgs {
- p.RestArgs[i].Class = int8(c.aclass(&p.RestArgs[i]) + 1)
+ p.RestArgs[i].Addr.Class = int8(c.aclass(&p.RestArgs[i].Addr) + 1)
}
// Mirrors the argument list in Optab.
sep = ", "
}
for i := range p.RestArgs {
- io.WriteString(w, sep)
- WriteDconv(w, p, &p.RestArgs[i])
- sep = ", "
+ if p.RestArgs[i].Pos == Source {
+ io.WriteString(w, sep)
+ WriteDconv(w, p, &p.RestArgs[i].Addr)
+ sep = ", "
+ }
}
if p.As == ATEXT {
if p.RegTo2 != REG_NONE {
fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
}
+ for i := range p.RestArgs {
+ if p.RestArgs[i].Pos == Destination {
+ io.WriteString(w, sep)
+ WriteDconv(w, p, &p.RestArgs[i].Addr)
+ sep = ", "
+ }
+ }
}
func (ctxt *Link) NewProg() *Prog {
args = append(args, ft)
}
for i := range p.RestArgs {
- args = append(args, oclass(ctxt, p, &p.RestArgs[i])*Ymax)
+ args = append(args, oclass(ctxt, p, &p.RestArgs[i].Addr)*Ymax)
}
if tt != Ynone*Ymax {
args = append(args, tt)
// unpackOps4 extracts 4 operands from p.
func unpackOps4(p *obj.Prog) (arg0, arg1, arg2, dst *obj.Addr) {
- return &p.From, &p.RestArgs[0], &p.RestArgs[1], &p.To
+ return &p.From, &p.RestArgs[0].Addr, &p.RestArgs[1].Addr, &p.To
}
// unpackOps5 extracts 5 operands from p.
func unpackOps5(p *obj.Prog) (arg0, arg1, arg2, arg3, dst *obj.Addr) {
- return &p.From, &p.RestArgs[0], &p.RestArgs[1], &p.RestArgs[2], &p.To
+ return &p.From, &p.RestArgs[0].Addr, &p.RestArgs[1].Addr, &p.RestArgs[2].Addr, &p.To
}