use crate::codegen::{AsmPrinter, InstructionSelector, MachineFunction, RegisterAllocator};
use crate::data_layout::DataLayout;
use crate::target_info::{TargetInfo, TargetRegistry};
use crate::triple::{Arch, Triple};
use crate::value::ValueRef;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CodeGenOptLevel {
None = 0,
Less = 1,
Default = 2,
Aggressive = 3,
}
impl CodeGenOptLevel {
pub fn should_optimize(&self) -> bool {
*self > CodeGenOptLevel::None
}
}
impl Default for CodeGenOptLevel {
fn default() -> Self {
Self::Default
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelocModel {
Static,
PIC, DynamicNoPic, ROPI, RWPI, ROPIRWPI, }
impl RelocModel {
pub fn is_pic(&self) -> bool {
matches!(
self,
RelocModel::PIC | RelocModel::ROPI | RelocModel::ROPIRWPI
)
}
}
impl Default for RelocModel {
fn default() -> Self {
Self::Static
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodeModel {
Tiny,
Small,
Kernel,
Medium,
Large,
}
impl Default for CodeModel {
fn default() -> Self {
Self::Small
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloatABI {
Default,
Soft, Hard, }
impl Default for FloatABI {
fn default() -> Self {
FloatABI::Default
}
}
#[derive(Debug, Clone)]
pub struct TargetOptions {
pub triple: String,
pub cpu: String,
pub features: String,
pub abi: String,
pub opt_level: CodeGenOptLevel,
pub reloc_model: RelocModel,
pub code_model: CodeModel,
pub float_abi: FloatABI,
pub position_independent: bool,
pub use_local_exec_tls: bool,
pub use_soft_float: bool,
pub disable_fp_elim: bool,
pub emit_unwind_tables: bool,
pub stack_alignment: u32,
pub output_file: Option<String>,
pub no_signed_zeros_fp_math: bool,
pub unsafe_fp_math: bool,
pub trap_unaligned: bool,
pub function_sections: bool,
pub data_sections: bool,
pub integrated_as: bool,
pub preserve_asm_comments: bool,
pub emit_compact_unwind: bool,
pub thread_model: String,
}
impl Default for TargetOptions {
fn default() -> Self {
Self {
triple: String::new(),
cpu: String::from("generic"),
features: String::new(),
abi: String::new(),
opt_level: CodeGenOptLevel::default(),
reloc_model: RelocModel::default(),
code_model: CodeModel::default(),
float_abi: FloatABI::Default,
position_independent: false,
use_local_exec_tls: false,
use_soft_float: false,
disable_fp_elim: false,
emit_unwind_tables: true,
stack_alignment: 0,
output_file: None,
no_signed_zeros_fp_math: false,
unsafe_fp_math: false,
trap_unaligned: false,
function_sections: false,
data_sections: false,
integrated_as: true,
preserve_asm_comments: false,
emit_compact_unwind: false,
thread_model: String::from("posix"),
}
}
}
impl TargetOptions {
pub fn from_triple(triple: &str) -> Self {
let t = Triple::parse(triple);
let mut opts = Self::default();
opts.triple = triple.to_string();
match t.arch {
Arch::X86_64
| Arch::AArch64
| Arch::RISCV64
| Arch::PowerPC64
| Arch::SystemZ
| Arch::Sparcv9
| Arch::Mips64
| Arch::LoongArch64
| Arch::NVPTX64
| Arch::WebAssembly64
| Arch::AMDGPU
| Arch::BPF => {
opts.code_model = CodeModel::Small;
}
_ => {
opts.code_model = CodeModel::Small;
}
}
if t.is_windows() {
opts.abi = String::from("msvc");
}
opts
}
}
pub struct TargetMachine {
pub info: TargetInfo,
pub options: TargetOptions,
pub registry: TargetRegistry,
}
impl TargetMachine {
pub fn new(triple_str: &str) -> Self {
let triple = Triple::parse(triple_str);
let info = TargetInfo::from_triple(triple.clone());
let options = TargetOptions::from_triple(triple_str);
let registry = TargetRegistry::new();
Self {
info,
options,
registry,
}
}
pub fn with_options(triple_str: &str, options: TargetOptions) -> Self {
let triple = Triple::parse(triple_str);
let info = TargetInfo::from_triple(triple);
let registry = TargetRegistry::new();
Self {
info,
options,
registry,
}
}
pub fn triple(&self) -> String {
self.info.triple.to_string()
}
pub fn data_layout(&self) -> &DataLayout {
&self.info.data_layout
}
pub fn get_triple(&self) -> &Triple {
&self.info.triple
}
pub fn is_64bit(&self) -> bool {
self.info.triple.is_64bit()
}
pub fn is_little_endian(&self) -> bool {
self.data_layout().is_little_endian()
}
pub fn pointer_size(&self) -> u32 {
self.data_layout().pointer_size()
}
pub fn opt_level(&self) -> CodeGenOptLevel {
self.options.opt_level
}
pub fn has_feature(&self, feature: &str) -> bool {
self.info.has_feature(feature)
}
pub fn enable_feature(&mut self, feature: &str) {
self.info.enable_feature(feature);
}
pub fn disable_feature(&mut self, feature: &str) {
self.info.disable_feature(feature);
}
pub fn abi(&self) -> &str {
&self.options.abi
}
pub fn is_pic(&self) -> bool {
self.options.position_independent || self.options.reloc_model.is_pic()
}
pub fn compile_function(&self, func: &ValueRef) -> String {
match self.info.triple.arch {
Arch::X86_64 | Arch::X86 => self.compile_x86(func),
Arch::AArch64 | Arch::ARM | Arch::ARMeb | Arch::Thumb => self.compile_arm(func),
Arch::RISCV64 | Arch::RISCV32 => self.compile_riscv(func),
Arch::WebAssembly32 | Arch::WebAssembly64 => self.compile_wasm(func),
Arch::AMDGPU => self.compile_amdgpu(func),
Arch::NVPTX | Arch::NVPTX64 => self.compile_nvptx(func),
Arch::Mips | Arch::Mips64 | Arch::Mipsel | Arch::Mips64el => self.compile_mips(func),
Arch::PowerPC | Arch::PowerPC64 | Arch::PowerPC64le => self.compile_powerpc(func),
Arch::SystemZ => self.compile_systemz(func),
Arch::Sparc | Arch::Sparcv9 => self.compile_sparc(func),
Arch::BPF | Arch::BPFEB | Arch::BPF64 => self.compile_bpf(func),
Arch::AVR => self.compile_avr(func),
Arch::MSP430 => self.compile_msp430(func),
Arch::Hexagon => self.compile_hexagon(func),
Arch::Lanai => self.compile_lanai(func),
Arch::ARC => self.compile_arc(func),
Arch::CSKY => self.compile_csky(func),
Arch::Xtensa => self.compile_xtensa(func),
_ => self.compile_generic(func),
}
}
pub fn compile_to_object(&self, func: &ValueRef) -> Vec<u8> {
let _asm = self.compile_function(func);
Vec::new()
}
pub fn compile_module_to_object(&self, funcs: &[&ValueRef]) -> Vec<u8> {
let mut obj = Vec::new();
for func in funcs {
obj.extend(self.compile_to_object(func));
}
obj
}
fn compile_x86(&self, func: &ValueRef) -> String {
let f = func.borrow();
let mut mf = MachineFunction::new(&f.name);
InstructionSelector::select(&mut mf, func);
let mut ra = RegisterAllocator::new();
ra.allocate(&mut mf);
let mut printer = AsmPrinter::new();
printer.print_function(&mf);
printer.output
}
fn compile_arm(&self, _func: &ValueRef) -> String {
String::new() }
fn compile_riscv(&self, func: &ValueRef) -> String {
let f = func.borrow();
let mut mf = MachineFunction::new(&f.name);
InstructionSelector::select(&mut mf, func);
let mut ra = RegisterAllocator::new();
ra.allocate(&mut mf);
let mut printer = AsmPrinter::new();
printer.print_function(&mf);
printer.output
}
fn compile_wasm(&self, _func: &ValueRef) -> String {
String::from("(module)\n")
}
fn compile_amdgpu(&self, _func: &ValueRef) -> String {
String::from("; AMDGPU kernel\n")
}
fn compile_nvptx(&self, _func: &ValueRef) -> String {
String::from("// NVPTX kernel\n")
}
fn compile_mips(&self, func: &ValueRef) -> String {
self.compile_riscv(func) }
fn compile_powerpc(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_systemz(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_sparc(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_bpf(&self, _func: &ValueRef) -> String {
String::from("; BPF bytecode\n")
}
fn compile_avr(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_msp430(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_hexagon(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_lanai(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_arc(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_csky(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_xtensa(&self, func: &ValueRef) -> String {
self.compile_riscv(func)
}
fn compile_generic(&self, func: &ValueRef) -> String {
self.compile_x86(func)
}
}
impl Default for TargetMachine {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
pub struct TargetMachineBuilder {
triple: String,
cpu: String,
features: String,
opt_level: CodeGenOptLevel,
reloc_model: RelocModel,
code_model: CodeModel,
float_abi: FloatABI,
position_independent: bool,
function_sections: bool,
data_sections: bool,
disable_fp_elim: bool,
emit_unwind_tables: bool,
}
impl TargetMachineBuilder {
pub fn new(triple: &str) -> Self {
Self {
triple: triple.to_string(),
cpu: String::from("generic"),
features: String::new(),
opt_level: CodeGenOptLevel::Default,
reloc_model: RelocModel::Static,
code_model: CodeModel::Small,
float_abi: FloatABI::Default,
position_independent: false,
function_sections: false,
data_sections: false,
disable_fp_elim: false,
emit_unwind_tables: true,
}
}
pub fn cpu(mut self, cpu: &str) -> Self {
self.cpu = cpu.to_string();
self
}
pub fn features(mut self, features: &str) -> Self {
self.features = features.to_string();
self
}
pub fn opt_level(mut self, level: CodeGenOptLevel) -> Self {
self.opt_level = level;
self
}
pub fn reloc_model(mut self, model: RelocModel) -> Self {
self.reloc_model = model;
self
}
pub fn code_model(mut self, model: CodeModel) -> Self {
self.code_model = model;
self
}
pub fn pic(mut self, pic: bool) -> Self {
self.position_independent = pic;
self
}
pub fn function_sections(mut self, fs: bool) -> Self {
self.function_sections = fs;
self
}
pub fn build(self) -> TargetMachine {
let opts = TargetOptions {
triple: self.triple.clone(),
cpu: self.cpu,
features: self.features,
opt_level: self.opt_level,
reloc_model: self.reloc_model,
code_model: self.code_model,
float_abi: self.float_abi,
position_independent: self.position_independent,
function_sections: self.function_sections,
data_sections: self.data_sections,
disable_fp_elim: self.disable_fp_elim,
emit_unwind_tables: self.emit_unwind_tables,
..Default::default()
};
TargetMachine::with_options(&self.triple, opts)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_x86_64() {
let tm = TargetMachine::new("x86_64-unknown-linux-gnu");
assert!(tm.is_64bit());
assert!(tm.is_little_endian());
}
#[test]
fn test_create_aarch64() {
let tm = TargetMachine::new("aarch64-unknown-linux-gnu");
assert!(tm.is_64bit());
}
#[test]
fn test_create_arm32() {
let tm = TargetMachine::new("armv7-unknown-linux-gnueabihf");
assert!(!tm.is_64bit());
}
#[test]
fn test_triple_roundtrip() {
let tm = TargetMachine::new("x86_64-pc-windows-msvc");
assert!(tm.triple().contains("x86_64"));
assert!(tm.triple().contains("windows"));
}
#[test]
fn test_data_layout() {
let tm = TargetMachine::new("x86_64-unknown-linux-gnu");
assert!(tm.data_layout().is_little_endian());
assert_eq!(tm.pointer_size(), 64);
}
#[test]
fn test_builder_defaults() {
let tm = TargetMachineBuilder::new("aarch64-unknown-linux-gnu").build();
assert!(tm.is_64bit());
assert_eq!(tm.opt_level(), CodeGenOptLevel::Default);
}
#[test]
fn test_builder_custom_options() {
let tm = TargetMachineBuilder::new("x86_64-unknown-linux-gnu")
.cpu("skylake")
.opt_level(CodeGenOptLevel::Aggressive)
.pic(true)
.function_sections(true)
.build();
assert!(tm.is_pic());
assert!(tm.options.function_sections);
assert_eq!(tm.options.cpu, "skylake");
}
#[test]
fn test_reloc_model_pic() {
assert!(RelocModel::PIC.is_pic());
assert!(RelocModel::ROPI.is_pic());
assert!(!RelocModel::Static.is_pic());
}
#[test]
fn test_codegen_opt_level() {
assert!(!CodeGenOptLevel::None.should_optimize());
assert!(CodeGenOptLevel::Default.should_optimize());
assert!(CodeGenOptLevel::Aggressive.should_optimize());
}
#[test]
fn test_enable_disable_feature() {
let mut tm = TargetMachine::new("x86_64-unknown-linux-gnu");
tm.enable_feature("+sse4.2");
assert!(tm.has_feature("+sse4.2"));
tm.disable_feature("+sse4.2");
assert!(!tm.has_feature("+sse4.2"));
}
#[test]
fn test_default_target_machine() {
let tm = TargetMachine::default();
assert!(tm.triple().contains("x86_64"));
}
#[test]
fn test_target_options_default() {
let opts = TargetOptions::default();
assert_eq!(opts.cpu, "generic");
assert_eq!(opts.opt_level, CodeGenOptLevel::Default);
assert_eq!(opts.thread_model, "posix");
}
}