1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package amd64 6 7 import ( 8 "cmd/compile/internal/gc" 9 "cmd/internal/obj" 10 "cmd/internal/obj/x86" 11 ) 12 13 // no floating point in note handlers on Plan 9 14 var isPlan9 = obj.GOOS == "plan9" 15 16 func defframe(ptxt *obj.Prog) { 17 // fill in argument size, stack size 18 ptxt.To.Type = obj.TYPE_TEXTSIZE 19 20 ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr))) 21 frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) 22 ptxt.To.Offset = int64(frame) 23 24 // insert code to zero ambiguously live variables 25 // so that the garbage collector only sees initialized values 26 // when it looks for pointers. 27 p := ptxt 28 29 hi := int64(0) 30 lo := hi 31 ax := uint32(0) 32 x0 := uint32(0) 33 34 // iterate through declarations - they are sorted in decreasing xoffset order. 35 for _, n := range gc.Curfn.Func.Dcl { 36 if !n.Name.Needzero { 37 continue 38 } 39 if n.Class != gc.PAUTO { 40 gc.Fatalf("needzero class %d", n.Class) 41 } 42 if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { 43 gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset)) 44 } 45 46 if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { 47 // merge with range we already have 48 lo = n.Xoffset 49 50 continue 51 } 52 53 // zero old range 54 p = zerorange(p, int64(frame), lo, hi, &ax, &x0) 55 56 // set new range 57 hi = n.Xoffset + n.Type.Width 58 59 lo = n.Xoffset 60 } 61 62 // zero final range 63 zerorange(p, int64(frame), lo, hi, &ax, &x0) 64 } 65 66 // DUFFZERO consists of repeated blocks of 4 MOVUPSs + ADD, 67 // See runtime/mkduff.go. 68 const ( 69 dzBlocks = 16 // number of MOV/ADD blocks 70 dzBlockLen = 4 // number of clears per block 71 dzBlockSize = 19 // size of instructions in a single block 72 dzMovSize = 4 // size of single MOV instruction w/ offset 73 dzAddSize = 4 // size of single ADD instruction 74 dzClearStep = 16 // number of bytes cleared by each MOV instruction 75 76 dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block 77 dzSize = dzBlocks * dzBlockSize 78 ) 79 80 // dzOff returns the offset for a jump into DUFFZERO. 81 // b is the number of bytes to zero. 82 func dzOff(b int64) int64 { 83 off := int64(dzSize) 84 off -= b / dzClearLen * dzBlockSize 85 tailLen := b % dzClearLen 86 if tailLen >= dzClearStep { 87 off -= dzAddSize + dzMovSize*(tailLen/dzClearStep) 88 } 89 return off 90 } 91 92 // duffzeroDI returns the pre-adjustment to DI for a call to DUFFZERO. 93 // b is the number of bytes to zero. 94 func dzDI(b int64) int64 { 95 tailLen := b % dzClearLen 96 if tailLen < dzClearStep { 97 return 0 98 } 99 tailSteps := tailLen / dzClearStep 100 return -dzClearStep * (dzBlockLen - tailSteps) 101 } 102 103 func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uint32) *obj.Prog { 104 cnt := hi - lo 105 if cnt == 0 { 106 return p 107 } 108 109 if cnt%int64(gc.Widthreg) != 0 { 110 // should only happen with nacl 111 if cnt%int64(gc.Widthptr) != 0 { 112 gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt) 113 } 114 if *ax == 0 { 115 p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) 116 *ax = 1 117 } 118 p = gc.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) 119 lo += int64(gc.Widthptr) 120 cnt -= int64(gc.Widthptr) 121 } 122 123 if cnt == 8 { 124 if *ax == 0 { 125 p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) 126 *ax = 1 127 } 128 p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) 129 } else if !isPlan9 && cnt <= int64(8*gc.Widthreg) { 130 if *x0 == 0 { 131 p = gc.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) 132 *x0 = 1 133 } 134 135 for i := int64(0); i < cnt/16; i++ { 136 p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i*16) 137 } 138 139 if cnt%16 != 0 { 140 p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16)) 141 } 142 } else if !gc.Nacl && !isPlan9 && (cnt <= int64(128*gc.Widthreg)) { 143 if *x0 == 0 { 144 p = gc.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0) 145 *x0 = 1 146 } 147 p = gc.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0) 148 p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt)) 149 p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) 150 151 if cnt%16 != 0 { 152 p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8)) 153 } 154 } else { 155 if *ax == 0 { 156 p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) 157 *ax = 1 158 } 159 160 p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) 161 p = gc.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) 162 p = gc.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) 163 p = gc.Appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) 164 } 165 166 return p 167 } 168 169 func ginsnop() { 170 // This is actually not the x86 NOP anymore, 171 // but at the point where it gets used, AX is dead 172 // so it's okay if we lose the high bits. 173 p := gc.Prog(x86.AXCHGL) 174 p.From.Type = obj.TYPE_REG 175 p.From.Reg = x86.REG_AX 176 p.To.Type = obj.TYPE_REG 177 p.To.Reg = x86.REG_AX 178 } 179