| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package x86 |
| |
| import ( |
| "cmd/compile/internal/gc" |
| "cmd/internal/obj" |
| "cmd/internal/obj/x86" |
| ) |
| |
| /* |
| * generate an addressable node in res, containing the value of n. |
| * n is an array index, and might be any size; res width is <= 32-bit. |
| * returns Prog* to patch to panic call. |
| */ |
| func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { |
| if !gc.Is64(n.Type) { |
| if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) { |
| // nothing to do. |
| *res = *n |
| } else { |
| gc.Tempname(res, gc.Types[gc.TUINT32]) |
| gc.Cgen(n, res) |
| } |
| |
| return nil |
| } |
| |
| var tmp gc.Node |
| gc.Tempname(&tmp, gc.Types[gc.TINT64]) |
| gc.Cgen(n, &tmp) |
| var lo gc.Node |
| var hi gc.Node |
| split64(&tmp, &lo, &hi) |
| gc.Tempname(res, gc.Types[gc.TUINT32]) |
| gmove(&lo, res) |
| if bounded { |
| splitclean() |
| return nil |
| } |
| |
| var zero gc.Node |
| gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) |
| gins(x86.ACMPL, &hi, &zero) |
| splitclean() |
| return gc.Gbranch(x86.AJNE, nil, +1) |
| } |
| |
| func blockcopy(n, res *gc.Node, osrc, odst, w int64) { |
| var dst gc.Node |
| gc.Nodreg(&dst, gc.Types[gc.Tptr], x86.REG_DI) |
| var src gc.Node |
| gc.Nodreg(&src, gc.Types[gc.Tptr], x86.REG_SI) |
| |
| var tsrc gc.Node |
| gc.Tempname(&tsrc, gc.Types[gc.Tptr]) |
| var tdst gc.Node |
| gc.Tempname(&tdst, gc.Types[gc.Tptr]) |
| if !n.Addable { |
| gc.Agen(n, &tsrc) |
| } |
| if !res.Addable { |
| gc.Agen(res, &tdst) |
| } |
| if n.Addable { |
| gc.Agen(n, &src) |
| } else { |
| gmove(&tsrc, &src) |
| } |
| |
| if res.Op == gc.ONAME { |
| gc.Gvardef(res) |
| } |
| |
| if res.Addable { |
| gc.Agen(res, &dst) |
| } else { |
| gmove(&tdst, &dst) |
| } |
| |
| c := int32(w % 4) // bytes |
| q := int32(w / 4) // doublewords |
| |
| // if we are copying forward on the stack and |
| // the src and dst overlap, then reverse direction |
| if osrc < odst && odst < osrc+w { |
| // reverse direction |
| gins(x86.ASTD, nil, nil) // set direction flag |
| if c > 0 { |
| gconreg(x86.AADDL, w-1, x86.REG_SI) |
| gconreg(x86.AADDL, w-1, x86.REG_DI) |
| |
| gconreg(x86.AMOVL, int64(c), x86.REG_CX) |
| gins(x86.AREP, nil, nil) // repeat |
| gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- |
| } |
| |
| if q > 0 { |
| if c > 0 { |
| gconreg(x86.AADDL, -3, x86.REG_SI) |
| gconreg(x86.AADDL, -3, x86.REG_DI) |
| } else { |
| gconreg(x86.AADDL, w-4, x86.REG_SI) |
| gconreg(x86.AADDL, w-4, x86.REG_DI) |
| } |
| |
| gconreg(x86.AMOVL, int64(q), x86.REG_CX) |
| gins(x86.AREP, nil, nil) // repeat |
| gins(x86.AMOVSL, nil, nil) // MOVL *(SI)-,*(DI)- |
| } |
| |
| // we leave with the flag clear |
| gins(x86.ACLD, nil, nil) |
| } else { |
| gins(x86.ACLD, nil, nil) // paranoia. TODO(rsc): remove? |
| |
| // normal direction |
| if q > 128 || (q >= 4 && gc.Nacl) { |
| gconreg(x86.AMOVL, int64(q), x86.REG_CX) |
| gins(x86.AREP, nil, nil) // repeat |
| gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ |
| } else if q >= 4 { |
| p := gins(obj.ADUFFCOPY, nil, nil) |
| p.To.Type = obj.TYPE_ADDR |
| p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) |
| |
| // 10 and 128 = magic constants: see ../../runtime/asm_386.s |
| p.To.Offset = 10 * (128 - int64(q)) |
| } else if !gc.Nacl && c == 0 { |
| var cx gc.Node |
| gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) |
| |
| // We don't need the MOVSL side-effect of updating SI and DI, |
| // and issuing a sequence of MOVLs directly is faster. |
| src.Op = gc.OINDREG |
| |
| dst.Op = gc.OINDREG |
| for q > 0 { |
| gmove(&src, &cx) // MOVL x+(SI),CX |
| gmove(&cx, &dst) // MOVL CX,x+(DI) |
| src.Xoffset += 4 |
| dst.Xoffset += 4 |
| q-- |
| } |
| } else { |
| for q > 0 { |
| gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ |
| q-- |
| } |
| } |
| |
| for c > 0 { |
| gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ |
| c-- |
| } |
| } |
| } |