tests: exercise wide 32-bit products and unsigned umul16

Add runtime markers for a full-width 32x32 multiply (0x12345678 * 0x9abcdef0
= 0x242d2080) and for a wide-dividend remainder, so a 16-bit-truncated MPYS or
a wrong FS1 cannot still produce the expected small results.  Compute the 16x16
unsigned product in unsigned arithmetic to remove the signed-overflow
annotation, and reject a regressed nuw nsw multiply in the optimized IR.
This commit is contained in:
arti committed 2026-11-12 06:17:29 +00:00
1 parent 7cd9a48ee3
commit 00522b56fc
3 files changed
+29 -10

No files matched your search

+9 -6
View File
@@ -14,6 +14,8 @@
-- 018A80E0 srem(-100,7) == -2
-- 018A8100 sumto(10) == 55
-- 018A8120 squares(5) == 30
-- 018A8140 mul(0x12345678,0x9abcdef0) == 0x242d2080
-- 018A8160 urem(0xfffffffe,7) == 2
dofile(os.getenv("M4_LOADER_LUA"))
@@ -28,16 +30,17 @@ emu.register_frame_done(function()
checked = true
local prog = manager.machine.devices[':maincpu'].spaces['program']
local m = {}
for i = 0, 9 do
for i = 0, 11 do
m[i] = prog:read_u32(0x018A8000 + i * 0x20)
end
print(string.format(
"M4: mul=%d umul16=%d sdiv=%d udiv=%d srem=%d urem=%d sdivneg=%d sremneg=%d sumto=%d squares=%d",
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]))
"M4: mul=%d umul16=%d sdiv=%d udiv=%d srem=%d urem=%d sdivneg=%d sremneg=%d sumto=%d squares=%d widemul=%d widerem=%d",
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9],
m[10], m[11]))
local want = {42, 0xfffe0001, 14, 613566756, 2, 2,
0xfffffff2, 0xfffffffe, 55, 30}
0xfffffff2, 0xfffffffe, 55, 30, 0x242d2080, 2}
local ok = true
for i = 0, 9 do
for i = 0, 11 do
if m[i] ~= want[i + 1] then
ok = false
end
@@ -45,7 +48,7 @@ emu.register_frame_done(function()
if ok then
print("M4 MAME: PASS")
else
print("M4 MAME: FAIL (want mul=42 umul16=4294836225 sdiv=14 udiv=613566756 srem=2 urem=2 sdivneg=-14 sremneg=-2 sumto=55 squares=30)")
print("M4 MAME: FAIL (want mul=42 umul16=4294836225 sdiv=14 udiv=613566756 srem=2 urem=2 sdivneg=-14 sremneg=-2 sumto=55 squares=30 widemul=606937216 widerem=2)")
end
end
end, "mul-check")
+10 -3
View File
@@ -9,15 +9,20 @@
* 018A80A0 urem(100,7) == 2
* 018A80C0 sdiv(-100,7) == -14
* 018A80E0 srem(-100,7) == -2
* 018A8140 mul(0x12345678,0x9ABCDEF0) == 0x242D2080 (wide 32x32 product)
* 018A8160 urem(0xfffffffe,7) == 2 (wide quotient in the remainder)
*/
#define MARK(n) (*(volatile unsigned int *)(0x018A8000u + (n)*0x20u))
int mul(int a, int b) { return a * b; }
/* A 16x16 unsigned product: both operands are masked to 16 bits, which is the
* form the target selects to MPYU. A plain unsigned short multiply promotes
* to a signed int multiply and would use MPYS. */
int umul16(int a, int b) { return (a & 0xffff) * (b & 0xffff); }
* form the target selects to MPYU. The product must be computed in unsigned
* arithmetic: 0xffff * 0xffff does not fit in signed int, so `int * int` would
* be signed overflow and the optimizer could narrow or rename the result. */
int umul16(int a, int b) {
return (int)((unsigned)(a & 0xffff) * (unsigned)(b & 0xffff));
}
int sdiv(int a, int b) { return a / b; }
unsigned udiv(unsigned a, unsigned b) { return a / b; }
@@ -33,5 +38,7 @@ int mul_test(void) {
MARK(5) = urem(100u, 7u);
MARK(6) = (unsigned)sdiv(-100, 7);
MARK(7) = (unsigned)srem(-100, 7);
MARK(10) = (unsigned)mul(0x12345678, 0x9ABCDEF0);
MARK(11) = urem(0xfffffffeu, 7u);
return 0;
}
+10 -1
View File
@@ -34,6 +34,15 @@ if grep -Eq '\bmul i(33|64)\b' "$WORK/asm/loop.ll"; then
fi
echo " ok driver -O2 loop IR has no i33/i64 multiply"
# The 16x16 unsigned product must not carry the old signed-overflow
# annotation. With the multiply done in unsigned arithmetic the IR is
# `mul nuw`, not `mul nuw nsw`; the latter contradicts the 0xfffe0001 result.
if awk '/^define .*@umul16\(/{infn=1} infn{print} infn && /^}/{exit}' \
"$WORK/asm/mul.ll" | grep -q 'mul nuw nsw'; then
fail "umul16 still has a signed no-wrap multiply (the product is unsigned)"
fi
echo " ok umul16 multiplies without signed no-wrap"
# Stop at the next function label (a column-0 label that is not an LBB block
# label), so a following local function cannot leak into this slice.
body() { # body FILE FUNC
@@ -49,7 +58,7 @@ body() { # body FILE FUNC
TMP=$(mktemp -d "${TMPDIR:-/tmp}/m4-mul.XXXXXX")
trap 'rm -rf "$TMP"' EXIT HUP INT TERM
EXPECTED="M4: mul=42 umul16=4294836225 sdiv=14 udiv=613566756 srem=2 urem=2 sdivneg=4294967282 sremneg=4294967294 sumto=55 squares=30"
EXPECTED="M4: mul=42 umul16=4294836225 sdiv=14 udiv=613566756 srem=2 urem=2 sdivneg=4294967282 sremneg=4294967294 sumto=55 squares=30 widemul=606937216 widerem=2"
grep -Fqx "$EXPECTED" "$WORK/mame.log" \
|| { grep -E '^M4: ' "$WORK/mame.log" >&2 || true; fail "runtime markers did not match: $EXPECTED"; }
echo " ok runtime markers: $EXPECTED"