| 1 | /* |
| 2 | * S/390 integer helper routines |
| 3 | * |
| 4 | * Copyright (c) 2009 Ulrich Hecht |
| 5 | * Copyright (c) 2009 Alexander Graf |
| 6 | * |
| 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This library is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 19 | */ |
| 20 | |
| 21 | #include "qemu/osdep.h" |
| 22 | #include "cpu.h" |
| 23 | #include "s390x-internal.h" |
| 24 | #include "tcg_s390x.h" |
| 25 | #include "qemu/host-utils.h" |
| 26 | #include "exec/helper-proto.h" |
| 27 | #include "accel/tcg/cpu-ldst.h" |
| 28 | |
| 29 | /* #define DEBUG_HELPER */ |
| 30 | #ifdef DEBUG_HELPER |
| 31 | #define HELPER_LOG(x...) qemu_log(x) |
| 32 | #else |
| 33 | #define HELPER_LOG(x...) |
| 34 | #endif |
| 35 | |
| 36 | /* 64/32 -> 32 signed division */ |
| 37 | uint64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) |
| 38 | { |
| 39 | int32_t b = b64; |
| 40 | int64_t q, r; |
| 41 | |
| 42 | /* Catch divide by zero, and non-representable quotient (MIN / -1). */ |
| 43 | if (b == 0 || (b == -1 && a == (1ll << 63))) { |
| 44 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 45 | } |
| 46 | |
| 47 | q = a / b; |
| 48 | r = a % b; |
| 49 | |
| 50 | /* Catch non-representable quotient. */ |
| 51 | if (q != (int32_t)q) { |
| 52 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 53 | } |
| 54 | |
| 55 | return deposit64(q, 32, 32, r); |
| 56 | } |
| 57 | |
| 58 | /* 64/32 -> 32 unsigned division */ |
| 59 | uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) |
| 60 | { |
| 61 | uint32_t b = b64; |
| 62 | uint64_t q, r; |
| 63 | |
| 64 | if (b == 0) { |
| 65 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 66 | } |
| 67 | |
| 68 | q = a / b; |
| 69 | r = a % b; |
| 70 | |
| 71 | /* Catch non-representable quotient. */ |
| 72 | if (q != (uint32_t)q) { |
| 73 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 74 | } |
| 75 | |
| 76 | return deposit64(q, 32, 32, r); |
| 77 | } |
| 78 | |
| 79 | /* 64/64 -> 64 signed division */ |
| 80 | Int128 HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) |
| 81 | { |
| 82 | /* Catch divide by zero, and non-representable quotient (MIN / -1). */ |
| 83 | if (b == 0 || (b == -1 && a == (1ll << 63))) { |
| 84 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 85 | } |
| 86 | return int128_make128(a / b, a % b); |
| 87 | } |
| 88 | |
| 89 | /* 128 -> 64/64 unsigned division */ |
| 90 | Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t b) |
| 91 | { |
| 92 | if (b != 0) { |
| 93 | uint64_t r = divu128(&al, &ah, b); |
| 94 | if (ah == 0) { |
| 95 | return int128_make128(al, r); |
| 96 | } |
| 97 | } |
| 98 | /* divide by zero or overflow */ |
| 99 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 100 | } |
| 101 | |
| 102 | void HELPER(cvb)(CPUS390XState *env, uint32_t r1, uint64_t dec) |
| 103 | { |
| 104 | int64_t pow10 = 1, bin = 0; |
| 105 | int digit, sign; |
| 106 | |
| 107 | sign = dec & 0xf; |
| 108 | if (sign < 0xa) { |
| 109 | tcg_s390_data_exception(env, 0, GETPC()); |
| 110 | } |
| 111 | dec >>= 4; |
| 112 | |
| 113 | while (dec) { |
| 114 | digit = dec & 0xf; |
| 115 | if (digit > 0x9) { |
| 116 | tcg_s390_data_exception(env, 0, GETPC()); |
| 117 | } |
| 118 | dec >>= 4; |
| 119 | bin += digit * pow10; |
| 120 | pow10 *= 10; |
| 121 | } |
| 122 | |
| 123 | if (sign == 0xb || sign == 0xd) { |
| 124 | bin = -bin; |
| 125 | } |
| 126 | |
| 127 | /* R1 is updated even on fixed-point-divide exception. */ |
| 128 | env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (uint32_t)bin; |
| 129 | if (bin != (int32_t)bin) { |
| 130 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | uint64_t HELPER(cvbg)(CPUS390XState *env, Int128 dec) |
| 135 | { |
| 136 | uint64_t dec64[] = {int128_getlo(dec), int128_gethi(dec)}; |
| 137 | int64_t bin = 0, pow10, tmp; |
| 138 | int digit, i, sign; |
| 139 | |
| 140 | sign = dec64[0] & 0xf; |
| 141 | if (sign < 0xa) { |
| 142 | tcg_s390_data_exception(env, 0, GETPC()); |
| 143 | } |
| 144 | dec64[0] >>= 4; |
| 145 | pow10 = (sign == 0xb || sign == 0xd) ? -1 : 1; |
| 146 | |
| 147 | for (i = 1; i < 20; i++) { |
| 148 | digit = dec64[i >> 4] & 0xf; |
| 149 | if (digit > 0x9) { |
| 150 | tcg_s390_data_exception(env, 0, GETPC()); |
| 151 | } |
| 152 | dec64[i >> 4] >>= 4; |
| 153 | /* |
| 154 | * Prepend the next digit and check for overflow. The multiplication |
| 155 | * cannot overflow, since, conveniently, the int64_t limits are |
| 156 | * approximately +-9.2E+18. If bin is zero, the addition cannot |
| 157 | * overflow. Otherwise bin is known to have the same sign as the rhs |
| 158 | * addend, in which case overflow happens if and only if the result |
| 159 | * has a different sign. |
| 160 | */ |
| 161 | tmp = bin + pow10 * digit; |
| 162 | if (bin && ((tmp ^ bin) < 0)) { |
| 163 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 164 | } |
| 165 | bin = tmp; |
| 166 | pow10 *= 10; |
| 167 | } |
| 168 | |
| 169 | g_assert(!dec64[0]); |
| 170 | if (dec64[1]) { |
| 171 | tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); |
| 172 | } |
| 173 | |
| 174 | return bin; |
| 175 | } |
| 176 | |
| 177 | uint64_t HELPER(cvd)(int32_t reg) |
| 178 | { |
| 179 | /* positive 0 */ |
| 180 | uint64_t dec = 0x0c; |
| 181 | int64_t bin = reg; |
| 182 | int shift; |
| 183 | |
| 184 | if (bin < 0) { |
| 185 | bin = -bin; |
| 186 | dec = 0x0d; |
| 187 | } |
| 188 | |
| 189 | for (shift = 4; (shift < 64) && bin; shift += 4) { |
| 190 | dec |= (bin % 10) << shift; |
| 191 | bin /= 10; |
| 192 | } |
| 193 | |
| 194 | return dec; |
| 195 | } |
| 196 | |
| 197 | Int128 HELPER(cvdg)(int64_t reg) |
| 198 | { |
| 199 | /* positive 0 */ |
| 200 | Int128 dec = int128_make64(0x0c); |
| 201 | Int128 bin = int128_makes64(reg); |
| 202 | Int128 base = int128_make64(10); |
| 203 | int shift; |
| 204 | |
| 205 | if (!int128_nonneg(bin)) { |
| 206 | bin = int128_neg(bin); |
| 207 | dec = int128_make64(0x0d); |
| 208 | } |
| 209 | |
| 210 | for (shift = 4; (shift < 128) && int128_nz(bin); shift += 4) { |
| 211 | dec = int128_or(dec, int128_lshift(int128_remu(bin, base), shift)); |
| 212 | bin = int128_divu(bin, base); |
| 213 | } |
| 214 | |
| 215 | return dec; |
| 216 | } |
| 217 | |
| 218 | uint64_t HELPER(popcnt)(uint64_t val) |
| 219 | { |
| 220 | /* Note that we don't fold past bytes. */ |
| 221 | val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL); |
| 222 | val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); |
| 223 | val = (val + (val >> 4)) & 0x0f0f0f0f0f0f0f0fULL; |
| 224 | return val; |
| 225 | } |