harm: harm-types crate - #33
Conversation
2874989 to
afd6767ComparePragmas for `harm-types`
Describe RegOrZero{N} vs RegOrSp{N} and the traits it implement.WalkthroughAdds a new workspace crate Changes
Sequence Diagram(s)sequenceDiagram
participant Harm as harm crate
participant Types as harm_types::A64::register
participant Encoder as instruction encoder
Note over Harm,Types: New dependency & re-export
Harm->>Types: reference Reg/RegOr* types
Harm->>Encoder: provide instruction with register operand
Encoder->>Types: call .index()
Types-->>Encoder: BitValue<5>
Encoder-->>Harm: assembled instruction code
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas to inspect closely:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
harm/src/register.rs (1)
7-7: Consider narrowing the public re-export instead of using a glob
pub use harm_types::A64::register::*;cleanly preserves the old surface, but it also makesharm’s public API sensitive to any future additions inharm-types(e.g., helper types/constants you may not want to expose). If API stability is a concern, consider listing the exported items explicitly here instead of using*.harm-types/src/A64/register.rs (2)
8-22: Minor doc polish for clarity and typosThe high-level docs are very helpful. There are a couple of small textual issues you might want to fix when convenient:
- “general-propose” → “general-purpose” (appears multiple times).
- The phrase “or
XZR/WZRasSP/WSP” is a bit hard to parse; consider rephrasing to more clearly distinguish the SP vs zero-register interpretations.
These are non-functional, purely documentation nits.
167-209: Preferor_elseoverorinTryFrom<u8>for lazy error handlingThe
TryFrom<u8>impls forRegOrSp*/RegOrZero*currently useReg{32,64}::try_from(value).map(Into::into).or({ ... }). BecauseResult::ortakes aResult, the{ ... }branch is evaluated even whentry_fromsucceeds, which is a minor inefficiency on the happy path.You can switch to
or_elseso the fallback is only computed on error, e.g.:- Reg64::try_from(value).map(Into::into).or({- if value == NICHE_REG {- Ok(Self::SP)- } else {- Err(RegisterError::InvalidRegisterCode(value))- }- })+ Reg64::try_from(value).map(Into::into).or_else(|_| {+ if value == NICHE_REG {+ Ok(Self::SP)+ } else {+ Err(RegisterError::InvalidRegisterCode(value))+ }+ })(Similarly for the other three implementations.)
Also applies to: 342-370
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
Cargo.toml(2 hunks)harm-types/Cargo.toml(1 hunks)harm-types/src/A64.rs(1 hunks)harm-types/src/A64/register.rs(1 hunks)harm-types/src/lib.rs(1 hunks)harm/Cargo.toml(1 hunks)harm/src/instructions/arith/macros.rs(3 hunks)harm/src/instructions/control/branch_imm.rs(2 hunks)harm/src/instructions/control/branch_reg.rs(3 hunks)harm/src/instructions/control/testbranch.rs(2 hunks)harm/src/instructions/dpimm/log_imm.rs(8 hunks)harm/src/instructions/dpimm/movewide.rs(3 hunks)harm/src/instructions/dpreg/log_shift.rs(10 hunks)harm/src/instructions/ldst/macros.rs(11 hunks)harm/src/register.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
harm/src/instructions/ldst/macros.rs (1)
harm-types/src/A64/register.rs (7)
index(32-32)index(374-376)index(381-383)index(388-393)index(398-403)index(408-413)index(418-423)
harm/src/instructions/control/branch_reg.rs (1)
aarchmrs-instructions/src/A64/control/branch_reg.rs (3)
RET_64R_branch_reg(124-130)BR_64_branch_reg(16-22)BLR_64_branch_reg(70-76)
harm/src/instructions/dpimm/movewide.rs (2)
harm/src/instructions/control/branch_imm.rs (5)
to_code(102-105)to_code(138-141)to_code(183-186)to_code(267-275)to_code(280-288)harm/src/instructions/control/branch_reg.rs (3)
to_code(39-41)to_code(48-50)to_code(62-64)
harm/src/instructions/dpreg/log_shift.rs (2)
harm/src/instructions/dpimm/log_imm/args.rs (8)
new(22-28)new(39-45)new(56-62)new(73-79)new(90-96)new(107-113)new(124-130)new(141-147)harm/src/register.rs (9)
into_reg(17-17)into_reg(22-24)into_reg(29-31)into_reg(36-38)into_reg(43-45)into_reg(50-52)into_reg(57-59)into_reg(64-66)into_reg(71-73)
harm/src/instructions/arith/macros.rs (1)
harm-types/src/A64/register.rs (7)
index(32-32)index(374-376)index(381-383)index(388-393)index(398-403)index(408-413)index(418-423)
🔇 Additional comments (26)
harm/Cargo.toml (2)
16-16: LGTM: Workspace dependency correctly added.The harm-types workspace dependency integration is properly configured.
4-4: No action needed—Rust edition 2024 is stable.Rust Edition 2024 shipped with Rust 1.85 (stable release on February 20, 2025), so the edition specification is valid and correct.
Cargo.toml (2)
9-9: LGTM: Workspace configuration correctly updated.The harm-types crate is properly integrated as a workspace member with matching version and path configuration.
Also applies to: 25-25
17-17: Resolver "3" is valid and fully supported.Resolver "3" is the latest Cargo resolver version, stabilized as an opt-in resolver in Rust 1.84.0 (Jan 9, 2025). It became the default for the Rust 2024 edition in Rust 1.85.0 (Feb 20, 2025). This configuration is correct and requires no changes.
harm-types/Cargo.toml (1)
1-15: Crate manifest structure looks good.The package metadata and dependencies are appropriately configured for a types-only crate.
harm/src/instructions/control/testbranch.rs (2)
57-75: LGTM: Register encoding updated to use index().The change from
code()toindex()for 64-bit test-branch encoding is consistent with the new register API.
77-90: LGTM: Register encoding updated to use index().The change from
code()toindex()for 32-bit test-branch encoding is consistent with the new register API.harm-types/src/A64.rs (1)
1-6: LGTM: Clean module structure.The A64 module properly exposes the register submodule as part of the public API.
harm/src/instructions/control/branch_imm.rs (2)
265-276: LGTM: Register encoding updated to use index().The change from
code()toindex()for 64-bit compare-branch encoding aligns with the new register API.
278-289: LGTM: Register encoding updated to use index().The change from
code()toindex()for 32-bit compare-branch encoding aligns with the new register API.harm/src/instructions/control/branch_reg.rs (3)
37-42: LGTM: Register encoding updated to use index().The RET instruction encoding now uses
index()consistently with the new register API.
46-51: LGTM: Register encoding updated to use index().The BR instruction encoding now uses
index()consistently with the new register API.
60-65: LGTM: Register encoding updated to use index().The BLR instruction encoding now uses
index()consistently with the new register API.harm/src/instructions/dpimm/movewide.rs (3)
164-168: LGTM: Register encoding updated to use index().The MOVK instruction encoding now uses
index()for both 32-bit and 64-bit variants, consistent with the new register API.Also applies to: 170-174
190-194: LGTM: Register encoding updated to use index().The MOVN instruction encoding now uses
index()for both 32-bit and 64-bit variants, consistent with the new register API.Also applies to: 196-200
216-220: LGTM: Register encoding updated to use index().The MOVZ instruction encoding now uses
index()for both 32-bit and 64-bit variants, consistent with the new register API.Also applies to: 222-226
harm/src/instructions/dpimm/log_imm.rs (1)
30-31: index() migration for DPIMM logical-immediate encodings looks consistentAll
rn/rdoperands now use.index()onRegOrSp*/RegOrZero*, withRegisterin scope, and argument ordering into the*_log_immhelpers is unchanged. Given the comprehensive LOG_IMM tests below, this should preserve exact encodings while aligning with the newRegister::indexAPI.Also applies to: 42-43, 54-55, 67-68, 79-80, 92-93, 104-105, 117-118
harm-types/src/lib.rs (1)
1-19: Crate attributes and module surface are well-chosen for harm-types
no_std,forbid(unsafe_code), and the deny/allow set are appropriate for a small register-types crate, and exportingA64as the root module gives a clear, minimal public surface.harm/src/instructions/ldst/macros.rs (1)
69-82: Load/store macros correctly switched to index()-based register encodingAcross reg-offset, (scaled/unscaled) imm-offset, PC-relative, and pair offset variants, all base/offset/rt operands now use
.index()onRegOrSp64/RegOrZero*while preserving the previous call signatures and argument ordering to theaarchmrs_instructionshelpers. This keeps encodings stable and centralizes the 5‑bit mapping in theRegisterimpls.Also applies to: 83-95, 97-109, 233-260, 331-337, 383-388, 559-599
harm/src/instructions/arith/macros.rs (1)
156-172: Arithmetic instruction encoders now consistently use Register::indexFor shifted, immediate, and extend forms,
rm,rn, andrdare taken via.index()on the register types, with the same bitfield wiring into the underlying encoder functions. This matches the new register API and should be a no-op for the actual encodings.Also applies to: 220-233, 312-323
harm/src/instructions/dpreg/log_shift.rs (2)
23-24: IntoReg-based logical-args builders are sound and avoid orphan issuesSwitching
Make*LogicalArgsto requireIntoReg<$rn>and usinginto_reg()forrd,rn, and the mask register keeps the public API ergonomic (e.g., still accepts plainReg32/Reg64) while routing conversions through your localIntoRegtrait instead of foreignInto. That’s the right pattern for sharing register types fromharm-typeswithout hitting the orphan rules.Also applies to: 31-33, 39-41, 49-51, 61-63
95-109: log-shift encoders correctly migrated to index()-based operandsAll DPREG logical-shift variants (
and,ands,eor,orr, 32/64‑bit) now feedmask,rn, andrdvia.index()onRegOrZero*, with the shift kind and amount handling unchanged. Given the extensive LOG_SHIFT_DB tests below, this looks like a safe mechanical migration to the newRegistertrait.Also applies to: 118-127, 136-145, 158-163, 176-181, 194-199, 211-217, 230-235
harm-types/src/A64/register.rs (4)
35-48: Check MSRV impact of usingcore::error::ErrorImplementing
::core::error::ErroronRegisterErroris nice forno_std, but it ties the crate to the Rust version wherecore::error::Errorwas stabilized. Please confirm that this aligns with your intended MSRV; if you need an older MSRV, you may have to fall back tostd::error::Errorbehind astdfeature or skip the Error impl.
89-127: Explicit narrow/extend mappings look correct and type-safeThe
Reg64::narrowandReg32::extendmatch arms cover all defined general-purpose registers and keep LR/WLR consistent across sizes. The explicit matches avoid relying on numeric assumptions and should be easy to audit/maintain.Also applies to: 250-287
372-424:Register::indeximplementations match the encoding modelAll
Registerimpls return a 5-bitBitValuederived from the underlyingrepr(u8)discriminant orNICHE_REGfor SP/XZR/WSP/WZR, which aligns with the architectural encoding (0–30 for GP regs and 31 for the special case). This looks correct and consistent across 32- and 64-bit variants.
426-566: Solid test coverage for conversions and size transitionsThe tests exercise:
TryFrom<u8>success/failure/NICHE_REGpaths for all register flavours.narrow/extendfor both plain and SP/zero variants, including LR/WLR.This gives good confidence in the mapping logic and boundary handling around code 31.
Uh oh!
There was an error while loading. Please reload this page.
monoid
commented
Nov 21, 2025
@CodeRabbit clippy complains about using |
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 932 🏁 Script executed: Length of output: 35 🏁 Script executed: Length of output: 7544 @monoid, I can see the issue. At lines 160 and 238 in Change this: movz.or_else(movk).ok_or(InvalidMovImm)To this: movz.or(movk()).ok_or(InvalidMovImm)This applies to both occurrences:
Since |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
harm-types/src/A64/register.rs (4)
6-22: Minor doc wording/clarity nit in module-level commentsThe docs are very clear overall; just a small phrasing tweak could improve readability:
- Line 18 reads: “where
XZR/WZRis allowed, useRegOrZero64orRegOrZero32is used.” — the trailing “is used” is redundant. Consider rephrasing to something like://! where `XZR`/`WZR` is allowed, use `RegOrZero64` or `RegOrZero32`.Purely cosmetic; no behavior impact.
181-208: Useor_elsefor lazy fallback inTryFrom<u8>implementationsThe
TryFrom<u8>impls forRegOrSp64,RegOrZero64,RegOrSp32, andRegOrZero32are logically correct, butResult::oreagerly evaluates the fallback branch even when the initialReg*::try_fromsucceeds. Usingor_elsewould keep the same behavior while avoiding unnecessary work and making the “fallback on error” intent a bit clearer.For example, for
RegOrSp64:- Reg64::try_from(value).map(Into::into).or({- if value == NICHE_REG {- Ok(Self::SP)- } else {- Err(RegisterError::InvalidRegisterCode(value))- }- })+ Reg64::try_from(value).map(Into::into).or_else(|_| {+ if value == NICHE_REG {+ Ok(Self::SP)+ } else {+ Err(RegisterError::InvalidRegisterCode(value))+ }+ })And analogously for
RegOrZero64,RegOrSp32, andRegOrZero32.This keeps the same observable semantics (including error values) but avoids constructing the fallback
Resulton the happy path.Also applies to: 342-370
372-424:Register::indeximplementations align with encoding constraintsAll
Registerimpls map variants toBitValue<5>using the underlyingu8discriminant or theNICHE_REGconstant for SP/XZR/WSP/WZR. Given the enums are#[repr(u8)]with values 0–30 and NICHE_REG = 31, this stays within 5 bits, which fits the stated “5-bit number” contract.You might optionally add a brief comment near
NICHE_REGnoting that it is expected to be within theBitValue<5>range (0–31), but behavior-wise this looks solid.
426-566: Test coverage is strong; optional additions forindex()The tests thoroughly exercise
TryFrom<u8>paths and the narrow/extend conversions, including the NICHE_REG boundary cases. That gives good confidence in the register modeling.If you want slightly more safety against future refactors, you could add a couple of small tests around
Register::index()(e.g., X0→0, LR→30, SP/XZR/WSP/WZR→31) to pin the encoding contract explicitly. Not required given how direct the implementations are, just an optional safeguard.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
harm-types/Cargo.toml(1 hunks)harm-types/src/A64/register.rs(1 hunks)
🔇 Additional comments (2)
harm-types/Cargo.toml (1)
1-15: Crate manifest looks consistent and focusedPackage metadata, workspace wiring, and dependency declarations all look sane for a non-published internal types crate. I don’t see issues here.
harm-types/src/A64/register.rs (1)
50-288: Register enums and narrow/extend mappings look correctThe
Reg64/Reg32definitions and thenarrow/extendimplementations appear exhaustive and symmetric (X0↔W0 … LR↔WLR), and the separation from SP/XZR/WSP/WZR via theRegOr*types matches AArch64’s encoding model.No functional issues spotted here.
monoid
commented
Nov 21, 2025
No, clippy doesn't complain here, probably because the expression is much more complex than simple value construction. It complains about your proposition to use |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
harm-types/src/A64/register.rs (2)
181-209: Consider simplifying theTryFrom<u8>patterns to avoid double error constructionThe
TryFrom<u8>impls forRegOrSp*/RegOrZero*are correct, but theRegX::try_from(value).map(Into::into).or({ ... })style creates an error twice for invalid codes (RegX::try_fromand the finalErr(RegisterError::InvalidRegisterCode(value))).A slightly more direct pattern would be:
implTryFrom<u8>forRegOrSp64{typeError = RegisterError;fntry_from(value:u8) -> Result<Self,Self::Error>{match value { v @ 0..=30 => Reg64::try_from(v).map(RegOrSp64::Reg), v if v == NICHE_REG => Ok(RegOrSp64::SP), v => Err(RegisterError::InvalidRegisterCode(v)),}}}and analogously for the other three types. This keeps the intent explicit and avoids constructing an intermediate error that’s thrown away, while preserving the same behavior.
Also applies to: 342-370
426-587: Tests give very good coverage; only tiny naming nitThe tests exercise:
TryFrom<u8>happy‑path and error cases for all register families.- All
narrow()/extend()combinations, including SP/XZR/WSP/WZR cases.index()values for both 32‑ and 64‑bit variants and their “or SP/zero” wrappers.That’s strong coverage for what’s otherwise subtle encoding logic. The only tiny nit: names like
test_reg64_or_sp64_from_u8_invalidactually testRegOrSp64; if you ever touch these again, aligning names (e.g.,test_reg_or_sp64_from_u8_invalid) would make them slightly clearer to future readers. Functionally, everything looks solid.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
harm-types/src/A64/register.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
harm-types/src/A64/register.rs (1)
aarchmrs-types/src/bit_value.rs (1)
into_inner(37-39)
🔇 Additional comments (3)
harm-types/src/A64/register.rs (3)
30-48: Register trait and error type look well‑scoped for this crate
Registerreturning aBitValue<5>cleanly expresses the architectural encoding, andRegisterError::InvalidRegisterCode(u8)is a focused error type that composes well with theTryFromimpls. No issues here from a correctness or design standpoint.
50-127: Explicit 32/64‑bit mappings are correct and easy to auditThe
Reg64/Reg32enums plusnarrow()/extend()matches look exhaustive and symmetric, including the LR/WLR mapping. Given how error‑prone encoding bugs can be, the explicit matches here are a good trade‑off vs. relying on casts/transmutes.Also applies to: 211-288
372-424:Register::index()implementations align with AArch64 encodingsAll
index()impls consistently map architectural register codes to 0–31, with LR/WLR → 30 and SP/XZR/WSP/WZR → 31 viaNICHE_REG. This matches the encoding described in the module docs and should integrate cleanly with the encoder paths usingBitValue<5>.
Move some definitions to a new
harm-typescrate.Define
IntoRegtrait inharmto avoid foreign crate definition problem.Closes#12.
Summary by CodeRabbit
New Features
Refactor
Chores
✏️ Tip: You can customize this high-level summary in your review settings.