1// Inferno's libkern/vlop-arm.s 2// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/vlop-arm.s 3// 4// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. 6// Portions Copyright 2009 The Go Authors. All rights reserved. 7// 8// Permission is hereby granted, free of charge, to any person obtaining a copy 9// of this software and associated documentation files (the "Software"), to deal 10// in the Software without restriction, including without limitation the rights 11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12// copies of the Software, and to permit persons to whom the Software is 13// furnished to do so, subject to the following conditions: 14// 15// The above copyright notice and this permission notice shall be included in 16// all copies or substantial portions of the Software. 17// 18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24// THE SOFTWARE. 25 26#include "go_asm.h" 27#include "go_tls.h" 28#include "funcdata.h" 29#include "textflag.h" 30 31// trampoline for _sfloat2. passes LR as arg0 and 32// saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can 33// be changed by _sfloat2. 34TEXT runtime·_sfloat(SB), NOSPLIT, $68-0 // 4 arg + 14*4 saved regs + cpsr + return value 35 MOVW R14, 4(R13) 36 MOVW R0, 8(R13) 37 MOVW $12(R13), R0 38 MOVM.IA.W [R1-R12], (R0) 39 MOVW $72(R13), R1 // correct for frame size 40 MOVW R1, 60(R13) 41 WORD $0xe10f1000 // mrs r1, cpsr 42 MOVW R1, 64(R13) 43 // Disable preemption of this goroutine during _sfloat2 by 44 // m->locks++ and m->locks-- around the call. 45 // Rescheduling this goroutine may cause the loss of the 46 // contents of the software floating point registers in 47 // m->freghi, m->freglo, m->fflag, if the goroutine is moved 48 // to a different m or another goroutine runs on this m. 49 // Rescheduling at ordinary function calls is okay because 50 // all registers are caller save, but _sfloat2 and the things 51 // that it runs are simulating the execution of individual 52 // program instructions, and those instructions do not expect 53 // the floating point registers to be lost. 54 // An alternative would be to move the software floating point 55 // registers into G, but they do not need to be kept at the 56 // usual places a goroutine reschedules (at function calls), 57 // so it would be a waste of 132 bytes per G. 58 MOVW g_m(g), R8 59 MOVW m_locks(R8), R1 60 ADD $1, R1 61 MOVW R1, m_locks(R8) 62 MOVW $1, R1 63 MOVW R1, m_softfloat(R8) 64 BL runtime·_sfloat2(SB) 65 MOVW 68(R13), R0 66 MOVW g_m(g), R8 67 MOVW m_locks(R8), R1 68 SUB $1, R1 69 MOVW R1, m_locks(R8) 70 MOVW $0, R1 71 MOVW R1, m_softfloat(R8) 72 MOVW R0, 0(R13) 73 MOVW 64(R13), R1 74 WORD $0xe128f001 // msr cpsr_f, r1 75 MOVW $12(R13), R0 76 // Restore R1-R12, R0. 77 MOVM.IA.W (R0), [R1-R12] 78 MOVW 8(R13), R0 79 RET 80 81// trampoline for _sfloat2 panic. 82// _sfloat2 instructs _sfloat to return here. 83// We need to push a fake saved LR onto the stack, 84// load the signal fault address into LR, and jump 85// to the real sigpanic. 86// This simulates what sighandler does for a memory fault. 87TEXT runtime·_sfloatpanic(SB),NOSPLIT,$-4 88 MOVW $0, R0 89 MOVW.W R0, -4(R13) 90 MOVW g_sigpc(g), LR 91 B runtime·sigpanic(SB) 92 93// func runtime·udiv(n, d uint32) (q, r uint32) 94// compiler knowns the register usage of this function 95// Reference: 96// Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software 97// Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740 98#define Rq R0 // input d, output q 99#define Rr R1 // input n, output r 100#define Rs R2 // three temporary variables 101#define RM R3 102#define Ra R11 103 104// Be careful: Ra == R11 will be used by the linker for synthesized instructions. 105TEXT runtime·udiv(SB),NOSPLIT,$-4 106 MOVBU runtime·hardDiv(SB), Ra 107 CMP $0, Ra 108 BNE udiv_hardware 109 110 CLZ Rq, Rs // find normalizing shift 111 MOVW.S Rq<<Rs, Ra 112 MOVW $fast_udiv_tab<>-64(SB), RM 113 ADD.NE Ra>>25, RM, Ra // index by most significant 7 bits of divisor 114 MOVBU.NE (Ra), Ra 115 116 SUB.S $7, Rs 117 RSB $0, Rq, RM // M = -q 118 MOVW.PL Ra<<Rs, Rq 119 120 // 1st Newton iteration 121 MUL.PL RM, Rq, Ra // a = -q*d 122 BMI udiv_by_large_d 123 MULAWT Ra, Rq, Rq, Rq // q approx q-(q*q*d>>32) 124 TEQ RM->1, RM // check for d=0 or d=1 125 126 // 2nd Newton iteration 127 MUL.NE RM, Rq, Ra 128 MOVW.NE $0, Rs 129 MULAL.NE Rq, Ra, (Rq,Rs) 130 BEQ udiv_by_0_or_1 131 132 // q now accurate enough for a remainder r, 0<=r<3*d 133 MULLU Rq, Rr, (Rq,Rs) // q = (r * q) >> 32 134 ADD RM, Rr, Rr // r = n - d 135 MULA RM, Rq, Rr, Rr // r = n - (q+1)*d 136 137 // since 0 <= n-q*d < 3*d; thus -d <= r < 2*d 138 CMN RM, Rr // t = r-d 139 SUB.CS RM, Rr, Rr // if (t<-d || t>=0) r=r+d 140 ADD.CC $1, Rq 141 ADD.PL RM<<1, Rr 142 ADD.PL $2, Rq 143 RET 144 145// use hardware divider 146udiv_hardware: 147 DIVUHW Rq, Rr, Rs 148 MUL Rs, Rq, RM 149 RSB Rr, RM, Rr 150 MOVW Rs, Rq 151 RET 152 153udiv_by_large_d: 154 // at this point we know d>=2^(31-6)=2^25 155 SUB $4, Ra, Ra 156 RSB $0, Rs, Rs 157 MOVW Ra>>Rs, Rq 158 MULLU Rq, Rr, (Rq,Rs) 159 MULA RM, Rq, Rr, Rr 160 161 // q now accurate enough for a remainder r, 0<=r<4*d 162 CMN Rr>>1, RM // if(r/2 >= d) 163 ADD.CS RM<<1, Rr 164 ADD.CS $2, Rq 165 CMN Rr, RM 166 ADD.CS RM, Rr 167 ADD.CS $1, Rq 168 RET 169 170udiv_by_0_or_1: 171 // carry set if d==1, carry clear if d==0 172 BCC udiv_by_0 173 MOVW Rr, Rq 174 MOVW $0, Rr 175 RET 176 177udiv_by_0: 178 MOVW $runtime·panicdivide(SB), R11 179 B (R11) 180 181// var tab [64]byte 182// tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) } 183// laid out here as little-endian uint32s 184DATA fast_udiv_tab<>+0x00(SB)/4, $0xf4f8fcff 185DATA fast_udiv_tab<>+0x04(SB)/4, $0xe6eaedf0 186DATA fast_udiv_tab<>+0x08(SB)/4, $0xdadde0e3 187DATA fast_udiv_tab<>+0x0c(SB)/4, $0xcfd2d4d7 188DATA fast_udiv_tab<>+0x10(SB)/4, $0xc5c7cacc 189DATA fast_udiv_tab<>+0x14(SB)/4, $0xbcbec0c3 190DATA fast_udiv_tab<>+0x18(SB)/4, $0xb4b6b8ba 191DATA fast_udiv_tab<>+0x1c(SB)/4, $0xacaeb0b2 192DATA fast_udiv_tab<>+0x20(SB)/4, $0xa5a7a8aa 193DATA fast_udiv_tab<>+0x24(SB)/4, $0x9fa0a2a3 194DATA fast_udiv_tab<>+0x28(SB)/4, $0x999a9c9d 195DATA fast_udiv_tab<>+0x2c(SB)/4, $0x93949697 196DATA fast_udiv_tab<>+0x30(SB)/4, $0x8e8f9092 197DATA fast_udiv_tab<>+0x34(SB)/4, $0x898a8c8d 198DATA fast_udiv_tab<>+0x38(SB)/4, $0x85868788 199DATA fast_udiv_tab<>+0x3c(SB)/4, $0x81828384 200GLOBL fast_udiv_tab<>(SB), RODATA, $64 201 202// The linker will pass numerator in R8 203#define Rn R8 204// The linker expects the result in RTMP 205#define RTMP R11 206 207TEXT runtime·_divu(SB), NOSPLIT, $16-0 208 // It's not strictly true that there are no local pointers. 209 // It could be that the saved registers Rq, Rr, Rs, and Rm 210 // contain pointers. However, the only way this can matter 211 // is if the stack grows (which it can't, udiv is nosplit) 212 // or if a fault happens and more frames are added to 213 // the stack due to deferred functions. 214 // In the latter case, the stack can grow arbitrarily, 215 // and garbage collection can happen, and those 216 // operations care about pointers, but in that case 217 // the calling frame is dead, and so are the saved 218 // registers. So we can claim there are no pointers here. 219 NO_LOCAL_POINTERS 220 MOVW Rq, 4(R13) 221 MOVW Rr, 8(R13) 222 MOVW Rs, 12(R13) 223 MOVW RM, 16(R13) 224 225 MOVW Rn, Rr /* numerator */ 226 MOVW g_m(g), Rq 227 MOVW m_divmod(Rq), Rq /* denominator */ 228 BL runtime·udiv(SB) 229 MOVW Rq, RTMP 230 MOVW 4(R13), Rq 231 MOVW 8(R13), Rr 232 MOVW 12(R13), Rs 233 MOVW 16(R13), RM 234 RET 235 236TEXT runtime·_modu(SB), NOSPLIT, $16-0 237 NO_LOCAL_POINTERS 238 MOVW Rq, 4(R13) 239 MOVW Rr, 8(R13) 240 MOVW Rs, 12(R13) 241 MOVW RM, 16(R13) 242 243 MOVW Rn, Rr /* numerator */ 244 MOVW g_m(g), Rq 245 MOVW m_divmod(Rq), Rq /* denominator */ 246 BL runtime·udiv(SB) 247 MOVW Rr, RTMP 248 MOVW 4(R13), Rq 249 MOVW 8(R13), Rr 250 MOVW 12(R13), Rs 251 MOVW 16(R13), RM 252 RET 253 254TEXT runtime·_div(SB),NOSPLIT,$16-0 255 NO_LOCAL_POINTERS 256 MOVW Rq, 4(R13) 257 MOVW Rr, 8(R13) 258 MOVW Rs, 12(R13) 259 MOVW RM, 16(R13) 260 MOVW Rn, Rr /* numerator */ 261 MOVW g_m(g), Rq 262 MOVW m_divmod(Rq), Rq /* denominator */ 263 CMP $0, Rr 264 BGE d1 265 RSB $0, Rr, Rr 266 CMP $0, Rq 267 BGE d2 268 RSB $0, Rq, Rq 269d0: 270 BL runtime·udiv(SB) /* none/both neg */ 271 MOVW Rq, RTMP 272 B out1 273d1: 274 CMP $0, Rq 275 BGE d0 276 RSB $0, Rq, Rq 277d2: 278 BL runtime·udiv(SB) /* one neg */ 279 RSB $0, Rq, RTMP 280out1: 281 MOVW 4(R13), Rq 282 MOVW 8(R13), Rr 283 MOVW 12(R13), Rs 284 MOVW 16(R13), RM 285 RET 286 287TEXT runtime·_mod(SB),NOSPLIT,$16-0 288 NO_LOCAL_POINTERS 289 MOVW Rq, 4(R13) 290 MOVW Rr, 8(R13) 291 MOVW Rs, 12(R13) 292 MOVW RM, 16(R13) 293 MOVW Rn, Rr /* numerator */ 294 MOVW g_m(g), Rq 295 MOVW m_divmod(Rq), Rq /* denominator */ 296 CMP $0, Rq 297 RSB.LT $0, Rq, Rq 298 CMP $0, Rr 299 BGE m1 300 RSB $0, Rr, Rr 301 BL runtime·udiv(SB) /* neg numerator */ 302 RSB $0, Rr, RTMP 303 B out 304m1: 305 BL runtime·udiv(SB) /* pos numerator */ 306 MOVW Rr, RTMP 307out: 308 MOVW 4(R13), Rq 309 MOVW 8(R13), Rr 310 MOVW 12(R13), Rs 311 MOVW 16(R13), RM 312 RET 313 314// _mul64by32 and _div64by32 not implemented on arm 315TEXT runtime·_mul64by32(SB), NOSPLIT, $0 316 MOVW $0, R0 317 MOVW (R0), R1 // crash 318 319TEXT runtime·_div64by32(SB), NOSPLIT, $0 320 MOVW $0, R0 321 MOVW (R0), R1 // crash 322