// 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) }
/* * 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 }
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 replaces v with s in a if f!=0 or indicates it if could if f==0. // Returns 1 on failure to substitute (it always succeeds on mips). func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { if f != 0 { if copyau(a, v) { a.Reg = s.Reg } } return 0 }
// setPseudoRegister sets the NAME field of addr for a pseudo-register reference such as (SB). func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, prefix rune) { if addr.Reg != 0 { p.errorf("internal error: reg %s already set in pseudo", reg) } switch reg { case "FP": addr.Name = obj.NAME_PARAM case "PC": if prefix != 0 { p.errorf("illegal addressing mode for PC") } addr.Type = obj.TYPE_BRANCH // We set the type and leave NAME untouched. See asmJump. case "SB": addr.Name = obj.NAME_EXTERN if isStatic { addr.Name = obj.NAME_STATIC } case "SP": addr.Name = obj.NAME_AUTO // The pseudo-stack. default: p.errorf("expected pseudo-register; found %s", reg) } if prefix == '$' { addr.Type = obj.TYPE_ADDR } }
// 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) }
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 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) }
/* * 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(int(s.Reg)) if reg >= x86.REG_AX && reg <= x86.REG_R15 || reg >= x86.REG_X0 && reg <= x86.REG_X0+15 { if f != 0 { a.Reg = int16(reg) } } return 0 } if regtyp(v) { reg := int(int(v.Reg)) if a.Type == obj.TYPE_MEM && int(a.Reg) == reg { if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_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 }
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++ }
// 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 arm.AADD, arm.ASUB, arm.AAND, arm.AORR, arm.AEOR, arm.AMOVB, arm.AMOVBS, arm.AMOVBU, arm.AMOVH, arm.AMOVHS, arm.AMOVHU, arm.AMOVW: 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.Fatalf("can't happen") } gins(arm.AMOVW, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Name = obj.NAME_NONE n1.Type = n.Type gc.Naddr(a, &n1) return true case gc.OINDEX: return false } return false }
// 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 }
func Afunclit(a *obj.Addr, n *Node) { if a.Type == obj.TYPE_ADDR && a.Name == obj.NAME_EXTERN { a.Type = obj.TYPE_MEM a.Sym = Linksym(n.Sym) } }
/* * 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 }
/* * 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.Fatalf("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 }
func mkvar(f *Flow, a *obj.Addr) Bits { // mark registers used if a.Type == obj.TYPE_NONE { return zbits } r := f.Data.(*Reg) r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB var n int switch a.Type { default: regu := Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB if regu == 0 { return zbits } bit := zbits bit.b[0] = regu return bit // TODO(rsc): Remove special case here. case obj.TYPE_ADDR: var bit Bits if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { goto memcase } a.Type = obj.TYPE_MEM bit = mkvar(f, a) setaddrs(bit) a.Type = obj.TYPE_ADDR Ostats.Naddr++ return zbits memcase: fallthrough case obj.TYPE_MEM: if r != nil { r.use1.b[0] |= Thearch.RtoB(int(a.Reg)) } /* NOTE: 5g did if(r->f.prog->scond & (C_PBIT|C_WBIT)) r->set.b[0] |= RtoB(a->reg); */ switch a.Name { default: // Note: This case handles NAME_EXTERN and NAME_STATIC. // We treat these as requiring eager writes to memory, due to // the possibility of a fault handler looking at them, so there is // not much point in registerizing the loads. // If we later choose the set of candidate variables from a // larger list, these cases could be deprioritized instead of // removed entirely. return zbits case obj.NAME_PARAM, obj.NAME_AUTO: n = int(a.Name) } } node, _ := a.Node.(*Node) if node == nil || node.Op != ONAME || node.Orig == nil { return zbits } node = node.Orig if node.Orig != node { Fatalf("%v: bad node", Ctxt.Dconv(a)) } if node.Sym == nil || node.Sym.Name[0] == '.' { return zbits } et := EType(a.Etype) o := a.Offset w := a.Width if w < 0 { Fatalf("bad width %d for %v", w, Ctxt.Dconv(a)) } flag := 0 var v *Var for i := 0; i < nvar; i++ { v = &vars[i] if v.node == node && int(v.name) == n { if v.offset == o { if v.etype == et { if int64(v.width) == w { // TODO(rsc): Remove special case for arm here. if flag == 0 || Thearch.Thechar != '5' { return blsh(uint(i)) } } } } // if they overlap, disable both if overlap_reg(v.offset, v.width, o, int(w)) { // print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); v.addr = 1 flag = 1 } } } switch et { case 0, TFUNC: return zbits } if nvar >= NVAR { if Debug['w'] > 1 && node != nil { Fatalf("variable not optimized: %v", Nconv(node, obj.FmtSharp)) } if Debug['v'] > 0 { Warn("variable not optimized: %v", Nconv(node, obj.FmtSharp)) } // If we're not tracking a word in a variable, mark the rest as // having its address taken, so that we keep the whole thing // live at all calls. otherwise we might optimize away part of // a variable but not all of it. var v *Var for i := 0; i < nvar; i++ { v = &vars[i] if v.node == node { v.addr = 1 } } return zbits } i := nvar nvar++ v = &vars[i] v.id = i v.offset = o v.name = int8(n) v.etype = et v.width = int(w) v.addr = int8(flag) // funny punning v.node = node // node->opt is the head of a linked list // of Vars within the given Node, so that // we can start at a Var and find all the other // Vars in the same Go variable. v.nextinnode, _ = node.Opt().(*Var) node.SetOpt(v) bit := blsh(uint(i)) if n == obj.NAME_EXTERN || n == obj.NAME_STATIC { for z := 0; z < BITS; z++ { externs.b[z] |= bit.b[z] } } if n == obj.NAME_PARAM { for z := 0; z < BITS; z++ { params.b[z] |= bit.b[z] } } if node.Class == PPARAM { for z := 0; z < BITS; z++ { ivar.b[z] |= bit.b[z] } } if node.Class == PPARAMOUT { for z := 0; z < BITS; z++ { ovar.b[z] |= bit.b[z] } } // Treat values with their address taken as live at calls, // because the garbage collector's liveness analysis in plive.go does. // These must be consistent or else we will elide stores and the garbage // collector will see uninitialized data. // The typical case where our own analysis is out of sync is when the // node appears to have its address taken but that code doesn't actually // get generated and therefore doesn't show up as an address being // taken when we analyze the instruction stream. // One instance of this case is when a closure uses the same name as // an outer variable for one of its own variables declared with :=. // The parser flags the outer variable as possibly shared, and therefore // sets addrtaken, even though it ends up not being actually shared. // If we were better about _ elision, _ = &x would suffice too. // The broader := in a closure problem is mentioned in a comment in // closure.go:/^typecheckclosure and dcl.go:/^oldname. if node.Addrtaken { v.addr = 1 } // Disable registerization for globals, because: // (1) we might panic at any time and we want the recovery code // to see the latest values (issue 1304). // (2) we don't know what pointers might point at them and we want // loads via those pointers to see updated values and vice versa (issue 7995). // // Disable registerization for results if using defer, because the deferred func // might recover and return, causing the current values to be used. if node.Class == PEXTERN || (hasdefer && node.Class == PPARAMOUT) { v.addr = 1 } if Debug['R'] != 0 { fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, Econv(et), o, w, Nconv(node, obj.FmtSharp), Ctxt.Dconv(a), v.addr) } Ostats.Nvar++ return bit }