1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * mov.go 12 * 13 * Created on Feb 02, 2019 14 * Author Massimiliano Ghilardi 15 */ 16 17package arm64 18 19// ============================================================================ 20type loadstore uint32 21 22const ( 23 load loadstore = 0x39400000 24 store loadstore = 0x39000000 25) 26 27func (arch Arm64) load(asm *Asm, src Mem, dst Reg) Arm64 { 28 return arch.loadstore(asm, load, src, dst) 29} 30 31func (arch Arm64) store(asm *Asm, src Reg, dst Mem) Arm64 { 32 return arch.loadstore(asm, store, dst, src) 33} 34 35func (arch Arm64) mov(asm *Asm, src Arg, dst Arg) Arm64 { 36 assert(SizeOf(src) == SizeOf(dst)) 37 38 if dst.Const() { 39 errorf("destination cannot be a constant: %v %v, %v", MOV, src, dst) 40 } 41 if src == dst { 42 return arch 43 } 44 45 switch dst := dst.(type) { 46 case Reg: 47 switch src := src.(type) { 48 case Const: 49 arch.movConstReg(asm, src, dst) 50 case Reg: 51 if src.RegId() != dst.RegId() { 52 arch.movRegReg(asm, src, dst) 53 } 54 case Mem: 55 arch.load(asm, src, dst) 56 default: 57 errorf("unknown source type %T, expecting Const, Reg or Mem: %v %v, %v", src, MOV, src, dst) 58 } 59 case Mem: 60 switch src := src.(type) { 61 case Const: 62 arch.movConstMem(asm, src, dst) 63 case Reg: 64 arch.store(asm, src, dst) 65 case Mem: 66 arch.movMemMem(asm, src, dst) 67 default: 68 errorf("unknown source type %T, expecting Const, Reg or Mem: %v %v, %v", src, MOV, src, dst) 69 } 70 default: 71 errorf("unknown destination type %T, expecting Reg or Mem: %v %v, %v", dst, MOV, src, dst) 72 } 73 return arch 74} 75 76func (arch Arm64) movRegReg(asm *Asm, src Reg, dst Reg) Arm64 { 77 // arm64 implements "mov src,dst" as "orr xzr,src,dst" 78 asm.Uint32(kbit(dst) | 0x2A0003E0 | valOrX31(src.RegId(), true)<<16 | val(dst)) 79 return arch 80} 81 82func (arch Arm64) movConstReg(asm *Asm, c Const, dst Reg) Arm64 { 83 cval := c.Val() 84 xzr := MakeReg(XZR, dst.Kind()) 85 var immcval uint32 86 var movk bool 87 if cval >= 0 && cval < 0x10000 { 88 immcval = 0x40<<19 | uint32(cval) 89 } else if cval < 0 && cval >= -0x10000 { 90 immcval = uint32(^cval) 91 } else if arch.tryOp3RegConstReg(asm, OR3, xzr, uint64(cval), dst) { 92 return arch 93 } else if arch.tryOp3RegConstReg(asm, OR3, xzr, uint64(uint32(cval)), dst) { 94 if dst.Kind().Size() == 8 { 95 arch.movk(asm, uint16(cval>>32), 32, dst) 96 arch.movk(asm, uint16(cval>>48), 48, dst) 97 } 98 return arch 99 } else { 100 immcval = 0x40<<19 | uint32(cval&0xFFFF) 101 movk = true 102 } 103 asm.Uint32(kbit(dst) | 0x12800000 | immcval<<5 | val(dst)) 104 if movk { 105 arch.movk(asm, uint16(cval>>16), 16, dst) 106 if dst.Kind().Size() == 8 { 107 arch.movk(asm, uint16(cval>>32), 32, dst) 108 arch.movk(asm, uint16(cval>>48), 48, dst) 109 } 110 } 111 return arch 112} 113 114// set some bits of dst, preserving others 115func (arch Arm64) movk(asm *Asm, cval uint16, shift uint8, dst Reg) Arm64 { 116 if cval != 0 { 117 asm.Uint32(kbit(dst) | 0xF2800000 | uint32(shift)<<17 | uint32(cval)<<5 | val(dst)) 118 } 119 return arch 120} 121 122func (arch Arm64) loadstore(asm *Asm, op loadstore, m Mem, r Reg) Arm64 { 123 assert(SizeOf(m) == SizeOf(r)) 124 off := m.Offset() 125 var sizebit uint32 126 mrid := m.RegId() 127 rid := r.RegId() 128 129 switch m.Kind().Size() { 130 case 1: 131 sizebit = 0 132 if off >= 0 && off <= 4095 { 133 asm.Uint32(sizebit | uint32(op) | uint32(off)<<10 | valOrX31(mrid, true)<<5 | valOrX31(rid, true)) 134 return arch 135 } 136 case 2: 137 sizebit = 0x4 << 28 138 if off >= 0 && off <= 8190 && off%2 == 0 { 139 asm.Uint32(sizebit | uint32(op) | uint32(off)<<9 | valOrX31(mrid, true)<<5 | valOrX31(rid, true)) 140 return arch 141 } 142 case 4: 143 sizebit = 0x8 << 28 144 if off >= 0 && off <= 16380 && off%4 == 0 { 145 asm.Uint32(sizebit | uint32(op) | uint32(off)<<8 | valOrX31(mrid, true)<<5 | valOrX31(rid, true)) 146 return arch 147 } 148 case 8: 149 sizebit = 0xC << 28 150 if off >= 0 && off <= 32760 && off%8 == 0 { 151 asm.Uint32(sizebit | uint32(op) | uint32(off)<<7 | valOrX31(mrid, true)<<5 | valOrX31(rid, true)) 152 return arch 153 } 154 } 155 // load offset in a register. we could also try "ldur" or "stur"... 156 tmp := asm.RegAlloc(Uint64) 157 arch.movConstReg(asm, ConstInt64(int64(off)), tmp) 158 159 asm.Uint32(sizebit | uint32(op^0x1206800) | val(tmp)<<16 | valOrX31(mrid, true)<<5 | val(r)) 160 161 asm.RegFree(tmp) 162 return arch 163} 164 165func (arch Arm64) movConstMem(asm *Asm, c Const, dst Mem) Arm64 { 166 if c.Val() == 0 { 167 return arch.zeroMem(asm, dst) 168 } 169 r := asm.RegAlloc(dst.Kind()) 170 arch.movConstReg(asm, c, r).store(asm, r, dst) 171 asm.RegFree(r) 172 return arch 173} 174 175func (arch Arm64) movMemMem(asm *Asm, src Mem, dst Mem) Arm64 { 176 r := asm.RegAlloc(src.Kind()) 177 arch.load(asm, src, r).store(asm, r, dst) 178 asm.RegFree(r) 179 return arch 180} 181 182// ============================================================================ 183func (arch Arm64) Cast(asm *Asm, src Arg, dst Arg) { 184 arch.Cast(asm, src, dst) 185} 186 187func (arch Arm64) cast(asm *Asm, src Arg, dst Arg) Arm64 { 188 if src == dst { 189 return arch 190 } else if SizeOf(src) == SizeOf(dst) { 191 return arch.mov(asm, src, dst) 192 } 193 194 switch dst := dst.(type) { 195 case Reg: 196 switch src := src.(type) { 197 case Reg: 198 arch.castRegReg(asm, src, dst) 199 case Mem: 200 arch.castMemReg(asm, src, dst) 201 case Const: 202 src = src.Cast(dst.Kind()) 203 arch.movConstReg(asm, src, dst) 204 default: 205 errorf("unsupported source type %T, expecting Const, Reg or Mem: %v %v %v", src, CAST, src, dst) 206 } 207 case Mem: 208 switch src := src.(type) { 209 case Reg: 210 arch.castRegMem(asm, src, dst) 211 case Mem: 212 arch.castMemMem(asm, src, dst) 213 case Const: 214 src = src.Cast(dst.Kind()) 215 arch.movConstMem(asm, src, dst) 216 default: 217 errorf("unsupported source type %T, expecting Const, Reg or Mem: %v %v %v", src, CAST, src, dst) 218 } 219 case Const: 220 errorf("destination cannot be a constant: %v %v %v", CAST, src, dst) 221 default: 222 errorf("unsupported destination type %T, expecting Reg or Mem: %v %v %v", dst, CAST, src, dst) 223 } 224 return arch 225} 226 227func (arch Arm64) castMemMem(asm *Asm, src Mem, dst Mem) Arm64 { 228 r1 := asm.RegAlloc(src.Kind()) 229 r2 := MakeReg(r1.RegId(), dst.Kind()) 230 arch.load(asm, src, r1).castRegReg(asm, r1, r2).store(asm, r2, dst) 231 asm.RegFree(r1) 232 return arch 233} 234 235func (arch Arm64) castMemReg(asm *Asm, src Mem, dst Reg) Arm64 { 236 r := MakeReg(dst.RegId(), src.Kind()) 237 return arch.load(asm, src, r).castRegReg(asm, r, dst) 238} 239 240func (arch Arm64) castRegMem(asm *Asm, src Reg, dst Mem) Arm64 { 241 r := MakeReg(src.RegId(), dst.Kind()) 242 if SizeOf(src) < SizeOf(dst) { 243 // extend src. we can safely overwrite its high bits: they are junk 244 return arch.castRegReg(asm, src, r).store(asm, r, dst) 245 } else { 246 // just ignore src high bits 247 return arch.store(asm, r, dst) 248 } 249} 250 251func (arch Arm64) castRegReg(asm *Asm, src Reg, dst Reg) Arm64 { 252 skind := src.Kind() 253 dkind := dst.Kind() 254 ssize := skind.Size() 255 dsize := dkind.Size() 256 if ssize >= dsize { 257 // truncate. easy, just ignore src high bits 258 return arch.mov(asm, MakeReg(src.RegId(), dst.Kind()), dst) 259 } else if skind.Signed() { 260 // sign-extend. use one of: 261 // "sxtb src, dst" 262 // "sxth src, dst" 263 // "sxtw src, dst" 264 kbit := uint32(dsize&8) * 0x10080000 265 op := 0x13000C00 | uint32(ssize*2-1)<<12 266 asm.Uint32(kbit | op | val(src)<<5 | val(dst)) 267 return arch 268 } else { 269 // zero-extend 270 if ssize == 4 { 271 // zero-extend 32 bit -> 64 bit: use 272 // "mov dst, src" 273 // must be kept even if src == dst to zero high bits, 274 // so use Asm.movRegReg() instead of too smart Asm.Mov() 275 return arch.movRegReg(asm, src, MakeReg(dst.RegId(), skind)) 276 } 277 // zero-extend, src is 8 bit or 16 bit. use one of: 278 // "and dst, src, #0xff" 279 // "and dst, src, #0xffff" 280 if dsize <= 4 { 281 dkind = Uint32 282 } 283 r := MakeReg(src.RegId(), dkind) 284 c := MakeConst(int64(0xffff)>>(16-ssize*8), dkind) 285 return arch.op3RegConstReg(asm, AND3, r, c, dst) 286 } 287} 288