diff --git a/README.md b/README.md index 96d7e938be2f2..f3d45560dc630 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# The Rust Programming Language +# The Rust Programming Language For Xtensa processors + +This fork enables projects to be built for the ESP32 and ESP8266 using [espressif's llvm fork](https://github.com/espressif/llvm-xtensa). The [esp-rs](https://github.com/esp-rs) organization has been formed to develop runtime, pac and hal crates for the esp32 and eventually esp8266. + +## Using this fork + +The [quickstart repo](https://github.com/MabezDev/xtensa-rust-quickstart) has more information on how to build this fork and use it to build xtensa compatible code. This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index 62a3757757b80..3cb07a90ea7bf 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -72,7 +72,7 @@ fn main() { let mut optional_components = vec!["x86", "arm", "aarch64", "amdgpu", "mips", "powerpc", "systemz", "jsbackend", "webassembly", "msp430", "sparc", "nvptx", - "hexagon"]; + "hexagon", "xtensa"]; let mut version_cmd = Command::new(&llvm_config); version_cmd.arg("--version"); diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 647d473f01572..75e020ee45f07 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -105,4 +105,11 @@ pub fn initialize_available_targets() { LLVMInitializeWebAssemblyTarget, LLVMInitializeWebAssemblyTargetMC, LLVMInitializeWebAssemblyAsmPrinter); + init_target!(llvm_component = "xtensa", + LLVMInitializeXtensaTargetInfo, + LLVMInitializeXtensaTarget, + LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmPrinter, + LLVMInitializeXtensaAsmParser); + } diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index fbbd120f934be..9746d76a6b7f4 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -22,6 +22,7 @@ mod x86; mod x86_64; mod x86_win64; mod wasm32; +mod xtensa; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum IgnoreMode { @@ -581,6 +582,7 @@ impl<'a, Ty> FnType<'a, Ty> { "hexagon" => hexagon::compute_abi_info(self), "riscv32" => riscv::compute_abi_info(self, 32), "riscv64" => riscv::compute_abi_info(self, 64), + "xtensa" => xtensa::compute_abi_info(self, 32), a => return Err(format!("unrecognized arch \"{}\" in target specification", a)) } diff --git a/src/librustc_target/abi/call/xtensa.rs b/src/librustc_target/abi/call/xtensa.rs new file mode 100644 index 0000000000000..86a4c34781589 --- /dev/null +++ b/src/librustc_target/abi/call/xtensa.rs @@ -0,0 +1,102 @@ +// reference: https://github.com/espressif/clang-xtensa/commit/6fb488d2553f06029e6611cf81c6efbd45b56e47#diff-aa74ae1e1ab6b7149789237edb78e688R8450 + +use crate::abi::call::{ArgType, FnType, Reg, Uniform}; + +const NUM_ARG_GPR: u64 = 6; +const MAX_ARG_IN_REGS_SIZE: u64 = 4 * 32; +// const MAX_ARG_DIRECT_SIZE: u64 = MAX_ARG_IN_REGS_SIZE; +const MAX_RET_IN_REGS_SIZE: u64 = 2 * 32; + +fn classify_ret_ty(arg: &mut ArgType<'_, Ty>, xlen: u64) { + // The rules for return and argument types are the same, so defer to + // classifyArgumentType. + classify_arg_ty(arg, xlen, &mut 2); // two as max return size +} + +fn classify_arg_ty(arg: &mut ArgType<'_, Ty>, xlen: u64, remaining_gpr: &mut u64) { + // Determine the number of GPRs needed to pass the current argument + // according to the ABI. 2*XLen-aligned varargs are passed in "aligned" + // register pairs, so may consume 3 registers. + + let arg_size = arg.layout.size; + if arg_size.bits() > MAX_ARG_IN_REGS_SIZE { + arg.make_indirect(); + return; + } + + let alignment = arg.layout.details.align.abi; + let mut required_gpr = 1u64; // at least one per arg + + if alignment.bits() == 2 * xlen { + required_gpr = 2 + (*remaining_gpr % 2); + } else if arg_size.bits() > xlen && arg_size.bits() <= MAX_ARG_IN_REGS_SIZE { + required_gpr = (arg_size.bits() + (xlen - 1)) / xlen; + } + + let mut stack_required = false; + if required_gpr > *remaining_gpr { + stack_required = true; + required_gpr = *remaining_gpr; + } + *remaining_gpr -= required_gpr; + + // if a value can fit in a reg and the + // stack is not required, extend + if !arg.layout.is_aggregate() { + // non-aggregate types + if arg_size.bits() < xlen && !stack_required { + arg.extend_integer_width_to(xlen); + } + } else if arg_size.bits() as u64 <= MAX_ARG_IN_REGS_SIZE { + // aggregate types + // Aggregates which are <= 4*32 will be passed in registers if possible, + // so coerce to integers. + + // Use a single XLen int if possible, 2*XLen if 2*XLen alignment is + // required, and a 2-element XLen array if only XLen alignment is + // required. + // if alignment == 2 * xlen { + // arg.extend_integer_width_to(xlen * 2); + // } else { + // arg.extend_integer_width_to(arg_size + (xlen - 1) / xlen); + // } + if alignment.bits() == 2 * xlen { + arg.cast_to(Uniform { unit: Reg::i64(), total: arg_size }); + } else { + //FIXME array type - this should be a homogenous array type + // arg.extend_integer_width_to(arg_size + (xlen - 1) / xlen); + } + } else { + // if we get here the stack is required + assert!(stack_required); + arg.make_indirect(); + } + + // if arg_size as u64 <= MAX_ARG_IN_REGS_SIZE { + // let align = arg.layout.align.abi.bytes(); + // let total = arg.layout.size; + // arg.cast_to(Uniform { + // unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, + // total + // }); + // return; + // } +} + +pub fn compute_abi_info(fty: &mut FnType<'_, Ty>, xlen: u64) { + if !fty.ret.is_ignore() { + classify_ret_ty(&mut fty.ret, xlen); + } + + let return_indirect = + fty.ret.layout.size.bits() > MAX_RET_IN_REGS_SIZE || fty.ret.is_indirect(); + + let mut remaining_gpr = if return_indirect { NUM_ARG_GPR - 1 } else { NUM_ARG_GPR }; + + for arg in &mut fty.args { + if arg.is_ignore() { + continue; + } + classify_arg_ty(arg, xlen, &mut remaining_gpr); + } +} diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index 626fa374a1bd4..54aad71acf3b5 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -473,6 +473,8 @@ supported_targets! { ("thumbv8m.main-none-eabihf", thumbv8m_main_none_eabihf), ("msp430-none-elf", msp430_none_elf), + ("xtensa-esp32-none-elf", xtensa_esp32_none_elf), + ("xtensa-esp8266-none-elf", xtensa_esp8266_none_elf), ("aarch64-unknown-cloudabi", aarch64_unknown_cloudabi), ("armv7-unknown-cloudabi-eabihf", armv7_unknown_cloudabi_eabihf), diff --git a/src/librustc_target/spec/xtensa_esp32_none_elf.rs b/src/librustc_target/spec/xtensa_esp32_none_elf.rs new file mode 100644 index 0000000000000..b250a40e83950 --- /dev/null +++ b/src/librustc_target/spec/xtensa_esp32_none_elf.rs @@ -0,0 +1,66 @@ +use crate::spec::{abi::Abi, LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +// use crate::spec::abi::Abi; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "xtensa-none-elf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-f64:64-a:0:32-n32".to_string(), + arch: "xtensa".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + executables: true, + cpu: "esp32".to_string(), + // The LLVM backend currently can't generate object files. To + // workaround this LLVM generates assembly files which then we feed + // to gcc to get object files. For this reason we have a hard + // dependency on this specific gcc. + // asm_args: vec!["-mcpu=esp32".to_string()], + linker: Some("xtensa-esp32-elf-gcc".to_string()), + no_integrated_as: true, + + max_atomic_width: Some(32), + atomic_cas: true, + + // Because these devices have very little resources having an + // unwinder is too onerous so we default to "abort" because the + // "unwind" strategy is very rare. + panic_strategy: PanicStrategy::Abort, + + // Similarly, one almost always never wants to use relocatable + // code because of the extra costs it involves. + relocation_model: "static".to_string(), + + // Right now we invoke an external assembler and this isn't + // compatible with multiple codegen units, and plus we probably + // don't want to invoke that many gcc instances. + default_codegen_units: Some(1), + + // Since MSP430 doesn't meaningfully support faulting on illegal + // instructions, LLVM generates a call to abort() function instead + // of a trap instruction. Such calls are 4 bytes long, and that is + // too much overhead for such small target. + trap_unreachable: false, + + // See the thumb_base.rs file for an explanation of this value + emit_debug_gdb_scripts: false, + + abi_blacklist: vec![ + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Win64, + Abi::SysV64, + ], + + ..Default::default() + }, + }) +} diff --git a/src/librustc_target/spec/xtensa_esp8266_none_elf.rs b/src/librustc_target/spec/xtensa_esp8266_none_elf.rs new file mode 100644 index 0000000000000..2d9a70355cbfb --- /dev/null +++ b/src/librustc_target/spec/xtensa_esp8266_none_elf.rs @@ -0,0 +1,66 @@ +use crate::spec::{abi::Abi, LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +// use crate::spec::abi::Abi; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "xtensa-none-elf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-f64:64-a:0:32-n32".to_string(), + arch: "xtensa".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + executables: true, + cpu: "esp8266".to_string(), + // The LLVM backend currently can't generate object files. To + // workaround this LLVM generates assembly files which then we feed + // to gcc to get object files. For this reason we have a hard + // dependency on this specific gcc. + // asm_args: vec!["-mcpu=esp8266".to_string()], + linker: Some("xtensa-esp32-elf-gcc".to_string()), + no_integrated_as: true, + + max_atomic_width: Some(32), + atomic_cas: true, + + // Because these devices have very little resources having an + // unwinder is too onerous so we default to "abort" because the + // "unwind" strategy is very rare. + panic_strategy: PanicStrategy::Abort, + + // Similarly, one almost always never wants to use relocatable + // code because of the extra costs it involves. + relocation_model: "static".to_string(), + + // Right now we invoke an external assembler and this isn't + // compatible with multiple codegen units, and plus we probably + // don't want to invoke that many gcc instances. + default_codegen_units: Some(1), + + // Since MSP430 doesn't meaningfully support faulting on illegal + // instructions, LLVM generates a call to abort() function instead + // of a trap instruction. Such calls are 4 bytes long, and that is + // too much overhead for such small target. + trap_unreachable: false, + + // See the thumb_base.rs file for an explanation of this value + emit_debug_gdb_scripts: false, + + abi_blacklist: vec![ + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Win64, + Abi::SysV64, + ], + + ..Default::default() + }, + }) +} diff --git a/src/librustc_target/spec/xtensa_none_elf.rs b/src/librustc_target/spec/xtensa_none_elf.rs new file mode 100644 index 0000000000000..cb6811ad3d1ae --- /dev/null +++ b/src/librustc_target/spec/xtensa_none_elf.rs @@ -0,0 +1,66 @@ +use crate::spec::{abi::Abi, LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +// use crate::spec::abi::Abi; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "xtensa-none-elf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-f64:64-a:0:32-n32".to_string(), + arch: "xtensa".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + executables: true, + + // The LLVM backend currently can't generate object files. To + // workaround this LLVM generates assembly files which then we feed + // to gcc to get object files. For this reason we have a hard + // dependency on this specific gcc. + // asm_args: vec!["-mcpu=generic".to_string()], + linker: Some("xtensa-esp32-elf-gcc".to_string()), + no_integrated_as: true, + + max_atomic_width: Some(32), + atomic_cas: true, + + // Because these devices have very little resources having an + // unwinder is too onerous so we default to "abort" because the + // "unwind" strategy is very rare. + panic_strategy: PanicStrategy::Abort, + + // Similarly, one almost always never wants to use relocatable + // code because of the extra costs it involves. + relocation_model: "static".to_string(), + + // Right now we invoke an external assembler and this isn't + // compatible with multiple codegen units, and plus we probably + // don't want to invoke that many gcc instances. + default_codegen_units: Some(1), + + // Since MSP430 doesn't meaningfully support faulting on illegal + // instructions, LLVM generates a call to abort() function instead + // of a trap instruction. Such calls are 4 bytes long, and that is + // too much overhead for such small target. + trap_unreachable: false, + + // See the thumb_base.rs file for an explanation of this value + emit_debug_gdb_scripts: false, + + abi_blacklist: vec![ + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Win64, + Abi::SysV64, + ], + + ..Default::default() + }, + }) +} diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 0cda3465dc093..6dba95ed35584 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -174,6 +174,14 @@ void LLVMRustAddLastExtensionPasses( #define SUBTARGET_HEXAGON #endif +#ifdef LLVM_COMPONENT_XTENSA +#define SUBTARGET_XTENSA SUBTARGET(XTENSA) +#else +#define SUBTARGET_XTENSA +#endif + + + #define GEN_SUBTARGETS \ SUBTARGET_X86 \ SUBTARGET_ARM \ @@ -185,6 +193,7 @@ void LLVMRustAddLastExtensionPasses( SUBTARGET_SPARC \ SUBTARGET_HEXAGON \ SUBTARGET_RISCV \ + SUBTARGET_XTENSA \ #define SUBTARGET(x) \ namespace llvm { \