diff options
author | Wim <wim@42.be> | 2018-08-06 21:47:05 +0200 |
---|---|---|
committer | Wim <wim@42.be> | 2018-08-06 21:47:05 +0200 |
commit | 51062863a5c34d81e296cf15c61140911037cf3b (patch) | |
tree | 9b5e044672486326c7a0ca8fb26430f37bf4d83c /vendor/github.com/google/gops/internal/obj/x86/obj6.go | |
parent | 4fb4b7aa6c02a54db8ad8dd98e4d321396926c0d (diff) | |
download | matterbridge-msglm-51062863a5c34d81e296cf15c61140911037cf3b.tar.gz matterbridge-msglm-51062863a5c34d81e296cf15c61140911037cf3b.tar.bz2 matterbridge-msglm-51062863a5c34d81e296cf15c61140911037cf3b.zip |
Use mod vendor for vendored directory (backwards compatible)
Diffstat (limited to 'vendor/github.com/google/gops/internal/obj/x86/obj6.go')
-rw-r--r-- | vendor/github.com/google/gops/internal/obj/x86/obj6.go | 1481 |
1 files changed, 0 insertions, 1481 deletions
diff --git a/vendor/github.com/google/gops/internal/obj/x86/obj6.go b/vendor/github.com/google/gops/internal/obj/x86/obj6.go deleted file mode 100644 index aad950bb..00000000 --- a/vendor/github.com/google/gops/internal/obj/x86/obj6.go +++ /dev/null @@ -1,1481 +0,0 @@ -// Inferno utils/6l/pass.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/pass.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package x86 - -import ( - "fmt" - "log" - "math" - "strings" - - "github.com/google/gops/internal/obj" - "github.com/google/gops/internal/sys" -) - -func CanUse1InsnTLS(ctxt *obj.Link) bool { - if isAndroid { - // For android, we use a disgusting hack that assumes - // the thread-local storage slot for g is allocated - // using pthread_key_create with a fixed offset - // (see src/runtime/cgo/gcc_android_amd64.c). - // This makes access to the TLS storage (for g) doable - // with 1 instruction. - return true - } - - if ctxt.Arch.RegSize == 4 { - switch ctxt.Headtype { - case obj.Hlinux, - obj.Hnacl, - obj.Hplan9, - obj.Hwindows, - obj.Hwindowsgui: - return false - } - - return true - } - - switch ctxt.Headtype { - case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui: - return false - case obj.Hlinux: - return !ctxt.Flag_shared - } - - return true -} - -func progedit(ctxt *obj.Link, p *obj.Prog) { - // Maintain information about code generation mode. - if ctxt.Mode == 0 { - ctxt.Mode = ctxt.Arch.RegSize * 8 - } - p.Mode = int8(ctxt.Mode) - - switch p.As { - case AMODE: - if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) { - switch int(p.From.Offset) { - case 16, 32, 64: - ctxt.Mode = int(p.From.Offset) - } - } - obj.Nopout(p) - } - - // Thread-local storage references use the TLS pseudo-register. - // As a register, TLS refers to the thread-local storage base, and it - // can only be loaded into another register: - // - // MOVQ TLS, AX - // - // An offset from the thread-local storage base is written off(reg)(TLS*1). - // Semantically it is off(reg), but the (TLS*1) annotation marks this as - // indexing from the loaded TLS base. This emits a relocation so that - // if the linker needs to adjust the offset, it can. For example: - // - // MOVQ TLS, AX - // MOVQ 0(AX)(TLS*1), CX // load g into CX - // - // On systems that support direct access to the TLS memory, this - // pair of instructions can be reduced to a direct TLS memory reference: - // - // MOVQ 0(TLS), CX // load g into CX - // - // The 2-instruction and 1-instruction forms correspond to the two code - // sequences for loading a TLS variable in the local exec model given in "ELF - // Handling For Thread-Local Storage". - // - // We apply this rewrite on systems that support the 1-instruction form. - // The decision is made using only the operating system and the -shared flag, - // not the link mode. If some link modes on a particular operating system - // require the 2-instruction form, then all builds for that operating system - // will use the 2-instruction form, so that the link mode decision can be - // delayed to link time. - // - // In this way, all supported systems use identical instructions to - // access TLS, and they are rewritten appropriately first here in - // liblink and then finally using relocations in the linker. - // - // When -shared is passed, we leave the code in the 2-instruction form but - // assemble (and relocate) them in different ways to generate the initial - // exec code sequence. It's a bit of a fluke that this is possible without - // rewriting the instructions more comprehensively, and it only does because - // we only support a single TLS variable (g). - - if CanUse1InsnTLS(ctxt) { - // Reduce 2-instruction sequence to 1-instruction sequence. - // Sequences like - // MOVQ TLS, BX - // ... off(BX)(TLS*1) ... - // become - // NOP - // ... off(TLS) ... - // - // TODO(rsc): Remove the Hsolaris special case. It exists only to - // guarantee we are producing byte-identical binaries as before this code. - // But it should be unnecessary. - if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != obj.Hsolaris { - obj.Nopout(p) - } - if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { - p.From.Reg = REG_TLS - p.From.Scale = 0 - p.From.Index = REG_NONE - } - - if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { - p.To.Reg = REG_TLS - p.To.Scale = 0 - p.To.Index = REG_NONE - } - } else { - // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it - // as the 2-instruction sequence if necessary. - // MOVQ 0(TLS), BX - // becomes - // MOVQ TLS, BX - // MOVQ 0(BX)(TLS*1), BX - if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { - q := obj.Appendp(ctxt, p) - q.As = p.As - q.From = p.From - q.From.Type = obj.TYPE_MEM - q.From.Reg = p.To.Reg - q.From.Index = REG_TLS - q.From.Scale = 2 // TODO: use 1 - q.To = p.To - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_TLS - p.From.Index = REG_NONE - p.From.Offset = 0 - } - } - - // TODO: Remove. - if (ctxt.Headtype == obj.Hwindows || ctxt.Headtype == obj.Hwindowsgui) && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 { - if p.From.Scale == 1 && p.From.Index == REG_TLS { - p.From.Scale = 2 - } - if p.To.Scale == 1 && p.To.Index == REG_TLS { - p.To.Scale = 2 - } - } - - // Rewrite 0 to $0 in 3rd argument to CMPPS etc. - // That's what the tables expect. - switch p.As { - case ACMPPD, ACMPPS, ACMPSD, ACMPSS: - if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil { - p.To.Type = obj.TYPE_CONST - } - } - - // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH. - switch p.As { - case obj.ACALL, obj.AJMP, obj.ARET: - if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { - p.To.Type = obj.TYPE_BRANCH - } - } - - // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ. - if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { - switch p.As { - case AMOVL: - p.As = ALEAL - p.From.Type = obj.TYPE_MEM - case AMOVQ: - p.As = ALEAQ - p.From.Type = obj.TYPE_MEM - } - } - - if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { - if p.From3 != nil { - nacladdr(ctxt, p, p.From3) - } - nacladdr(ctxt, p, &p.From) - nacladdr(ctxt, p, &p.To) - } - - // Rewrite float constants to values stored in memory. - switch p.As { - // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx - case AMOVSS: - if p.From.Type == obj.TYPE_FCONST { - // f == 0 can't be used here due to -0, so use Float64bits - if f := p.From.Val.(float64); math.Float64bits(f) == 0 { - if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { - p.As = AXORPS - p.From = p.To - break - } - } - } - fallthrough - - case AFMOVF, - AFADDF, - AFSUBF, - AFSUBRF, - AFMULF, - AFDIVF, - AFDIVRF, - AFCOMF, - AFCOMFP, - AADDSS, - ASUBSS, - AMULSS, - ADIVSS, - ACOMISS, - AUCOMISS: - if p.From.Type == obj.TYPE_FCONST { - f32 := float32(p.From.Val.(float64)) - i32 := math.Float32bits(f32) - literal := fmt.Sprintf("$f32.%08x", i32) - s := obj.Linklookup(ctxt, literal, 0) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = s - p.From.Sym.Set(obj.AttrLocal, true) - p.From.Offset = 0 - } - - case AMOVSD: - // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx - if p.From.Type == obj.TYPE_FCONST { - // f == 0 can't be used here due to -0, so use Float64bits - if f := p.From.Val.(float64); math.Float64bits(f) == 0 { - if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { - p.As = AXORPS - p.From = p.To - break - } - } - } - fallthrough - - case AFMOVD, - AFADDD, - AFSUBD, - AFSUBRD, - AFMULD, - AFDIVD, - AFDIVRD, - AFCOMD, - AFCOMDP, - AADDSD, - ASUBSD, - AMULSD, - ADIVSD, - ACOMISD, - AUCOMISD: - if p.From.Type == obj.TYPE_FCONST { - i64 := math.Float64bits(p.From.Val.(float64)) - literal := fmt.Sprintf("$f64.%016x", i64) - s := obj.Linklookup(ctxt, literal, 0) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = s - p.From.Sym.Set(obj.AttrLocal, true) - p.From.Offset = 0 - } - } - - if ctxt.Flag_dynlink { - rewriteToUseGot(ctxt, p) - } - - if ctxt.Flag_shared && p.Mode == 32 { - rewriteToPcrel(ctxt, p) - } -} - -// Rewrite p, if necessary, to access global data via the global offset table. -func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { - var add, lea, mov obj.As - var reg int16 - if p.Mode == 64 { - add = AADDQ - lea = ALEAQ - mov = AMOVQ - reg = REG_R15 - } else { - add = AADDL - lea = ALEAL - mov = AMOVL - reg = REG_CX - if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { - // Special case: clobber the destination register with - // the PC so we don't have to clobber CX. - // The SSA backend depends on CX not being clobbered across LEAL. - // See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared). - reg = p.To.Reg - } - } - - if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { - // ADUFFxxx $offset - // becomes - // $MOV runtime.duffxxx@GOT, $reg - // $ADD $offset, $reg - // CALL $reg - var sym *obj.LSym - if p.As == obj.ADUFFZERO { - sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) - } else { - sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) - } - offset := p.To.Offset - p.As = mov - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_GOTREF - p.From.Sym = sym - p.To.Type = obj.TYPE_REG - p.To.Reg = reg - p.To.Offset = 0 - p.To.Sym = nil - p1 := obj.Appendp(ctxt, p) - p1.As = add - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = offset - p1.To.Type = obj.TYPE_REG - p1.To.Reg = reg - p2 := obj.Appendp(ctxt, p1) - p2.As = obj.ACALL - p2.To.Type = obj.TYPE_REG - p2.To.Reg = reg - } - - // We only care about global data: NAME_EXTERN means a global - // symbol in the Go sense, and p.Sym.Local is true for a few - // internally defined symbols. - if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { - // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below - p.As = mov - p.From.Type = obj.TYPE_ADDR - } - if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { - // $MOV $sym, Rx becomes $MOV sym@GOT, Rx - // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx - // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX - cmplxdest := false - pAs := p.As - var dest obj.Addr - if p.To.Type != obj.TYPE_REG || pAs != mov { - if p.Mode == 64 { - ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) - } - cmplxdest = true - dest = p.To - p.As = mov - p.To.Type = obj.TYPE_REG - p.To.Reg = reg - p.To.Sym = nil - p.To.Name = obj.NAME_NONE - } - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_GOTREF - q := p - if p.From.Offset != 0 { - q = obj.Appendp(ctxt, p) - q.As = lea - q.From.Type = obj.TYPE_MEM - q.From.Reg = p.To.Reg - q.From.Offset = p.From.Offset - q.To = p.To - p.From.Offset = 0 - } - if cmplxdest { - q = obj.Appendp(ctxt, q) - q.As = pAs - q.To = dest - q.From.Type = obj.TYPE_REG - q.From.Reg = reg - } - } - if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { - ctxt.Diag("don't know how to handle %v with -dynlink", p) - } - var source *obj.Addr - // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry - // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) - // An addition may be inserted between the two MOVs if there is an offset. - if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { - if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { - ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) - } - source = &p.From - } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { - source = &p.To - } else { - return - } - if p.As == obj.ACALL { - // When dynlinking on 386, almost any call might end up being a call - // to a PLT, so make sure the GOT pointer is loaded into BX. - // RegTo2 is set on the replacement call insn to stop it being - // processed when it is in turn passed to progedit. - if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 { - return - } - p1 := obj.Appendp(ctxt, p) - p2 := obj.Appendp(ctxt, p1) - - p1.As = ALEAL - p1.From.Type = obj.TYPE_MEM - p1.From.Name = obj.NAME_STATIC - p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0) - p1.To.Type = obj.TYPE_REG - p1.To.Reg = REG_BX - - p2.As = p.As - p2.Scond = p.Scond - p2.From = p.From - p2.From3 = p.From3 - p2.Reg = p.Reg - p2.To = p.To - // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr - // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 - // itself gets passed to progedit. - p2.To.Type = obj.TYPE_MEM - p2.RegTo2 = 1 - - obj.Nopout(p) - return - - } - if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { - return - } - if source.Type != obj.TYPE_MEM { - ctxt.Diag("don't know how to handle %v with -dynlink", p) - } - p1 := obj.Appendp(ctxt, p) - p2 := obj.Appendp(ctxt, p1) - - p1.As = mov - p1.From.Type = obj.TYPE_MEM - p1.From.Sym = source.Sym - p1.From.Name = obj.NAME_GOTREF - p1.To.Type = obj.TYPE_REG - p1.To.Reg = reg - - p2.As = p.As - p2.From = p.From - p2.To = p.To - if p.From.Name == obj.NAME_EXTERN { - p2.From.Reg = reg - p2.From.Name = obj.NAME_NONE - p2.From.Sym = nil - } else if p.To.Name == obj.NAME_EXTERN { - p2.To.Reg = reg - p2.To.Name = obj.NAME_NONE - p2.To.Sym = nil - } else { - return - } - obj.Nopout(p) -} - -func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { - // RegTo2 is set on the instructions we insert here so they don't get - // processed twice. - if p.RegTo2 != 0 { - return - } - if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { - return - } - // Any Prog (aside from the above special cases) with an Addr with Name == - // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX - // inserted before it. - isName := func(a *obj.Addr) bool { - if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 { - return false - } - if a.Sym.Type == obj.STLSBSS { - return false - } - return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF - } - - if isName(&p.From) && p.From.Type == obj.TYPE_ADDR { - // Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting - // to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX" - // respectively. - if p.To.Type != obj.TYPE_REG { - q := obj.Appendp(ctxt, p) - q.As = p.As - q.From.Type = obj.TYPE_REG - q.From.Reg = REG_CX - q.To = p.To - p.As = AMOVL - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_CX - p.To.Sym = nil - p.To.Name = obj.NAME_NONE - } - } - - if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) { - return - } - var dst int16 = REG_CX - if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { - dst = p.To.Reg - // Why? See the comment near the top of rewriteToUseGot above. - // AMOVLs might be introduced by the GOT rewrites. - } - q := obj.Appendp(ctxt, p) - q.RegTo2 = 1 - r := obj.Appendp(ctxt, q) - r.RegTo2 = 1 - q.As = obj.ACALL - q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0) - q.To.Type = obj.TYPE_MEM - q.To.Name = obj.NAME_EXTERN - q.To.Sym.Set(obj.AttrLocal, true) - r.As = p.As - r.Scond = p.Scond - r.From = p.From - r.From3 = p.From3 - r.Reg = p.Reg - r.To = p.To - if isName(&p.From) { - r.From.Reg = dst - } - if isName(&p.To) { - r.To.Reg = dst - } - if p.From3 != nil && isName(p.From3) { - r.From3.Reg = dst - } - obj.Nopout(p) -} - -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 - } - } -} - -func preprocess(ctxt *obj.Link, cursym *obj.LSym) { - if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { - ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) - } - - ctxt.Cursym = cursym - - if cursym.Text == nil || cursym.Text.Link == nil { - return - } - - p := cursym.Text - autoffset := int32(p.To.Offset) - if autoffset < 0 { - autoffset = 0 - } - - var bpsize int - if p.Mode == 64 && ctxt.Framepointer_enabled && autoffset > 0 && p.From3.Offset&obj.NOFRAME == 0 { - // Make room for to save a base pointer. If autoffset == 0, - // this might do something special like a tail jump to - // another function, so in that case we omit this. - bpsize = ctxt.Arch.PtrSize - autoffset += int32(bpsize) - p.To.Offset += int64(bpsize) - } else { - bpsize = 0 - } - - textarg := int64(p.To.Val.(int32)) - cursym.Args = int32(textarg) - cursym.Locals = int32(p.To.Offset) - - // TODO(rsc): Remove. - if p.Mode == 32 && cursym.Locals < 0 { - cursym.Locals = 0 - } - - // TODO(rsc): Remove 'p.Mode == 64 &&'. - if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { - leaf := true - LeafSearch: - for q := p; q != nil; q = q.Link { - switch q.As { - case obj.ACALL: - // Treat common runtime calls that take no arguments - // the same as duffcopy and duffzero. - if !isZeroArgRuntimeCall(q.To.Sym) { - leaf = false - break LeafSearch - } - fallthrough - case obj.ADUFFCOPY, obj.ADUFFZERO: - if autoffset >= obj.StackSmall-8 { - leaf = false - break LeafSearch - } - } - } - - if leaf { - p.From3.Offset |= obj.NOSPLIT - } - } - - if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { - p = obj.Appendp(ctxt, p) - p = load_g_cx(ctxt, p) // load g into CX - } - - if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { - p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check - } - - if autoffset != 0 { - if autoffset%int32(ctxt.Arch.RegSize) != 0 { - ctxt.Diag("unaligned stack size %d", autoffset) - } - p = obj.Appendp(ctxt, p) - p.As = AADJSP - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(autoffset) - p.Spadj = autoffset - } - - deltasp := autoffset - - if bpsize > 0 { - // Save caller's BP - p = obj.Appendp(ctxt, p) - - p.As = AMOVQ - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_BP - p.To.Type = obj.TYPE_MEM - p.To.Reg = REG_SP - p.To.Scale = 1 - p.To.Offset = int64(autoffset) - int64(bpsize) - - // Move current frame to BP - p = obj.Appendp(ctxt, p) - - p.As = ALEAQ - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_SP - p.From.Scale = 1 - p.From.Offset = int64(autoffset) - int64(bpsize) - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_BP - } - - if cursym.Text.From3Offset()&obj.WRAPPER != 0 { - // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame - // - // MOVQ g_panic(CX), BX - // TESTQ BX, BX - // JEQ end - // LEAQ (autoffset+8)(SP), DI - // CMPQ panic_argp(BX), DI - // JNE end - // MOVQ SP, panic_argp(BX) - // end: - // NOP - // - // The NOP is needed to give the jumps somewhere to land. - // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. - - p = obj.Appendp(ctxt, p) - - p.As = AMOVQ - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_CX - p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_BX - if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { - p.As = AMOVL - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_R15 - p.From.Scale = 1 - p.From.Index = REG_CX - } - if p.Mode == 32 { - p.As = AMOVL - } - - p = obj.Appendp(ctxt, p) - p.As = ATESTQ - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_BX - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_BX - if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { - p.As = ATESTL - } - - p = obj.Appendp(ctxt, p) - p.As = AJEQ - p.To.Type = obj.TYPE_BRANCH - p1 := p - - p = obj.Appendp(ctxt, p) - p.As = ALEAQ - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_SP - p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_DI - if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { - p.As = ALEAL - } - - p = obj.Appendp(ctxt, p) - p.As = ACMPQ - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_BX - p.From.Offset = 0 // Panic.argp - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_DI - if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { - p.As = ACMPL - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_R15 - p.From.Scale = 1 - p.From.Index = REG_BX - } - if p.Mode == 32 { - p.As = ACMPL - } - - p = obj.Appendp(ctxt, p) - p.As = AJNE - p.To.Type = obj.TYPE_BRANCH - p2 := p - - p = obj.Appendp(ctxt, p) - p.As = AMOVQ - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_SP - p.To.Type = obj.TYPE_MEM - p.To.Reg = REG_BX - p.To.Offset = 0 // Panic.argp - if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { - p.As = AMOVL - p.To.Type = obj.TYPE_MEM - p.To.Reg = REG_R15 - p.To.Scale = 1 - p.To.Index = REG_BX - } - if p.Mode == 32 { - p.As = AMOVL - } - - p = obj.Appendp(ctxt, p) - p.As = obj.ANOP - p1.Pcond = p - p2.Pcond = p - } - - for ; p != nil; p = p.Link { - pcsize := int(p.Mode) / 8 - switch p.From.Name { - case obj.NAME_AUTO: - p.From.Offset += int64(deltasp) - int64(bpsize) - case obj.NAME_PARAM: - p.From.Offset += int64(deltasp) + int64(pcsize) - } - if p.From3 != nil { - switch p.From3.Name { - case obj.NAME_AUTO: - p.From3.Offset += int64(deltasp) - int64(bpsize) - case obj.NAME_PARAM: - p.From3.Offset += int64(deltasp) + int64(pcsize) - } - } - switch p.To.Name { - case obj.NAME_AUTO: - p.To.Offset += int64(deltasp) - int64(bpsize) - case obj.NAME_PARAM: - p.To.Offset += int64(deltasp) + int64(pcsize) - } - - switch p.As { - default: - continue - - case APUSHL, APUSHFL: - deltasp += 4 - p.Spadj = 4 - continue - - case APUSHQ, APUSHFQ: - deltasp += 8 - p.Spadj = 8 - continue - - case APUSHW, APUSHFW: - deltasp += 2 - p.Spadj = 2 - continue - - case APOPL, APOPFL: - deltasp -= 4 - p.Spadj = -4 - continue - - case APOPQ, APOPFQ: - deltasp -= 8 - p.Spadj = -8 - continue - - case APOPW, APOPFW: - deltasp -= 2 - p.Spadj = -2 - continue - - case obj.ARET: - // do nothing - } - - if autoffset != deltasp { - ctxt.Diag("unbalanced PUSH/POP") - } - - if autoffset != 0 { - if bpsize > 0 { - // Restore caller's BP - p.As = AMOVQ - - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_SP - p.From.Scale = 1 - p.From.Offset = int64(autoffset) - int64(bpsize) - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_BP - p = obj.Appendp(ctxt, p) - } - - p.As = AADJSP - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-autoffset) - p.Spadj = -autoffset - p = obj.Appendp(ctxt, p) - p.As = obj.ARET - - // If there are instructions following - // this ARET, they come from a branch - // with the same stackframe, so undo - // the cleanup. - p.Spadj = +autoffset - } - - if p.To.Sym != nil { // retjmp - p.As = obj.AJMP - } - } -} - -func isZeroArgRuntimeCall(s *obj.LSym) bool { - if s == nil { - return false - } - switch s.Name { - case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide": - return true - } - 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 -} - -// Append code to p to load g into cx. -// Overwrites p with the first instruction (no first appendp). -// Overwriting p is unusual but it lets use this in both the -// prologue (caller must call appendp first) and in the epilogue. -// Returns last new instruction. -func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { - p.As = AMOVQ - if ctxt.Arch.PtrSize == 4 { - p.As = AMOVL - } - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_TLS - p.From.Offset = 0 - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_CX - - next := p.Link - progedit(ctxt, p) - for p.Link != next { - p = p.Link - } - - if p.From.Index == REG_TLS { - p.From.Scale = 2 - } - - return p -} - -// Append code to p to check for stack split. -// Appends to (does not overwrite) p. -// Assumes g is in CX. -// Returns last new instruction. -func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { - cmp := ACMPQ - lea := ALEAQ - mov := AMOVQ - sub := ASUBQ - - if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { - cmp = ACMPL - lea = ALEAL - mov = AMOVL - sub = ASUBL - } - - var q1 *obj.Prog - if framesize <= obj.StackSmall { - // small stack: SP <= stackguard - // CMPQ SP, stackguard - p = obj.Appendp(ctxt, p) - - p.As = cmp - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_SP - indir_cx(ctxt, p, &p.To) - p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 - if ctxt.Cursym.CFunc() { - p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 - } - } else if framesize <= obj.StackBig { - // large stack: SP-framesize <= stackguard-StackSmall - // LEAQ -xxx(SP), AX - // CMPQ AX, stackguard - p = obj.Appendp(ctxt, p) - - p.As = lea - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_SP - p.From.Offset = -(int64(framesize) - obj.StackSmall) - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_AX - - p = obj.Appendp(ctxt, p) - p.As = cmp - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_AX - indir_cx(ctxt, p, &p.To) - p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 - if ctxt.Cursym.CFunc() { - p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 - } - } else { - // Such a large stack we need to protect against wraparound. - // If SP is close to zero: - // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) - // The +StackGuard on both sides is required to keep the left side positive: - // SP is allowed to be slightly below stackguard. See stack.h. - // - // Preemption sets stackguard to StackPreempt, a very large value. - // That breaks the math above, so we have to check for that explicitly. - // MOVQ stackguard, CX - // CMPQ CX, $StackPreempt - // JEQ label-of-call-to-morestack - // LEAQ StackGuard(SP), AX - // SUBQ CX, AX - // CMPQ AX, $(framesize+(StackGuard-StackSmall)) - - p = obj.Appendp(ctxt, p) - - p.As = mov - indir_cx(ctxt, p, &p.From) - p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 - if ctxt.Cursym.CFunc() { - p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 - } - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_SI - - p = obj.Appendp(ctxt, p) - p.As = cmp - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_SI - p.To.Type = obj.TYPE_CONST - p.To.Offset = obj.StackPreempt - if p.Mode == 32 { - p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) - } - - p = obj.Appendp(ctxt, p) - p.As = AJEQ - p.To.Type = obj.TYPE_BRANCH - q1 = p - - p = obj.Appendp(ctxt, p) - p.As = lea - p.From.Type = obj.TYPE_MEM - p.From.Reg = REG_SP - p.From.Offset = obj.StackGuard - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_AX - - p = obj.Appendp(ctxt, p) - p.As = sub - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_SI - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_AX - - p = obj.Appendp(ctxt, p) - p.As = cmp - p.From.Type = obj.TYPE_REG - p.From.Reg = REG_AX - p.To.Type = obj.TYPE_CONST - p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) - } - - // common - jls := obj.Appendp(ctxt, p) - jls.As = AJLS - jls.To.Type = obj.TYPE_BRANCH - - var last *obj.Prog - for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { - } - - // Now we are at the end of the function, but logically - // we are still in function prologue. We need to fix the - // SP data and PCDATA. - spfix := obj.Appendp(ctxt, last) - spfix.As = obj.ANOP - spfix.Spadj = -framesize - - pcdata := obj.Appendp(ctxt, spfix) - pcdata.Lineno = ctxt.Cursym.Text.Lineno - pcdata.Mode = ctxt.Cursym.Text.Mode - pcdata.As = obj.APCDATA - pcdata.From.Type = obj.TYPE_CONST - pcdata.From.Offset = obj.PCDATA_StackMapIndex - pcdata.To.Type = obj.TYPE_CONST - pcdata.To.Offset = -1 // pcdata starts at -1 at function entry - - call := obj.Appendp(ctxt, pcdata) - call.Lineno = ctxt.Cursym.Text.Lineno - call.Mode = ctxt.Cursym.Text.Mode - call.As = obj.ACALL - call.To.Type = obj.TYPE_BRANCH - call.To.Name = obj.NAME_EXTERN - morestack := "runtime.morestack" - switch { - case ctxt.Cursym.CFunc(): - morestack = "runtime.morestackc" - case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: - morestack = "runtime.morestack_noctxt" - } - call.To.Sym = obj.Linklookup(ctxt, morestack, 0) - // When compiling 386 code for dynamic linking, the call needs to be adjusted - // to follow PIC rules. This in turn can insert more instructions, so we need - // to keep track of the start of the call (where the jump will be to) and the - // end (which following instructions are appended to). - callend := call - progedit(ctxt, callend) - for ; callend.Link != nil; callend = callend.Link { - progedit(ctxt, callend.Link) - } - - jmp := obj.Appendp(ctxt, callend) - jmp.As = obj.AJMP - jmp.To.Type = obj.TYPE_BRANCH - jmp.Pcond = ctxt.Cursym.Text.Link - jmp.Spadj = +framesize - - jls.Pcond = call - if q1 != nil { - q1.Pcond = call - } - - return jls -} - -func follow(ctxt *obj.Link, s *obj.LSym) { - ctxt.Cursym = s - - firstp := ctxt.NewProg() - lastp := firstp - xfol(ctxt, s.Text, &lastp) - lastp.Link = nil - s.Text = firstp.Link -} - -func nofollow(a obj.As) bool { - switch a { - case obj.AJMP, - obj.ARET, - AIRETL, - AIRETQ, - AIRETW, - ARETFL, - ARETFQ, - ARETFW, - obj.AUNDEF: - return true - } - - return false -} - -func pushpop(a obj.As) bool { - switch a { - case APUSHL, - APUSHFL, - APUSHQ, - APUSHFQ, - APUSHW, - APUSHFW, - APOPL, - APOPFL, - APOPQ, - APOPFQ, - APOPW, - APOPFW: - return true - } - - return false -} - -func relinv(a obj.As) obj.As { - switch a { - case AJEQ: - return AJNE - case AJNE: - return AJEQ - case AJLE: - return AJGT - case AJLS: - return AJHI - case AJLT: - return AJGE - case AJMI: - return AJPL - case AJGE: - return AJLT - case AJPL: - return AJMI - case AJGT: - return AJLE - case AJHI: - return AJLS - case AJCS: - return AJCC - case AJCC: - return AJCS - case AJPS: - return AJPC - case AJPC: - return AJPS - case AJOS: - return AJOC - case AJOC: - return AJOS - } - - log.Fatalf("unknown relation: %s", a) - return 0 -} - -func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { - var q *obj.Prog - var i int - var a obj.As - -loop: - if p == nil { - return - } - if p.As == obj.AJMP { - q = p.Pcond - if q != nil && q.As != obj.ATEXT { - /* mark instruction as done and continue layout at target of jump */ - p.Mark |= DONE - - p = q - if p.Mark&DONE == 0 { - goto loop - } - } - } - - if p.Mark&DONE != 0 { - /* - * p goes here, but already used it elsewhere. - * copy up to 4 instructions or else branch to other copy. - */ - i = 0 - q = p - for ; i < 4; i, q = i+1, q.Link { - if q == nil { - break - } - if q == *last { - break - } - a = q.As - if a == obj.ANOP { - i-- - continue - } - - if nofollow(a) || pushpop(a) { - break // NOTE(rsc): arm does goto copy - } - if q.Pcond == nil || q.Pcond.Mark&DONE != 0 { - continue - } - if a == obj.ACALL || a == ALOOP { - continue - } - for { - if p.As == obj.ANOP { - p = p.Link - continue - } - - q = obj.Copyp(ctxt, p) - p = p.Link - q.Mark |= DONE - (*last).Link = q - *last = q - if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 { - continue - } - - q.As = relinv(q.As) - p = q.Pcond - q.Pcond = q.Link - q.Link = p - xfol(ctxt, q.Link, last) - p = q.Link - if p.Mark&DONE != 0 { - return - } - goto loop - /* */ - } - } - q = ctxt.NewProg() - q.As = obj.AJMP - q.Lineno = p.Lineno - q.To.Type = obj.TYPE_BRANCH - q.To.Offset = p.Pc - q.Pcond = p - p = q - } - - /* emit p */ - p.Mark |= DONE - - (*last).Link = p - *last = p - a = p.As - - /* continue loop with what comes after p */ - if nofollow(a) { - return - } - if p.Pcond != nil && a != obj.ACALL { - /* - * some kind of conditional branch. - * recurse to follow one path. - * continue loop on the other. - */ - q = obj.Brchain(ctxt, p.Pcond) - if q != nil { - p.Pcond = q - } - q = obj.Brchain(ctxt, p.Link) - if q != nil { - p.Link = q - } - if p.From.Type == obj.TYPE_CONST { - if p.From.Offset == 1 { - /* - * expect conditional jump to be taken. - * rewrite so that's the fall-through case. - */ - p.As = relinv(a) - - q = p.Link - p.Link = p.Pcond - p.Pcond = q - } - } else { - q = p.Link - if q.Mark&DONE != 0 { - if a != ALOOP { - p.As = relinv(a) - p.Link = p.Pcond - p.Pcond = q - } - } - } - - xfol(ctxt, p.Link, last) - if p.Pcond.Mark&DONE != 0 { - return - } - p = p.Pcond - goto loop - } - - p = p.Link - goto loop -} - -var unaryDst = map[obj.As]bool{ - ABSWAPL: true, - ABSWAPQ: true, - ACMPXCHG8B: true, - ADECB: true, - ADECL: true, - ADECQ: true, - ADECW: true, - AINCB: true, - AINCL: true, - AINCQ: true, - AINCW: true, - ANEGB: true, - ANEGL: true, - ANEGQ: true, - ANEGW: true, - ANOTB: true, - ANOTL: true, - ANOTQ: true, - ANOTW: true, - APOPL: true, - APOPQ: true, - APOPW: true, - ASETCC: true, - ASETCS: true, - ASETEQ: true, - ASETGE: true, - ASETGT: true, - ASETHI: true, - ASETLE: true, - ASETLS: true, - ASETLT: true, - ASETMI: true, - ASETNE: true, - ASETOC: true, - ASETOS: true, - ASETPC: true, - ASETPL: true, - ASETPS: true, - AFFREE: true, - AFLDENV: true, - AFSAVE: true, - AFSTCW: true, - AFSTENV: true, - AFSTSW: true, - AFXSAVE: true, - AFXSAVE64: true, - ASTMXCSR: true, -} - -var Linkamd64 = obj.LinkArch{ - Arch: sys.ArchAMD64, - Preprocess: preprocess, - Assemble: span6, - Follow: follow, - Progedit: progedit, - UnaryDst: unaryDst, -} - -var Linkamd64p32 = obj.LinkArch{ - Arch: sys.ArchAMD64P32, - Preprocess: preprocess, - Assemble: span6, - Follow: follow, - Progedit: progedit, - UnaryDst: unaryDst, -} - -var Link386 = obj.LinkArch{ - Arch: sys.Arch386, - Preprocess: preprocess, - Assemble: span6, - Follow: follow, - Progedit: progedit, - UnaryDst: unaryDst, -} |