func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { if p.As == ALEAL || p.As == ALEAQ { return } if a.Reg == REG_BP { ctxt.Diag("invalid address: %v", p) return } if a.Reg == REG_TLS { a.Reg = REG_BP } if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { switch a.Reg { // all ok case REG_BP, REG_SP, REG_R15: break default: if a.Index != REG_NONE { ctxt.Diag("invalid address %v", p) } a.Index = a.Reg if a.Index != REG_NONE { a.Scale = 1 } a.Reg = REG_R15 } } }
// copysub substitute s for v in a. // copysub returns true on failure to substitute. TODO(dfc) reverse this logic, copysub should return false on failure func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { if copyas(a, v) { if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_R15 || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X0+15 { if f { a.Reg = s.Reg } } return false } if regtyp(v) { if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { return true /* can't use BP-base with index */ } if f { a.Reg = s.Reg } } if a.Index == v.Reg { if f { a.Index = s.Reg } } } return false }
func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { a.Type = obj.TYPE_MEM a.Reg = REG_R15 a.Index = REG_CX a.Scale = 1 return } a.Type = obj.TYPE_MEM a.Reg = REG_CX }
/* * substitute s for v in a * return failure to substitute */ func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { if copyas(a, v) { reg := int(s.Reg) if reg >= x86.REG_AX && reg <= x86.REG_DI || reg >= x86.REG_X0 && reg <= x86.REG_X7 { if f != 0 { a.Reg = int16(reg) } } return 0 } if regtyp(v) { reg := int(v.Reg) if a.Type == obj.TYPE_MEM && int(a.Reg) == reg { if (s.Reg == x86.REG_BP) && a.Index != obj.TYPE_NONE { return 1 /* can't use BP-base with index */ } if f != 0 { a.Reg = s.Reg } } // return 0; if int(a.Index) == reg { if f != 0 { a.Index = s.Reg } return 0 } return 0 } return 0 }
// registerIndirect parses the general form of a register indirection. // It is can be (R1), (R2*scale), or (R1)(R2*scale) where R1 may be a simple // register or register pair R:R or (R, R) or (R+R). // Or it might be a pseudo-indirection like (FP). // We are sitting on the opening parenthesis. func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { p.get('(') tok := p.next() name := tok.String() r1, r2, scale, ok := p.register(name, 0) if !ok { p.errorf("indirect through non-register %s", tok) } p.get(')') a.Type = obj.TYPE_MEM if r1 < 0 { // Pseudo-register reference. if r2 != 0 { p.errorf("cannot use pseudo-register in pair") return } // For SB, SP, and FP, there must be a name here. 0(FP) is not legal. if name != "PC" && a.Name == obj.NAME_NONE { p.errorf("cannot reference %s without a symbol", name) } p.setPseudoRegister(a, name, false, prefix) return } a.Reg = r1 if r2 != 0 { // TODO: Consistency in the encoding would be nice here. if p.arch.Thechar == '5' || p.arch.Thechar == '7' { // Special form // ARM: destination register pair (R1, R2). // ARM64: register pair (R1, R2) for LDP/STP. 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 return } if p.arch.Thechar == '9' { // Special form for PPC64: (R1+R2); alias for (R1)(R2*1). if prefix != 0 || scale != 0 { p.errorf("illegal address mode for register+register") return } a.Type = obj.TYPE_MEM a.Scale = 1 a.Index = r2 // Nothing may follow. return } } if r2 != 0 { p.errorf("indirect through register pair") } if prefix == '$' { a.Type = obj.TYPE_ADDR } if r1 == arch.RPC && prefix != 0 { p.errorf("illegal addressing mode for PC") } if scale == 0 && p.peek() == '(' { // General form (R)(R*scale). p.next() tok := p.next() r1, r2, scale, ok = p.register(tok.String(), 0) if !ok { p.errorf("indirect through non-register %s", tok) } if r2 != 0 { p.errorf("unimplemented two-register form") } a.Index = r1 a.Scale = int16(scale) p.get(')') } else if scale != 0 { // First (R) was missing, all we have is (R*scale). a.Reg = 0 a.Index = r1 a.Scale = int16(scale) } }
/* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { break } v := n.Int() if v >= 32000 || v <= -32000 { break } switch as { default: return false case x86.AADDB, x86.AADDW, x86.AADDL, x86.AADDQ, x86.ASUBB, x86.ASUBW, x86.ASUBL, x86.ASUBQ, x86.AANDB, x86.AANDW, x86.AANDL, x86.AANDQ, x86.AORB, x86.AORW, x86.AORL, x86.AORQ, x86.AXORB, x86.AXORW, x86.AXORL, x86.AXORQ, x86.AINCB, x86.AINCW, x86.AINCL, x86.AINCQ, x86.ADECB, x86.ADECW, x86.ADECL, x86.ADECQ, x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ: break } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY gc.Naddr(a, n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] gc.Naddr(a, &n1) return true } gc.Regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { gc.Agen(nn, reg) n1.Xoffset = oary[0] } else { gc.Cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatal("can't happen") } gins(movptr, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Index = obj.TYPE_NONE gc.Fixlargeoffset(&n1) gc.Naddr(a, &n1) return true case gc.OINDEX: return false } return false }
/* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { return false } v := n.Int64() switch as { default: return false // operations that can cope with a 32-bit immediate // TODO(mundaym): logical operations can work on high bits case s390x.AADD, s390x.AADDC, s390x.ASUB, s390x.AMULLW, s390x.AAND, s390x.AOR, s390x.AXOR, s390x.ASLD, s390x.ASLW, s390x.ASRAW, s390x.ASRAD, s390x.ASRW, s390x.ASRD, s390x.AMOVB, s390x.AMOVBZ, s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVW, s390x.AMOVWZ, s390x.AMOVD: if int64(int32(v)) != v { return false } // for comparisons avoid immediates unless they can // fit into a int8/uint8 // this favours combined compare and branch instructions case s390x.ACMP: if int64(int8(v)) != v { return false } case s390x.ACMPU: if int64(uint8(v)) != v { return false } } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY gc.Naddr(a, n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] // check that the offset fits into a 12-bit displacement if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { sudoclean() return false } gc.Naddr(a, &n1) return true } gc.Regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { gc.Agen(nn, reg) n1.Xoffset = oary[0] } else { gc.Cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatalf("can't happen") } gins(s390x.AMOVD, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Index = 0 // check that the offset fits into a 12-bit displacement if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { tmp := n1 tmp.Op = gc.OREGISTER tmp.Type = gc.Types[gc.Tptr] tmp.Xoffset = 0 gc.Cgen_checknil(&tmp) ginscon(s390x.AADD, n1.Xoffset, &tmp) n1.Xoffset = 0 } gc.Naddr(a, &n1) return true } return false }