use std::collections::HashMap;
use llvm_native_core::mc_assembler;
use llvm_native_core::mc_disassembler::{DecodedInst, DecodedOperand, X86FullDecoder, X86Mode};
use llvm_native_core::mc_inst::MCInst;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ObjectFormat {
ELF,
COFF,
MachO,
Wasm,
}
pub trait MCTarget {
fn name(&self) -> &str;
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait>;
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait>;
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter>;
fn supports_object_format(&self, format: ObjectFormat) -> bool;
fn get_pointer_size(&self) -> u8;
fn triple_prefix(&self) -> &str;
}
pub trait MCAssemblerTrait {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>>;
fn get_instruction_size(&self, inst: &MCInst) -> usize;
fn target_name(&self) -> &str;
}
pub trait MCDisassemblerTrait {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst>;
fn decode_all(&self, bytes: &[u8]) -> Vec<DecodedInst> {
let mut results = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
if let Some(di) = self.decode_one(bytes, offset) {
offset += di.size;
results.push(di);
} else {
offset += 1;
}
}
results
}
}
pub trait MCInstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String;
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String;
}
pub struct X86MCTarget;
impl MCTarget for X86MCTarget {
fn name(&self) -> &str {
"x86-64"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(X86AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(X86DisassemblerAdapter {
decoder: X86FullDecoder::new(X86Mode::Mode64),
})
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(X86InstPrinter)
}
fn supports_object_format(&self, format: ObjectFormat) -> bool {
matches!(
format,
ObjectFormat::ELF | ObjectFormat::COFF | ObjectFormat::MachO
)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"x86_64"
}
}
struct X86AssemblerAdapter;
impl MCAssemblerTrait for X86AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
mc_assembler::encode_x86_64(inst)
}
fn get_instruction_size(&self, inst: &MCInst) -> usize {
mc_assembler::encode_x86_64(inst).map_or(0, |v| v.len())
}
fn target_name(&self) -> &str {
"x86-64"
}
}
struct X86DisassemblerAdapter {
decoder: X86FullDecoder,
}
impl MCDisassemblerTrait for X86DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
self.decoder.decode_one(bytes, offset)
}
fn decode_all(&self, bytes: &[u8]) -> Vec<DecodedInst> {
self.decoder.decode_all(bytes)
}
}
struct X86InstPrinter;
impl MCInstPrinter for X86InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
let mnemonic = llvm_native_core::mc_streamer::x86_mnemonic(inst.opcode);
let ops: Vec<String> = inst
.operands
.iter()
.map(|op| self.print_operand(op))
.collect();
if ops.is_empty() {
mnemonic.to_string()
} else {
format!("{} {}", mnemonic, ops.join(", "))
}
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => {
format!("%{}", llvm_native_core::mc_streamer::x86_reg_name(*r))
}
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("${}", v),
llvm_native_core::mc_inst::MCOperand::FpImm(v) => format!("{}", v),
llvm_native_core::mc_inst::MCOperand::Expr(e) => format!("{}", e),
llvm_native_core::mc_inst::MCOperand::Inst(_) => "<inst>".to_string(),
}
}
}
pub struct AArch64MCTarget;
impl MCTarget for AArch64MCTarget {
fn name(&self) -> &str {
"aarch64"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(AArch64AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(AArch64DisassemblerAdapter { base_address: 0 })
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(AArch64InstPrinter)
}
fn supports_object_format(&self, format: ObjectFormat) -> bool {
matches!(format, ObjectFormat::ELF | ObjectFormat::MachO)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"aarch64"
}
}
struct AArch64AssemblerAdapter;
impl MCAssemblerTrait for AArch64AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
mc_assembler::encode_aarch64(inst)
}
fn get_instruction_size(&self, _inst: &MCInst) -> usize {
4 }
fn target_name(&self) -> &str {
"aarch64"
}
}
struct AArch64DisassemblerAdapter {
base_address: u64,
}
impl MCDisassemblerTrait for AArch64DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_le_bytes(bytes[offset..offset + 4].try_into().ok()?);
let (mnemonic, ops) = if word == 0xD503201F {
("nop", vec![])
} else if word == 0xD65F03C0 {
("ret", vec![])
} else if (word >> 25) & 0x7 == 0x5 {
let imm26 = (word & 0x03FFFFFF) as i32;
let offset = (imm26 << 2) as i64;
let is_link = ((word >> 31) & 1) == 1;
let mnem = if is_link { "bl" } else { "b" };
(mnem, vec![DecodedOperand::RelOffset(offset)])
} else if (word >> 23) & 0x3F == 0x22 {
let is_sub = ((word >> 30) & 1) == 1;
let mnem = if is_sub { "sub" } else { "add" };
let rd = (word & 0x1F) as u32;
let rn = ((word >> 5) & 0x1F) as u32;
let imm12 = ((word >> 10) & 0xFFF) as i64;
(
mnem,
vec![
DecodedOperand::reg(&aarch64_reg_name(rd), 8),
DecodedOperand::reg(&aarch64_reg_name(rn), 8),
DecodedOperand::Imm(imm12),
],
)
} else if (word >> 24) & 0x1F == 0x0A {
let opc = (word >> 29) & 0x3;
let mnem = match opc {
0 => "and",
1 => "orr",
2 => "eor",
_ => "and",
};
let rd = (word & 0x1F) as u32;
let rn = ((word >> 5) & 0x1F) as u32;
let rm = ((word >> 16) & 0x1F) as u32;
(
mnem,
vec![
DecodedOperand::reg(&aarch64_reg_name(rd), 8),
DecodedOperand::reg(&aarch64_reg_name(rn), 8),
DecodedOperand::reg(&aarch64_reg_name(rm), 8),
],
)
} else {
return None;
};
Some(DecodedInst {
opcode: word,
mnemonic: mnemonic.to_string(),
operands: ops,
size: 4,
address: self.base_address + offset as u64,
})
}
}
fn aarch64_reg_name(reg: u32) -> String {
match reg {
0..=28 => format!("x{}", reg),
29 => "fp".into(),
30 => "lr".into(),
31 => "sp".into(),
_ => format!("r{}", reg),
}
}
struct AArch64InstPrinter;
impl MCInstPrinter for AArch64InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
let mnemonic = match inst.opcode {
0 => "nop",
1 => "ret",
2 => "add",
3 => "sub",
4 => "mul",
5 => "and",
6 => "orr",
7 => "eor",
8 => "b",
9 => "bl",
_ => "unknown",
};
let ops: Vec<String> = inst
.operands
.iter()
.map(|op| self.print_operand(op))
.collect();
if ops.is_empty() {
mnemonic.to_string()
} else {
format!("{} {}", mnemonic, ops.join(", "))
}
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("%x{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("#{}", v),
llvm_native_core::mc_inst::MCOperand::FpImm(v) => format!("{}", v),
llvm_native_core::mc_inst::MCOperand::Expr(e) => format!("{}", e),
llvm_native_core::mc_inst::MCOperand::Inst(_) => "<inst>".to_string(),
}
}
}
pub struct ARM32MCTarget;
impl MCTarget for ARM32MCTarget {
fn name(&self) -> &str {
"arm"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(ARM32AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(ARM32DisassemblerAdapter { base_address: 0 })
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(ARM32InstPrinter)
}
fn supports_object_format(&self, format: ObjectFormat) -> bool {
matches!(format, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
4
}
fn triple_prefix(&self) -> &str {
"arm"
}
}
struct ARM32AssemblerAdapter;
impl MCAssemblerTrait for ARM32AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
mc_assembler::encode_arm32(inst)
}
fn get_instruction_size(&self, _inst: &MCInst) -> usize {
4 }
fn target_name(&self) -> &str {
"arm"
}
}
struct ARM32DisassemblerAdapter {
base_address: u64,
}
impl MCDisassemblerTrait for ARM32DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_le_bytes(bytes[offset..offset + 4].try_into().ok()?);
let (mnemonic, ops) = if word == 0xE1A00000 {
("nop", vec![])
} else if (word & 0x0FFFFFFF) == 0x01A00000 {
("mov", vec![])
} else if (word >> 28) == 0xE {
let opcode = (word >> 21) & 0x0F;
let is_imm = ((word >> 25) & 1) == 1;
let rd = ((word >> 12) & 0x0F) as u32;
let rn = ((word >> 16) & 0x0F) as u32;
let rm = (word & 0x0F) as u32;
let mnem = match opcode {
0x0 => "and",
0x1 => "eor",
0x2 => "sub",
0x3 => "rsb",
0x4 => "add",
0x8 => "tst",
0x9 => "teq",
0xA => "cmp",
0xB => "cmn",
0xC => "orr",
0xD => "mov",
0xE => "bic",
0xF => "mvn",
_ => "unknown",
};
if mnem == "mov" || mnem == "mvn" {
(mnem, vec![DecodedOperand::reg(&arm32_reg_name(rd), 4)])
} else {
(
mnem,
vec![
DecodedOperand::reg(&arm32_reg_name(rd), 4),
DecodedOperand::reg(&arm32_reg_name(rn), 4),
if is_imm {
let imm8 = word & 0xFF;
let rotate = ((word >> 8) & 0x0F) * 2;
let val = (imm8 as u32).rotate_right(rotate);
DecodedOperand::Imm(val as i64)
} else {
DecodedOperand::reg(&arm32_reg_name(rm), 4)
},
],
)
}
} else if (word >> 24) == 0xEA {
let offset = ((word & 0x00FFFFFF) as i32) << 2;
("b", vec![DecodedOperand::RelOffset(offset as i64)])
} else if (word >> 24) == 0xEB {
let offset = ((word & 0x00FFFFFF) as i32) << 2;
("bl", vec![DecodedOperand::RelOffset(offset as i64)])
} else {
return None;
};
Some(DecodedInst {
opcode: word,
mnemonic: mnemonic.to_string(),
operands: ops,
size: 4,
address: self.base_address + offset as u64,
})
}
}
fn arm32_reg_name(reg: u32) -> String {
match reg {
0..=12 => format!("r{}", reg),
13 => "sp".into(),
14 => "lr".into(),
15 => "pc".into(),
_ => format!("r{}", reg),
}
}
struct ARM32InstPrinter;
impl MCInstPrinter for ARM32InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
let mnemonic = match inst.opcode {
0 => "nop",
1 => "mov",
2 => "add",
3 => "sub",
4 => "mul",
5 => "and",
6 => "orr",
7 => "eor",
_ => "unknown",
};
let ops: Vec<String> = inst
.operands
.iter()
.map(|op| self.print_operand(op))
.collect();
if ops.is_empty() {
mnemonic.to_string()
} else {
format!("{} {}", mnemonic, ops.join(", "))
}
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("%r{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("#{}", v),
llvm_native_core::mc_inst::MCOperand::FpImm(v) => format!("{}", v),
llvm_native_core::mc_inst::MCOperand::Expr(e) => format!("{}", e),
llvm_native_core::mc_inst::MCOperand::Inst(_) => "<inst>".to_string(),
}
}
}
pub struct MCTargetRegistry {
pub targets: HashMap<String, Box<dyn MCTarget>>,
}
impl MCTargetRegistry {
pub fn new() -> Self {
Self {
targets: HashMap::new(),
}
}
pub fn register(&mut self, target: Box<dyn MCTarget>) {
let key = target.triple_prefix().to_string();
self.targets.insert(key, target);
}
pub fn register_default_targets(&mut self) {
self.register(Box::new(X86MCTarget));
self.register(Box::new(AArch64MCTarget));
self.register(Box::new(ARM32MCTarget));
}
pub fn with_default_targets() -> Self {
let mut reg = Self::new();
reg.register_default_targets();
reg
}
pub fn lookup_target(&self, triple: &str) -> Option<&dyn MCTarget> {
let arch = triple.split('-').next().unwrap_or(triple);
let key = match arch {
"x86_64" | "amd64" | "x64" => "x86_64",
"aarch64" | "arm64" => "aarch64",
"arm" | "armv7" | "armv7l" | "armv8l" | "thumb" | "thumbv7" => "arm",
other if other.starts_with("arm") => "arm",
other if other.starts_with("thumb") => "arm",
other => other,
};
self.targets.get(key).map(|boxed| boxed.as_ref())
}
pub fn get_target_by_name(&self, name: &str) -> Option<&dyn MCTarget> {
self.targets
.values()
.find(|t| t.name() == name)
.map(|boxed| boxed.as_ref())
}
pub fn len(&self) -> usize {
self.targets.len()
}
pub fn is_empty(&self) -> bool {
self.targets.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &dyn MCTarget> {
self.targets.values().map(|boxed| boxed.as_ref())
}
}
impl Default for MCTargetRegistry {
fn default() -> Self {
Self::with_default_targets()
}
}
impl MCTargetRegistry {
pub fn register_mc_asm_backend(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_code_emitter(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_instr_info(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_register_info(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_subtarget_info(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_asm_parser(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_disassembler(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_object_writer(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn register_mc_asm_streamer(&mut self, triple_prefix: &str) -> bool {
self.targets.contains_key(triple_prefix)
}
pub fn lookup_target_by_arch_name(&self, arch: &str) -> Option<&dyn MCTarget> {
let key = match arch {
"x86" | "x86-64" | "amd64" | "x86_64" | "i386" | "i686" => "x86_64",
"aarch64" | "arm64" | "armv8" | "arm64e" => "aarch64",
"arm" | "armv7" | "armv7a" | "armv7l" | "thumb" => "arm",
"riscv64" | "riscv32" => arch,
"mips64" | "mips" => arch,
"powerpc64" | "powerpc64le" | "powerpc" => arch,
"sparcv9" | "sparc" => arch,
"s390x" | "systemz" => "systemz",
"bpf" | "bpfeb" | "bpf64" => "bpf",
"wasm32" | "wasm64" => "wasm",
"nvptx" | "nvptx64" => "nvptx",
"amdgcn" => "amdgpu",
"hexagon" => "hexagon",
"avr" => "avr",
"msp430" => "msp430",
"lanai" => "lanai",
"arc" => "arc",
"csky" => "csky",
"xtensa" => "xtensa",
"loongarch64" => "loongarch",
"ve" => "ve",
_ => arch,
};
self.targets.get(key).map(|b| b.as_ref())
}
pub fn lookup_target_by_triple(&self, triple: &str) -> Option<&dyn MCTarget> {
self.lookup_target(triple)
}
pub fn get_closest_target_for_jit(&self) -> Option<&dyn MCTarget> {
#[cfg(target_arch = "x86_64")]
{
return self.lookup_target_by_arch_name("x86_64");
}
#[cfg(target_arch = "aarch64")]
{
return self.lookup_target_by_arch_name("aarch64");
}
#[cfg(target_arch = "arm")]
{
return self.lookup_target_by_arch_name("arm");
}
#[cfg(target_arch = "riscv64")]
{
return self.lookup_target_by_arch_name("riscv64");
}
None
}
}
pub trait MCAsmBackend {
fn target_name(&self) -> &str;
fn create_code_emitter(&self) -> Box<dyn MCCodeEmitter>;
fn create_object_writer(&self) -> Box<dyn MCObjectWriter>;
fn get_relaxed_instruction_size(&self) -> usize;
}
pub trait MCCodeEmitter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>>;
fn target_name(&self) -> &str;
}
pub trait MCObjectWriter {
fn write_header(&self) -> Vec<u8>;
fn write_section(&self, name: &str, data: &[u8], flags: u32) -> Vec<u8>;
fn finish(&self) -> Vec<u8>;
}
pub struct X86MCAsmBackend;
impl MCAsmBackend for X86MCAsmBackend {
fn target_name(&self) -> &str {
"x86-64"
}
fn create_code_emitter(&self) -> Box<dyn MCCodeEmitter> {
Box::new(X86MCCodeEmitter)
}
fn create_object_writer(&self) -> Box<dyn MCObjectWriter> {
Box::new(X86MCObjectWriter)
}
fn get_relaxed_instruction_size(&self) -> usize {
15
}
}
pub struct X86MCCodeEmitter;
impl MCCodeEmitter for X86MCCodeEmitter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
mc_assembler::encode_x86_64(inst)
}
fn target_name(&self) -> &str {
"x86-64"
}
}
pub struct X86MCObjectWriter;
impl MCObjectWriter for X86MCObjectWriter {
fn write_header(&self) -> Vec<u8> {
Vec::new()
}
fn write_section(&self, _name: &str, data: &[u8], _flags: u32) -> Vec<u8> {
data.to_vec()
}
fn finish(&self) -> Vec<u8> {
Vec::new()
}
}
pub struct MCInstrAnalysis;
impl MCInstrAnalysis {
pub fn evaluate_branch(inst: &MCInst, pc: u64, _size: usize) -> Option<u64> {
if inst.is_branch() {
if let Some(offset) = MCInstrAnalysis::get_branch_target(inst) {
Some(pc.wrapping_add(offset as u64))
} else {
None
}
} else {
None
}
}
pub fn get_branch_target(inst: &MCInst) -> Option<i64> {
for op in &inst.operands {
if let llvm_native_core::mc_inst::MCOperand::Imm(v) = op {
return Some(*v);
}
}
None
}
pub fn is_call(&self, inst: &MCInst) -> bool {
inst.is_call()
}
pub fn is_return(&self, inst: &MCInst) -> bool {
inst.is_return()
}
pub fn is_branch(&self, inst: &MCInst) -> bool {
inst.is_branch() || inst.is_call() || inst.is_return()
}
pub fn is_unconditional_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_uncond_branch || inst.is_return() || inst.is_call()
}
pub fn is_conditional_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_cond_branch
}
pub fn is_indirect_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_indirect_branch || inst.is_return()
}
pub fn is_barrier(&self, inst: &MCInst) -> bool {
inst.flags.is_barrier || inst.is_return()
}
pub fn is_terminator(&self, inst: &MCInst) -> bool {
inst.is_terminator()
}
}
#[derive(Debug, Clone)]
pub struct MCRegisterClass {
pub name: String,
pub regs: Vec<u32>,
pub alignment: u32,
pub copy_cost: u32,
pub allocatable: bool,
}
impl MCRegisterClass {
pub fn new(name: &str, regs: Vec<u32>) -> Self {
Self {
name: name.to_string(),
regs,
alignment: 1,
copy_cost: 1,
allocatable: true,
}
}
pub fn contains(&self, reg: u32) -> bool {
self.regs.contains(®)
}
}
pub struct MCRegisterInfo {
pub reg_classes: Vec<MCRegisterClass>,
pub sub_to_super: HashMap<u32, u32>,
pub super_to_sub: HashMap<u32, Vec<u32>>,
pub allocatable_set: Vec<u32>,
}
impl MCRegisterInfo {
pub fn new() -> Self {
Self {
reg_classes: Vec::new(),
sub_to_super: HashMap::new(),
super_to_sub: HashMap::new(),
allocatable_set: Vec::new(),
}
}
pub fn get_reg_class(&self, name: &str) -> Option<&MCRegisterClass> {
self.reg_classes.iter().find(|rc| rc.name == name)
}
pub fn get_reg_classes(&self) -> &[MCRegisterClass] {
&self.reg_classes
}
pub fn get_sub_reg(&self, reg: u32, index: usize) -> Option<u32> {
self.super_to_sub.get(®)?.get(index).copied()
}
pub fn get_super_reg(&self, reg: u32) -> Option<u32> {
self.sub_to_super.get(®).copied()
}
pub fn get_sub_reg_index(&self, sub: u32) -> Option<usize> {
if let Some(&sup) = self.sub_to_super.get(&sub) {
if let Some(subs) = self.super_to_sub.get(&sup) {
return subs.iter().position(|&s| s == sub);
}
}
None
}
pub fn get_minimal_phys_reg_class(&self, reg: u32) -> Option<&MCRegisterClass> {
self.reg_classes
.iter()
.filter(|rc| rc.contains(reg))
.min_by_key(|rc| rc.regs.len())
}
pub fn get_allocatable_set(&self) -> &[u32] {
&self.allocatable_set
}
pub fn get_matching_super_reg(&self, reg: u32, sub_idx: usize) -> Option<u32> {
for (sup, subs) in &self.super_to_sub {
if subs.get(sub_idx) == Some(®) {
return Some(*sup);
}
}
None
}
}
pub fn x86_64_register_info() -> MCRegisterInfo {
let mut info = MCRegisterInfo::new();
let gprs: Vec<u32> = (0..16).collect();
info.reg_classes
.push(MCRegisterClass::new("GR64", gprs.clone()));
info.reg_classes
.push(MCRegisterClass::new("GR32", gprs.clone()));
info.reg_classes
.push(MCRegisterClass::new("GR16", gprs.clone()));
info.reg_classes
.push(MCRegisterClass::new("GR8", (0..8).collect()));
info.allocatable_set = gprs;
info
}
pub fn aarch64_register_info() -> MCRegisterInfo {
let mut info = MCRegisterInfo::new();
let gprs: Vec<u32> = (0..32).collect();
info.reg_classes
.push(MCRegisterClass::new("GPR64", gprs.clone()));
info.reg_classes
.push(MCRegisterClass::new("GPR32", gprs.clone()));
info.allocatable_set = (0..30).collect(); info
}
pub struct MCSubtargetInfo {
feature_bits: HashMap<String, bool>,
sched_model: String,
}
impl MCSubtargetInfo {
pub fn new() -> Self {
Self {
feature_bits: HashMap::new(),
sched_model: "generic".into(),
}
}
pub fn get_feature_bits(&self) -> &HashMap<String, bool> {
&self.feature_bits
}
pub fn set_feature_bits(&mut self, bits: HashMap<String, bool>) {
self.feature_bits = bits;
}
pub fn has_feature(&self, name: &str) -> bool {
self.feature_bits.get(name).copied().unwrap_or(false)
}
pub fn apply_feature_flag(&mut self, name: &str, enabled: bool) {
self.feature_bits.insert(name.to_string(), enabled);
}
pub fn toggle_feature(&mut self, name: &str) {
let current = self.feature_bits.get(name).copied().unwrap_or(false);
self.feature_bits.insert(name.to_string(), !current);
}
pub fn get_sched_model(&self) -> &str {
&self.sched_model
}
pub fn set_sched_model(&mut self, model: &str) {
self.sched_model = model.to_string();
}
}
impl Default for MCSubtargetInfo {
fn default() -> Self {
Self::new()
}
}
pub struct MCTargetBase;
impl MCTargetBase {
pub fn create_mc_asm_backend() -> Option<Box<dyn MCAsmBackend>> {
None
}
pub fn create_mc_code_emitter() -> Option<Box<dyn MCCodeEmitter>> {
None
}
pub fn create_mc_instr_info() -> Option<MCSubtargetInfo> {
None
}
pub fn create_mc_register_info() -> Option<MCRegisterInfo> {
None
}
pub fn create_mc_subtarget_info() -> Option<MCSubtargetInfo> {
None
}
pub fn create_mc_asm_parser() -> Option<Box<dyn Fn(&str) -> Vec<MCInst>>> {
None
}
pub fn create_mc_disassembler() -> Option<Box<dyn MCDisassemblerTrait>> {
None
}
}
pub struct RISCV64MCTarget;
impl MCTarget for RISCV64MCTarget {
fn name(&self) -> &str {
"riscv64"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(RISCV64AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(RISCV64DisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(RISCV64InstPrinter)
}
fn supports_object_format(&self, format: ObjectFormat) -> bool {
matches!(format, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"riscv64"
}
}
struct RISCV64AssemblerAdapter;
impl MCAssemblerTrait for RISCV64AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
let opcode = inst.opcode;
if opcode == 0 {
return Some(vec![0x13, 0x00, 0x00, 0x00]);
} Some(vec![
(opcode & 0x7F) as u8,
((opcode >> 7) & 0xFF) as u8,
((opcode >> 15) & 0xFF) as u8,
((opcode >> 23) & 0xFF) as u8,
])
}
fn get_instruction_size(&self, _inst: &MCInst) -> usize {
4
}
fn target_name(&self) -> &str {
"riscv64"
}
}
struct RISCV64DisassemblerAdapter;
impl MCDisassemblerTrait for RISCV64DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_le_bytes(bytes[offset..offset + 4].try_into().ok()?);
if word == 0x00000013 {
return Some(DecodedInst {
opcode: word,
mnemonic: "nop".into(),
operands: vec![],
size: 4,
address: offset as u64,
});
}
None
}
}
struct RISCV64InstPrinter;
impl MCInstPrinter for RISCV64InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
let m = match inst.opcode {
0 => "nop",
1 => "add",
2 => "sub",
3 => "addi",
4 => "ld",
5 => "sd",
_ => "unknown",
};
format!(
"{} {}",
m,
inst.operands
.iter()
.map(|o| self.print_operand(o))
.collect::<Vec<_>>()
.join(", ")
)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("x{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct Wasm32MCTarget;
impl MCTarget for Wasm32MCTarget {
fn name(&self) -> &str {
"wasm32"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(WasmAssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(WasmDisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(WasmInstPrinter)
}
fn supports_object_format(&self, format: ObjectFormat) -> bool {
matches!(format, ObjectFormat::Wasm)
}
fn get_pointer_size(&self) -> u8 {
4
}
fn triple_prefix(&self) -> &str {
"wasm32"
}
}
struct WasmAssemblerAdapter;
impl MCAssemblerTrait for WasmAssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
match inst.opcode {
0x00 => Some(vec![0x00]), 0x01 => Some(vec![0x01]), 0x0B => Some(vec![0x0B]), 0x41 => Some({
let mut v = vec![0x41];
if let Some(llvm_native_core::mc_inst::MCOperand::Imm(i)) = inst.operands.first() {
v.extend_from_slice(&(*i as i32).to_le_bytes());
} else {
v.extend_from_slice(&0i32.to_le_bytes());
}
v
}),
_ => None,
}
}
fn get_instruction_size(&self, inst: &MCInst) -> usize {
self.encode_instruction(inst).map_or(0, |v| v.len())
}
fn target_name(&self) -> &str {
"wasm32"
}
}
struct WasmDisassemblerAdapter;
impl MCDisassemblerTrait for WasmDisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset >= bytes.len() {
return None;
}
let op = bytes[offset];
let (mnemonic, size) = match op {
0x00 => ("unreachable", 1),
0x01 => ("nop", 1),
0x0B => ("end", 1),
0x41 => ("i32.const", 5),
_ => return None,
};
Some(DecodedInst {
opcode: op as u32,
mnemonic: mnemonic.into(),
operands: vec![],
size,
address: offset as u64,
})
}
}
struct WasmInstPrinter;
impl MCInstPrinter for WasmInstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
match inst.opcode {
0x00 => "unreachable".into(),
0x01 => "nop".into(),
0x0B => "end".into(),
0x41 => format!(
"i32.const {}",
inst.operands
.first()
.map(|o| self.print_operand(o))
.unwrap_or_default()
),
_ => format!("unknown.{}", inst.opcode),
}
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct PPC64MCTarget;
impl MCTarget for PPC64MCTarget {
fn name(&self) -> &str {
"ppc64"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(PPC64AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(PPC64DisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(PPC64InstPrinter)
}
fn supports_object_format(&self, fmt: ObjectFormat) -> bool {
matches!(fmt, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"ppc64"
}
}
struct PPC64AssemblerAdapter;
impl MCAssemblerTrait for PPC64AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
Some(inst.opcode.to_be_bytes().to_vec())
}
fn get_instruction_size(&self, _: &MCInst) -> usize {
4
}
fn target_name(&self) -> &str {
"ppc64"
}
}
struct PPC64DisassemblerAdapter;
impl MCDisassemblerTrait for PPC64DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_be_bytes(bytes[offset..offset + 4].try_into().ok()?);
Some(DecodedInst {
opcode: word,
mnemonic: "unknown".into(),
operands: vec![],
size: 4,
address: offset as u64,
})
}
}
struct PPC64InstPrinter;
impl MCInstPrinter for PPC64InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
let m = match inst.opcode {
0 => "nop",
1 => "add",
2 => "sub",
_ => "unknown",
};
format!(
"{} {}",
m,
inst.operands
.iter()
.map(|o| self.print_operand(o))
.collect::<Vec<_>>()
.join(", ")
)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("r{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct SystemZMCTarget;
impl MCTarget for SystemZMCTarget {
fn name(&self) -> &str {
"s390x"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(SystemZAssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(SystemZDisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(SystemZInstPrinter)
}
fn supports_object_format(&self, fmt: ObjectFormat) -> bool {
matches!(fmt, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"s390x"
}
}
struct SystemZAssemblerAdapter;
impl MCAssemblerTrait for SystemZAssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
Some((inst.opcode as u16).to_be_bytes().to_vec())
}
fn get_instruction_size(&self, _: &MCInst) -> usize {
2
}
fn target_name(&self) -> &str {
"s390x"
}
}
struct SystemZDisassemblerAdapter;
impl MCDisassemblerTrait for SystemZDisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 2 > bytes.len() {
return None;
}
let op = u16::from_be_bytes(bytes[offset..offset + 2].try_into().ok()?);
Some(DecodedInst {
opcode: op as u32,
mnemonic: "unknown".into(),
operands: vec![],
size: 2,
address: offset as u64,
})
}
}
struct SystemZInstPrinter;
impl MCInstPrinter for SystemZInstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
format!("op{:#x}", inst.opcode)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("%r{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct Mips64MCTarget;
impl MCTarget for Mips64MCTarget {
fn name(&self) -> &str {
"mips64"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(Mips64AssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(Mips64DisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(Mips64InstPrinter)
}
fn supports_object_format(&self, fmt: ObjectFormat) -> bool {
matches!(fmt, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"mips64"
}
}
struct Mips64AssemblerAdapter;
impl MCAssemblerTrait for Mips64AssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
Some(inst.opcode.to_be_bytes().to_vec())
}
fn get_instruction_size(&self, _: &MCInst) -> usize {
4
}
fn target_name(&self) -> &str {
"mips64"
}
}
struct Mips64DisassemblerAdapter;
impl MCDisassemblerTrait for Mips64DisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_be_bytes(bytes[offset..offset + 4].try_into().ok()?);
Some(DecodedInst {
opcode: word,
mnemonic: "unknown".into(),
operands: vec![],
size: 4,
address: offset as u64,
})
}
}
struct Mips64InstPrinter;
impl MCInstPrinter for Mips64InstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
format!("op{:#x}", inst.opcode)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("${}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct BPFMCTarget;
impl MCTarget for BPFMCTarget {
fn name(&self) -> &str {
"bpf"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(BPFAssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(BPFDisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(BPFInstPrinter)
}
fn supports_object_format(&self, fmt: ObjectFormat) -> bool {
matches!(fmt, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"bpf"
}
}
struct BPFAssemblerAdapter;
impl MCAssemblerTrait for BPFAssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
let v = (inst.opcode as u64).to_le_bytes().to_vec();
Some(v)
}
fn get_instruction_size(&self, _: &MCInst) -> usize {
8
}
fn target_name(&self) -> &str {
"bpf"
}
}
struct BPFDisassemblerAdapter;
impl MCDisassemblerTrait for BPFDisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 8 > bytes.len() {
return None;
}
let word = u64::from_le_bytes(bytes[offset..offset + 8].try_into().ok()?);
Some(DecodedInst {
opcode: word as u32,
mnemonic: "unknown".into(),
operands: vec![],
size: 8,
address: offset as u64,
})
}
}
struct BPFInstPrinter;
impl MCInstPrinter for BPFInstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
format!("op{:#x}", inst.opcode)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("r{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
pub struct SparcMCTarget;
impl MCTarget for SparcMCTarget {
fn name(&self) -> &str {
"sparc"
}
fn get_assembler(&self) -> Box<dyn MCAssemblerTrait> {
Box::new(SparcAssemblerAdapter)
}
fn get_disassembler(&self) -> Box<dyn MCDisassemblerTrait> {
Box::new(SparcDisassemblerAdapter)
}
fn get_inst_printer(&self) -> Box<dyn MCInstPrinter> {
Box::new(SparcInstPrinter)
}
fn supports_object_format(&self, fmt: ObjectFormat) -> bool {
matches!(fmt, ObjectFormat::ELF)
}
fn get_pointer_size(&self) -> u8 {
8
}
fn triple_prefix(&self) -> &str {
"sparc"
}
}
struct SparcAssemblerAdapter;
impl MCAssemblerTrait for SparcAssemblerAdapter {
fn encode_instruction(&self, inst: &MCInst) -> Option<Vec<u8>> {
Some(inst.opcode.to_be_bytes().to_vec())
}
fn get_instruction_size(&self, _: &MCInst) -> usize {
4
}
fn target_name(&self) -> &str {
"sparc"
}
}
struct SparcDisassemblerAdapter;
impl MCDisassemblerTrait for SparcDisassemblerAdapter {
fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset + 4 > bytes.len() {
return None;
}
let word = u32::from_be_bytes(bytes[offset..offset + 4].try_into().ok()?);
Some(DecodedInst {
opcode: word,
mnemonic: "unknown".into(),
operands: vec![],
size: 4,
address: offset as u64,
})
}
}
struct SparcInstPrinter;
impl MCInstPrinter for SparcInstPrinter {
fn print_instruction(&self, inst: &MCInst) -> String {
format!("op{:#x}", inst.opcode)
}
fn print_operand(&self, op: &llvm_native_core::mc_inst::MCOperand) -> String {
match op {
llvm_native_core::mc_inst::MCOperand::Reg(r) => format!("%r{}", r),
llvm_native_core::mc_inst::MCOperand::Imm(v) => format!("{}", v),
_ => "?".into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_registry() -> MCTargetRegistry {
MCTargetRegistry::with_default_targets()
}
#[test]
fn test_registry_new_is_empty() {
let reg = MCTargetRegistry::new();
assert!(reg.is_empty());
assert_eq!(reg.len(), 0);
}
#[test]
fn test_registry_default_has_three_targets() {
let reg = make_registry();
assert_eq!(reg.len(), 3);
assert!(!reg.is_empty());
}
#[test]
fn test_lookup_x86_64() {
let reg = make_registry();
let target = reg.lookup_target("x86_64-unknown-linux-gnu").unwrap();
assert_eq!(target.name(), "x86-64");
assert_eq!(target.get_pointer_size(), 8);
}
#[test]
fn test_lookup_aarch64() {
let reg = make_registry();
let target = reg.lookup_target("aarch64-unknown-linux-gnu").unwrap();
assert_eq!(target.name(), "aarch64");
assert_eq!(target.get_pointer_size(), 8);
}
#[test]
fn test_lookup_arm() {
let reg = make_registry();
let target = reg.lookup_target("armv7-unknown-linux-gnueabihf").unwrap();
assert_eq!(target.name(), "arm");
assert_eq!(target.get_pointer_size(), 4);
}
#[test]
fn test_lookup_alias_amd64() {
let reg = make_registry();
let target = reg.lookup_target("amd64-pc-windows-msvc").unwrap();
assert_eq!(target.name(), "x86-64");
}
#[test]
fn test_lookup_alias_arm64() {
let reg = make_registry();
let target = reg.lookup_target("arm64-apple-darwin").unwrap();
assert_eq!(target.name(), "aarch64");
}
#[test]
fn test_lookup_unknown_triple() {
let reg = make_registry();
assert!(reg.lookup_target("riscv64-unknown-elf").is_none());
}
#[test]
fn test_get_target_by_name() {
let reg = make_registry();
let target = reg.get_target_by_name("aarch64").unwrap();
assert_eq!(target.name(), "aarch64");
assert!(reg.get_target_by_name("nonexistent").is_none());
}
#[test]
fn test_x86_supports_elf() {
let reg = make_registry();
let target = reg.lookup_target("x86_64-unknown-linux-gnu").unwrap();
assert!(target.supports_object_format(ObjectFormat::ELF));
assert!(target.supports_object_format(ObjectFormat::COFF));
assert!(target.supports_object_format(ObjectFormat::MachO));
assert!(!target.supports_object_format(ObjectFormat::Wasm));
}
#[test]
fn test_aarch64_supports_elf() {
let reg = make_registry();
let target = reg.lookup_target("aarch64-unknown-linux-gnu").unwrap();
assert!(target.supports_object_format(ObjectFormat::ELF));
assert!(target.supports_object_format(ObjectFormat::MachO));
assert!(!target.supports_object_format(ObjectFormat::COFF));
}
#[test]
fn test_arm_supports_elf_only() {
let reg = make_registry();
let target = reg.lookup_target("arm-unknown-linux-gnueabi").unwrap();
assert!(target.supports_object_format(ObjectFormat::ELF));
assert!(!target.supports_object_format(ObjectFormat::MachO));
}
#[test]
fn test_x86_assembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("x86_64-unknown-linux-gnu").unwrap();
let asm = target.get_assembler();
let mut inst = MCInst::new(0); let bytes = asm.encode_instruction(&inst).unwrap();
assert_eq!(bytes, vec![0x90]);
assert_eq!(asm.get_instruction_size(&inst), 1);
}
#[test]
fn test_aarch64_assembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("aarch64-unknown-linux-gnu").unwrap();
let asm = target.get_assembler();
let mut inst = MCInst::new(0); let bytes = asm.encode_instruction(&inst).unwrap();
assert_eq!(bytes.len(), 4);
}
#[test]
fn test_arm32_assembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("arm-unknown-linux-gnueabi").unwrap();
let asm = target.get_assembler();
let mut inst = MCInst::new(0); let bytes = asm.encode_instruction(&inst).unwrap();
assert_eq!(bytes.len(), 4);
}
#[test]
fn test_x86_disassembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("x86_64-unknown-linux-gnu").unwrap();
let dis = target.get_disassembler();
let inst = dis.decode_one(&[0x90], 0).unwrap();
assert_eq!(inst.mnemonic, "nop");
assert_eq!(inst.size, 1);
}
#[test]
fn test_aarch64_disassembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("aarch64-unknown-linux-gnu").unwrap();
let dis = target.get_disassembler();
let nop_bytes = 0xD503201Fu32.to_le_bytes();
let inst = dis.decode_one(&nop_bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "nop");
assert_eq!(inst.size, 4);
}
#[test]
fn test_arm32_disassembler_nop() {
let reg = make_registry();
let target = reg.lookup_target("arm-unknown-linux-gnueabi").unwrap();
let dis = target.get_disassembler();
let nop_bytes = 0xE1A00000u32.to_le_bytes();
let inst = dis.decode_one(&nop_bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "nop");
assert_eq!(inst.size, 4);
}
#[test]
fn test_x86_inst_printer() {
let reg = make_registry();
let target = reg.lookup_target("x86_64-unknown-linux-gnu").unwrap();
let printer = target.get_inst_printer();
let mut inst = MCInst::new(llvm_native_core::mc_streamer::x86_opcodes::MOV);
inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(0)); inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(1));
let text = printer.print_instruction(&inst);
assert!(text.contains("mov"));
assert!(text.contains("%rax"));
assert!(text.contains("%rcx"));
}
#[test]
fn test_aarch64_inst_printer() {
let reg = make_registry();
let target = reg.lookup_target("aarch64-unknown-linux-gnu").unwrap();
let printer = target.get_inst_printer();
let mut inst = MCInst::new(2); inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(0));
inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(1));
inst.add_operand(llvm_native_core::mc_inst::MCOperand::imm(42));
let text = printer.print_instruction(&inst);
assert!(text.contains("add"));
}
#[test]
fn test_arm32_inst_printer() {
let reg = make_registry();
let target = reg.lookup_target("arm-unknown-linux-gnueabi").unwrap();
let printer = target.get_inst_printer();
let mut inst = MCInst::new(2); inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(0));
inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(1));
inst.add_operand(llvm_native_core::mc_inst::MCOperand::reg(2));
let text = printer.print_instruction(&inst);
assert!(text.contains("add"));
}
#[test]
fn test_registry_iter() {
let reg = make_registry();
let names: Vec<&str> = reg.iter().map(|t| t.name()).collect();
assert!(names.contains(&"x86-64"));
assert!(names.contains(&"aarch64"));
assert!(names.contains(&"arm"));
}
#[test]
fn test_triple_normalization_variants() {
let reg = make_registry();
let x86 = reg.lookup_target("x86_64-pc-linux-gnu").unwrap();
assert_eq!(x86.name(), "x86-64");
let x86_msvc = reg.lookup_target("amd64-pc-windows-msvc").unwrap();
assert_eq!(x86_msvc.name(), "x86-64");
let arm = reg.lookup_target("armv7l-unknown-linux-gnueabihf").unwrap();
assert_eq!(arm.name(), "arm");
let thumb = reg.lookup_target("thumbv7-unknown-linux-gnueabi").unwrap();
assert_eq!(thumb.name(), "arm");
}
}