Skip to content

Commit aff3b97

Browse files
authored
[mono-move] flesh out u64 arithmetic / bitwise / shift micro-ops (#19591)
Adds the missing reg-reg and reg-imm forms so the lowerer can stop bail!ing on common Move arithmetic. New micro-ops: reg-reg: SubU64, MulU64, DivU64, BitAndU64, BitOrU64, ShlU64, ShrU64 imm: MulU64Imm, DivU64Imm, ModU64Imm, ShlU64Imm All checked: overflow / underflow / div-by-zero / shift>=64 abort. Rename: XorU64 -> BitXorU64 for naming consistency with the new BitAnd/BitOr (and to disambiguate from forthcoming logical Or/And/Not). Wires the new ops through every layer: enum + Display, gas HasCfgInfo / RemapTargets / cost, runtime verifier + interpreter, and the specializer's BinaryOp / BinaryOpImm lowering. Reverse-immediate forms for non-commutative ops other than Sub are deferred. The verifier rejects DivU64Imm / ModU64Imm with imm == 0 and ShlU64Imm / ShrU64Imm with imm >= 64 -- always-abort patterns become hard errors. Refactor: arithmetic arms in step() factored through five helpers (binop_u64, checked_binop_u64, imm_op_u64, checked_imm_op_u64, shift_u64) with #[inline(always)]. Inlining verified by inspecting release-build x64 asm on 2026-04-30; per-block // SAFETY: comments and INVARIANT docs in place; debug_assert!s defend the verifier-guaranteed invariants on imm-form Div/Mod/Shl/Shr; error messages standardized to the "OpName: short-message" form. Snapshot baseline regenerated with extended coverage; runtime tests exercise each new op including abort paths, plus verifier tests cover the new static checks. Boolean Or/And/Not, comparison-to-register, and Negate are still TODO.
1 parent 38059a7 commit aff3b97

10 files changed

Lines changed: 1217 additions & 93 deletions

File tree

third_party/move/mono-move/core/src/instruction/gas.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,22 @@ impl HasCfgInfo for MicroOp {
4141
| MicroOp::Move { .. }
4242
| MicroOp::AddU64 { .. }
4343
| MicroOp::AddU64Imm { .. }
44+
| MicroOp::SubU64 { .. }
4445
| MicroOp::SubU64Imm { .. }
4546
| MicroOp::RSubU64Imm { .. }
46-
| MicroOp::XorU64 { .. }
47-
| MicroOp::ShrU64Imm { .. }
47+
| MicroOp::MulU64 { .. }
48+
| MicroOp::MulU64Imm { .. }
49+
| MicroOp::DivU64 { .. }
50+
| MicroOp::DivU64Imm { .. }
4851
| MicroOp::ModU64 { .. }
52+
| MicroOp::ModU64Imm { .. }
53+
| MicroOp::BitAndU64 { .. }
54+
| MicroOp::BitOrU64 { .. }
55+
| MicroOp::BitXorU64 { .. }
56+
| MicroOp::ShlU64 { .. }
57+
| MicroOp::ShlU64Imm { .. }
58+
| MicroOp::ShrU64 { .. }
59+
| MicroOp::ShrU64Imm { .. }
4960
| MicroOp::Return
5061
| MicroOp::CallFunc { .. }
5162
| MicroOp::CallIndirect { .. }
@@ -131,11 +142,22 @@ impl RemapTargets for MicroOp {
131142
| MicroOp::Move { .. }
132143
| MicroOp::AddU64 { .. }
133144
| MicroOp::AddU64Imm { .. }
145+
| MicroOp::SubU64 { .. }
134146
| MicroOp::SubU64Imm { .. }
135147
| MicroOp::RSubU64Imm { .. }
136-
| MicroOp::XorU64 { .. }
137-
| MicroOp::ShrU64Imm { .. }
148+
| MicroOp::MulU64 { .. }
149+
| MicroOp::MulU64Imm { .. }
150+
| MicroOp::DivU64 { .. }
151+
| MicroOp::DivU64Imm { .. }
138152
| MicroOp::ModU64 { .. }
153+
| MicroOp::ModU64Imm { .. }
154+
| MicroOp::BitAndU64 { .. }
155+
| MicroOp::BitOrU64 { .. }
156+
| MicroOp::BitXorU64 { .. }
157+
| MicroOp::ShlU64 { .. }
158+
| MicroOp::ShlU64Imm { .. }
159+
| MicroOp::ShrU64 { .. }
160+
| MicroOp::ShrU64Imm { .. }
139161
| MicroOp::Return
140162
| MicroOp::CallFunc { .. }
141163
| MicroOp::CallIndirect { .. }
@@ -186,11 +208,21 @@ impl GasSchedule<MicroOp> for MicroOpGasSchedule {
186208
// --- Arithmetic ---
187209
MicroOp::AddU64 { .. }
188210
| MicroOp::AddU64Imm { .. }
211+
| MicroOp::SubU64 { .. }
189212
| MicroOp::SubU64Imm { .. }
190213
| MicroOp::RSubU64Imm { .. }
191-
| MicroOp::XorU64 { .. }
214+
| MicroOp::BitAndU64 { .. }
215+
| MicroOp::BitOrU64 { .. }
216+
| MicroOp::BitXorU64 { .. }
217+
| MicroOp::ShlU64 { .. }
218+
| MicroOp::ShlU64Imm { .. }
219+
| MicroOp::ShrU64 { .. }
192220
| MicroOp::ShrU64Imm { .. } => 3,
193-
MicroOp::ModU64 { .. } => 5,
221+
MicroOp::MulU64 { .. } | MicroOp::MulU64Imm { .. } => 4,
222+
MicroOp::DivU64 { .. }
223+
| MicroOp::DivU64Imm { .. }
224+
| MicroOp::ModU64 { .. }
225+
| MicroOp::ModU64Imm { .. } => 5,
194226

195227
// --- Control flow ---
196228
MicroOp::CallFunc { .. }

third_party/move/mono-move/core/src/instruction/mod.rs

Lines changed: 143 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,21 @@ pub enum MicroOp {
268268
// Arithmetic & bitwise
269269
//======================================================================
270270
// Currently u64-only. Each op has a reg-reg form and/or an immediate
271-
// form; we add variants as the compiler needs them.
271+
// form; we add variants as the compiler needs them. All arithmetic is
272+
// checked: overflow / underflow / division by zero / oversize shift
273+
// all abort.
274+
//
275+
// Bitwise ops carry the `Bit` prefix to disambiguate from the boolean
276+
// logical Or/And/Not (which will not have it).
272277
//
273278
// May want:
274-
// - Mul, Div, Sub (reg-reg), Negate, bitwise And/Or/Xor/Not, Shl,
279+
// - Logical Or/And/Not (Phase 3), Negate (signed), bitwise Not,
280+
// - Reverse-imm forms: RDiv, RMod, RShl, RShr (mirroring RSubU64Imm),
275281
// - u8, u16, u32 variants (mask on u64?), u128/u256 (multi-word),
276282
// - signed integer support.
277283
//======================================================================
284+
285+
// --- Add ---
278286
/// `dst = lhs + rhs` (u64, checked).
279287
AddU64 {
280288
dst: FrameOffset,
@@ -289,6 +297,14 @@ pub enum MicroOp {
289297
imm: u64,
290298
},
291299

300+
// --- Sub ---
301+
/// `dst = lhs - rhs` (u64, checked).
302+
SubU64 {
303+
dst: FrameOffset,
304+
lhs: FrameOffset,
305+
rhs: FrameOffset,
306+
},
307+
292308
/// `dst = src - imm` (u64, checked).
293309
SubU64Imm {
294310
dst: FrameOffset,
@@ -303,27 +319,109 @@ pub enum MicroOp {
303319
imm: u64,
304320
},
305321

306-
/// `dst = lhs ^ rhs` (u64, bitwise XOR).
307-
XorU64 {
322+
// --- Mul ---
323+
/// `dst = lhs * rhs` (u64, checked).
324+
MulU64 {
308325
dst: FrameOffset,
309326
lhs: FrameOffset,
310327
rhs: FrameOffset,
311328
},
312329

313-
/// `dst = src >> imm` (u64, logical right shift).
314-
ShrU64Imm {
330+
/// `dst = src * imm` (u64, checked).
331+
MulU64Imm {
332+
dst: FrameOffset,
333+
src: FrameOffset,
334+
imm: u64,
335+
},
336+
337+
// --- Div ---
338+
/// `dst = lhs / rhs` (u64). Aborts on division by zero.
339+
DivU64 {
340+
dst: FrameOffset,
341+
lhs: FrameOffset,
342+
rhs: FrameOffset,
343+
},
344+
345+
/// `dst = src / imm` (u64). Aborts if `imm == 0`.
346+
DivU64Imm {
315347
dst: FrameOffset,
316348
src: FrameOffset,
317349
imm: u64,
318350
},
319351

320-
/// `dst = lhs % rhs` (u64 modulo). Panics on division by zero.
352+
// --- Mod ---
353+
/// `dst = lhs % rhs` (u64). Aborts on division by zero.
321354
ModU64 {
322355
dst: FrameOffset,
323356
lhs: FrameOffset,
324357
rhs: FrameOffset,
325358
},
326359

360+
/// `dst = src % imm` (u64). Aborts if `imm == 0`.
361+
ModU64Imm {
362+
dst: FrameOffset,
363+
src: FrameOffset,
364+
imm: u64,
365+
},
366+
367+
// --- Bitwise ---
368+
/// `dst = lhs & rhs` (u64, bitwise AND).
369+
BitAndU64 {
370+
dst: FrameOffset,
371+
lhs: FrameOffset,
372+
rhs: FrameOffset,
373+
},
374+
375+
/// `dst = lhs | rhs` (u64, bitwise OR).
376+
BitOrU64 {
377+
dst: FrameOffset,
378+
lhs: FrameOffset,
379+
rhs: FrameOffset,
380+
},
381+
382+
/// `dst = lhs ^ rhs` (u64, bitwise XOR).
383+
BitXorU64 {
384+
dst: FrameOffset,
385+
lhs: FrameOffset,
386+
rhs: FrameOffset,
387+
},
388+
389+
// --- Shifts ---
390+
//
391+
// TODO: in old Move bytecode the shift amount is a u8, not a u64. We
392+
// currently model `rhs` as a full 8-byte slot to match the rest of
393+
// the slot ABI; the value living there is zero-extended from a u8 by
394+
// the lowerer. Reconsider whether `rhs` (and the imm field of the
395+
// imm-form ops) should be narrower once the slot-alignment story is
396+
// sorted out — affects destack, lowering, and slot allocation.
397+
/// `dst = lhs << rhs` (u64). Aborts if `rhs >= 64`.
398+
ShlU64 {
399+
dst: FrameOffset,
400+
lhs: FrameOffset,
401+
rhs: FrameOffset,
402+
},
403+
404+
/// `dst = src << imm` (u64). Aborts if `imm >= 64`.
405+
ShlU64Imm {
406+
dst: FrameOffset,
407+
src: FrameOffset,
408+
imm: u64,
409+
},
410+
411+
/// `dst = lhs >> rhs` (u64, logical right shift). Aborts if `rhs >= 64`.
412+
ShrU64 {
413+
dst: FrameOffset,
414+
lhs: FrameOffset,
415+
rhs: FrameOffset,
416+
},
417+
418+
/// `dst = src >> imm` (u64, logical right shift). Aborts if `imm >= 64`.
419+
ShrU64Imm {
420+
dst: FrameOffset,
421+
src: FrameOffset,
422+
imm: u64,
423+
},
424+
327425
//======================================================================
328426
// Control flow
329427
//======================================================================
@@ -705,18 +803,54 @@ impl fmt::Display for MicroOp {
705803
MicroOp::AddU64Imm { dst, src, imm } => {
706804
write!(f, "AddU64Imm [{}] <- [{}] + #{}", dst.0, src.0, imm)
707805
},
806+
MicroOp::SubU64 { dst, lhs, rhs } => {
807+
write!(f, "SubU64 [{}] <- [{}] - [{}]", dst.0, lhs.0, rhs.0)
808+
},
708809
MicroOp::SubU64Imm { dst, src, imm } => {
709810
write!(f, "SubU64Imm [{}] <- [{}] - #{}", dst.0, src.0, imm)
710811
},
711812
MicroOp::RSubU64Imm { dst, src, imm } => {
712813
write!(f, "RSubU64Imm [{}] <- #{} - [{}]", dst.0, imm, src.0)
713814
},
714-
MicroOp::ShrU64Imm { dst, src, imm } => {
715-
write!(f, "ShrU64Imm [{}] <- [{}] >> #{}", dst.0, src.0, imm)
815+
MicroOp::MulU64 { dst, lhs, rhs } => {
816+
write!(f, "MulU64 [{}] <- [{}] * [{}]", dst.0, lhs.0, rhs.0)
817+
},
818+
MicroOp::MulU64Imm { dst, src, imm } => {
819+
write!(f, "MulU64Imm [{}] <- [{}] * #{}", dst.0, src.0, imm)
820+
},
821+
MicroOp::DivU64 { dst, lhs, rhs } => {
822+
write!(f, "DivU64 [{}] <- [{}] / [{}]", dst.0, lhs.0, rhs.0)
823+
},
824+
MicroOp::DivU64Imm { dst, src, imm } => {
825+
write!(f, "DivU64Imm [{}] <- [{}] / #{}", dst.0, src.0, imm)
716826
},
717827
MicroOp::ModU64 { dst, lhs, rhs } => {
718828
write!(f, "ModU64 [{}] <- [{}] % [{}]", dst.0, lhs.0, rhs.0)
719829
},
830+
MicroOp::ModU64Imm { dst, src, imm } => {
831+
write!(f, "ModU64Imm [{}] <- [{}] % #{}", dst.0, src.0, imm)
832+
},
833+
MicroOp::BitAndU64 { dst, lhs, rhs } => {
834+
write!(f, "BitAndU64 [{}] <- [{}] & [{}]", dst.0, lhs.0, rhs.0)
835+
},
836+
MicroOp::BitOrU64 { dst, lhs, rhs } => {
837+
write!(f, "BitOrU64 [{}] <- [{}] | [{}]", dst.0, lhs.0, rhs.0)
838+
},
839+
MicroOp::BitXorU64 { dst, lhs, rhs } => {
840+
write!(f, "BitXorU64 [{}] <- [{}] ^ [{}]", dst.0, lhs.0, rhs.0)
841+
},
842+
MicroOp::ShlU64 { dst, lhs, rhs } => {
843+
write!(f, "ShlU64 [{}] <- [{}] << [{}]", dst.0, lhs.0, rhs.0)
844+
},
845+
MicroOp::ShlU64Imm { dst, src, imm } => {
846+
write!(f, "ShlU64Imm [{}] <- [{}] << #{}", dst.0, src.0, imm)
847+
},
848+
MicroOp::ShrU64 { dst, lhs, rhs } => {
849+
write!(f, "ShrU64 [{}] <- [{}] >> [{}]", dst.0, lhs.0, rhs.0)
850+
},
851+
MicroOp::ShrU64Imm { dst, src, imm } => {
852+
write!(f, "ShrU64Imm [{}] <- [{}] >> #{}", dst.0, src.0, imm)
853+
},
720854
MicroOp::CallFunc { func_id } => {
721855
write!(f, "CallFunc #{}", func_id)
722856
},
@@ -895,9 +1029,6 @@ impl fmt::Display for MicroOp {
8951029
MicroOp::StoreRandomU64 { dst } => {
8961030
write!(f, "StoreRandomU64 [{}]", dst.0)
8971031
},
898-
MicroOp::XorU64 { dst, lhs, rhs } => {
899-
write!(f, "XorU64 [{}] <- [{}] ^ [{}]", dst.0, lhs.0, rhs.0)
900-
},
9011032
MicroOp::JumpLessU64Imm { target, src, imm } => {
9021033
write!(f, "JumpLessU64Imm @{} [{}] < #{}", target.0, src.0, imm)
9031034
},

third_party/move/mono-move/programs/src/nested_loop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ mod micro_op {
9393
JumpLessU64 { target: CO(7), lhs: FO(j), rhs: FO(n) }, // 5
9494
Jump { target: CO(11) }, // 6: goto INNER_END
9595
// INNER_BODY: sum += i ^ j; j += 1
96-
XorU64 { dst: FO(tmp), lhs: FO(i), rhs: FO(j) }, // 7
96+
BitXorU64 { dst: FO(tmp), lhs: FO(i), rhs: FO(j) }, // 7
9797
AddU64 { dst: FO(sum), lhs: FO(sum), rhs: FO(tmp) }, // 8
9898
AddU64Imm { dst: FO(j), src: FO(j), imm: 1 }, // 9
9999
Jump { target: CO(5) }, // 10: goto INNER_LOOP

0 commit comments

Comments
 (0)