From 9864b8b2ca6f862d88b3561e6e13d6daeb24bb12 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 28 Sep 2025 10:16:59 +0200 Subject: [PATCH 01/15] harm: Relocations From d5987e687669ecb3929cc18c417b04d4457b52a8 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 28 Sep 2025 13:02:31 +0200 Subject: [PATCH 02/15] Labels --- Cargo.toml | 1 + harm-runtime/Cargo.toml | 18 ++++++++ harm-runtime/src/labels.rs | 95 ++++++++++++++++++++++++++++++++++++++ harm-runtime/src/lib.rs | 6 +++ harm/src/reloc.rs | 29 +++++++++++- 5 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 harm-runtime/Cargo.toml create mode 100644 harm-runtime/src/labels.rs create mode 100644 harm-runtime/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index eaba9fe8..5546eb00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "aarchmrs-parser", "aarchmrs-types", "harm", + "harm-runtime", "harm-types", "tools/aarchmrs-generate", "tools/harm-asm-regen", diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml new file mode 100644 index 00000000..24e47fce --- /dev/null +++ b/harm-runtime/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "harm-runtime" +version = "0.1.0" +edition = "2024" +description = "AArch64 (ARM64) dynamic assembler runtime" +license = "BSD-3-Clause" +authors = ["Ivan Boldyrev "] +repository = "https://github.com/monoid/harm/" +keywords = ["arm", "aarch64", "aarchmrs", "assembler", "jit"] +categories = ["compilers", "hardware-support"] +publish = false + +[dependencies] +harm = { workspace = true } + +[dev-dependencies] + +[features] diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs new file mode 100644 index 00000000..2c411121 --- /dev/null +++ b/harm-runtime/src/labels.rs @@ -0,0 +1,95 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use std::collections::HashMap; + +use harm::reloc::{LabelId, Offset}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum LabelInfo { + Forward, + // TODO segment + Offset(Offset), +} + +#[derive(Debug, Default)] +pub struct LabelRegistry { + named_labels: HashMap, + labels: HashMap, + next_id: usize, +} + +impl LabelRegistry { + #[inline] + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub fn forward_named_label(&mut self, name: &str) -> LabelId { + if let Some(id) = self.named_labels.get(name) { + *id + } else { + let id = self.next_label(); + self.named_labels.insert(name.to_string(), id); + self.labels.insert(id, LabelInfo::Forward); + id + } + } + + #[inline] + pub fn forward_label(&mut self) -> LabelId { + let id = self.next_label(); + self.labels.insert(id, LabelInfo::Forward); + id + } + + pub fn define_label(&mut self, id: LabelId, offset: Offset) { + if let Some(info) = self.labels.get_mut(&id) { + match info { + LabelInfo::Forward => { + *info = LabelInfo::Offset(offset); + } + LabelInfo::Offset(_) => { + todo!("Label {id:?} is already defined"); + } + } + } else { + panic!("Label {id:?} is not registered"); + } + } + + #[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 + } + } + + pub fn name_label(&mut self, id: LabelId, name: &str) { + if self.labels.contains_key(&id) { + self.named_labels.insert(name.to_string(), id); + } else { + panic!("Label {id:?} is not registered"); + } + } + + #[inline] + pub fn label_info(&self, id: LabelId) -> Option<&LabelInfo> { + self.labels.get(&id) + } + + fn next_label(&mut self) -> LabelId { + let id = LabelId(self.next_id); + self.next_id += 1; + id + } +} diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs new file mode 100644 index 00000000..cdcc60b9 --- /dev/null +++ b/harm-runtime/src/lib.rs @@ -0,0 +1,6 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +pub mod labels; diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index 893ec752..913e1aa0 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -1,5 +1,30 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +// TODO These type definitions should probably be moved to `harm-types` crate. + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct LabelId(pub usize); + +// Every offset in an instruction does fit in i32. +// But what if we want to support virtual instructions with larger offsets? +pub type Offset = i64; + +pub type Addr = u64; + +// b_cond(Cond, LabelRef) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct LabelRef { + pub id: LabelId, + pub offset: Offset, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Reloc { - // This is a stub type which is intentionally a zero-variant enum. - // Currently it is used as Option which is always None. + Label(LabelRef), + // TODO lower bits, middle bits, signed and unsigned highest bits + Delta(LabelRef, LabelRef), // label1 - label2 } From 6cdab44ba29784b595d3ca2ced6e0bee6add3967 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Thu, 2 Oct 2025 00:11:42 +0200 Subject: [PATCH 03/15] Runtime --- harm-runtime/Cargo.toml | 5 +-- harm-runtime/src/lib.rs | 1 + harm-runtime/src/runtime.rs | 70 +++++++++++++++++++++++++++++++++++++ harm/src/reloc.rs | 2 +- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 harm-runtime/src/runtime.rs diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index 24e47fce..fc17686e 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -12,7 +12,8 @@ publish = false [dependencies] harm = { workspace = true } - +memmap = "0.7.0" + [dev-dependencies] - + [features] diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index cdcc60b9..cf7cc344 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -4,3 +4,4 @@ */ pub mod labels; +pub mod runtime; diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs new file mode 100644 index 00000000..539d6ae0 --- /dev/null +++ b/harm-runtime/src/runtime.rs @@ -0,0 +1,70 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use std::collections::HashMap; + +use crate::labels::LabelRegistry; +use harm::InstructionCode; +use harm::reloc::{Reloc, LabelId}; +use harm::instructions::InstructionSeq; + +// N.N. we keep here internal relocation type, and convert it to external on serialization. +#[derive(Default)] +pub struct Assembler { + label_manager: LabelRegistry, + instructions: Vec, + relocations: HashMap, +} + +impl Assembler { + #[inline] + pub fn new() -> Self { + <_>::default() + } + + #[inline] + pub fn with_capacity(cap: usize) -> Self { + Self { + label_manager: LabelRegistry::new(), + instructions: Vec::with_capacity(cap), + relocations: HashMap::new(), + } + } + + pub fn insert(&mut self, s: InstSeq) { + for (inst, rel) in s.encode() { + let pos = self.instructions.len(); + if let Some(rel) = rel { + self.relocations.insert(pos, rel); + } + self.instructions.push(inst); + } + } + + // TODO the label have to be aligned. + // For an instruction, it is alwasy 4 bytes, but for data it can be different, from 1 to N bytes. + pub fn current_label(&mut self) -> LabelId { + let pos = self.instructions.len(); + todo!() + } + + pub fn current_named_label(&mut self, name: &str) -> LabelId { + let id = self.new_forward_named_label(name); + self.assign_forward_label(id); + id + } + + pub fn new_forward_label(&mut self) -> LabelId { + todo!() + } + + pub fn new_forward_named_label(&mut self, name: &str) -> LabelId { + todo!() + } + + pub fn assign_forward_label(&mut self, label: LabelId) { + todo!() + } +} diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index 913e1aa0..8eef7d2c 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -10,7 +10,7 @@ pub struct LabelId(pub usize); // Every offset in an instruction does fit in i32. -// But what if we want to support virtual instructions with larger offsets? +// But "[relocation] is sign-extended to 64 bits". pub type Offset = i64; pub type Addr = u64; From b82484d9c7d0df009ba15b71e2bf9f787b0adcfa Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 13 Dec 2025 18:02:52 +0100 Subject: [PATCH 04/15] Initial relocations --- harm-runtime/src/runtime.rs | 4 +- harm/src/instructions.rs | 10 +- harm/src/instructions/control/branch_imm.rs | 129 ++++++++++++++++---- harm/src/reloc.rs | 31 ++++- 4 files changed, 136 insertions(+), 38 deletions(-) diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 539d6ae0..2b4a3868 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use crate::labels::LabelRegistry; use harm::InstructionCode; -use harm::reloc::{Reloc, LabelId}; +use harm::reloc::{Rel64, LabelId}; use harm::instructions::InstructionSeq; // N.N. we keep here internal relocation type, and convert it to external on serialization. @@ -15,7 +15,7 @@ use harm::instructions::InstructionSeq; pub struct Assembler { label_manager: LabelRegistry, instructions: Vec, - relocations: HashMap, + relocations: HashMap, } impl Assembler { diff --git a/harm/src/instructions.rs b/harm/src/instructions.rs index 4958c940..a9fb8596 100644 --- a/harm/src/instructions.rs +++ b/harm/src/instructions.rs @@ -5,7 +5,7 @@ use aarchmrs_types::InstructionCode; -use crate::reloc::Reloc; +use crate::reloc::Rel64; pub mod arith; pub mod control; @@ -20,14 +20,14 @@ pub trait RawInstruction { } pub trait RelocatableInstruction { - fn to_code_with_reloc(&self) -> (InstructionCode, Option); + fn to_code_with_reloc(&self) -> (InstructionCode, Option); } impl RelocatableInstruction for I where I: RawInstruction, { - fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { (self.to_code(), None) } } @@ -42,7 +42,7 @@ where // When the bug is fixed, it can be changed to &self. // // OTOH, currently all implementations are `Copy`. pub trait InstructionSeq: Sized { - fn encode(self) -> impl Iterator)> + 'static; + fn encode(self) -> impl Iterator)> + 'static; #[inline] fn instructions(self) -> impl Iterator + 'static { @@ -60,7 +60,7 @@ impl InstructionSeq for I where I: RelocatableInstruction, { - fn encode(self) -> impl Iterator)> + 'static { + fn encode(self) -> impl Iterator)> + 'static { let code = self.to_code_with_reloc(); core::iter::once(code) } diff --git a/harm/src/instructions/control/branch_imm.rs b/harm/src/instructions/control/branch_imm.rs index e6db5af1..298474fc 100644 --- a/harm/src/instructions/control/branch_imm.rs +++ b/harm/src/instructions/control/branch_imm.rs @@ -7,12 +7,13 @@ use aarchmrs_instructions::A64::control::{ branch_imm::{B_only_branch_imm::B_only_branch_imm, BL_only_branch_imm::BL_only_branch_imm}, condbranch::B_only_condbranch::B_only_condbranch, // TODO BC: branch consistent conditionally }; -use aarchmrs_types::InstructionCode; +use aarchmrs_types::{BitValue, InstructionCode}; use crate::{ bits::{BitError, SBitValue}, - instructions::RawInstruction, + instructions::{RawInstruction, RelocatableInstruction}, register::{RegOrZero32, RegOrZero64, Register as _}, + reloc::LabelRef, sealed::Sealed, }; @@ -37,6 +38,12 @@ pub enum BranchCond { NV = 0b1111, // always } +impl Into> for BranchCond { + fn into(self) -> BitValue<4> { + BitValue::new_u32(self as u8 as u32) + } +} + pub type BranchOffset = SBitValue<26, 2>; pub type BranchCondOffset = SBitValue<19, 2>; @@ -105,6 +112,27 @@ impl RawInstruction for Branch { } } +impl MakeBranch for Branch { + type Output = Self; + + #[inline] + fn make(label_ref: LabelRef) -> Self::Output { + Self(label_ref) + } +} + +impl RelocatableInstruction for Branch { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let zero = BranchOffset::default(); + + let code = B_only_branch_imm(zero.into()); + let reloc = crate::reloc::Rel64::Jump26(self.0); + + (code, Some(reloc)) + } +} + pub fn b_cond( cond: BranchCond, offset: InpAddr, @@ -124,6 +152,7 @@ impl MakeBranchCond for Branch<(BranchCond, BranchCondOffset)> } } +#[cfg(feature = "rich_api")] impl MakeBranchCond for Branch<(BranchCond, BranchCondOffset)> { type Output = Result; @@ -137,7 +166,29 @@ impl RawInstruction for Branch<(BranchCond, BranchCondOffset)> { #[inline] fn to_code(&self) -> InstructionCode { let (cond, imm19) = self.0; - B_only_condbranch(imm19.into(), (cond as u8).into()) + B_only_condbranch(imm19.into(), cond.into()) + } +} + +impl MakeBranchCond for Branch<(BranchCond, LabelRef)> { + type Output = Self; + + #[inline] + fn make(cond: BranchCond, label_ref: LabelRef) -> Self::Output { + Self((cond, label_ref)) + } +} + +impl RelocatableInstruction for Branch<(BranchCond, LabelRef)> { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let zero = BranchCondOffset::default(); + let (cond, label_ref) = self.0; + + let code = B_only_condbranch(zero.into(), cond.into()); + let reloc = crate::reloc::Rel64::CondBr19(label_ref); + + (code, Some(reloc)) } } @@ -148,19 +199,20 @@ pub trait MakeBranchLink: Sealed { } #[inline] -pub fn bl(args: InpArgs) -> >::Output +pub fn bl(args: InpArgs) -> as MakeBranchLink>::Output where - BranchLink: MakeBranchLink, + BranchLink: MakeBranchLink, { - >::make(args) + as MakeBranchLink>::make(args) } #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct BranchLink(BranchOffset); +pub struct BranchLink(Offset); -impl Sealed for BranchLink {} +impl Sealed for BranchLink {} +impl Sealed for BranchLink {} -impl MakeBranchLink for BranchLink { +impl MakeBranchLink for BranchLink { type Output = Self; #[inline] @@ -169,7 +221,8 @@ impl MakeBranchLink for BranchLink { } } -impl MakeBranchLink for BranchLink { +#[cfg(feature = "rich_api")] +impl MakeBranchLink for BranchLink { type Output = Result; #[inline] @@ -178,7 +231,7 @@ impl MakeBranchLink for BranchLink { } } -impl RawInstruction for BranchLink { +impl RawInstruction for BranchLink { #[inline] fn to_code(&self) -> InstructionCode { let imm26 = self.0; @@ -186,13 +239,35 @@ impl RawInstruction for BranchLink { } } -pub struct CompareBranch { +impl MakeBranchLink for BranchLink { + type Output = Self; + + #[inline] + fn make(label_ref: LabelRef) -> Self::Output { + Self(label_ref) + } +} + +impl RelocatableInstruction for BranchLink { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let zero = BranchOffset::default(); + + let code = BL_only_branch_imm(zero.into()); + let reloc = crate::reloc::Rel64::Call26(self.0); + + (code, Some(reloc)) + } +} + +pub struct CompareBranch { equal: bool, reg: Reg, - offset: CompareBranchOffset, + offset: Offset, } -impl Sealed for CompareBranch {} +impl Sealed for CompareBranch {} +impl Sealed for CompareBranch {} pub trait MakeCompareBranch: Sealed { type Output; @@ -200,7 +275,8 @@ pub trait MakeCompareBranch: Sealed { fn new(equal: bool, reg: Reg, offset: Offset) -> Self::Output; } -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch + for CompareBranch where R64: Into, { @@ -217,7 +293,7 @@ where // N.B. joining these two implementation abstracting over `RegDst` in `CompareBranch` // doesn't work: Rust doesn't seems to be able to deduce the `RegDst` type -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch for CompareBranch where RegOrZero64: From, { @@ -232,7 +308,7 @@ where } } -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch for CompareBranch where RegOrZero32: From, { @@ -247,7 +323,8 @@ where } } -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch + for CompareBranch where R32: Into, { @@ -262,7 +339,7 @@ where } } -impl RawInstruction for CompareBranch { +impl RawInstruction for CompareBranch { #[inline] fn to_code(&self) -> InstructionCode { use aarchmrs_instructions::A64::control::compbranch; @@ -275,7 +352,7 @@ impl RawInstruction for CompareBranch { } } -impl RawInstruction for CompareBranch { +impl RawInstruction for CompareBranch { #[inline] fn to_code(&self) -> InstructionCode { use aarchmrs_instructions::A64::control::compbranch; @@ -288,22 +365,22 @@ impl RawInstruction for CompareBranch { } } -pub fn cbz( +pub fn cbz( reg: RegSrc, offset: Offset, -) -> as MakeCompareBranch>::Output +) -> as MakeCompareBranch>::Output where - CompareBranch: MakeCompareBranch, + CompareBranch: MakeCompareBranch, { CompareBranch::new(true, reg, offset) } -pub fn cbnz( +pub fn cbnz( reg: RegSrc, offset: Offset, -) -> as MakeCompareBranch>::Output +) -> as MakeCompareBranch>::Output where - CompareBranch: MakeCompareBranch, + CompareBranch: MakeCompareBranch, { CompareBranch::new(false, reg, offset) } diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index 8eef7d2c..ba9cd242 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -19,12 +19,33 @@ pub type Addr = u64; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LabelRef { pub id: LabelId, - pub offset: Offset, + pub addend: Offset, } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Reloc { - Label(LabelRef), - // TODO lower bits, middle bits, signed and unsigned highest bits - Delta(LabelRef, LabelRef), // label1 - label2 +pub enum Rel64 { + None, + // Static data relocations + // ... + Abs64(LabelRef), + Abs32(LabelRef), + Abs16(LabelRef), + PRel64(LabelRef), + PRel32(LabelRef), + PRel16(LabelRef), + Plt32(LabelRef), + + // Static AArch64 relocations + LdPrelLo19(LabelRef), + AdrPrelLo21(LabelRef), + AdrPrelPgHi21(LabelRef), + AdrPrelPgHi21Nc(LabelRef), + AddAbsLo12Nc(LabelRef), + // Static control flow relocations + TstBr14(LabelRef), + CondBr19(LabelRef), + Jump26(LabelRef), + Call26(LabelRef), // same as Jump26 actually? + + // TODO `MOVW` and some `add`/`ldst`-related relocations } From 579e9f8df7d69ec632e99a9fa90958482af4ca51 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 19 Dec 2025 12:23:41 +0100 Subject: [PATCH 05/15] harm: `ldr`/`ldrsw` PC-relative relocations --- harm/src/instructions/ldst/ldr.rs | 18 ++++++++++++-- harm/src/instructions/ldst/ldrsw.rs | 9 ++++++- harm/src/instructions/ldst/macros.rs | 35 ++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/harm/src/instructions/ldst/ldr.rs b/harm/src/instructions/ldst/ldr.rs index 0c0fbc23..44b0caf8 100644 --- a/harm/src/instructions/ldst/ldr.rs +++ b/harm/src/instructions/ldst/ldr.rs @@ -165,8 +165,22 @@ define_imm_offset_rules!(Ldr, MakeLdr, LDR, RegOrZero32, 32, ScaledOffset32); // // ## LDR (PC-relative literal) // -define_pc_offset_rules!(Ldr, MakeLdr, LDR, RegOrZero64, 64); -define_pc_offset_rules!(Ldr, MakeLdr, LDR, RegOrZero32, 32); +define_pc_offset_rules!( + Ldr, + MakeLdr, + LDR, + RegOrZero64, + 64, + crate::reloc::Rel64::LdPrelLo19 +); +define_pc_offset_rules!( + Ldr, + MakeLdr, + LDR, + RegOrZero32, + 32, + crate::reloc::Rel64::LdPrelLo19 +); // // ## Faillible diff --git a/harm/src/instructions/ldst/ldrsw.rs b/harm/src/instructions/ldst/ldrsw.rs index bc796d9c..d4b74043 100644 --- a/harm/src/instructions/ldst/ldrsw.rs +++ b/harm/src/instructions/ldst/ldrsw.rs @@ -157,7 +157,14 @@ define_imm_offset_rules!(Ldrsw, MakeLdrsw, LDRSW, RegOrZero64, 64, ScaledOffset3 // // ## LDRSW (PC-relative literal) // -define_pc_offset_rules!(Ldrsw, MakeLdrsw, LDRSW, RegOrZero64, 64); +define_pc_offset_rules!( + Ldrsw, + MakeLdrsw, + LDRSW, + RegOrZero64, + 64, + crate::reloc::Rel64::LdPrelLo19 +); // // ## Faillible diff --git a/harm/src/instructions/ldst/macros.rs b/harm/src/instructions/ldst/macros.rs index ed066c2b..dd9545bc 100644 --- a/harm/src/instructions/ldst/macros.rs +++ b/harm/src/instructions/ldst/macros.rs @@ -340,7 +340,7 @@ macro_rules! define_unscaled_imm_offset_rules { } macro_rules! define_pc_offset_rules { - ($name:ident, $trait_name:ident, $mnem:ident, $rt:ty, $bitness:expr) => { + ($name:ident, $trait_name:ident, $mnem:ident, $rt:ty, $bitness:expr, $rel:expr) => { #[doc = "`"] #[doc = stringify!($mnem)] #[doc = "` with an immediate PC-related offset."] @@ -379,8 +379,30 @@ macro_rules! define_pc_offset_rules { } } + #[doc = "`"] + #[doc = stringify!($mnem)] + #[doc = "` with a label reference."] + impl $trait_name + for $name<$rt, $crate::reloc::LabelRef> + where + Rt: IntoReg<$rt>, + { + type Output = Self; + + #[inline] + fn new(rt: Rt, addr: $crate::reloc::LabelRef) -> Self { + Self { + rt: rt.into_reg(), + addr, + } + } + } + ::paste::paste! { - impl $crate::instructions::RawInstruction for $name<$rt, ($crate::instructions::ldst::Pc, $crate::instructions::ldst::LdStPcOffset)> { + impl $crate::instructions::RawInstruction for $name< + $rt, + ($crate::instructions::ldst::Pc, $crate::instructions::ldst::LdStPcOffset) + > { #[inline] fn to_code(&self) -> $crate::InstructionCode { let (_pc, offset) = self.addr; @@ -388,6 +410,15 @@ macro_rules! define_pc_offset_rules { code } } + + impl $crate::instructions::RelocatableInstruction for $name<$rt, $crate::reloc::LabelRef> { + #[inline] + fn to_code_with_reloc(&self) -> ($crate::InstructionCode, Option<$crate::reloc::Rel64>) { + let code = [<$mnem:upper _ $bitness _ loadlit>](0.into(), self.rt.index()); + let rel = $rel(self.addr); + (code, Some(rel)) + } + } } }; } From 7f30104e86bcd2a35094280385c07cc466a756f0 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sun, 21 Dec 2025 19:09:44 +0100 Subject: [PATCH 06/15] `movX` relocations --- harm/src/instructions/dpimm/movewide.rs | 4 + .../dpimm/movewide/movewide_reloc.rs | 180 ++++++++++++++++++ harm/src/reloc.rs | 1 + 3 files changed, 185 insertions(+) create mode 100644 harm/src/instructions/dpimm/movewide/movewide_reloc.rs diff --git a/harm/src/instructions/dpimm/movewide.rs b/harm/src/instructions/dpimm/movewide.rs index 373ce519..8bedd644 100644 --- a/harm/src/instructions/dpimm/movewide.rs +++ b/harm/src/instructions/dpimm/movewide.rs @@ -7,12 +7,15 @@ * Module for move instructions, like `MOVZ`, `MOVN` (not to be confused with `MVN`), `MOVK` in AArch64. */ +pub(crate) mod movewide_reloc; + use aarchmrs_instructions::A64::dpimm::movewide::{ MOVK_32_movewide::MOVK_32_movewide, MOVK_64_movewide::MOVK_64_movewide, MOVN_32_movewide::MOVN_32_movewide, MOVN_64_movewide::MOVN_64_movewide, MOVZ_32_movewide::MOVZ_32_movewide, MOVZ_64_movewide::MOVZ_64_movewide, }; +pub use self::movewide_reloc::*; use crate::{ bits::{BitError, UBitValue}, instructions::RawInstruction, @@ -201,6 +204,7 @@ impl RawInstruction for MovN> { pub struct MovZ(Args); +// TODO use a type tag to make both immediates and labels work. pub fn movz( rd: RIn, val: Val, diff --git a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs new file mode 100644 index 00000000..bc69a762 --- /dev/null +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -0,0 +1,180 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use harm_types::A64::register::{RegOrZero32, RegOrZero64}; + +use crate::{ + instructions::{RawInstruction as _, RelocatableInstruction}, + outcome::Unfallible, + register::IntoReg, + reloc::{LabelRef, Rel64}, + sealed::Sealed, +}; + +use super::{MakeMovArgs, MovZ, MoveImm16, Shift32, Shift64, movz}; + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG0(LabelRef); + +/// Lowest nibble (bits `[15:0]`) of the absolute address of the label, checked for unsigned overflow. +#[inline] +pub fn abs_g0(label: LabelRef) -> AbsG0 { + AbsG0(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG0Nc(LabelRef); + +/// Lowest nibble (bits `[15:0]`) of the absolute address of the label, non-checked. +#[inline] +pub fn abs_g0_nc(label: LabelRef) -> AbsG0Nc { + AbsG0Nc(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG0S(LabelRef); + +/// Lowest nibble (bits `[15:0]`) of the absolute address of the label, checked for signed overflow. +#[inline] +pub fn abs_g0_s(label: LabelRef) -> AbsG0S { + AbsG0S(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG1(LabelRef); + +/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for unsinged overflow. +#[inline] +pub fn abs_g1(label: LabelRef) -> AbsG1 { + AbsG1(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG1Nc(LabelRef); + +/// First nibble (bits `[31:16]`) of the absolute address of the label, non-checked. +#[inline] +pub fn abs_g1_nc(label: LabelRef) -> AbsG1Nc { + AbsG1Nc(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG1S(LabelRef); + +/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for singed overflow. +#[inline] +pub fn abs_g1_s(label: LabelRef) -> AbsG1S { + AbsG1S(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG2(LabelRef); + +/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for unsinged overflow. +#[inline] +pub fn abs_g2(label: LabelRef) -> AbsG2 { + AbsG2(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG2Nc(LabelRef); + +/// Second nibble (bits `[47:32]`) of the absolute address of the label, non-checked. +#[inline] +pub fn abs_g2_nc(label: LabelRef) -> AbsG2Nc { + AbsG2Nc(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG2S(LabelRef); + +/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for singed overflow. +#[inline] +pub fn abs_g2_s(label: LabelRef) -> AbsG2S { + AbsG2S(label) +} + +#[doc(hidden)] +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct AbsG3(LabelRef); + +/// Third nibble (bits `[63:48]`) of the absolute address of the label, cannot overflow. +#[inline] +pub fn abs_g3(label: LabelRef) -> AbsG3 { + AbsG3(label) +} + +#[derive(Debug, Copy, Clone)] +pub struct MovRelArgs { + pub rd: Rd, + pub rel: Rel, +} + +impl Sealed for MovRelArgs {} + +impl> MakeMovArgs for MovRelArgs { + type Outcome = Unfallible; + + fn new(rd: RIn, rel: AbsG0) -> Self::Outcome { + Unfallible(Self { + rd: rd.into_reg(), + rel, + }) + } +} + +impl RelocatableInstruction for MovZ> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let code = movz(self.0.rd, (MoveImm16::default(), Shift64::new(0).unwrap())).to_code(); + let rel = Rel64::AbsG0(self.0.rel.0); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for MovZ> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let code = movz(self.0.rd, (MoveImm16::default(), Shift32::new(0).unwrap())).to_code(); + let rel = Rel64::AbsG0(self.0.rel.0); + (code, Some(rel)) + } +} + +#[cfg(test)] +mod tests { + use crate::register::Reg64::X1; + use crate::reloc::LabelId; + + use super::*; + + #[test] + fn test_rel() { + let label = LabelRef { + id: LabelId(1), + addend: 42, + }; + let inst = movz(X1, abs_g0(label)).to_code_with_reloc(); + assert_eq!(inst.0.0, movz(X1, (0, 0)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::AbsG0(label))); + } +} diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index ba9cd242..0ccf2c1c 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -48,4 +48,5 @@ pub enum Rel64 { Call26(LabelRef), // same as Jump26 actually? // TODO `MOVW` and some `add`/`ldst`-related relocations + AbsG0(LabelRef), } From e264d25bef22c518d8e4dad4f8ebd768e68e7b65 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Tue, 23 Dec 2025 18:11:32 +0100 Subject: [PATCH 07/15] harm: fix `movewide` definitions to accept alternative forms Implement typetag pattern for `MovImmArgs` and `MovRelArgs`. --- harm/src/instructions/dpimm/movewide.rs | 79 +++-- .../dpimm/movewide/movewide_reloc.rs | 335 +++++++++++++++++- harm/src/instructions/mov/mov_imm.rs | 14 +- harm/src/reloc.rs | 11 +- 4 files changed, 380 insertions(+), 59 deletions(-) diff --git a/harm/src/instructions/dpimm/movewide.rs b/harm/src/instructions/dpimm/movewide.rs index 8bedd644..736502bd 100644 --- a/harm/src/instructions/dpimm/movewide.rs +++ b/harm/src/instructions/dpimm/movewide.rs @@ -9,6 +9,8 @@ pub(crate) mod movewide_reloc; +use core::marker::PhantomData; + use aarchmrs_instructions::A64::dpimm::movewide::{ MOVK_32_movewide::MOVK_32_movewide, MOVK_64_movewide::MOVK_64_movewide, MOVN_32_movewide::MOVN_32_movewide, MOVN_64_movewide::MOVN_64_movewide, @@ -24,6 +26,12 @@ use crate::{ sealed::Sealed, }; +/// This is a technical type used for type constraints. It is never constructed, but only used in trait definitions +/// and constraints. +pub struct MovWideTypeTag(PhantomData); + +impl Sealed for MovWideTypeTag {} + // Either 0 or 16 = 1 << 4. pub type Shift32 = UBitValue<1, 4>; // Either 0, 16, 32 or 48. @@ -59,12 +67,12 @@ pub struct MovImmArgs { impl Sealed for MovImmArgs {} impl, Imm16: Into> MakeMovArgs - for MovImmArgs + for MovWideTypeTag> { - type Outcome = Unfallible; + type Outcome = Unfallible>; fn new(rd: RIn, (imm16, shift): (Imm16, ::Shift)) -> Self::Outcome { - Unfallible(Self { + Unfallible(MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift, @@ -72,15 +80,15 @@ impl, Imm16: Into> MakeMovArgs MakeMovArgs for MovImmArgs +impl MakeMovArgs for MovWideTypeTag> where RIn: IntoReg, Imm16: Into, { - type Outcome = Result; + type Outcome = Result, BitError>; fn new(rd: RIn, (imm16, shift): (Imm16, u8)) -> Self::Outcome { - (shift as u32).try_into().map(|shift| Self { + (shift as u32).try_into().map(|shift| MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift, @@ -88,15 +96,15 @@ where } } -impl MakeMovArgs for MovImmArgs +impl MakeMovArgs for MovWideTypeTag> where RIn: IntoReg, Imm16: Into, { - type Outcome = Unfallible; + type Outcome = Unfallible>; fn new(rd: RIn, imm16: Imm16) -> Self::Outcome { - Unfallible(Self { + Unfallible(MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift: <_>::default(), @@ -105,12 +113,13 @@ where } impl, Imm16: Into> - MakeMovArgs::Shift)> for MovImmArgs + MakeMovArgs::Shift)> + for MovWideTypeTag> { - type Outcome = Unfallible; + type Outcome = Unfallible>; fn new(rd: RIn, (imm16, shift): (Imm16, Shift64)) -> Self::Outcome { - Unfallible(Self { + Unfallible(MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift, @@ -118,15 +127,15 @@ impl, Imm16: Into> } } -impl MakeMovArgs for MovImmArgs +impl MakeMovArgs for MovWideTypeTag> where RIn: IntoReg, Imm16: Into, { - type Outcome = Unfallible; + type Outcome = Unfallible>; fn new(rd: RIn, imm16: Imm16) -> Self::Outcome { - Unfallible(Self { + Unfallible(MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift: <_>::default(), @@ -134,15 +143,15 @@ where } } -impl MakeMovArgs for MovImmArgs +impl MakeMovArgs for MovWideTypeTag> where RIn: IntoReg, Imm16: Into, { - type Outcome = Result; + type Outcome = Result, BitError>; fn new(rd: RIn, (imm16, shift): (Imm16, u8)) -> Self::Outcome { - (shift as u32).try_into().map(|shift| Self { + (shift as u32).try_into().map(|shift| MovImmArgs { rd: rd.into_reg(), imm16: imm16.into(), shift, @@ -152,16 +161,15 @@ where pub struct MovK(Args); -pub fn movk( +pub fn movk( rd: RIn, val: Val, -) -> < as MakeMovArgs>::Outcome as Outcome>::Output>> +) -> < as MakeMovArgs>::Outcome as Outcome>::Output> where - ROut: MoveShift, - MovImmArgs: MakeMovArgs, - as MakeMovArgs>::Outcome: Outcome>, + MovWideTypeTag: MakeMovArgs, + as MakeMovArgs>::Outcome: Outcome, { - ( as MakeMovArgs>::new(rd, val)).map(MovK) + ( as MakeMovArgs>::new(rd, val)).map(MovK) } impl RawInstruction for MovK> { @@ -178,16 +186,15 @@ impl RawInstruction for MovK> { pub struct MovN(Args); -pub fn movn( +pub fn movn( rd: RIn, val: Val, -) -> < as MakeMovArgs>::Outcome as Outcome>::Output>> +) -> < as MakeMovArgs>::Outcome as Outcome>::Output> where - ROut: MoveShift, - MovImmArgs: MakeMovArgs, - as MakeMovArgs>::Outcome: Outcome>, + MovWideTypeTag: MakeMovArgs, + as MakeMovArgs>::Outcome: Outcome, { - as MakeMovArgs>::new(rd, val).map(MovN) + ( as MakeMovArgs>::new(rd, val)).map(MovN) } impl RawInstruction for MovN> { @@ -204,17 +211,15 @@ impl RawInstruction for MovN> { pub struct MovZ(Args); -// TODO use a type tag to make both immediates and labels work. -pub fn movz( +pub fn movz( rd: RIn, val: Val, -) -> < as MakeMovArgs>::Outcome as Outcome>::Output>> +) -> < as MakeMovArgs>::Outcome as Outcome>::Output> where - ROut: MoveShift, - MovImmArgs: MakeMovArgs, - as MakeMovArgs>::Outcome: Outcome>, + MovWideTypeTag: MakeMovArgs, + as MakeMovArgs>::Outcome: Outcome, { - as MakeMovArgs>::new(rd, val).map(MovZ) + ( as MakeMovArgs>::new(rd, val)).map(MovZ) } impl RawInstruction for MovZ> { diff --git a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs index bc69a762..1b612d41 100644 --- a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -13,7 +13,9 @@ use crate::{ sealed::Sealed, }; -use super::{MakeMovArgs, MovZ, MoveImm16, Shift32, Shift64, movz}; +use super::{ + MakeMovArgs, MovK, MovN, MovWideTypeTag, MovZ, MoveImm16, Shift32, Shift64, movk, movn, movz, +}; #[doc(hidden)] #[derive(Debug, Copy, Clone)] @@ -125,56 +127,357 @@ pub fn abs_g3(label: LabelRef) -> AbsG3 { AbsG3(label) } +impl Sealed for AbsG0 {} +impl Sealed for AbsG0Nc {} +impl Sealed for AbsG0S {} +impl Sealed for AbsG1 {} +impl Sealed for AbsG1Nc {} +impl Sealed for AbsG1S {} +impl Sealed for AbsG2 {} +impl Sealed for AbsG2Nc {} +impl Sealed for AbsG2S {} +impl Sealed for AbsG3 {} + #[derive(Debug, Copy, Clone)] pub struct MovRelArgs { pub rd: Rd, pub rel: Rel, } -impl Sealed for MovRelArgs {} +pub trait MoveWideReloc64: Sealed { + fn get_shift64(&self) -> Shift64; + fn get_relocation(&self) -> Rel64; +} + +pub trait MoveWideReloc32: MoveWideReloc64 { + fn get_shift32(&self) -> Shift32; +} -impl> MakeMovArgs for MovRelArgs { - type Outcome = Unfallible; +impl Sealed for MovRelArgs {} +impl Sealed for MovRelArgs {} + +impl MoveWideReloc64 for AbsG0 { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(0).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG0(self.0) + } +} + +impl MoveWideReloc64 for AbsG0Nc { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(0).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG0Nc(self.0) + } +} + +impl MoveWideReloc64 for AbsG0S { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(0).unwrap() + } - fn new(rd: RIn, rel: AbsG0) -> Self::Outcome { - Unfallible(Self { + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG0S(self.0) + } +} + +impl MoveWideReloc64 for AbsG1 { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(1 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG1(self.0) + } +} + +impl MoveWideReloc64 for AbsG1Nc { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(1 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG1Nc(self.0) + } +} + +impl MoveWideReloc64 for AbsG1S { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(1 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG1S(self.0) + } +} + +impl MoveWideReloc64 for AbsG2 { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(2 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG2(self.0) + } +} + +impl MoveWideReloc64 for AbsG2Nc { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(2 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG2Nc(self.0) + } +} + +impl MoveWideReloc64 for AbsG2S { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(2 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG2S(self.0) + } +} + +impl MoveWideReloc64 for AbsG3 { + #[inline] + fn get_shift64(&self) -> Shift64 { + Shift64::new(3 << 4).unwrap() + } + + #[inline] + fn get_relocation(&self) -> Rel64 { + Rel64::MovWAbsG3(self.0) + } +} + +impl MoveWideReloc32 for AbsG0 { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(0).unwrap() + } +} + +impl MoveWideReloc32 for AbsG0Nc { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(0).unwrap() + } +} + +impl MoveWideReloc32 for AbsG0S { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(0).unwrap() + } +} + +impl MoveWideReloc32 for AbsG1 { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(1 << 4).unwrap() + } +} + +impl MoveWideReloc32 for AbsG1Nc { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(1 << 4).unwrap() + } +} + +impl MoveWideReloc32 for AbsG1S { + #[inline] + fn get_shift32(&self) -> Shift32 { + Shift32::new(1 << 4).unwrap() + } +} + +impl, Rel: MoveWideReloc64> MakeMovArgs + for MovWideTypeTag> +{ + type Outcome = Unfallible>; + + fn new(rd: RIn, rel: Rel) -> Self::Outcome { + Unfallible(MovRelArgs { rd: rd.into_reg(), rel, }) } } -impl RelocatableInstruction for MovZ> { +impl RelocatableInstruction for MovZ> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let MovRelArgs { rd, ref rel } = self.0; + let code = movz(rd, (MoveImm16::default(), rel.get_shift64())).to_code(); + let rel = rel.get_relocation(); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for MovZ> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let MovRelArgs { rd, ref rel } = self.0; + let code = movz(rd, (MoveImm16::default(), rel.get_shift32())).to_code(); + let rel = rel.get_relocation(); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for MovK> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let MovRelArgs { rd, ref rel } = self.0; + let code = movk(rd, (MoveImm16::default(), rel.get_shift64())).to_code(); + let rel = rel.get_relocation(); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for MovK> { fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { - let code = movz(self.0.rd, (MoveImm16::default(), Shift64::new(0).unwrap())).to_code(); - let rel = Rel64::AbsG0(self.0.rel.0); + let MovRelArgs { rd, ref rel } = self.0; + let code = movk(rd, (MoveImm16::default(), rel.get_shift32())).to_code(); + let rel = rel.get_relocation(); (code, Some(rel)) } } -impl RelocatableInstruction for MovZ> { +impl RelocatableInstruction for MovN> { fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { - let code = movz(self.0.rd, (MoveImm16::default(), Shift32::new(0).unwrap())).to_code(); - let rel = Rel64::AbsG0(self.0.rel.0); + let MovRelArgs { rd, ref rel } = self.0; + let code = movn(rd, (MoveImm16::default(), rel.get_shift64())).to_code(); + let rel = rel.get_relocation(); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for MovN> { + fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { + let MovRelArgs { rd, ref rel } = self.0; + let code = movn(rd, (MoveImm16::default(), rel.get_shift32())).to_code(); + let rel = rel.get_relocation(); (code, Some(rel)) } } #[cfg(test)] mod tests { - use crate::register::Reg64::X1; + use crate::register::Reg64::*; use crate::reloc::LabelId; use super::*; #[test] - fn test_rel() { + fn test_64_abs_g0_movz() { let label = LabelRef { id: LabelId(1), addend: 42, }; let inst = movz(X1, abs_g0(label)).to_code_with_reloc(); - assert_eq!(inst.0.0, movz(X1, (0, 0)).unwrap().to_code()); - assert_eq!(inst.1, Some(Rel64::AbsG0(label))); + assert_eq!(inst.0, movz(X1, (0, 0)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG0(label))); + } + + #[test] + fn test_64_abs_g1_movk() { + let label = LabelRef { + id: LabelId(2), + addend: 43, + }; + let inst = movk(X2, abs_g1(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(X2, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1(label))); + } + + #[test] + fn test_64_abs_g1s_movn() { + let label = LabelRef { + id: LabelId(3), + addend: 44, + }; + // Makes little sense, but gas accepts it. + let inst = movn(X3, abs_g1_s(label)).to_code_with_reloc(); + assert_eq!(inst.0, movn(X3, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1S(label))); + } + + #[test] + fn test_64_abs_g1nc_movk() { + let label = LabelRef { + id: LabelId(4), + addend: 45, + }; + let inst = movk(X4, abs_g1_nc(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(X4, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1Nc(label))); + } + + #[test] + fn test_64_abs_g2_movz() { + let label = LabelRef { + id: LabelId(5), + addend: 46, + }; + let inst = movz(X5, abs_g2(label)).to_code_with_reloc(); + assert_eq!(inst.0, movz(X5, (0, 32)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG2(label))); + } + + #[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_g3_movk() { + let label = LabelRef { + id: LabelId(8), + addend: 49, + }; + let inst = movk(X8, abs_g3(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(X8, (0, 48)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG3(label))); } } diff --git a/harm/src/instructions/mov/mov_imm.rs b/harm/src/instructions/mov/mov_imm.rs index de77f2ec..46f02d3e 100644 --- a/harm/src/instructions/mov/mov_imm.rs +++ b/harm/src/instructions/mov/mov_imm.rs @@ -7,7 +7,7 @@ use super::*; use crate::instructions::RawInstruction; use crate::instructions::dpimm::immediate::LogicalImmFields; use crate::instructions::dpimm::{ - MakeMovArgs, MovImmArgs, MoveImm16, MoveShift, Shift32, Shift64, movn, movz, + MakeMovArgs, MovImmArgs, MovWideTypeTag, MoveImm16, MoveShift, Shift32, Shift64, movn, movz, }; use crate::instructions::logical::{LogicalArgs, Orr, orr}; use crate::outcome::Outcome; @@ -142,7 +142,8 @@ fn try_into_mov_z_or_k_32( let shift = MOVZ_HALFWORD_SIZE * min_hw; let shift32 = Shift32::new(shift).expect("invalid shift generated"); matches_halfword(v.into(), shift).map(|val| { - MovImmArgs::new(reg, (val, shift32)).map(|args| MovNOrZImm { neg: false, args }) + > as MakeMovArgs<_, _>>::new(reg, (val, shift32)) + .map(|args| MovNOrZImm { neg: false, args }) }) }; @@ -152,7 +153,8 @@ fn try_into_mov_z_or_k_32( let shift = MOVZ_HALFWORD_SIZE * min_hw; let shift32 = Shift32::new(shift).expect("invalid shift generated"); matches_halfword(neg_v.into(), shift).map(|val| { - MovImmArgs::new(reg, (val, shift32)).map(|args| MovNOrZImm { neg: true, args }) + > as MakeMovArgs<_, _>>::new(reg, (val, shift32)) + .map(|args| MovNOrZImm { neg: true, args }) }) }; @@ -220,7 +222,8 @@ fn try_into_mov_z_or_k_64( let shift = MOVZ_HALFWORD_SIZE * min_hw; let shift64 = Shift64::new(shift).expect("invalid shift generated"); matches_halfword(v, shift).map(|val| { - MovImmArgs::new(reg, (val, shift64)).map(|args| MovNOrZImm { neg: false, args }) + > as MakeMovArgs<_, _>>::new(reg, (val, shift64)) + .map(|args| MovNOrZImm { neg: false, args }) }) }; @@ -230,7 +233,8 @@ fn try_into_mov_z_or_k_64( let shift = MOVZ_HALFWORD_SIZE * min_hw; let shift64 = Shift64::new(shift).expect("invalid shift generated"); matches_halfword(neg_v, shift).map(|val| { - MovImmArgs::new(reg, (val, shift64)).map(|args| MovNOrZImm { neg: true, args }) + > as MakeMovArgs<_, _>>::new(reg, (val, shift64)) + .map(|args| MovNOrZImm { neg: true, args }) }) }; diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index 0ccf2c1c..0c77dd3c 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -48,5 +48,14 @@ pub enum Rel64 { Call26(LabelRef), // same as Jump26 actually? // TODO `MOVW` and some `add`/`ldst`-related relocations - AbsG0(LabelRef), + MovWAbsG0(LabelRef), + MovWAbsG0Nc(LabelRef), + MovWAbsG0S(LabelRef), + MovWAbsG1(LabelRef), + MovWAbsG1Nc(LabelRef), + MovWAbsG1S(LabelRef), + MovWAbsG2(LabelRef), + MovWAbsG2Nc(LabelRef), + MovWAbsG2S(LabelRef), + MovWAbsG3(LabelRef), } From c1ab640f935eb7ad413dc6e2236834d7072e0164 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Thu, 25 Dec 2025 16:33:43 +0100 Subject: [PATCH 08/15] Make fmt happy --- harm-runtime/src/runtime.rs | 2 +- harm/src/reloc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs index 2b4a3868..e66f14a2 100644 --- a/harm-runtime/src/runtime.rs +++ b/harm-runtime/src/runtime.rs @@ -7,8 +7,8 @@ use std::collections::HashMap; use crate::labels::LabelRegistry; use harm::InstructionCode; -use harm::reloc::{Rel64, LabelId}; use harm::instructions::InstructionSeq; +use harm::reloc::{LabelId, Rel64}; // N.N. we keep here internal relocation type, and convert it to external on serialization. #[derive(Default)] diff --git a/harm/src/reloc.rs b/harm/src/reloc.rs index 0c77dd3c..e6e3957f 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -45,7 +45,7 @@ pub enum Rel64 { TstBr14(LabelRef), CondBr19(LabelRef), Jump26(LabelRef), - Call26(LabelRef), // same as Jump26 actually? + Call26(LabelRef), // same as Jump26 actually? // TODO `MOVW` and some `add`/`ldst`-related relocations MovWAbsG0(LabelRef), From cc8a4be42450a168ea8cabfbba884beb947ee0d7 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Thu, 25 Dec 2025 18:45:59 +0100 Subject: [PATCH 09/15] harm: allow forgotten 32-bit register wide moves with rel --- .../dpimm/movewide/movewide_reloc.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs index 1b612d41..e623ddd0 100644 --- a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -331,6 +331,20 @@ impl, Rel: MoveWideReloc64> MakeMovArgs } } +impl, Rel: MoveWideReloc32> MakeMovArgs + for MovWideTypeTag> +{ + type Outcome = Unfallible>; + + fn new(rd: RIn, rel: Rel) -> Self::Outcome { + Unfallible(MovRelArgs { + rd: rd.into_reg(), + rel, + }) + } +} + + impl RelocatableInstruction for MovZ> { fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { let MovRelArgs { rd, ref rel } = self.0; @@ -388,6 +402,7 @@ impl RelocatableInstruction for MovN Date: Thu, 25 Dec 2025 20:48:28 +0100 Subject: [PATCH 10/15] Some review fixes --- harm-runtime/Cargo.toml | 6 +----- harm/src/instructions/control/branch_imm.rs | 6 +++--- .../src/instructions/dpimm/movewide/movewide_reloc.rs | 11 +++++------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/harm-runtime/Cargo.toml b/harm-runtime/Cargo.toml index fc17686e..aa4a5a11 100644 --- a/harm-runtime/Cargo.toml +++ b/harm-runtime/Cargo.toml @@ -12,8 +12,4 @@ publish = false [dependencies] harm = { workspace = true } -memmap = "0.7.0" - -[dev-dependencies] - -[features] +memmap2 = "0.9.9" diff --git a/harm/src/instructions/control/branch_imm.rs b/harm/src/instructions/control/branch_imm.rs index 298474fc..523b81d3 100644 --- a/harm/src/instructions/control/branch_imm.rs +++ b/harm/src/instructions/control/branch_imm.rs @@ -38,9 +38,9 @@ pub enum BranchCond { NV = 0b1111, // always } -impl Into> for BranchCond { - fn into(self) -> BitValue<4> { - BitValue::new_u32(self as u8 as u32) +impl From for BitValue<4> { + fn from(cond: BranchCond) -> Self { + Self::new_u32(cond as u8 as u32) } } diff --git a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs index e623ddd0..2f3bd80d 100644 --- a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -55,7 +55,7 @@ pub fn abs_g0_s(label: LabelRef) -> AbsG0S { #[repr(transparent)] pub struct AbsG1(LabelRef); -/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for unsinged overflow. +/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for unsigned overflow. #[inline] pub fn abs_g1(label: LabelRef) -> AbsG1 { AbsG1(label) @@ -77,7 +77,7 @@ pub fn abs_g1_nc(label: LabelRef) -> AbsG1Nc { #[repr(transparent)] pub struct AbsG1S(LabelRef); -/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for singed overflow. +/// First nibble (bits `[31:16]`) of the absolute address of the label, checked for signed overflow. #[inline] pub fn abs_g1_s(label: LabelRef) -> AbsG1S { AbsG1S(label) @@ -88,7 +88,7 @@ pub fn abs_g1_s(label: LabelRef) -> AbsG1S { #[repr(transparent)] pub struct AbsG2(LabelRef); -/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for unsinged overflow. +/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for unsigned overflow. #[inline] pub fn abs_g2(label: LabelRef) -> AbsG2 { AbsG2(label) @@ -110,7 +110,7 @@ pub fn abs_g2_nc(label: LabelRef) -> AbsG2Nc { #[repr(transparent)] pub struct AbsG2S(LabelRef); -/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for singed overflow. +/// Second nibble (bits `[47:32]`) of the absolute address of the label, checked for signed overflow. #[inline] pub fn abs_g2_s(label: LabelRef) -> AbsG2S { AbsG2S(label) @@ -344,7 +344,6 @@ impl, Rel: MoveWideReloc32> MakeMovArgs } } - impl RelocatableInstruction for MovZ> { fn to_code_with_reloc(&self) -> (aarchmrs_types::InstructionCode, Option) { let MovRelArgs { rd, ref rel } = self.0; @@ -401,8 +400,8 @@ impl RelocatableInstruction for MovN Date: Thu, 25 Dec 2025 21:14:32 +0100 Subject: [PATCH 11/15] Allow `cbz`/`cbnz` with relocs --- harm/src/instructions/control/branch_imm.rs | 159 +++++++++++++++++--- 1 file changed, 139 insertions(+), 20 deletions(-) diff --git a/harm/src/instructions/control/branch_imm.rs b/harm/src/instructions/control/branch_imm.rs index 523b81d3..7624a3ac 100644 --- a/harm/src/instructions/control/branch_imm.rs +++ b/harm/src/instructions/control/branch_imm.rs @@ -13,7 +13,7 @@ use crate::{ bits::{BitError, SBitValue}, instructions::{RawInstruction, RelocatableInstruction}, register::{RegOrZero32, RegOrZero64, Register as _}, - reloc::LabelRef, + reloc::{LabelRef, Rel64}, sealed::Sealed, }; @@ -123,11 +123,11 @@ impl MakeBranch for Branch { impl RelocatableInstruction for Branch { #[inline] - fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { let zero = BranchOffset::default(); let code = B_only_branch_imm(zero.into()); - let reloc = crate::reloc::Rel64::Jump26(self.0); + let reloc = Rel64::Jump26(self.0); (code, Some(reloc)) } @@ -181,12 +181,12 @@ impl MakeBranchCond for Branch<(BranchCond, LabelRef)> { impl RelocatableInstruction for Branch<(BranchCond, LabelRef)> { #[inline] - fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { let zero = BranchCondOffset::default(); let (cond, label_ref) = self.0; let code = B_only_condbranch(zero.into(), cond.into()); - let reloc = crate::reloc::Rel64::CondBr19(label_ref); + let reloc = Rel64::CondBr19(label_ref); (code, Some(reloc)) } @@ -250,11 +250,11 @@ impl MakeBranchLink for BranchLink { impl RelocatableInstruction for BranchLink { #[inline] - fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { let zero = BranchOffset::default(); let code = BL_only_branch_imm(zero.into()); - let reloc = crate::reloc::Rel64::Call26(self.0); + let reloc = Rel64::Call26(self.0); (code, Some(reloc)) } @@ -275,14 +275,14 @@ pub trait MakeCompareBranch: Sealed { fn new(equal: bool, reg: Reg, offset: Offset) -> Self::Output; } -impl MakeCompareBranch +impl MakeCompareBranch for CompareBranch where - R64: Into, + RegSrc64: Into, { type Output = Self; - fn new(equal: bool, reg: R64, offset: CompareBranchOffset) -> Self { + fn new(equal: bool, reg: RegSrc64, offset: CompareBranchOffset) -> Self { Self { equal, reg: reg.into(), @@ -293,13 +293,13 @@ where // N.B. joining these two implementation abstracting over `RegDst` in `CompareBranch` // doesn't work: Rust doesn't seems to be able to deduce the `RegDst` type -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch for CompareBranch where - RegOrZero64: From, + RegOrZero64: From, { type Output = Result; - fn new(equal: bool, reg: RegSrc, offset: i32) -> Result { + fn new(equal: bool, reg: RegSrc64, offset: i32) -> Result { CompareBranchOffset::try_from(offset).map(|offset| Self { equal, reg: reg.into(), @@ -308,13 +308,44 @@ where } } -impl MakeCompareBranch for CompareBranch +impl MakeCompareBranch for CompareBranch where - RegOrZero32: From, + RegSrc64: Into, +{ + type Output = Self; + + fn new(equal: bool, reg: RegSrc64, offset: LabelRef) -> Self { + Self { + equal, + reg: reg.into(), + offset, + } + } +} + +impl MakeCompareBranch + for CompareBranch +where + RegSrc32: Into, +{ + type Output = Self; + + fn new(equal: bool, reg: RegSrc32, offset: CompareBranchOffset) -> Self { + Self { + equal, + reg: reg.into(), + offset, + } + } +} + +impl MakeCompareBranch for CompareBranch +where + RegOrZero32: From, { type Output = Result; - fn new(equal: bool, reg: RegSrc, offset: i32) -> Result { + fn new(equal: bool, reg: RegSrc32, offset: i32) -> Result { CompareBranchOffset::try_from(offset).map(|offset| Self { equal, reg: reg.into(), @@ -323,14 +354,13 @@ where } } -impl MakeCompareBranch - for CompareBranch +impl MakeCompareBranch for CompareBranch where - R32: Into, + RegSrc32: Into, { type Output = Self; - fn new(equal: bool, reg: R32, offset: CompareBranchOffset) -> Self { + fn new(equal: bool, reg: RegSrc32, offset: LabelRef) -> Self { Self { equal, reg: reg.into(), @@ -352,6 +382,24 @@ impl RawInstruction for CompareBranch { } } +impl RelocatableInstruction for CompareBranch { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let zero = CompareBranchOffset::default(); + + let code = CompareBranch { + equal: self.equal, + reg: self.reg, + offset: zero, + } + .to_code(); + + let reloc = Rel64::TstBr14(self.offset); + + (code, Some(reloc)) + } +} + impl RawInstruction for CompareBranch { #[inline] fn to_code(&self) -> InstructionCode { @@ -365,6 +413,24 @@ impl RawInstruction for CompareBranch { } } +impl RelocatableInstruction for CompareBranch { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let zero = CompareBranchOffset::default(); + + let code = CompareBranch { + equal: self.equal, + reg: self.reg, + offset: zero, + } + .to_code(); + + let reloc = Rel64::TstBr14(self.offset); + + (code, Some(reloc)) + } +} + pub fn cbz( reg: RegSrc, offset: Offset, @@ -394,6 +460,7 @@ mod tests { use crate::register::Reg64::*; use crate::register::RegOrZero32::WZR; use crate::register::RegOrZero64::XZR; + use crate::reloc::LabelId; use alloc::vec::Vec; use harm_test_utils::inst; @@ -634,4 +701,56 @@ mod tests { let codes: Vec<_> = it.encode().collect(); assert_eq!(codes, inst!(0x34000042)); } + + #[test] + fn test_cbz_64_label() { + let label = LabelRef { + id: LabelId(1), + addend: 2, + }; + let inst = cbz(X3, label); + + let (code, reloc) = inst.to_code_with_reloc(); + assert_eq!(code, cbz(X3, CompareBranchOffset::default()).to_code()); + assert_eq!(reloc, Some(Rel64::TstBr14(label))); + } + + #[test] + fn test_cbz_32_label() { + let label = LabelRef { + id: LabelId(2), + addend: 3, + }; + let inst = cbz(W4, label); + + let (code, reloc) = inst.to_code_with_reloc(); + assert_eq!(code, cbz(W4, CompareBranchOffset::default()).to_code()); + assert_eq!(reloc, Some(Rel64::TstBr14(label))); + } + + #[test] + fn test_cbnz_64_label() { + let label = LabelRef { + id: LabelId(3), + addend: 4, + }; + let inst = cbnz(X5, label); + + let (code, reloc) = inst.to_code_with_reloc(); + assert_eq!(code, cbnz(X5, CompareBranchOffset::default()).to_code()); + assert_eq!(reloc, Some(Rel64::TstBr14(label))); + } + + #[test] + fn test_cbnz_32_label() { + let label = LabelRef { + id: LabelId(4), + addend: 5, + }; + let inst = cbnz(W6, label); + + let (code, reloc) = inst.to_code_with_reloc(); + assert_eq!(code, cbnz(W6, CompareBranchOffset::default()).to_code()); + assert_eq!(reloc, Some(Rel64::TstBr14(label))); + } } From c5b8df454eacba074cf45187b6e8d11cc7193c89 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 26 Dec 2025 21:20:43 +0100 Subject: [PATCH 12/15] harm: correct `cbz`/`cbnz` relocation type --- harm/src/instructions/control/branch_imm.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/harm/src/instructions/control/branch_imm.rs b/harm/src/instructions/control/branch_imm.rs index 7624a3ac..70ad4edd 100644 --- a/harm/src/instructions/control/branch_imm.rs +++ b/harm/src/instructions/control/branch_imm.rs @@ -394,7 +394,7 @@ impl RelocatableInstruction for CompareBranch { } .to_code(); - let reloc = Rel64::TstBr14(self.offset); + let reloc = Rel64::CondBr19(self.offset); (code, Some(reloc)) } @@ -425,7 +425,7 @@ impl RelocatableInstruction for CompareBranch { } .to_code(); - let reloc = Rel64::TstBr14(self.offset); + let reloc = Rel64::CondBr19(self.offset); (code, Some(reloc)) } @@ -712,7 +712,7 @@ mod tests { let (code, reloc) = inst.to_code_with_reloc(); assert_eq!(code, cbz(X3, CompareBranchOffset::default()).to_code()); - assert_eq!(reloc, Some(Rel64::TstBr14(label))); + assert_eq!(reloc, Some(Rel64::CondBr19(label))); } #[test] @@ -725,7 +725,7 @@ mod tests { let (code, reloc) = inst.to_code_with_reloc(); assert_eq!(code, cbz(W4, CompareBranchOffset::default()).to_code()); - assert_eq!(reloc, Some(Rel64::TstBr14(label))); + assert_eq!(reloc, Some(Rel64::CondBr19(label))); } #[test] @@ -738,7 +738,7 @@ mod tests { let (code, reloc) = inst.to_code_with_reloc(); assert_eq!(code, cbnz(X5, CompareBranchOffset::default()).to_code()); - assert_eq!(reloc, Some(Rel64::TstBr14(label))); + assert_eq!(reloc, Some(Rel64::CondBr19(label))); } #[test] @@ -751,6 +751,6 @@ mod tests { let (code, reloc) = inst.to_code_with_reloc(); assert_eq!(code, cbnz(W6, CompareBranchOffset::default()).to_code()); - assert_eq!(reloc, Some(Rel64::TstBr14(label))); + assert_eq!(reloc, Some(Rel64::CondBr19(label))); } } From 53f2e41195d72fdc3085e91e781966d0b6e33efe Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Fri, 26 Dec 2025 21:23:31 +0100 Subject: [PATCH 13/15] Review fixes --- harm/src/instructions/dpimm/movewide/movewide_reloc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs index 2f3bd80d..554c22cb 100644 --- a/harm/src/instructions/dpimm/movewide/movewide_reloc.rs +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -463,7 +463,7 @@ mod tests { } #[test] - fn test_64_abs_g2s_movk() { + fn test_64_abs_g2s_movz() { let label = LabelRef { id: LabelId(6), addend: 47, @@ -479,8 +479,8 @@ mod tests { 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()); + let inst = movk(X7, abs_g2_nc(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(X7, (0, 32)).unwrap().to_code()); assert_eq!(inst.1, Some(Rel64::MovWAbsG2Nc(label))); } From 8d57860c2c89736753ca0613fd15f71d17b55532 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Sat, 27 Dec 2025 17:57:30 +0100 Subject: [PATCH 14/15] harm: `tbz`/`tbnz` relocations --- harm/src/instructions/control/testbranch.rs | 86 ++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/harm/src/instructions/control/testbranch.rs b/harm/src/instructions/control/testbranch.rs index 4d51c41b..e428f75e 100644 --- a/harm/src/instructions/control/testbranch.rs +++ b/harm/src/instructions/control/testbranch.rs @@ -10,8 +10,9 @@ use aarchmrs_types::InstructionCode; use crate::{ bits::{SBitValue, UBitValue}, - instructions::RawInstruction, + instructions::{RawInstruction, RelocatableInstruction}, register::{IntoReg, RegOrZero32, RegOrZero64, Register as _}, + reloc::{LabelRef, Rel64}, sealed::Sealed, }; @@ -28,6 +29,8 @@ pub struct TestBranch { impl Sealed for TestBranch {} impl Sealed for TestBranch {} +impl Sealed for TestBranch {} +impl Sealed for TestBranch {} pub trait MakeTestBranch: Sealed { fn new(op: bool, reg: Reg, bit: Bit, offset: Offset) -> Self; @@ -59,6 +62,32 @@ impl> MakeTestBranch> MakeTestBranch + for TestBranch +{ + fn new(op: bool, reg: R, bit: TestBranchBit64, offset: LabelRef) -> Self { + Self { + op, + reg: reg.into_reg(), + bit, + offset, + } + } +} + +impl> MakeTestBranch + for TestBranch +{ + fn new(op: bool, reg: R, bit: TestBranchBit32, offset: LabelRef) -> Self { + Self { + op, + reg: reg.into_reg(), + bit, + offset, + } + } +} + impl RawInstruction for TestBranch { #[inline] fn to_code(&self) -> InstructionCode { @@ -89,6 +118,36 @@ impl RawInstruction for TestBranch { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let code = TestBranch { + op: self.op, + reg: self.reg, + bit: self.bit, + offset: TestBranchOffset::default(), + } + .to_code(); + let rel = Rel64::TstBr14(self.offset.clone()); + (code, Some(rel)) + } +} + +impl RelocatableInstruction for TestBranch { + #[inline] + fn to_code_with_reloc(&self) -> (InstructionCode, Option) { + let code = TestBranch { + op: self.op, + reg: self.reg, + bit: self.bit, + offset: TestBranchOffset::default(), + } + .to_code(); + let rel = Rel64::TstBr14(self.offset.clone()); + (code, Some(rel)) + } +} + pub fn tbnz( reg: InpReg, bit: Bit, @@ -118,6 +177,7 @@ mod tests { use crate::register::Reg64::*; use crate::register::RegOrZero32::WZR; use crate::register::RegOrZero64::XZR; + use crate::reloc::LabelId; use alloc::vec::Vec; #[test] @@ -251,4 +311,28 @@ mod tests { let words: Vec<_> = it.encode().collect(); assert_eq!(words, inst!(0x37e8027f)); } + + #[test] + fn test_tbz_64_reloc() { + let bit = TestBranchBit64::new(29).unwrap(); + let label = LabelRef { + id: LabelId(8), + addend: 3, + }; + let (opcode, rel) = tbz(X2, bit, label.clone()).to_code_with_reloc(); + assert_eq!(opcode, tbz(X2, bit, TestBranchOffset::default()).to_code()); + assert_eq!(rel, Some(Rel64::TstBr14(label))); + } + + #[test] + fn test_tbnz_64_reloc() { + let bit = TestBranchBit64::new(29).unwrap(); + let label = LabelRef { + id: LabelId(8), + addend: 3, + }; + let (opcode, rel) = tbnz(X2, bit, label.clone()).to_code_with_reloc(); + assert_eq!(opcode, tbnz(X2, bit, TestBranchOffset::default()).to_code()); + assert_eq!(rel, Some(Rel64::TstBr14(label))); + } } From 0bbae7c892eb08c5640662eff6d641b312111482 Mon Sep 17 00:00:00 2001 From: Ivan Boldyrev Date: Mon, 29 Dec 2025 13:34:53 +0100 Subject: [PATCH 15/15] runtime: Remove unfinished code --- harm-runtime/src/labels.rs | 84 +------------------------------------ harm-runtime/src/lib.rs | 1 - harm-runtime/src/runtime.rs | 70 ------------------------------- 3 files changed, 1 insertion(+), 154 deletions(-) delete mode 100644 harm-runtime/src/runtime.rs diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs index 2c411121..c6552564 100644 --- a/harm-runtime/src/labels.rs +++ b/harm-runtime/src/labels.rs @@ -3,9 +3,7 @@ * This document is licensed under the BSD 3-clause license. */ -use std::collections::HashMap; - -use harm::reloc::{LabelId, Offset}; +use harm::reloc::Offset; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum LabelInfo { @@ -13,83 +11,3 @@ pub enum LabelInfo { // TODO segment Offset(Offset), } - -#[derive(Debug, Default)] -pub struct LabelRegistry { - named_labels: HashMap, - labels: HashMap, - next_id: usize, -} - -impl LabelRegistry { - #[inline] - pub fn new() -> Self { - Self::default() - } - - #[inline] - pub fn forward_named_label(&mut self, name: &str) -> LabelId { - if let Some(id) = self.named_labels.get(name) { - *id - } else { - let id = self.next_label(); - self.named_labels.insert(name.to_string(), id); - self.labels.insert(id, LabelInfo::Forward); - id - } - } - - #[inline] - pub fn forward_label(&mut self) -> LabelId { - let id = self.next_label(); - self.labels.insert(id, LabelInfo::Forward); - id - } - - pub fn define_label(&mut self, id: LabelId, offset: Offset) { - if let Some(info) = self.labels.get_mut(&id) { - match info { - LabelInfo::Forward => { - *info = LabelInfo::Offset(offset); - } - LabelInfo::Offset(_) => { - todo!("Label {id:?} is already defined"); - } - } - } else { - panic!("Label {id:?} is not registered"); - } - } - - #[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 - } - } - - pub fn name_label(&mut self, id: LabelId, name: &str) { - if self.labels.contains_key(&id) { - self.named_labels.insert(name.to_string(), id); - } else { - panic!("Label {id:?} is not registered"); - } - } - - #[inline] - pub fn label_info(&self, id: LabelId) -> Option<&LabelInfo> { - self.labels.get(&id) - } - - fn next_label(&mut self) -> LabelId { - let id = LabelId(self.next_id); - self.next_id += 1; - id - } -} diff --git a/harm-runtime/src/lib.rs b/harm-runtime/src/lib.rs index cf7cc344..cdcc60b9 100644 --- a/harm-runtime/src/lib.rs +++ b/harm-runtime/src/lib.rs @@ -4,4 +4,3 @@ */ pub mod labels; -pub mod runtime; diff --git a/harm-runtime/src/runtime.rs b/harm-runtime/src/runtime.rs deleted file mode 100644 index e66f14a2..00000000 --- a/harm-runtime/src/runtime.rs +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (C) 2025 Ivan Boldyrev - * - * This document is licensed under the BSD 3-clause license. - */ - -use std::collections::HashMap; - -use crate::labels::LabelRegistry; -use harm::InstructionCode; -use harm::instructions::InstructionSeq; -use harm::reloc::{LabelId, Rel64}; - -// N.N. we keep here internal relocation type, and convert it to external on serialization. -#[derive(Default)] -pub struct Assembler { - label_manager: LabelRegistry, - instructions: Vec, - relocations: HashMap, -} - -impl Assembler { - #[inline] - pub fn new() -> Self { - <_>::default() - } - - #[inline] - pub fn with_capacity(cap: usize) -> Self { - Self { - label_manager: LabelRegistry::new(), - instructions: Vec::with_capacity(cap), - relocations: HashMap::new(), - } - } - - pub fn insert(&mut self, s: InstSeq) { - for (inst, rel) in s.encode() { - let pos = self.instructions.len(); - if let Some(rel) = rel { - self.relocations.insert(pos, rel); - } - self.instructions.push(inst); - } - } - - // TODO the label have to be aligned. - // For an instruction, it is alwasy 4 bytes, but for data it can be different, from 1 to N bytes. - pub fn current_label(&mut self) -> LabelId { - let pos = self.instructions.len(); - todo!() - } - - pub fn current_named_label(&mut self, name: &str) -> LabelId { - let id = self.new_forward_named_label(name); - self.assign_forward_label(id); - id - } - - pub fn new_forward_label(&mut self) -> LabelId { - todo!() - } - - pub fn new_forward_named_label(&mut self, name: &str) -> LabelId { - todo!() - } - - pub fn assign_forward_label(&mut self, label: LabelId) { - todo!() - } -}