Skip to content

Commit 476896d

Browse files
Fix division edge cases.
1 parent f263fba commit 476896d

2 files changed

Lines changed: 29 additions & 9 deletions

File tree

compiler/ir.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,11 @@ var inlineHelpers = map[string]inlineHelper{
191191
fmt.Sprintf("local %s = %s", aVar, args[0]),
192192
fmt.Sprintf("local %s = %s", bVar, args[1]),
193193
fmt.Sprintf("local %s", target),
194-
fmt.Sprintf("if %s == 0 then error(\"division by zero\") end", bVar),
195-
fmt.Sprintf("if %s >= 0 then", aVar),
194+
fmt.Sprintf("if %s == 0 then", bVar),
195+
fmt.Sprintf("\t%s = -1", target),
196+
fmt.Sprintf("elseif %s == -1 and %s == -2147483648 then", bVar, aVar),
197+
fmt.Sprintf("\t%s = -2147483648", target),
198+
fmt.Sprintf("elseif %s >= 0 then", aVar),
196199
fmt.Sprintf("\t%s = (%s - (%s %% %s)) // %s", target, aVar, aVar, bVar, bVar),
197200
"else",
198201
fmt.Sprintf("\t%s = -((-%s) - ((-%s) %% %s)) // %s", target, aVar, aVar, bVar, bVar),

compiler/math.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ func rem(w *OutputWriter, command AssemblyCommand) { /* rem & remu instructions
3535
dst := irArgExpr(w, command.Arguments[0])
3636
lhs := irArgExpr(w, command.Arguments[1])
3737
rhs := irArgExpr(w, command.Arguments[2])
38+
39+
// RISC-V: division by zero: rem(a, 0) = a
3840
if command.Name == "remu" {
39-
Emit(w, IRStmtAssign(dst, irU32(w, IRBinop("%", irU32(w, lhs), irU32(w, rhs)))))
41+
u_lhs := irU32(w, lhs)
42+
u_rhs := irU32(w, rhs)
43+
Emit(w, IRStmtAssign(dst, IRIfExpr(IRBinop("==", u_rhs, IRLit(0)), u_lhs, irU32(w, IRBinop("%", u_lhs, u_rhs)))))
4044
} else {
41-
Emit(w, IRStmtAssign(dst, irI32(w, IRBinop("%", irI32(w, lhs), irI32(w, rhs)))))
45+
i_lhs := irI32(w, lhs)
46+
i_rhs := irI32(w, rhs)
47+
Emit(w, IRStmtAssign(dst, IRIfExpr(IRBinop("==", i_rhs, IRLit(0)), i_lhs, irI32(w, IRBinop("%", i_lhs, i_rhs)))))
4248
}
4349
}
4450
func neg(w *OutputWriter, command AssemblyCommand) { /* neg & negi instructions */
@@ -47,13 +53,24 @@ func neg(w *OutputWriter, command AssemblyCommand) { /* neg & negi instructions
4753
Emit(w, IRStmtAssign(dst, irI32(w, IRUnop("-", irI32(w, src)))))
4854
}
4955

50-
/** mulh — high 32 bits of 64-bit product */
5156
func mulh(w *OutputWriter, command AssemblyCommand) {
5257
dst := irArgExpr(w, command.Arguments[0])
5358
lhs := irArgExpr(w, command.Arguments[1])
5459
rhs := irArgExpr(w, command.Arguments[2])
55-
Emit(w, IRStmtAssign(dst,
56-
IRCall(BIT32_BAND,
57-
IRCall(BIT32_LSHIFT, lhs, rhs),
58-
IRLitHex(0xFFFFFFFF))))
60+
61+
// Use math.floor( (a * b) / 2^32 ) for mulh
62+
// We use u32 for all operands to ensure we stay in the realm of Luau's 53-bit mantissa correctly if possible,
63+
// but for full 64-bit mul we might need care.
64+
// RISC-V mulhu/mulh/mulhsu have different sign handling.
65+
66+
if command.Name == "mulhu" {
67+
u_lhs := irU32(w, lhs)
68+
u_rhs := irU32(w, rhs)
69+
Emit(w, IRStmtAssign(dst, irU32(w, IRCall(MATH_FLOOR, IRBinop("/", IRBinop("*", u_lhs, u_rhs), IRLit(0x100000000))))))
70+
} else {
71+
// mulh (signed)
72+
i_lhs := irI32(w, lhs)
73+
i_rhs := irI32(w, rhs)
74+
Emit(w, IRStmtAssign(dst, irI32(w, IRCall(MATH_FLOOR, IRBinop("/", IRBinop("*", i_lhs, i_rhs), IRLit(0x100000000))))))
75+
}
5976
}

0 commit comments

Comments
 (0)