/* * 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 f != 0 { if copyau(a, v) { if a.Type == obj.TYPE_SHIFT { if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf } if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8 } } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { if a.Offset == int64(v.Reg) { a.Offset = int64(s.Reg) } if a.Reg == v.Reg { a.Reg = s.Reg } } else { a.Reg = s.Reg } } } return 0 }
// symbolReference parses a symbol that is known not to be a register. func (p *Parser) symbolReference(a *obj.Addr, name string, prefix rune) { // Identifier is a name. switch prefix { case 0: a.Type = obj.TYPE_MEM case '$': a.Type = obj.TYPE_ADDR case '*': a.Type = obj.TYPE_INDIR } // Weirdness with statics: Might now have "<>". isStatic := 0 // TODO: Really a boolean, but Linklookup wants a "version" integer. if p.peek() == '<' { isStatic = 1 p.next() p.get('>') } if p.peek() == '+' || p.peek() == '-' { a.Offset = int64(p.expr()) } a.Sym = obj.Linklookup(p.ctxt, name, isStatic) if p.peek() == scanner.EOF { if prefix == 0 && p.isJump { // Symbols without prefix or suffix are jump labels. return } p.errorf("illegal or missing addressing mode for symbol %s", name) return } // Expect (SB), (FP), (PC), or (SP) p.get('(') reg := p.get(scanner.Ident).String() p.get(')') p.setPseudoRegister(a, reg, isStatic != 0, prefix) }
func datagostring(sval string, a *obj.Addr) { symhdr, _ := stringsym(sval) a.Type = obj.TYPE_MEM a.Name = obj.NAME_EXTERN a.Sym = Linksym(symhdr) a.Node = symhdr.Def a.Offset = 0 a.Etype = uint8(TSTRING) }
func Datastring(s string, a *obj.Addr) { _, symdata := stringsym(s) a.Type = obj.TYPE_MEM a.Name = obj.NAME_EXTERN a.Sym = Linksym(symdata) a.Node = symdata.Def a.Offset = 0 a.Etype = uint8(Simtype[TINT]) }
func addreg(a *obj.Addr, rn int) { a.Sym = nil a.Node = nil a.Offset = 0 a.Type = obj.TYPE_REG a.Reg = int16(rn) a.Name = 0 Ostats.Ncvtreg++ }
// registerList parses an ARM register list expression, a list of registers in []. // There may be comma-separated ranges or individual registers, as in // [R1,R3-R5]. Only R0 through R15 may appear. // The opening bracket has been consumed. func (p *Parser) registerList(a *obj.Addr) { // One range per loop. const maxReg = 16 var bits uint16 ListLoop: for { tok := p.next() switch tok.ScanToken { case ']': break ListLoop case scanner.EOF: p.errorf("missing ']' in register list") return } // Parse the upper and lower bounds. lo := p.registerNumber(tok.String()) hi := lo if p.peek() == '-' { p.next() hi = p.registerNumber(p.next().String()) } if hi < lo { lo, hi = hi, lo } // Check there are no duplicates in the register list. for i := 0; lo <= hi && i < maxReg; i++ { if bits&(1<<lo) != 0 { p.errorf("register R%d already in list", lo) } bits |= 1 << lo lo++ } if p.peek() != ']' { p.get(',') } } a.Type = obj.TYPE_REGLIST a.Offset = int64(bits) }
// 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) } }
// operand parses a general operand and stores the result in *a. func (p *Parser) operand(a *obj.Addr) bool { //fmt.Printf("Operand: %v\n", p.input) if len(p.input) == 0 { p.errorf("empty operand: cannot happen") return false } // General address (with a few exceptions) looks like // $sym±offset(SB)(reg)(index*scale) // Exceptions are: // // R1 // offset // $offset // Every piece is optional, so we scan left to right and what // we discover tells us where we are. // Prefix: $. var prefix rune switch tok := p.peek(); tok { case '$', '*': prefix = rune(tok) p.next() } // Symbol: sym±offset(SB) tok := p.next() name := tok.String() if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) { // We have a symbol. Parse $sym±offset(symkind) p.symbolReference(a, name, prefix) // fmt.Printf("SYM %s\n", obj.Dconv(&emptyProg, 0, a)) if p.peek() == scanner.EOF { return true } } // Special register list syntax for arm: [R1,R3-R7] if tok.ScanToken == '[' { if prefix != 0 { p.errorf("illegal use of register list") } p.registerList(a) p.expect(scanner.EOF) return true } // Register: R1 if tok.ScanToken == scanner.Ident && p.atStartOfRegister(name) { if p.atRegisterShift() { // ARM shifted register such as R1<<R2 or R1>>2. a.Type = obj.TYPE_SHIFT a.Offset = p.registerShift(tok.String(), prefix) if p.peek() == '(' { // Can only be a literal register here. p.next() tok := p.next() name := tok.String() if !p.atStartOfRegister(name) { p.errorf("expected register; found %s", name) } a.Reg, _ = p.registerReference(name) p.get(')') } } else if r1, r2, scale, ok := p.register(tok.String(), prefix); ok { if scale != 0 { p.errorf("expected simple register reference") } a.Type = obj.TYPE_REG a.Reg = r1 if r2 != 0 { // Form is R1:R2. It is on RHS and the second register // needs to go into the LHS. panic("cannot happen (Addr.Reg2)") } } // fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a)) p.expect(scanner.EOF) return true } // Constant. haveConstant := false switch tok.ScanToken { case scanner.Int, scanner.Float, scanner.String, scanner.Char, '+', '-', '~': haveConstant = true case '(': // Could be parenthesized expression or (R). Must be something, though. tok := p.next() if tok.ScanToken == scanner.EOF { p.errorf("missing right parenthesis") return false } rname := tok.String() p.back() haveConstant = !p.atStartOfRegister(rname) if !haveConstant { p.back() // Put back the '('. } } if haveConstant { p.back() if p.have(scanner.Float) { if prefix != '$' { p.errorf("floating-point constant must be an immediate") } a.Type = obj.TYPE_FCONST a.Val = p.floatExpr() // fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a)) p.expect(scanner.EOF) return true } if p.have(scanner.String) { if prefix != '$' { p.errorf("string constant must be an immediate") return false } str, err := strconv.Unquote(p.get(scanner.String).String()) if err != nil { p.errorf("string parse error: %s", err) } a.Type = obj.TYPE_SCONST a.Val = str // fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a)) p.expect(scanner.EOF) return true } a.Offset = int64(p.expr()) if p.peek() != '(' { switch prefix { case '$': a.Type = obj.TYPE_CONST case '*': a.Type = obj.TYPE_INDIR // Can appear but is illegal, will be rejected by the linker. default: a.Type = obj.TYPE_MEM } // fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a)) p.expect(scanner.EOF) return true } // fmt.Printf("offset %d \n", a.Offset) } // Register indirection: (reg) or (index*scale). We are on the opening paren. p.registerIndirect(a, prefix) // fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a)) p.expect(scanner.EOF) return true }
/* * xtramodes enables the ARM post increment and * shift offset addressing modes to transform * MOVW 0(R3),R1 * ADD $4,R3,R3 * into * MOVW.P 4(R3),R1 * and * ADD R0,R1 * MOVBU 0(R1),R0 * into * MOVBU R0<<0(R1),R0 */ func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool { p := (*obj.Prog)(r.Prog) v := obj.Addr(*a) v.Type = obj.TYPE_REG r1 := (*gc.Flow)(findpre(r, &v)) if r1 != nil { p1 := r1.Prog if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg { switch p1.As { case arm.AADD: if p1.Scond&arm.C_SBIT != 0 { // avoid altering ADD.S/ADC sequences. break } if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) { if nochange(gc.Uniqs(r1), r, p1) { if a != &p.From || v.Reg != p.To.Reg { if finduse(g, r.S1, &v) { if p1.Reg == 0 || p1.Reg == v.Reg { /* pre-indexing */ p.Scond |= arm.C_WBIT } else { return false } } } switch p1.From.Type { /* register offset */ case obj.TYPE_REG: if gc.Nacl { return false } *a = obj.Addr{} a.Type = obj.TYPE_SHIFT a.Offset = int64(p1.From.Reg) & 15 /* scaled register offset */ case obj.TYPE_SHIFT: if gc.Nacl { return false } *a = obj.Addr{} a.Type = obj.TYPE_SHIFT fallthrough /* immediate offset */ case obj.TYPE_CONST, obj.TYPE_ADDR: a.Offset = p1.From.Offset } if p1.Reg != 0 { a.Reg = p1.Reg } excise(r1) return true } } case arm.AMOVW: if p1.From.Type == obj.TYPE_REG { r2 := (*gc.Flow)(findinc(r1, r, &p1.From)) if r2 != nil { var r3 *gc.Flow for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) { } if r3 == r { /* post-indexing */ p1 := r2.Prog a.Reg = p1.To.Reg a.Offset = p1.From.Offset p.Scond |= arm.C_PBIT if !finduse(g, r, &r1.Prog.To) { excise(r1) } excise(r2) return true } } } } } } if a != &p.From || a.Reg != p.To.Reg { r1 := (*gc.Flow)(findinc(r, nil, &v)) if r1 != nil { /* post-indexing */ p1 := r1.Prog a.Offset = p1.From.Offset p.Scond |= arm.C_PBIT excise(r1) return true } } return false }
// Naddr rewrites a to refer to n. // It assumes that a is zeroed on entry. func Naddr(a *obj.Addr, n *Node) { if n == nil { return } if n.Type != nil && n.Type.Etype != TIDEAL { // TODO(rsc): This is undone by the selective clearing of width below, // to match architectures that were not as aggressive in setting width // during naddr. Those widths must be cleared to avoid triggering // failures in gins when it detects real but heretofore latent (and one // hopes innocuous) type mismatches. // The type mismatches should be fixed and the clearing below removed. dowidth(n.Type) a.Width = n.Type.Width } switch n.Op { default: a := a // copy to let escape into Ctxt.Dconv Debug['h'] = 1 Dump("naddr", n) Fatalf("naddr: bad %v %v", Oconv(int(n.Op), 0), Ctxt.Dconv(a)) case OREGISTER: a.Type = obj.TYPE_REG a.Reg = n.Reg a.Sym = nil if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. a.Width = 0 } case OINDREG: a.Type = obj.TYPE_MEM a.Reg = n.Reg a.Sym = Linksym(n.Sym) a.Offset = n.Xoffset if a.Offset != int64(int32(a.Offset)) { Yyerror("offset %d too large for OINDREG", a.Offset) } if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. a.Width = 0 } // n->left is PHEAP ONAME for stack parameter. // compute address of actual parameter on stack. case OPARAM: a.Etype = uint8(Simtype[n.Left.Type.Etype]) a.Width = n.Left.Type.Width a.Offset = n.Xoffset a.Sym = Linksym(n.Left.Sym) a.Type = obj.TYPE_MEM a.Name = obj.NAME_PARAM a.Node = n.Left.Orig case OCLOSUREVAR: if !Curfn.Func.Needctxt { Fatalf("closurevar without needctxt") } a.Type = obj.TYPE_MEM a.Reg = int16(Thearch.REGCTXT) a.Sym = nil a.Offset = n.Xoffset case OCFUNC: Naddr(a, n.Left) a.Sym = Linksym(n.Left.Sym) case ONAME: a.Etype = 0 if n.Type != nil { a.Etype = uint8(Simtype[n.Type.Etype]) } a.Offset = n.Xoffset s := n.Sym a.Node = n.Orig //if(a->node >= (Node*)&n) // fatal("stack node"); if s == nil { s = Lookup(".noname") } if n.Name.Method { if n.Type != nil { if n.Type.Sym != nil { if n.Type.Sym.Pkg != nil { s = Pkglookup(s.Name, n.Type.Sym.Pkg) } } } } a.Type = obj.TYPE_MEM switch n.Class { default: Fatalf("naddr: ONAME class %v %d\n", n.Sym, n.Class) case PEXTERN: a.Name = obj.NAME_EXTERN case PAUTO: a.Name = obj.NAME_AUTO case PPARAM, PPARAMOUT: a.Name = obj.NAME_PARAM case PFUNC: a.Name = obj.NAME_EXTERN a.Type = obj.TYPE_ADDR a.Width = int64(Widthptr) s = funcsym(s) } a.Sym = Linksym(s) case ODOT: // A special case to make write barriers more efficient. // Taking the address of the first field of a named struct // is the same as taking the address of the struct. if n.Left.Type.Etype != TSTRUCT || n.Left.Type.Type.Sym != n.Right.Sym { Debug['h'] = 1 Dump("naddr", n) Fatalf("naddr: bad %v %v", Oconv(int(n.Op), 0), Ctxt.Dconv(a)) } Naddr(a, n.Left) case OLITERAL: if Thearch.Thechar == '8' { a.Width = 0 } switch n.Val().Ctype() { default: Fatalf("naddr: const %v", Tconv(n.Type, obj.FmtLong)) case CTFLT: a.Type = obj.TYPE_FCONST a.Val = mpgetflt(n.Val().U.(*Mpflt)) case CTINT, CTRUNE: a.Sym = nil a.Type = obj.TYPE_CONST a.Offset = Mpgetfix(n.Val().U.(*Mpint)) case CTSTR: datagostring(n.Val().U.(string), a) case CTBOOL: a.Sym = nil a.Type = obj.TYPE_CONST a.Offset = int64(obj.Bool2int(n.Val().U.(bool))) case CTNIL: a.Sym = nil a.Type = obj.TYPE_CONST a.Offset = 0 } case OADDR: Naddr(a, n.Left) a.Etype = uint8(Tptr) if Thearch.Thechar != '0' && Thearch.Thechar != '5' && Thearch.Thechar != '7' && Thearch.Thechar != '9' { // TODO(rsc): Do this even for arm, ppc64. a.Width = int64(Widthptr) } if a.Type != obj.TYPE_MEM { a := a // copy to let escape into Ctxt.Dconv Fatalf("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), Oconv(int(n.Left.Op), 0)) } a.Type = obj.TYPE_ADDR // itable of interface value case OITAB: Naddr(a, n.Left) if a.Type == obj.TYPE_CONST && a.Offset == 0 { break // itab(nil) } a.Etype = uint8(Tptr) a.Width = int64(Widthptr) // pointer in a string or slice case OSPTR: Naddr(a, n.Left) if a.Type == obj.TYPE_CONST && a.Offset == 0 { break // ptr(nil) } a.Etype = uint8(Simtype[Tptr]) a.Offset += int64(Array_array) a.Width = int64(Widthptr) // len of string or slice case OLEN: Naddr(a, n.Left) if a.Type == obj.TYPE_CONST && a.Offset == 0 { break // len(nil) } a.Etype = uint8(Simtype[TUINT]) a.Offset += int64(Array_nel) if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. a.Width = int64(Widthint) } // cap of string or slice case OCAP: Naddr(a, n.Left) if a.Type == obj.TYPE_CONST && a.Offset == 0 { break // cap(nil) } a.Etype = uint8(Simtype[TUINT]) a.Offset += int64(Array_cap) if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. a.Width = int64(Widthint) } } return }