use crate::target_machine::{CodeGenOptLevel, CodeModel, RelocModel};
use crate::types::TypeKind;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetArch {
X86,
X86_64,
ARM,
AArch64,
Thumb,
RISCV32,
RISCV64,
Mips,
Mips64,
Mipsel,
PowerPC,
PowerPC64,
PowerPC64LE,
SystemZ,
Sparc,
SparcV9,
BPF,
BPFEB,
BPFLE,
AVR,
MSP430,
Hexagon,
XCore,
Lanai,
Arc,
CSKY,
Xtensa,
NVPTX,
NVPTX64,
AMDGPU,
R600,
WebAssembly32,
WebAssembly64,
Unknown,
}
impl TargetArch {
pub fn from_triple(triple: &str) -> Self {
let t = triple.to_lowercase();
if t.starts_with("x86_64") || t.starts_with("amd64") {
return TargetArch::X86_64;
}
if t.starts_with("i386")
|| t.starts_with("i486")
|| t.starts_with("i586")
|| t.starts_with("i686")
{
return TargetArch::X86;
}
if t.starts_with("aarch64") || t.starts_with("arm64") {
return TargetArch::AArch64;
}
if t.starts_with("arm") || t.starts_with("thumb") {
return TargetArch::ARM;
}
if t.starts_with("riscv64") {
return TargetArch::RISCV64;
}
if t.starts_with("riscv32") {
return TargetArch::RISCV32;
}
if t.contains("mips64") {
return TargetArch::Mips64;
}
if t.contains("mips") {
return TargetArch::Mips;
}
if t.contains("powerpc64le") {
return TargetArch::PowerPC64LE;
}
if t.contains("powerpc64") {
return TargetArch::PowerPC64;
}
if t.contains("powerpc") || t.contains("ppc") {
return TargetArch::PowerPC;
}
if t.contains("s390x") {
return TargetArch::SystemZ;
}
if t.contains("sparcv9") {
return TargetArch::SparcV9;
}
if t.contains("sparc") {
return TargetArch::Sparc;
}
if t.contains("bpfeb") {
return TargetArch::BPFEB;
}
if t.contains("bpf") {
return TargetArch::BPF;
}
if t.starts_with("avr") {
return TargetArch::AVR;
}
if t.starts_with("msp430") {
return TargetArch::MSP430;
}
if t.contains("hexagon") {
return TargetArch::Hexagon;
}
if t.contains("xcore") {
return TargetArch::XCore;
}
if t.contains("lanai") {
return TargetArch::Lanai;
}
if t.contains("arc") {
return TargetArch::Arc;
}
if t.contains("csky") {
return TargetArch::CSKY;
}
if t.contains("xtensa") {
return TargetArch::Xtensa;
}
if t.contains("nvptx64") {
return TargetArch::NVPTX64;
}
if t.contains("nvptx") {
return TargetArch::NVPTX;
}
if t.contains("amdgcn") || t.contains("r600") {
return TargetArch::AMDGPU;
}
if t.contains("wasm64") {
return TargetArch::WebAssembly64;
}
if t.contains("wasm32") {
return TargetArch::WebAssembly32;
}
TargetArch::Unknown
}
pub fn is_64bit(&self) -> bool {
matches!(
self,
TargetArch::X86_64
| TargetArch::AArch64
| TargetArch::RISCV64
| TargetArch::Mips64
| TargetArch::PowerPC64
| TargetArch::PowerPC64LE
| TargetArch::SystemZ
| TargetArch::SparcV9
| TargetArch::NVPTX64
| TargetArch::WebAssembly64
| TargetArch::BPF
)
}
pub fn default_data_layout(&self) -> &'static str {
match self {
TargetArch::X86_64 => {
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
}
TargetArch::X86 => {
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32-S128"
}
TargetArch::AArch64 => "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
TargetArch::ARM => "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
TargetArch::RISCV64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
TargetArch::RISCV32 => "e-m:e-p:32:32-i64:64-n32-S128",
TargetArch::Mips | TargetArch::Mips64 => {
"E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128"
}
TargetArch::PowerPC64LE => "e-m:e-i64:64-n32:64-S128",
TargetArch::SystemZ => "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64",
TargetArch::BPF | TargetArch::BPFEB | TargetArch::BPFLE => {
"e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
}
TargetArch::WebAssembly32 | TargetArch::WebAssembly64 => {
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
}
_ => "e-m:e-p:32:32-i64:64-n32:64-S128",
}
}
pub fn default_triple(&self) -> &'static str {
match self {
TargetArch::X86_64 => "x86_64-unknown-linux-gnu",
TargetArch::X86 => "i386-unknown-linux-gnu",
TargetArch::AArch64 => "aarch64-unknown-linux-gnu",
TargetArch::ARM => "arm-unknown-linux-gnueabihf",
TargetArch::RISCV64 => "riscv64-unknown-linux-gnu",
TargetArch::RISCV32 => "riscv32-unknown-linux-gnu",
TargetArch::Mips | TargetArch::Mips64 => "mips64-unknown-linux-gnu",
TargetArch::PowerPC64LE => "powerpc64le-unknown-linux-gnu",
TargetArch::SystemZ => "s390x-unknown-linux-gnu",
TargetArch::Sparc | TargetArch::SparcV9 => "sparc64-unknown-linux-gnu",
TargetArch::BPF | TargetArch::BPFEB | TargetArch::BPFLE => "bpf-unknown-none",
TargetArch::AVR => "avr-unknown-none",
TargetArch::MSP430 => "msp430-unknown-none",
TargetArch::NVPTX | TargetArch::NVPTX64 => "nvptx64-nvidia-cuda",
TargetArch::AMDGPU => "amdgcn-amd-amdhsa",
TargetArch::WebAssembly32 | TargetArch::WebAssembly64 => "wasm32-unknown-unknown",
_ => "unknown-unknown-unknown",
}
}
}
#[derive(Debug, Clone)]
pub struct TargetOptions {
pub cpu: String,
pub features: String,
pub abi: String,
pub float_abi: FloatABI,
pub frame_pointer: FramePointerKind,
pub stack_protector: StackProtector,
pub exception_model: ExceptionHandling,
pub debug_info_kind: DebugInfoKind,
pub thread_model: ThreadModel,
pub relocation_model: RelocModel,
pub code_model: CodeModel,
pub position_independent: bool,
pub use_init_array: bool,
pub no_exec_stack: bool,
pub function_sections: bool,
pub data_sections: bool,
pub unique_section_names: bool,
pub trap_unreachable: bool,
pub emulate_tls: bool,
pub enable_ipra: bool,
pub guarantee_tail_call_opt: bool,
pub stack_symbol_ordering: bool,
pub enable_dead_stripping: bool,
pub enable_linkonceodr_outlining: bool,
pub machine_outliner: bool,
pub supports_default_outlining: bool,
pub emit_stack_size_section: bool,
pub relax_elf_relocations: bool,
pub preserve_as_comments: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloatABI {
Default,
Soft,
Hard,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FramePointerKind {
None,
NonLeaf,
All,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackProtector {
None,
Strong,
Req,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExceptionHandling {
None,
DwarfCFI,
SjLj,
WinEH,
WasmEH,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DebugInfoKind {
None,
LineTablesOnly,
Limited,
Full,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThreadModel {
POSIX,
Single,
}
impl Default for TargetOptions {
fn default() -> Self {
TargetOptions {
cpu: "generic".to_string(),
features: String::new(),
abi: String::new(),
float_abi: FloatABI::Default,
frame_pointer: FramePointerKind::All,
stack_protector: StackProtector::None,
exception_model: ExceptionHandling::None,
debug_info_kind: DebugInfoKind::None,
thread_model: ThreadModel::POSIX,
relocation_model: RelocModel::PIC,
code_model: CodeModel::Small,
position_independent: false,
use_init_array: true,
no_exec_stack: false,
function_sections: false,
data_sections: false,
unique_section_names: true,
trap_unreachable: true,
emulate_tls: false,
enable_ipra: false,
guarantee_tail_call_opt: false,
stack_symbol_ordering: false,
enable_dead_stripping: false,
enable_linkonceodr_outlining: false,
machine_outliner: false,
supports_default_outlining: false,
emit_stack_size_section: false,
relax_elf_relocations: false,
preserve_as_comments: false,
}
}
}
#[derive(Debug, Clone)]
pub struct TargetTransformInfo {
pub arch: TargetArch,
pub cpu: String,
pub features: String,
}
impl TargetTransformInfo {
pub fn new(arch: TargetArch) -> Self {
TargetTransformInfo {
arch,
cpu: String::new(),
features: String::new(),
}
}
pub fn get_arith_cost(&self, _opcode: &str, _ty: &TypeKind) -> u32 {
match self.arch {
TargetArch::X86_64 | TargetArch::X86 => 1, TargetArch::AArch64 => 1,
TargetArch::ARM => 1,
_ => 2,
}
}
pub fn get_memory_op_cost(&self, _ty: &TypeKind, _is_load: bool) -> u32 {
match self.arch {
TargetArch::X86_64 | TargetArch::AArch64 => 4, _ => 5,
}
}
pub fn get_vector_op_cost(&self, _opcode: &str, _ty: &TypeKind, _factor: u32) -> u32 {
match self.arch {
TargetArch::X86_64 => 1, TargetArch::AArch64 => 2,
_ => 4,
}
}
pub fn get_max_vector_width(&self) -> u32 {
match self.arch {
TargetArch::X86_64 => 512, TargetArch::X86 => 256,
TargetArch::AArch64 => 256, TargetArch::ARM => 128, TargetArch::RISCV64 => 256, _ => 128,
}
}
pub fn get_preferred_vector_width(&self) -> u32 {
match self.arch {
TargetArch::X86_64 => 256, TargetArch::AArch64 => 128,
_ => self.get_max_vector_width(),
}
}
pub fn get_instruction_throughput(&self, _opcode: &str) -> u32 {
match self.arch {
TargetArch::X86_64 => 1,
_ => 1,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SubtargetFeatures {
pub features: HashMap<String, bool>, }
impl SubtargetFeatures {
pub fn new() -> Self {
SubtargetFeatures {
features: HashMap::new(),
}
}
pub fn parse(feature_string: &str) -> Self {
let mut sf = SubtargetFeatures::new();
for part in feature_string.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some(feat) = part.strip_prefix('+') {
sf.features.insert(feat.to_string(), true);
} else if let Some(feat) = part.strip_prefix('-') {
sf.features.insert(feat.to_string(), false);
}
}
sf
}
pub fn has_feature(&self, name: &str) -> bool {
self.features.get(name).copied().unwrap_or(false)
}
pub fn add_feature(&mut self, name: &str, enabled: bool) {
self.features.insert(name.to_string(), enabled);
}
pub fn to_string(&self) -> String {
let mut parts: Vec<String> = self
.features
.iter()
.map(|(k, v)| {
if *v {
format!("+{}", k)
} else {
format!("-{}", k)
}
})
.collect();
parts.sort();
parts.join(",")
}
pub fn is_empty(&self) -> bool {
self.features.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct MachineFunctionInfo {
pub is_leaf_function: bool,
pub has_calls: bool,
pub has_inline_asm: bool,
pub frame_size: u64,
pub max_call_frame_size: u64,
pub adjusts_stack: bool,
pub has_naked_attr: bool,
pub has_patches: bool,
pub is_split: bool,
pub needs_frame_setup: bool,
pub callee_saved_regs: Vec<u32>,
pub local_area_offset: i32,
pub save_fp_mode: FPSaveMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FPSaveMode {
Default,
Save,
Restore,
None,
}
impl Default for MachineFunctionInfo {
fn default() -> Self {
MachineFunctionInfo {
is_leaf_function: false,
has_calls: false,
has_inline_asm: false,
frame_size: 0,
max_call_frame_size: 0,
adjusts_stack: false,
has_naked_attr: false,
has_patches: false,
is_split: false,
needs_frame_setup: false,
callee_saved_regs: Vec::new(),
local_area_offset: 0,
save_fp_mode: FPSaveMode::Default,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_target_arch_from_triple() {
assert_eq!(
TargetArch::from_triple("x86_64-unknown-linux-gnu"),
TargetArch::X86_64
);
assert_eq!(
TargetArch::from_triple("aarch64-apple-darwin"),
TargetArch::AArch64
);
assert_eq!(
TargetArch::from_triple("riscv64-unknown-elf"),
TargetArch::RISCV64
);
assert_eq!(TargetArch::from_triple("arm-none-eabi"), TargetArch::ARM);
}
#[test]
fn test_subtarget_features_parse() {
let sf = SubtargetFeatures::parse("+avx2,-sse4.1,+fma");
assert!(sf.has_feature("avx2"));
assert!(!sf.has_feature("sse4.1"));
assert!(sf.has_feature("fma"));
}
#[test]
fn test_subtarget_features_to_string() {
let mut sf = SubtargetFeatures::new();
sf.add_feature("avx2", true);
sf.add_feature("sse4.1", false);
let s = sf.to_string();
assert!(s.contains("+avx2"));
assert!(s.contains("-sse4.1"));
}
#[test]
fn test_target_transform_info() {
let tti = TargetTransformInfo::new(TargetArch::X86_64);
assert_eq!(
tti.get_arith_cost("add", &TypeKind::Integer { bits: 32 }),
1
);
assert_eq!(tti.get_max_vector_width(), 512);
}
#[test]
fn test_target_options_default() {
let opts = TargetOptions::default();
assert_eq!(opts.cpu, "generic");
assert_eq!(opts.float_abi, FloatABI::Default);
}
#[test]
fn test_default_data_layouts() {
assert!(TargetArch::X86_64.default_data_layout().contains("p270"));
assert!(TargetArch::AArch64.default_data_layout().contains("S128"));
}
#[test]
fn test_machine_function_info_default() {
let mfi = MachineFunctionInfo::default();
assert!(!mfi.is_leaf_function);
assert_eq!(mfi.frame_size, 0);
}
}