Skip to content

harm: some relocations#47

Merged
monoid merged 15 commits intomasterfrom
harm/relocations
Dec 29, 2025
Merged

harm: some relocations#47
monoid merged 15 commits intomasterfrom
harm/relocations

Conversation

@monoid
Copy link
Owner

@monoid monoid commented Dec 25, 2025

Implement some instructions referencing labels and returning relocations.

  • branching
  • ldr/ldrsw
  • movX

Refs #31.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new runtime module for enhanced instruction processing capabilities.
    • Added comprehensive label-based relocation support for branch and load instructions, enabling improved code generation.
    • Expanded move-wide instruction relocation variants for better address resolution.
  • Refactor

    • Restructured instruction trait system for cleaner generic argument handling.
    • Modernized relocation type system with extended variant coverage.
  • Tests

    • Added test coverage for new relocation scenarios across instruction types.

✏️ Tip: You can customize this high-level summary in your review settings.

@monoid monoid self-assigned this Dec 25, 2025
@monoid monoid added enhancement New feature or request harm The `harm` dynamic assembler labels Dec 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

This PR introduces the harm-runtime crate and extends relocation support across ARM64 instruction encoders. The relocation system (Rel64) now supports control flow, memory, and move-wide instruction types with label-based references, enabling multiple instruction variants to generate code with relocation metadata.

Changes

Cohort / File(s) Summary
New harm-runtime crate
Cargo.toml, harm-runtime/Cargo.toml, harm-runtime/src/lib.rs, harm-runtime/src/labels.rs
Added new harm-runtime workspace member with LabelInfo enum (Forward/Offset variants) for label tracking.
Relocation system expansion
harm/src/reloc.rs, harm/src/instructions.rs
Renamed Reloc → Rel64 and expanded variant set to cover static data, control-flow (Jump26, Call26, CondBr19, TstBr14), memory (LdPrelLo19), and move-wide (MovWAbsG0–G3 families) relocations. Updated instruction trait signatures to use Rel64 throughout.
Branch relocation support
harm/src/instructions/control/branch_imm.rs
Made Branch, BranchLink, and CompareBranch generic over Offset type; added RelocatableInstruction implementations for LabelRef-based variants. Extended cbz/cbnz/bl to support label offsets with relocation generation.
Move-wide relocation support
harm/src/instructions/dpimm/movewide.rs, harm/src/instructions/dpimm/movewide/movewide_reloc.rs
Introduced MovWideTypeTag indirection for generic argument construction. New movewide_reloc module with AbsG0–G3 variants and MoveWideReloc64/32 traits; implemented RelocatableInstruction for movz/movk/movn with relocation metadata.
Load/store PC-relative relocation
harm/src/instructions/ldst/ldr.rs, harm/src/instructions/ldst/ldrsw.rs, harm/src/instructions/ldst/macros.rs
Extended define_pc_offset_rules macro to accept relocation type parameter. Updated ldr and ldrsw to specify Rel64::LdPrelLo19 for PC-relative literals; generated LabelRef trait implementations and RelocatableInstruction for both variants.
Move instruction relocation
harm/src/instructions/mov/mov_imm.rs
Updated argument construction to use MovWideTypeTag\<MovImmArgs\> via MakeMovArgs trait for movz/movk/movn helper functions.
Test-branch relocation support
harm/src/instructions/control/testbranch.rs
Added RelocatableInstruction implementations for TestBranch with LabelRef offsets; extended trait coverage with Sealed and MakeTestBranch for 64-bit and 32-bit variants. Added relocation-based tests for tbz/tbnz instructions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • harm: refactor tbz/tbnz #48: Adds RelocatableInstruction for TestBranch variants with generic offset/LabelRef support, directly overlapping with testbranch.rs modifications in this PR.
  • harm: ADR/ADRP #43: Introduces ADR/ADRP PC-relative instructions that integrate with the Rel64 relocation kinds and PC-offset macro machinery expanded here.

Poem

🐰 With labels flying swift and free,
Relocations dancing wild with glee,
MovWide tags and branches bound,
The runtime hops on sturdy ground.
Offsets generic, control-flows sing—
What a bundle of ARM64 wing!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.64% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'harm: some relocations' is partially related to the changeset. While it mentions 'relocations', which is a key theme in the PR, the title uses vague language ('some') that doesn't clearly convey the primary changes. The PR implements multiple relocation features including branching with labels, PC-relative relocations, and move-wide relocations. The title would benefit from being more specific about the scope of changes. Consider a more descriptive title such as 'harm: add label-based relocations for branches, loads, and move instructions' to better convey the specific changes and their scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch harm/relocations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (6)
harm-runtime/src/lib.rs (1)

1-7: New crate root is minimal and functional.

Consider adding a crate-level doc comment (//!) to describe the purpose of harm-runtime (dynamic assembler runtime with label/relocation management), which would improve discoverability in generated docs.

harm-runtime/Cargo.toml (2)

15-15: Consider using memmap2 instead of memmap.

The memmap crate (0.7.0) is unmaintained. The memmap2 crate is the actively maintained fork with security fixes and API improvements.

🔎 Suggested dependency update
 [dependencies]
 harm = { workspace = true }
-memmap = "0.7.0"
+memmap2 = "0.9"

17-19: Remove empty sections for cleaner manifest.

Empty [dev-dependencies] and [features] sections can be removed without affecting functionality.

🔎 Suggested cleanup
 harm = { workspace = true }
-memmap = "0.7.0"
- 
-[dev-dependencies]
- 
-[features]
+memmap2 = "0.9"
harm/src/reloc.rs (1)

50-60: TODO comment indicates incomplete work.

The TODO on line 50 mentions MOVW and some add/ldst-related relocations. The MovWAbsG* variants are already implemented below, so consider updating or removing this comment if they fulfill the MOVW requirement.

harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)

388-483: Tests provide good coverage of relocation variants.

The tests verify that:

  1. Generated instruction codes match expectations with zero immediates
  2. Correct Rel64 variants are produced for each abs_g* function

Note: Test names test_64_abs_g2s_movk and test_64_abs_g2nc_movk (lines 451-471) actually test movz, not movk. Consider renaming for accuracy.

harm/src/instructions/control/branch_imm.rs (1)

41-45: Prefer implementing From instead of Into.

Rust convention is to implement From<T> rather than Into<T>, as Into is automatically derived from From. The Into trait is typically only implemented directly when the target type is from a foreign crate.

