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..aa4a5a11 --- /dev/null +++ b/harm-runtime/Cargo.toml @@ -0,0 +1,15 @@ +[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 } +memmap2 = "0.9.9" diff --git a/harm-runtime/src/labels.rs b/harm-runtime/src/labels.rs new file mode 100644 index 00000000..c6552564 --- /dev/null +++ b/harm-runtime/src/labels.rs @@ -0,0 +1,13 @@ +/* Copyright (C) 2025 Ivan Boldyrev + * + * This document is licensed under the BSD 3-clause license. + */ + +use harm::reloc::Offset; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum LabelInfo { + Forward, + // TODO segment + Offset(Offset), +} 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/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..70ad4edd 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, Rel64}, sealed::Sealed, }; @@ -37,6 +38,12 @@ pub enum BranchCond { NV = 0b1111, // always } +impl From for BitValue<4> { + fn from(cond: BranchCond) -> Self { + Self::new_u32(cond 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 = 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 = 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 = 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,13 +275,14 @@ 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, + 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(), @@ -217,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(), @@ -232,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(), @@ -247,13 +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(), @@ -262,7 +369,7 @@ where } } -impl RawInstruction for CompareBranch { +impl RawInstruction for CompareBranch { #[inline] fn to_code(&self) -> InstructionCode { use aarchmrs_instructions::A64::control::compbranch; @@ -275,7 +382,25 @@ impl RawInstruction for CompareBranch { } } -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::CondBr19(self.offset); + + (code, Some(reloc)) + } +} + +impl RawInstruction for CompareBranch { #[inline] fn to_code(&self) -> InstructionCode { use aarchmrs_instructions::A64::control::compbranch; @@ -288,22 +413,40 @@ impl RawInstruction for CompareBranch { } } -pub fn cbz( +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::CondBr19(self.offset); + + (code, Some(reloc)) + } +} + +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) } @@ -317,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; @@ -557,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::CondBr19(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::CondBr19(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::CondBr19(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::CondBr19(label))); + } } 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))); + } } diff --git a/harm/src/instructions/dpimm/movewide.rs b/harm/src/instructions/dpimm/movewide.rs index 373ce519..736502bd 100644 --- a/harm/src/instructions/dpimm/movewide.rs +++ b/harm/src/instructions/dpimm/movewide.rs @@ -7,12 +7,17 @@ * Module for move instructions, like `MOVZ`, `MOVN` (not to be confused with `MVN`), `MOVK` in AArch64. */ +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, MOVZ_32_movewide::MOVZ_32_movewide, MOVZ_64_movewide::MOVZ_64_movewide, }; +pub use self::movewide_reloc::*; use crate::{ bits::{BitError, UBitValue}, instructions::RawInstruction, @@ -21,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. @@ -56,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, @@ -69,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, @@ -85,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(), @@ -102,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, @@ -115,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(), @@ -131,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, @@ -149,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> { @@ -175,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> { @@ -201,16 +211,15 @@ impl RawInstruction for MovN> { pub struct MovZ(Args); -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 new file mode 100644 index 00000000..554c22cb --- /dev/null +++ b/harm/src/instructions/dpimm/movewide/movewide_reloc.rs @@ -0,0 +1,542 @@ +/* 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, MovK, MovN, MovWideTypeTag, MovZ, MoveImm16, Shift32, Shift64, movk, movn, 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 unsigned 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 signed 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 unsigned 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 signed 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) +} + +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, +} + +pub trait MoveWideReloc64: Sealed { + fn get_shift64(&self) -> Shift64; + fn get_relocation(&self) -> Rel64; +} + +pub trait MoveWideReloc32: MoveWideReloc64 { + fn get_shift32(&self) -> Shift32; +} + +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() + } + + #[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, 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; + 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 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 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_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::Reg32::*; + use crate::register::Reg64::*; + use crate::reloc::LabelId; + + use super::*; + + #[test] + 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, 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_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() { + let label = LabelRef { + id: LabelId(7), + addend: 48, + }; + 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))); + } + + #[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))); + } + + #[test] + fn test_32_abs_g0_movz() { + let label = LabelRef { + id: LabelId(1), + addend: 42, + }; + let inst = movz(W1, abs_g0(label)).to_code_with_reloc(); + assert_eq!(inst.0, movz(W1, (0, 0)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG0(label))); + } + + #[test] + fn test_32_abs_g1_movk() { + let label = LabelRef { + id: LabelId(2), + addend: 43, + }; + let inst = movk(W2, abs_g1(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(W2, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1(label))); + } + + #[test] + fn test_32_abs_g1s_movn() { + let label = LabelRef { + id: LabelId(3), + addend: 44, + }; + // Makes little sense, but gas accepts it. + let inst = movn(W3, abs_g1_s(label)).to_code_with_reloc(); + assert_eq!(inst.0, movn(W3, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1S(label))); + } + + #[test] + fn test_32_abs_g1nc_movk() { + let label = LabelRef { + id: LabelId(4), + addend: 45, + }; + let inst = movk(W4, abs_g1_nc(label)).to_code_with_reloc(); + assert_eq!(inst.0, movk(W4, (0, 16)).unwrap().to_code()); + assert_eq!(inst.1, Some(Rel64::MovWAbsG1Nc(label))); + } +} 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)) + } + } } }; } 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 893ec752..e6e3957f 100644 --- a/harm/src/reloc.rs +++ b/harm/src/reloc.rs @@ -1,5 +1,61 @@ +/* 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 "[relocation] is sign-extended to 64 bits". +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 addend: 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. +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 + MovWAbsG0(LabelRef), + MovWAbsG0Nc(LabelRef), + MovWAbsG0S(LabelRef), + MovWAbsG1(LabelRef), + MovWAbsG1Nc(LabelRef), + MovWAbsG1S(LabelRef), + MovWAbsG2(LabelRef), + MovWAbsG2Nc(LabelRef), + MovWAbsG2S(LabelRef), + MovWAbsG3(LabelRef), }