Proposed fix
-impl Into<BitValue<4>> for BranchCond {
-    fn into(self) -> BitValue<4> {
+impl From<BranchCond> for BitValue<4> {
+    fn from(cond: BranchCond) -> BitValue<4> {
-        BitValue::new_u32(self as u8 as u32)
+        BitValue::new_u32(cond as u8 as u32)
     }
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a759507 and 854b6ad.

📒 Files selected for processing (14)
  • Cargo.toml
  • harm-runtime/Cargo.toml
  • harm-runtime/src/labels.rs
  • harm-runtime/src/lib.rs
  • harm-runtime/src/runtime.rs
  • harm/src/instructions.rs
  • harm/src/instructions/control/branch_imm.rs
  • harm/src/instructions/dpimm/movewide.rs
  • harm/src/instructions/dpimm/movewide/movewide_reloc.rs
  • harm/src/instructions/ldst/ldr.rs
  • harm/src/instructions/ldst/ldrsw.rs
  • harm/src/instructions/ldst/macros.rs
  • harm/src/instructions/mov/mov_imm.rs
  • harm/src/reloc.rs
🧰 Additional context used
🧬 Code graph analysis (7)
harm/src/instructions/mov/mov_imm.rs (1)
harm/src/instructions/dpimm/movewide.rs (9)
  • movn (189-198)
  • movz (214-223)
  • new (57-57)
  • new (74-80)
  • new (90-96)
  • new (106-112)
  • new (121-127)
  • new (137-143)
  • new (153-159)
harm-runtime/src/runtime.rs (2)
harm/src/instructions.rs (1)
  • instructions (48-50)
harm/src/instructions/control/branch_imm.rs (5)
  • new (275-275)
  • new (285-291)
  • new (302-308)
  • new (317-323)
  • new (333-339)
harm/src/instructions.rs (1)
harm/src/instructions/control/branch_imm.rs (3)
  • to_code_with_reloc (126-133)
  • to_code_with_reloc (184-192)
  • to_code_with_reloc (253-260)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (3)
harm/src/instructions.rs (1)
  • instructions (48-50)
harm/src/instructions/dpimm/movewide.rs (16)
  • movk (164-173)
  • movn (189-198)
  • movz (214-223)
  • new (57-57)
  • new (74-80)
  • new (90-96)
  • new (106-112)
  • new (121-127)
  • new (137-143)
  • new (153-159)
  • to_code (176-178)
  • to_code (182-184)
  • to_code (201-203)
  • to_code (207-209)
  • to_code (226-228)
  • to_code (232-234)
harm/src/instructions/mov/mov_imm.rs (4)
  • to_code (88-94)
  • to_code (99-104)
  • to_code (247-253)
  • to_code (258-263)
harm/src/instructions/dpimm/movewide.rs (1)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)
  • new (326-331)
harm/src/instructions/ldst/macros.rs (1)
harm/src/instructions.rs (3)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
harm/src/instructions/control/branch_imm.rs (3)
harm/src/instructions.rs (4)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
  • to_code (19-19)
harm/src/instructions/control/branch_reg.rs (5)
  • to_code (39-41)
  • to_code (48-50)
  • to_code (62-64)
  • new (26-28)
  • reg (31-34)
harm-runtime/src/runtime.rs (1)
  • new (23-25)
🪛 GitHub Actions: Rust
harm/src/reloc.rs

[error] 45-45: cargo fmt -- --check reported formatting issue: 'Call26(LabelRef), // same as Jump26 actually?' should be 'Call26(LabelRef), // same as Jump26 actually?'

harm-runtime/src/runtime.rs

[error] 7-7: cargo fmt -- --check reported formatting issue: import order changed between 'use harm::reloc::{Rel64, LabelId};' and 'use harm::reloc::{LabelId, Rel64};'

🔇 Additional comments (30)
harm/src/instructions/dpimm/movewide.rs (4)

10-11: New module structure for relocation support looks good.

The addition of movewide_reloc as a pub(crate) module with a wildcard re-export cleanly separates relocation-specific logic while maintaining API ergonomics.

Also applies to: 20-20


29-33: Type-level tag pattern is appropriate for this use case.

MovWideTypeTag<X> acts as a phantom type tag enabling the same MakeMovArgs trait to dispatch to different argument types (immediate vs relocation). The Sealed bound on X correctly restricts instantiation to internal types.


69-80: MakeMovArgs implementation correctly updated for MovWideTypeTag indirection.

The trait implementation now routes through MovWideTypeTag<MovImmArgs<RegOrZero32>> while returning Unfallible<MovImmArgs<RegOrZero32>>, maintaining the same effective behavior with added type flexibility.


164-173: Generic movk function signature enables relocation-aware variants.

The function is now generic over Args, using MovWideTypeTag<Args> as the dispatch mechanism. The Outcome<Inner = Args> constraint ensures type consistency between the tag and the wrapped result.

harm/src/instructions/ldst/ldrsw.rs (1)

160-167: PC-relative relocation correctly specified for LDRSW.

The Rel64::LdPrelLo19 relocation type is appropriate for the 19-bit signed PC-relative offset used by LDRSW literal loads.

Cargo.toml (1)

9-9: Workspace member added correctly.

Note that harm-runtime is not added to [workspace.dependencies]. This is fine if other workspace members won't depend on it yet, but consider adding it for consistency when inter-crate dependencies are needed.

harm/src/instructions/ldst/ldr.rs (1)

168-183: PC-relative relocations correctly applied to both LDR variants.

Both 32-bit and 64-bit LDR literal forms use Rel64::LdPrelLo19, which is correct—the relocation type encodes the 19-bit PC-relative offset field, independent of the load width.

harm/src/instructions/mov/mov_imm.rs (3)

10-10: Import updated to include MovWideTypeTag.

The import correctly brings in MovWideTypeTag alongside the existing move-wide types.


145-147: 32-bit move construction updated to use MovWideTypeTag pathway.

The fully-qualified trait method call correctly routes through the new type-tagged implementation while preserving the original logic flow.

Also applies to: 156-158


225-227: 64-bit move construction follows the same pattern consistently.

The changes mirror the 32-bit variant, maintaining consistency across the codebase.

Also applies to: 236-238

harm/src/instructions.rs (3)

8-8: Relocation type updated to Rel64.

The import change from Reloc to Rel64 aligns with the expanded relocation system introduced in this PR.


22-24: RelocatableInstruction trait updated with Rel64 return type.

The trait and blanket implementation correctly propagate the new relocation type.

Also applies to: 30-32


44-45: InstructionSeq encode() signatures updated consistently.

Both the trait definition and the blanket implementation return Option<Rel64>, maintaining type consistency throughout the instruction encoding pipeline.

Also applies to: 63-66

harm-runtime/Cargo.toml (1)

4-4: Verify Rust edition 2024 compatibility with CI/MSRV.

Edition 2024 requires Rust 1.85 (released Feb 2025). Update your CI configuration and project's minimum supported Rust version (MSRV) to 1.85 or higher if not already set.

harm/src/reloc.rs (1)

1-24: LGTM on type definitions.

The LabelId, Offset, Addr, and LabelRef types are well-designed for the relocation system. Using i64 for Offset aligns with the comment about sign-extension to 64 bits.

harm/src/instructions/ldst/macros.rs (3)

342-343: Macro signature extended for relocation support.

The addition of $rel:expr parameter cleanly enables relocation-aware PC-offset handling. This is a good design that maintains backward compatibility while adding the new capability.


382-399: New LabelRef trait implementation looks correct.

The generated impl<Rt> $trait_name<Rt, $crate::reloc::LabelRef> follows the same pattern as the existing PC-offset implementations, correctly storing the label reference in the addr field.


414-421: RelocatableInstruction implementation is well-structured.

The to_code_with_reloc implementation correctly:

  1. Generates instruction code with a zero offset placeholder
  2. Invokes the relocation expression $rel(self.addr) to produce the Rel64 variant
  3. Returns both as a tuple with Some(rel)

This aligns with the RelocatableInstruction trait signature from harm/src/instructions.rs.

harm-runtime/src/labels.rs (2)

10-15: LabelInfo enum design is appropriate.

The Forward and Offset variants cleanly represent the two states of a label during assembly. The TODO comment about segments indicates future extensibility.


77-83: name_label allows aliasing a label with multiple names.

This is likely intentional, but note that the reverse lookup (name → id) will only remember the most recently inserted name for that id in terms of which name was used. If multiple names point to the same id, that's fine, but there's no way to enumerate all names for a given label.

harm-runtime/src/runtime.rs (2)

36-44: insert method implementation is correct.

The method properly iterates over encoded instructions, records relocations by position, and appends instructions. The position-based relocation tracking using self.instructions.len() before push is the correct approach.


46-69: Multiple todo!() placeholders indicate incomplete implementation.

The label management methods (current_label, new_forward_label, new_forward_named_label, assign_forward_label) are stubs. Note that current_named_label on lines 53-57 will panic at runtime because it calls new_forward_named_label and assign_forward_label, both of which contain todo!().

Consider either:

  1. Marking these methods as unimplemented/unstable in documentation
  2. Implementing them before merging
  3. Making them #[doc(hidden)] or feature-gated if this is intentional WIP
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (3)

20-29: AbsG0 and factory function are well-designed.

The #[repr(transparent)] ensures ABI compatibility, and the inline factory function provides a clean API.


141-154: MovRelArgs and relocation traits are well-structured.

The MoveWideReloc64 and MoveWideReloc32 traits with Sealed constraint provide a clean type-level API. The trait hierarchy (MoveWideReloc32: MoveWideReloc64) correctly reflects that 32-bit relocations are a subset of 64-bit ones.


334-386: RelocatableInstruction implementations are consistent.

All six implementations (MovZ/MovK/MovN × 64/32-bit) follow the same pattern:

  1. Destructure MovRelArgs to get register and relocation
  2. Generate instruction code with default immediate and appropriate shift
  3. Extract the Rel64 relocation variant
  4. Return the tuple

This is clean and maintainable.

harm/src/instructions/control/branch_imm.rs (5)

115-134: Branch relocation for LabelRef is correctly implemented.

The MakeBranch<LabelRef> and RelocatableInstruction implementations properly:

  1. Store the label reference
  2. Generate instruction with zero offset placeholder
  3. Emit Rel64::Jump26 relocation

173-192: Conditional branch relocation is correct.

The CondBr19 relocation type is appropriate for the 19-bit conditional branch offset field.


242-261: BranchLink relocation correctly uses Call26.

The implementation mirrors Branch<LabelRef> but uses Call26 instead of Jump26, which is the correct distinction for BL vs B instructions.


269-270: CompareBranch has Sealed for LabelRef but no RelocatableInstruction impl.

Line 270 adds impl<Reg> Sealed for CompareBranch<Reg, LabelRef> {}, indicating intent to support label-based compare-and-branch instructions (CBZ/CBNZ). However, there's no corresponding MakeCompareBranch<*, LabelRef> or RelocatableInstruction implementation.

Is this intentional incomplete work, or should these implementations be added? The relocation type would likely be TstBr14 as defined in Rel64.


368-386: cbz and cbnz function signatures updated correctly.

The generic OffsetDst parameter allows future support for LabelRef once the MakeCompareBranch implementations are added.

Comment on lines +64 to +75
#[inline]
pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId {
if let Some(id) = self.named_labels.get(name).copied() {
self.labels.insert(id, LabelInfo::Offset(offset));
id
} else {
let id = self.next_label();
self.named_labels.insert(name.to_string(), id);
self.labels.insert(id, LabelInfo::Offset(offset));
id
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

define_named_label may silently overwrite an already-defined label.

Unlike define_label, this method doesn't check if the label already has an Offset before overwriting. If a named label was previously defined with an offset (not just forward-declared), this will silently redefine it, which could mask programming errors.

Proposed fix
     #[inline]
     pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId {
         if let Some(id) = self.named_labels.get(name).copied() {
+            if let Some(LabelInfo::Offset(_)) = self.labels.get(&id) {
+                panic!("Named label {name:?} is already defined");
+            }
             self.labels.insert(id, LabelInfo::Offset(offset));
             id
         } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[inline]
pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId {
if let Some(id) = self.named_labels.get(name).copied() {
self.labels.insert(id, LabelInfo::Offset(offset));
id
} else {
let id = self.next_label();
self.named_labels.insert(name.to_string(), id);
self.labels.insert(id, LabelInfo::Offset(offset));
id
}
}
#[inline]
pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId {
if let Some(id) = self.named_labels.get(name).copied() {
if let Some(LabelInfo::Offset(_)) = self.labels.get(&id) {
panic!("Named label {name:?} is already defined");
}
self.labels.insert(id, LabelInfo::Offset(offset));
id
} else {
let id = self.next_label();
self.named_labels.insert(name.to_string(), id);
self.labels.insert(id, LabelInfo::Offset(offset));
id
}
}
🤖 Prompt for AI Agents
In harm-runtime/src/labels.rs around lines 64 to 75, define_named_label
currently overwrites an existing label's Offset without checking; update it to
first check the current LabelInfo for the found id and if it's already
LabelInfo::Offset, fail fast (panic or return an Err consistent with the crate's
error handling) instead of silently overwriting; only insert the Offset when the
existing entry is a forward-declared placeholder (or absent), and preserve
existing behavior for creating new ids when the name is not present.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
harm/src/reloc.rs (1)

48-48: Fix formatting: extra space before comment.

There's an extra space before the comment marker. This was already flagged in a previous review.

🧹 Nitpick comments (1)
harm/src/reloc.rs (1)

25-61: Consider adding Copy derive and documentation.

Since all variants of Rel64 contain only Copy types (None is zero-sized, and LabelRef is Copy), adding #[derive(Copy)] would enable more ergonomic usage patterns.

Additionally, consider adding documentation comments for the public Rel64 enum and its variants. AArch64 relocation types have specific meanings and use cases that would benefit from brief explanations, especially for variants like Nc (no-check) and S (signed) suffixes.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 854b6ad and 4c17c66.

📒 Files selected for processing (2)
  • harm-runtime/src/runtime.rs
  • harm/src/reloc.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • harm-runtime/src/runtime.rs
🔇 Additional comments (3)
harm/src/reloc.rs (3)

8-10: LGTM!

The LabelId newtype is well-designed with appropriate derives and repr(transparent).


12-16: LGTM!

The type aliases are clear, and the comment explaining why Offset is i64 provides helpful context.


18-23: LGTM!

The LabelRef structure appropriately represents label references with offsets.

Jump26(LabelRef),
Call26(LabelRef), // same as Jump26 actually?

// TODO `MOVW` and some `add`/`ldst`-related relocations
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify or remove the TODO comment.

The TODO mentions "MOVW" relocations, but MovW variants are implemented immediately below (lines 51-60). If additional MOVW or add/ldst-related relocations are still needed, please clarify which ones; otherwise, consider removing or updating this comment to reflect what's been completed.

🤖 Prompt for AI Agents
In harm/src/reloc.rs around line 50, the TODO commenting that "`MOVW` and some
`add`/`ldst`-related relocations" are missing is stale because MovW variants are
implemented directly below; either remove the TODO or update it to be specific:
if there are remaining relocations to implement, list the exact relocation names
(e.g., which add/ldst forms or encodings) and/or reference an issue/PR number
and expected work; otherwise delete the comment so it no longer misleads
readers. Ensure the updated comment clearly states remaining tasks (if any) or
is removed to reflect current implementation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)

58-58: Typos remain unfixed from previous review.

The following typos flagged in previous reviews are still present:

  • Line 58: "unsinged" should be "unsigned"
  • Line 80: "singed" should be "signed"
  • Line 91: "unsinged" should be "unsigned"
  • Line 113: "singed" should be "signed"

Please correct these spelling errors in the documentation comments.

Also applies to: 80-80, 91-91, 113-113

🧹 Nitpick comments (2)
harm/src/instructions/control/branch_imm.rs (1)

41-45: Consider simplifying the double cast.

The conversion cond as u8 as u32 works correctly but can be simplified to cond as u32 since Rust allows direct casting from u8 (the repr type) to u32.

🔎 Proposed simplification
 impl From<BranchCond> for BitValue<4> {
     fn from(cond: BranchCond) -> Self {
-        Self::new_u32(cond as u8 as u32)
+        Self::new_u32(cond as u32)
     }
 }
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)

159-277: Consider using .expect() instead of .unwrap() for hardcoded shift values.

While the hardcoded shift values (0, 16, 32, 48) should never fail, using .expect("shift value must be valid") would make debugging easier if the assumption is ever violated and would better document the invariant.

Example refactor for one implementation
 impl MoveWideReloc64 for AbsG0 {
     #[inline]
     fn get_shift64(&self) -> Shift64 {
-        Shift64::new(0).unwrap()
+        Shift64::new(0).expect("shift value 0 must be valid")
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c17c66 and 204c1e0.

📒 Files selected for processing (3)
  • harm-runtime/Cargo.toml
  • harm/src/instructions/control/branch_imm.rs
  • harm/src/instructions/dpimm/movewide/movewide_reloc.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • harm-runtime/Cargo.toml
🧰 Additional context used
🧬 Code graph analysis (2)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (2)
harm/src/instructions/dpimm/movewide.rs (16)
  • movk (164-173)
  • movn (189-198)
  • movz (214-223)
  • new (57-57)
  • new (74-80)
  • new (90-96)
  • new (106-112)
  • new (121-127)
  • new (137-143)
  • new (153-159)
  • to_code (176-178)
  • to_code (182-184)
  • to_code (201-203)
  • to_code (207-209)
  • to_code (226-228)
  • to_code (232-234)
harm/src/instructions/mov/mov_imm.rs (4)
  • to_code (88-94)
  • to_code (99-104)
  • to_code (247-253)
  • to_code (258-263)
harm/src/instructions/control/branch_imm.rs (1)
harm/src/instructions.rs (4)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
  • to_code (19-19)
🔇 Additional comments (13)
harm/src/instructions/control/branch_imm.rs (9)

115-134: LGTM! Label-based unconditional branch implementation is correct.

The implementation correctly emits a zero offset placeholder in the instruction code and returns a Jump26 relocation record for later resolution.


169-171: LGTM! Good use of the new conversion trait.

The updated code properly uses cond.into() to leverage the new From<BranchCond> trait, making the code more idiomatic.


173-193: LGTM! Conditional branch with label reference is correctly implemented.

The implementation properly emits a zero offset placeholder and returns a CondBr19 relocation record for conditional branches to labels.


202-207: LGTM! Generic signature properly supports both offset types.

The refactored function signature allows bl to work with both immediate offsets and label references while maintaining type safety.


210-213: LGTM! Generic BranchLink structure is well designed.

The use of the sealed trait pattern for both BranchOffset and LabelRef properly constrains the API while enabling both immediate and label-based variants.


242-261: LGTM! Branch-and-link with label reference is correctly implemented.

The implementation properly uses Call26 relocation type for function call semantics and emits the appropriate zero offset placeholder.


278-340: LGTM! Compare-branch trait implementations are complete and correct.

The implementations properly handle both 32-bit and 64-bit register variants with support for direct offsets and i32 conversions.


342-366: LGTM! Instruction encoding for compare-branch is correct.

Both 32-bit and 64-bit variants properly dispatch to the correct CBZ/CBNZ instruction encodings based on the equal field.


368-386: LGTM! Generic signatures are well designed.

The generic signatures for cbz and cbnz properly support multiple register and offset types through trait bounds. Note that label-based offsets cannot be used until the missing implementations flagged above (Lines 263-270) are completed.

harm/src/instructions/dpimm/movewide/movewide_reloc.rs (4)

130-157: LGTM!

The trait hierarchy and Sealed pattern are well-designed. The MoveWideReloc32: MoveWideReloc64 inheritance correctly ensures 32-bit relocations can provide both shift types.


279-319: LGTM!

Correctly implements only AbsG0 and AbsG1 variants for 32-bit relocations, since 32-bit addresses only require two 16-bit nibbles.


321-345: LGTM!

The MakeMovArgs implementations correctly use Unfallible wrappers since relocation-based instructions cannot fail at construction time (the actual address computation happens at link time).


347-399: LGTM!

The RelocatableInstruction implementations correctly use MoveImm16::default() as a placeholder since the linker will patch the actual immediate value during relocation. The pattern is consistent across all instruction variants.

Comment on lines +465 to +485
#[test]
fn test_64_abs_g2s_movk() {
let label = LabelRef {
id: LabelId(6),
addend: 47,
};
let inst = movz(X6, abs_g2_s(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X6, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2S(label)));
}

#[test]
fn test_64_abs_g2nc_movk() {
let label = LabelRef {
id: LabelId(7),
addend: 48,
};
let inst = movz(X7, abs_g2_nc(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X7, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2Nc(label)));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test names don't match the instructions being tested.

Lines 466 and 477 have naming inconsistencies:

  • test_64_abs_g2s_movk uses movz but the name says movk
  • test_64_abs_g2nc_movk uses movz but the name says movk

These should be renamed to test_64_abs_g2s_movz and test_64_abs_g2nc_movz respectively for clarity.

Proposed fix
     #[test]
-    fn test_64_abs_g2s_movk() {
+    fn test_64_abs_g2s_movz() {
         let label = LabelRef {
             id: LabelId(6),
             addend: 47,
         };
         let inst = movz(X6, abs_g2_s(label)).to_code_with_reloc();
         assert_eq!(inst.0, movz(X6, (0, 32)).unwrap().to_code());
         assert_eq!(inst.1, Some(Rel64::MovWAbsG2S(label)));
     }

     #[test]
-    fn test_64_abs_g2nc_movk() {
+    fn test_64_abs_g2nc_movz() {
         let label = LabelRef {
             id: LabelId(7),
             addend: 48,
         };
         let inst = movz(X7, abs_g2_nc(label)).to_code_with_reloc();
         assert_eq!(inst.0, movz(X7, (0, 32)).unwrap().to_code());
         assert_eq!(inst.1, Some(Rel64::MovWAbsG2Nc(label)));
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[test]
fn test_64_abs_g2s_movk() {
let label = LabelRef {
id: LabelId(6),
addend: 47,
};
let inst = movz(X6, abs_g2_s(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X6, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2S(label)));
}
#[test]
fn test_64_abs_g2nc_movk() {
let label = LabelRef {
id: LabelId(7),
addend: 48,
};
let inst = movz(X7, abs_g2_nc(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X7, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2Nc(label)));
}
#[test]
fn test_64_abs_g2s_movz() {
let label = LabelRef {
id: LabelId(6),
addend: 47,
};
let inst = movz(X6, abs_g2_s(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X6, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2S(label)));
}
#[test]
fn test_64_abs_g2nc_movz() {
let label = LabelRef {
id: LabelId(7),
addend: 48,
};
let inst = movz(X7, abs_g2_nc(label)).to_code_with_reloc();
assert_eq!(inst.0, movz(X7, (0, 32)).unwrap().to_code());
assert_eq!(inst.1, Some(Rel64::MovWAbsG2Nc(label)));
}
🤖 Prompt for AI Agents
In harm/src/instructions/dpimm/movewide/movewide_reloc.rs around lines 465 to
485, the two test function names are inconsistent with the instruction used:
rename test_64_abs_g2s_movk to test_64_abs_g2s_movz and test_64_abs_g2nc_movk to
test_64_abs_g2nc_movz so the function names reflect that they call movz; update
the two fn identifiers only (no logic changes) to keep names and tested
instructions consistent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 204c1e0 and 5e208d3.

📒 Files selected for processing (1)
  • harm/src/instructions/control/branch_imm.rs
🧰 Additional context used
🧬 Code graph analysis (1)
harm/src/instructions/control/branch_imm.rs (3)
harm/src/instructions.rs (4)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
  • to_code (19-19)
harm-types/src/A64/register.rs (4)
  • from (169-171)
  • from (176-178)
  • from (330-332)
  • from (337-339)
harm/src/instructions/control/branch_reg.rs (4)
  • to_code (39-41)
  • to_code (48-50)
  • to_code (62-64)
  • reg (31-34)
🔇 Additional comments (13)
harm/src/instructions/control/branch_imm.rs (13)

10-10: LGTM! Imports support the new relocation functionality.

The added imports (BitValue, RelocatableInstruction, LabelRef, Rel64) are all necessary for implementing label-based branch instructions and relocations.

Also applies to: 14-14, 16-16


41-45: LGTM! Branch condition conversion is correct.

The From<BranchCond> for BitValue<4> implementation correctly converts the 4-bit condition codes to BitValue<4>.


115-134: LGTM! Unconditional branch with label reference is correctly implemented.

The implementation properly creates a Branch<LabelRef> and emits a zero-offset instruction with a Jump26 relocation, following the standard relocation pattern.


169-171: LGTM! Uses the new Into<BitValue<4>> conversion.

The change to use cond.into() is cleaner and leverages the newly implemented From<BranchCond> for BitValue<4> trait.


173-193: LGTM! Conditional branch with label reference is correctly implemented.

The implementation properly creates conditional branches with label references and uses CondBr19 relocation, which is appropriate for the 19-bit conditional branch offset field.


202-213: LGTM! Branch-and-link generics follow the established pattern.

Making BranchLink generic over Offset and updating the bl function signature is consistent with the approach used for Branch and b.


215-261: LGTM! Branch-and-link implementations are complete and correct.

All BranchLink implementations follow established patterns:

  • Direct offset construction
  • Feature-gated i32 convenience conversion
  • LabelRef support with Call26 relocation (correct for branch-and-link instructions)

263-270: LGTM! Generic structure enables label reference support.

Making CompareBranch generic over Offset enables the necessary flexibility for both immediate offsets and label references.


278-324: LGTM! 64-bit compare-branch implementations are complete.

The MakeCompareBranch implementations for RegOrZero64 correctly handle:

  • Direct CompareBranchOffset construction
  • Fallible i32 conversion
  • Label reference support

This addresses the past review comment about incomplete implementation for label references.


326-370: LGTM! 32-bit compare-branch implementations mirror the 64-bit variants.

The implementations for RegOrZero32 follow the same correct pattern as the 64-bit variants, providing complete coverage for compare-branch operations with label references.


372-383: LGTM! Raw instruction implementations correctly emit CBZ/CBNZ.

The RawInstruction implementations properly select between CBZ (equal) and CBNZ (not equal) variants for both 64-bit and 32-bit registers.

Also applies to: 403-414


434-452: LGTM! Generic signatures enable flexible compare-branch API.

The generic signatures for cbz and cbnz enable the functions to work with various register types (32/64-bit) and offset types (immediate, i32, LabelRef) while maintaining type safety.


705-755: Test coverage is comprehensive, but may need updates if relocation type changes.

The tests thoroughly cover label-based relocations for all four CBZ/CBNZ variants (64/32-bit, zero/nonzero). They correctly verify that:

  1. Instructions generate machine code matching the zero-offset variant
  2. Relocations are properly emitted

Note: If the relocation type for CBZ/CBNZ changes from TstBr14 to a 19-bit type (per the previous comment), these test assertions will need to be updated accordingly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)

401-542: Consider adding test coverage for G0 Nc/S variants (optional).

The test suite provides good coverage of the relocation types and instructions. For completeness, you could add tests for abs_g0_nc and abs_g0_s variants (both 32-bit and 64-bit paths), though these are less common in practice.

Example test additions
#[test]
fn test_64_abs_g0nc_movk() {
    let label = LabelRef {
        id: LabelId(9),
        addend: 50,
    };
    let inst = movk(X9, abs_g0_nc(label)).to_code_with_reloc();
    assert_eq!(inst.0, movk(X9, (0, 0)).unwrap().to_code());
    assert_eq!(inst.1, Some(Rel64::MovWAbsG0Nc(label)));
}

#[test]
fn test_64_abs_g0s_movn() {
    let label = LabelRef {
        id: LabelId(10),
        addend: 51,
    };
    let inst = movn(X10, abs_g0_s(label)).to_code_with_reloc();
    assert_eq!(inst.0, movn(X10, (0, 0)).unwrap().to_code());
    assert_eq!(inst.1, Some(Rel64::MovWAbsG0S(label)));
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e208d3 and 446c581.

📒 Files selected for processing (2)
  • harm/src/instructions/control/branch_imm.rs
  • harm/src/instructions/dpimm/movewide/movewide_reloc.rs
🔇 Additional comments (14)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (7)

20-128: LGTM! Clean relocation type definitions.

The AbsG* struct definitions and their constructor functions follow a consistent, idiomatic pattern. The documentation accurately describes the bit ranges and overflow semantics for each nibble (G0 through G3), and all the typos from previous reviews have been properly addressed.


130-157: LGTM! Well-designed trait hierarchy.

The trait design cleanly separates 32-bit and 64-bit relocation paths using the MoveWideReloc32 extension of MoveWideReloc64. The Sealed trait pattern provides good encapsulation, and MovRelArgs effectively bundles the destination register with the relocation descriptor.


159-277: LGTM! Correct shift and relocation calculations.

All ten MoveWideReloc64 implementations correctly calculate the bit shifts (0, 16, 32, 48) corresponding to the four 16-bit nibbles of a 64-bit address. The relocation enum variants are consistently mapped, and the use of .unwrap() on compile-time constant shifts is safe.


279-319: LGTM! Correct 32-bit relocation subset.

The MoveWideReloc32 implementations correctly cover only the G0 and G1 nibbles (bits [31:0]), which is appropriate for 32-bit addressing. G2 and G3 variants correctly do not implement this trait since they reference bits beyond the 32-bit address space.


321-345: LGTM! Type-safe argument construction.

The MakeMovArgs implementations provide clean type-level dispatch for 32-bit vs 64-bit register paths. The use of Unfallible appropriately signals that these conversions cannot fail, and the trait bounds ensure compile-time correctness.


347-399: LGTM! Consistent relocatable instruction encoding.

All six RelocatableInstruction implementations follow a consistent, correct pattern: they generate instruction code with a placeholder immediate and the appropriate shift, then return the code paired with the relocation metadata. The separation of 64-bit and 32-bit paths via trait bounds ensures type safety.


477-485: Clarification: test name is actually correct.

The past review comment suggested renaming test_64_abs_g2nc_movk to test_64_abs_g2nc_movz, but this was incorrect. Line 482 uses movk(X7, abs_g2_nc(label)), so the current function name accurately reflects the instruction being tested.

harm/src/instructions/control/branch_imm.rs (7)

41-45: LGTM!

The conversion from BranchCond to BitValue<4> is correct. All enum values fit within 4 bits, and the cast chain is safe.


115-134: LGTM!

The label-based relocation support for unconditional branch is correctly implemented:

  • Jump26 is the appropriate relocation type for the 26-bit immediate field
  • Zero offset placeholder is correctly used in the instruction encoding

173-193: LGTM!

The conditional branch relocation implementation is correct:

  • CondBr19 is the appropriate relocation type for conditional branches with 19-bit immediate fields
  • The cond.into() conversion uses the newly added From implementation

242-261: LGTM!

The branch-with-link relocation implementation is correct:

  • Call26 is the appropriate relocation type for BL instructions
  • The generic structure allows both offset-based and label-based calls

434-452: LGTM!

The generic function signatures for cbz and cbnz are well-designed:

  • Type inference allows callers to use either immediate offsets or label references
  • The trait bounds ensure type safety while providing flexibility

705-755: LGTM!

Comprehensive test coverage for label-based CBZ/CBNZ relocations:

  • All register width variants (32-bit and 64-bit) are tested
  • Tests verify both instruction encoding and relocation generation
  • Different label IDs and addends are used to ensure uniqueness

385-432: Code is correct; no separate compare-branch relocation type exists.

The Rel64 enum contains only a single 19-bit relocation type (CondBr19) for all instructions with 19-bit immediate fields (conditional branches, compare-and-branch, etc.). The test-and-branch instructions use the distinct TstBr14 type. Since CBZ/CBNZ and B.cond share identical 19-bit encoding in ARM64, reusing CondBr19 for both is the intended design.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
harm/src/reloc.rs (1)

50-60: TODO comment is stale — MovW variants are implemented.

The comment on line 50 mentions "MOVW and some add/ldst-related relocations" as TODO, but MovWAbsG0 through MovWAbsG3 (and their Nc/S variants) are already implemented directly below. Consider updating the TODO to specify only the remaining add/ldst relocations, or remove it if all intended work is complete.

harm-runtime/src/labels.rs (2)

49-62: Replace todo!() with panic!() for consistent error handling.

Line 56 uses todo!() for the already-defined label case, which will panic with a less informative message. For consistency with the panic on line 60 and clearer runtime diagnostics, use panic!() directly.

Proposed fix
                 LabelInfo::Offset(_) => {
-                    todo!("Label {id:?} is already defined");
+                    panic!("Label {id:?} is already defined");
                 }

64-75: define_named_label may silently overwrite an already-defined label.

Unlike define_label, this method doesn't check if the label already has an Offset before overwriting. If a named label was previously defined (not just forward-declared), this will silently redefine it, potentially masking programming errors.

Proposed fix
     pub fn define_named_label(&mut self, name: &str, offset: Offset) -> LabelId {
         if let Some(id) = self.named_labels.get(name).copied() {
+            if let Some(LabelInfo::Offset(_)) = self.labels.get(&id) {
+                panic!("Named label {name:?} is already defined");
+            }
             self.labels.insert(id, LabelInfo::Offset(offset));
             id
         } else {
🧹 Nitpick comments (6)
harm-runtime/src/labels.rs (1)

77-83: name_label may overwrite an existing name mapping without warning.

If the same name is already associated with a different LabelId, this will silently replace the mapping. Consider checking for existing name conflicts to prevent accidental aliasing bugs.

Proposed fix
     pub fn name_label(&mut self, id: LabelId, name: &str) {
         if self.labels.contains_key(&id) {
+            if let Some(&existing_id) = self.named_labels.get(name) {
+                if existing_id != id {
+                    panic!("Name {name:?} is already assigned to label {existing_id:?}");
+                }
+            }
             self.named_labels.insert(name.to_string(), id);
         } else {
             panic!("Label {id:?} is not registered");
         }
     }
harm-runtime/src/runtime.rs (1)

27-34: Consider pre-allocating relocations in with_capacity.

with_capacity pre-allocates instructions but not relocations. If the caller expects many relocations, consider accepting a second capacity hint or using HashMap::with_capacity with a reasonable fraction of cap.

harm/src/instructions/dpimm/movewide.rs (2)

10-20: Consider explicit re-exports instead of wildcard.

The wildcard re-export pub use self::movewide_reloc::*; makes it unclear which items are being exposed publicly. This can make it harder for users to understand the API surface and may lead to unintended exports if the module is extended later.

🔎 Alternative approach with explicit re-exports
-pub use self::movewide_reloc::*;
+pub use self::movewide_reloc::{
+    abs_g0, abs_g0_nc, abs_g0_s, abs_g1, abs_g1_nc, abs_g1_s,
+    abs_g2, abs_g2_nc, abs_g2_s, abs_g3, MovRelArgs
+};

This makes the public API explicit and easier to understand.


29-33: Expand documentation for the type-level tag pattern.

The documentation briefly states that MovWideTypeTag is "never constructed," but doesn't explain the design rationale or how it enables the relocation functionality. Users encountering this type in error messages or documentation may be confused about its purpose.

🔎 Suggested documentation improvement
-/// This is a technical type used for type constraints.  It is never constructed, but only used in trait definitions
-/// and constraints.
+/// A type-level tag used to enable type-safe dispatch for move-wide instruction arguments.
+///
+/// This zero-sized type is never instantiated at runtime but serves as a marker in trait bounds
+/// to allow the same public API (`movk`, `movn`, `movz`) to work with both immediate values
+/// (`MovImmArgs`) and relocatable label references (`MovRelArgs`). The generic parameter `X`
+/// determines which argument type the instruction will accept.
+///
+/// Users typically don't interact with this type directly; it's used internally by the
+/// move-wide instruction constructors.
 pub struct MovWideTypeTag<X>(PhantomData<X>);
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)

401-542: Good test coverage with room for optional expansion.

The tests cover the primary relocation scenarios for both 64-bit and 32-bit variants. Each test correctly verifies that the emitted instruction matches a zero-immediate variant and that the appropriate relocation is attached.

Consider adding tests for the currently untested combinations:

  • 64-bit: abs_g0_nc and abs_g0_s variants with MovZ/MovK/MovN
  • 64-bit: MovK with AbsG0, MovN with AbsG2
  • This would improve coverage of the matrix of instruction types × relocation types
Example test structure
#[test]
fn test_64_abs_g0nc_movk() {
    let label = LabelRef {
        id: LabelId(9),
        addend: 50,
    };
    let inst = movk(X9, abs_g0_nc(label)).to_code_with_reloc();
    assert_eq!(inst.0, movk(X9, (0, 0)).unwrap().to_code());
    assert_eq!(inst.1, Some(Rel64::MovWAbsG0Nc(label)));
}
harm/src/instructions/control/branch_imm.rs (1)

705-755: Good test coverage for compare-and-branch; consider adding tests for B and BL.

The tests comprehensively cover CBZ/CBNZ with label references for both 32-bit and 64-bit variants. Each test correctly verifies that the code matches a zero-offset instruction and the appropriate CondBr19 relocation is attached.

Consider adding tests for the other label-based branch instructions introduced in this PR:

  • Branch<LabelRef> (unconditional branch with Jump26 relocation)
  • Branch<(BranchCond, LabelRef)> (conditional branch with CondBr19)
  • BranchLink<LabelRef> (branch-with-link using Call26)
Example test structure
#[test]
fn test_b_label() {
    let label = LabelRef {
        id: LabelId(10),
        addend: 0,
    };
    let inst = b::<_, LabelRef>(label);
    let (code, reloc) = inst.to_code_with_reloc();
    assert_eq!(code, b(BranchOffset::default()).to_code());
    assert_eq!(reloc, Some(Rel64::Jump26(label)));
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 446c581 and 8d57860.

📒 Files selected for processing (15)
  • Cargo.toml
  • harm-runtime/Cargo.toml
  • harm-runtime/src/labels.rs
  • harm-runtime/src/lib.rs
  • harm-runtime/src/runtime.rs
  • harm/src/instructions.rs
  • harm/src/instructions/control/branch_imm.rs
  • harm/src/instructions/control/testbranch.rs
  • harm/src/instructions/dpimm/movewide.rs
  • harm/src/instructions/dpimm/movewide/movewide_reloc.rs
  • harm/src/instructions/ldst/ldr.rs
  • harm/src/instructions/ldst/ldrsw.rs
  • harm/src/instructions/ldst/macros.rs
  • harm/src/instructions/mov/mov_imm.rs
  • harm/src/reloc.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • harm-runtime/Cargo.toml
  • harm/src/instructions/ldst/ldrsw.rs
  • Cargo.toml
  • harm-runtime/src/lib.rs
🧰 Additional context used
🧬 Code graph analysis (7)
harm/src/instructions/mov/mov_imm.rs (1)
harm/src/instructions/dpimm/movewide.rs (9)
  • movn (189-198)
  • movz (214-223)
  • new (57-57)
  • new (74-80)
  • new (90-96)
  • new (106-112)
  • new (121-127)
  • new (137-143)
  • new (153-159)
harm-runtime/src/runtime.rs (2)
harm/src/instructions.rs (1)
  • instructions (48-50)
harm/src/instructions/control/branch_imm.rs (7)
  • new (275-275)
  • new (285-291)
  • new (302-308)
  • new (317-323)
  • new (333-339)
  • new (348-354)
  • new (363-369)
harm/src/instructions.rs (2)
harm/src/instructions/control/testbranch.rs (2)
  • to_code_with_reloc (123-133)
  • to_code_with_reloc (138-148)
harm/src/instructions/control/branch_imm.rs (5)
  • to_code_with_reloc (126-133)
  • to_code_with_reloc (184-192)
  • to_code_with_reloc (253-260)
  • to_code_with_reloc (387-400)
  • to_code_with_reloc (418-431)
harm/src/instructions/ldst/macros.rs (2)
harm/src/instructions.rs (3)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
harm/src/instructions/control/testbranch.rs (4)
  • to_code (93-103)
  • to_code (108-118)
  • to_code_with_reloc (123-133)
  • to_code_with_reloc (138-148)
harm/src/instructions/control/testbranch.rs (1)
harm/src/instructions.rs (4)
  • instructions (48-50)
  • to_code_with_reloc (23-23)
  • to_code_with_reloc (30-32)
  • to_code (19-19)
harm/src/instructions/dpimm/movewide.rs (1)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (2)
  • new (326-331)
  • new (339-344)
harm/src/instructions/dpimm/movewide/movewide_reloc.rs (1)
harm/src/instructions/dpimm/movewide.rs (16)
  • movk (164-173)
  • movn (189-198)
  • movz (214-223)
  • new (57-57)
  • new (74-80)
  • new (90-96)
  • new (106-112)
  • new (121-127)
  • new (137-143)
  • new (153-159)
  • to_code (176-178)
  • to_code (182-184)
  • to_code (201-203)
  • to_code (207-209)
  • to_code (226-228)
  • to_code (232-234)
🔇 Additional comments (24)
harm/src/reloc.rs (1)

1-24: LGTM!

The foundational types (LabelId, Offset, Addr, LabelRef) are well-designed for relocation handling. Using #[repr(transparent)] on LabelId is appropriate for potential FFI compatibility, and the type aliases provide clear semantics.

harm/src/instructions/ldst/ldr.rs (1)

168-183: LGTM!

The PC-relative literal LDR rules now correctly specify Rel64::LdPrelLo19 as the relocation type, consistent with the AArch64 relocation model for 19-bit PC-relative load offsets. Both 32-bit and 64-bit variants appropriately use the same relocation type.

harm/src/instructions/mov/mov_imm.rs (3)

10-11: LGTM!

The import of MovWideTypeTag alongside MakeMovArgs and MovImmArgs sets up the type-level dispatch needed for the generic move-wide argument construction path.


144-158: LGTM!

The refactored 32-bit path correctly uses the MovWideTypeTag<MovImmArgs<_>> indirection through MakeMovArgs::new, enabling the unified construction path that supports both immediate and relocation-based variants. The .map() chain properly transforms the outcome into MovNOrZImm.


224-238: LGTM!

The 64-bit path mirrors the 32-bit implementation correctly, using the same MovWideTypeTag<MovImmArgs<_>> pattern for consistency across register widths.

harm/src/instructions/ldst/macros.rs (1)

342-424: LGTM!

The macro extension cleanly adds LabelRef support for PC-relative loads:

  • The new $rel:expr parameter allows callers to specify the relocation type.
  • The LabelRef trait implementation (lines 385-399) mirrors the existing Pc + LdStPcOffset path.
  • The RelocatableInstruction impl (lines 414-421) correctly emits a zero offset in the opcode and returns the appropriate Rel64 relocation constructed via $rel(self.addr).

This pattern is consistent with other relocation-aware instructions like TestBranch.

harm/src/instructions/control/testbranch.rs (3)

32-33: LGTM!

The Sealed impls and MakeTestBranch impls for LabelRef offset variants are correctly added for both 64-bit and 32-bit register widths. The implementation pattern is consistent with the existing TestBranchOffset variants.

Also applies to: 65-89


121-149: LGTM!

The RelocatableInstruction implementations correctly:

  1. Generate the base opcode using a default (zero) offset via the existing RawInstruction impl.
  2. Return Rel64::TstBr14 with the label reference for the linker to patch.

This is the correct pattern for producing relocatable machine code.


315-337: LGTM!

Good test coverage for the relocation path. The tests verify that:

  • The opcode matches the non-relocated version with a default offset.
  • The relocation correctly captures the LabelRef in Rel64::TstBr14.

Consider adding 32-bit variant tests (test_tbz_32_reloc, test_tbnz_32_reloc) for completeness, though the implementation is symmetric.

harm/src/instructions.rs (2)

8-8: LGTM!

The Rel64 type is correctly propagated through the instruction encoding traits:

  • RelocatableInstruction::to_code_with_reloc signature updated.
  • Blanket impl for RawInstruction correctly returns None for non-relocatable instructions.

This is a clean API evolution to support the expanded relocation model.

Also applies to: 22-33


44-66: LGTM!

The InstructionSeq trait and its blanket implementation correctly propagate Rel64 through the encoding pipeline. The instructions() and bytes() helper methods properly discard relocations when only the raw instruction stream is needed.

harm/src/instructions/dpimm/movewide.rs (3)

69-160: LGTM: Type-level indirection implemented consistently.

The refactoring from direct MovImmArgs implementations to MovWideTypeTag<MovImmArgs> is applied consistently across all register and shift variants. This indirection enables supporting both immediate and relocation-based arguments through the same trait, which aligns with the PR's objective of adding relocation support.


189-223: LGTM: Consistent pattern applied to movn and movz.

The same type-level indirection pattern is applied consistently to movn and movz, maintaining API symmetry with movk. The existing tests at lines 323-363 demonstrate that the changes maintain backward compatibility for common usage patterns.


164-173: The movk function's new generic Args parameter maintains full backward compatibility. All existing callers work without modification, as the compiler correctly infers Args from the register type. The comprehensive test suite (50+ test cases) confirms type inference succeeds for all patterns: simple integers, tuples with shifts, and fallible forms. No cryptic error messages are introduced for correctly-formed calls.

harm/src/instructions/dpimm/movewide/movewide_reloc.rs (5)

20-139: LGTM: Well-structured relocation type hierarchy.

The AbsGx types provide a type-safe way to express which 16-bit nibble of an address a move-wide instruction targets. The #[doc(hidden)] on types with public constructors is an effective pattern to keep the type namespace clean while allowing usage. Documentation clearly maps each type to its corresponding bit range in the 64-bit address space.


141-154: LGTM: Clean trait design for relocation metadata.

The trait hierarchy appropriately models the relationship between 32-bit and 64-bit relocations. MoveWideReloc32 extending MoveWideReloc64 allows code reuse while maintaining type safety, since 32-bit instructions can use the lower address groups (G0, G1) that also exist in 64-bit.


159-277: LGTM: Correct shift and relocation mapping.

All shift calculations correctly map to their respective 16-bit nibbles (0, 16, 32, 48 for G0-G3). The unwrap() calls on Shift64::new() are safe since the shift values are compile-time constants that fit within the valid range. Each relocation type correctly corresponds to its address group and checking variant.


347-399: LGTM: Relocatable instruction emission follows standard pattern.

The implementations correctly emit instructions with zero immediates and attach the appropriate relocation records. The linker will later patch the immediate fields with the computed label offsets. Reusing the existing movk, movn, and movz functions from the parent module maintains consistency and avoids duplication.


279-319: The exclusion of AbsG2/G3 from MoveWideReloc32 is correct.

AbsG2 and AbsG3 address bits [47:32] and [63:48] respectively, which lie beyond the 32-bit address space [31:0]. Implementing MoveWideReloc32 for these types would be architecturally unsound since 32-bit move-wide instructions cannot reference bits beyond [31:0]. The test suite confirms this design: all 32-bit register tests (W1–W4) only use AbsG0 and AbsG1 variants, and no attempts are made to use 32-bit registers with AbsG2 or AbsG3.

harm/src/instructions/control/branch_imm.rs (5)

41-45: LGTM: Straightforward enum-to-bits conversion.

The From implementation correctly converts BranchCond to a 4-bit value. The cast chain as u8 as u32 is safe since all enum variants fit in 4 bits (0x0-0xF). This implementation enables using cond.into() in the instruction encoding functions at lines 169 and 188.


115-134: LGTM: Correct relocation for unconditional branch to label.

The implementation correctly uses Rel64::Jump26 for unconditional branch instructions, which matches the 26-bit immediate field in the B instruction encoding. The zero placeholder offset will be patched by the linker during relocation resolution.


173-193: LGTM: Correct relocation for conditional branch to label.

The implementation correctly uses Rel64::CondBr19 for conditional branch instructions, matching the 19-bit immediate field in the B.cond encoding. This aligns with the ARM64 ELF relocation specification for conditional branches, as confirmed by the previous review.


202-261: LGTM: Branch-with-link correctly uses Call26 relocation.

The BranchLink<LabelRef> implementation appropriately uses Rel64::Call26 for function call relocations, distinguishing it from the Jump26 used for unconditional branches. This distinction is important for tooling that analyzes call graphs or performs call-specific optimizations.


385-432: LGTM: Compare-and-branch correctly uses CondBr19 relocation.

The RelocatableInstruction implementations for CompareBranch with labels correctly use Rel64::CondBr19 for both 32-bit and 64-bit variants. This matches the 19-bit immediate field in CBZ/CBNZ instructions, as confirmed by previous reviews and the ARM64 specification.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
harm-runtime/src/labels.rs (2)

11-11: Clarify or address the TODO comment.

The TODO mentions "segment" but doesn't explain what needs to be implemented. This could refer to tracking which segment (e.g., .text, .data) a label belongs to, but the intent is unclear.

Would you like me to:

  1. Open an issue to track this work with more details, or
  2. Help implement segment tracking if you can clarify the requirements?

8-13: Consider adding documentation for the public API.

The LabelInfo enum and its variants lack documentation. Adding doc comments would improve developer experience by clarifying:

  • What each variant represents
  • When to use Forward vs Offset
  • The overall purpose of the enum in the label resolution system
Example documentation structure
+/// Information about a label's resolution state.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum LabelInfo {
+    /// Label has been referenced but not yet defined (forward reference).
     Forward,
     // TODO segment
+    /// Label has been defined at the given offset.
     Offset(Offset),
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d57860 and 0bbae7c.

📒 Files selected for processing (2)
  • harm-runtime/src/labels.rs
  • harm-runtime/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • harm-runtime/src/lib.rs
🔇 Additional comments (2)
harm-runtime/src/labels.rs (2)

1-5: LGTM!

Standard copyright header with BSD 3-clause license is present and properly formatted.


6-6: LGTM!

Import correctly references the Offset type from the harm relocation module.

@monoid monoid merged commit 127f982 into master Dec 29, 2025
2 checks passed
@monoid monoid deleted the harm/relocations branch December 29, 2025 12:42
@coderabbitai coderabbitai bot mentioned this pull request Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request harm The `harm` dynamic assembler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant