WIP: Implement unwinding

This commit is contained in:
Antoni Boucher 2022-10-19 09:23:23 -04:00
parent 0a54f243f6
commit 889a33a500
13 changed files with 379 additions and 87 deletions

2
Cargo.lock generated
View file

@ -41,7 +41,6 @@ dependencies = [
[[package]]
name = "gccjit"
version = "1.0.0"
source = "git+https://github.com/antoyo/gccjit.rs#f30cc2bd330f4fda3d625f305bdfd7e523e2d8f8"
dependencies = [
"gccjit_sys",
]
@ -49,7 +48,6 @@ dependencies = [
[[package]]
name = "gccjit_sys"
version = "0.0.1"
source = "git+https://github.com/antoyo/gccjit.rs#f30cc2bd330f4fda3d625f305bdfd7e523e2d8f8"
dependencies = [
"libc 0.1.12",
]

View file

@ -22,10 +22,10 @@ default = ["master"]
master = ["gccjit/master"]
[dependencies]
gccjit = { git = "https://github.com/antoyo/gccjit.rs" }
#gccjit = { git = "https://github.com/antoyo/gccjit.rs" }
# Local copy.
#gccjit = { path = "../gccjit.rs" }
gccjit = { path = "../gccjit.rs" }
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
target-lexicon = "0.10.0"

View file

@ -162,6 +162,14 @@ To print a debug representation of a tree:
debug_tree(expr);
```
(defined in print-tree.h)
To print a debug reprensentation of a gimple struct:
```c
debug_gimple_stmt(gimple_struct)
```
To get the `rustc` command to run in `gdb`, add the `--verbose` flag to `cargo build`.
### How to use a custom-build rustc

View file

@ -16,7 +16,7 @@ rm Cargo.lock test_target/Cargo.lock 2>/dev/null || true
rm -r sysroot/ 2>/dev/null || true
# Build libs
export RUSTFLAGS="$RUSTFLAGS -Z force-unstable-if-unmarked -Cpanic=abort"
export RUSTFLAGS="$RUSTFLAGS -Z force-unstable-if-unmarked"
if [[ "$1" == "--release" ]]; then
sysroot_channel='release'
RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release

View file

@ -38,7 +38,7 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
fi
fi
export RUSTFLAGS="$CG_RUSTFLAGS $linker -Cpanic=abort -Csymbol-mangling-version=v0 -Cdebuginfo=2 -Clto=off -Zpanic-abort-tests -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot"
export RUSTFLAGS="$CG_RUSTFLAGS $linker -Csymbol-mangling-version=v0 -Cdebuginfo=2 -Clto=off -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot"
# FIXME(antoyo): remove once the atomic shim is gone
if [[ `uname` == 'Darwin' ]]; then

View file

@ -352,8 +352,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
inputs.push(AsmInOperand {
constraint: "X".into(),
rust_idx,
val: self.cx.rvalue_as_function(get_fn(self.cx, instance))
.get_address(None),
val: get_fn(self.cx, instance, false).get_address(None),
});
}
@ -739,7 +738,7 @@ impl<'gcc, 'tcx> AsmMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
GlobalAsmOperandRef::SymFn { instance } => {
let function = self.rvalue_as_function(get_fn(self, instance));
let function = get_fn(self, instance, false);
self.add_used_function(function);
// TODO(@Amanieu): Additional mangling is needed on
// some targets to add a leading underscore (Mach-O)

View file

@ -87,6 +87,16 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_
// Instantiate monomorphizations without filling out definitions yet...
//let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
let context = Context::default();
context.add_command_line_option("-fexceptions");
context.add_driver_option("-fexceptions");
/*context.add_command_line_option("-fasynchronous-unwind-tables");
context.add_driver_option("-fasynchronous-unwind-tables");
context.add_command_line_option("-funwind-tables");
context.add_driver_option("-funwind-tables");*/
// TODO(antoyo): only set on x86 platforms.
context.add_command_line_option("-masm=intel");
// TODO(antoyo): only add the following cli argument if the feature is supported.
@ -147,6 +157,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_
}
// TODO(bjorn3): Remove once unwinding is properly implemented
// TODO: remove.
context.set_allow_unreachable_blocks(true);
{

View file

@ -13,7 +13,7 @@ use gccjit::{
RValue,
ToRValue,
Type,
UnaryOp,
UnaryOp, FunctionType,
};
use rustc_codegen_ssa::MemFlags;
use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope};
@ -372,10 +372,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
}
}
impl<'gcc, 'tcx> Deref for Builder<'_, 'gcc, 'tcx> {
impl<'a, 'gcc, 'tcx> Deref for Builder<'a, 'gcc, 'tcx> {
type Target = CodegenCx<'gcc, 'tcx>;
fn deref(&self) -> &Self::Target {
fn deref<'b>(&'b self) -> &'a Self::Target
{
self.cx
}
}
@ -393,7 +394,7 @@ impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> {
}
impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Builder<'a, 'gcc, 'tcx> {
Builder::with_cx(cx, block)
}
@ -450,8 +451,36 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
self.block.end_with_switch(None, value, default_block, &gcc_cases);
}
#[cfg(feature="master")]
fn invoke(&mut self, typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
let try_block = self.current_func().new_block("try");
let current_block = self.block.clone();
self.block = try_block;
let call = self.call(typ, func, args, None); // TODO: use funclet here?
self.block = current_block;
let return_value = self.current_func()
.new_local(None, call.get_type(), "invokeResult");
try_block.add_assignment(None, return_value, call);
try_block.end_with_jump(None, then);
self.block.add_try_catch(None, try_block, catch);
self.block.end_with_jump(None, then);
// NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
// state need to be updated.
// FIXME: not sure it's actually needed.
self.switch_to_block(then);
return_value.to_rvalue()
}
#[cfg(not(feature="master"))]
fn invoke(&mut self, typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
// TODO(bjorn3): Properly implement unwinding.
let call_site = self.call(typ, func, args, None);
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
self.llbb().end_with_conditional(None, condition, then, catch);
@ -1160,23 +1189,56 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
aggregate_value
}
fn set_personality_fn(&mut self, _personality: RValue<'gcc>) {
// TODO(antoyo)
fn set_personality_fn(&mut self, personality: RValue<'gcc>) {
let personality = self.rvalue_as_function(personality); // FIXME: why calling
//rvalue_as_function doesn't work?
//let personality = unsafe { std::mem::transmute(personality) };
#[cfg(feature="master")]
self.current_func().set_personality_function(personality);
// FIXME: rustc manages to generate the symbol DW.ref.rust_eh_personality multiple times
// for the same asm file, which causes an assembler error.
}
fn cleanup_landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>) -> RValue<'gcc> {
let field1 = self.context.new_field(None, self.u8_type.make_pointer(), "landing_pad_field_1");
let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1");
fn cleanup_landing_pad(&mut self, _ty: Type<'gcc>, pers_fn: RValue<'gcc>) -> RValue<'gcc> {
self.set_personality_fn(pers_fn);
// FIXME: we're probably not creating a real cleanup pad here.
// FIXME: FIXME: FIXME: It seems to be the actual problem:
// libunwind finds a catch, so returns _URC_HANDLER_FOUND instead of _URC_CONTINUE_UNWIND.
// TODO: can we generate a goto from the finally to the cleanup landing pad?
// TODO: TODO: TODO: add this block to a cleanup_blocks variable and generate a try/finally instead if
// the catch block for it is a cleanup block.
//
// TODO: look at TRY_CATCH_IS_CLEANUP, CLEANUP_POINT_EXPR, WITH_CLEANUP_EXPR, CLEANUP_EH_ONLY.
let eh_pointer_builtin = self.cx.context.get_target_builtin_function("__builtin_eh_pointer");
let zero = self.cx.context.new_rvalue_zero(self.int_type);
let ptr = self.cx.context.new_call(None, eh_pointer_builtin, &[zero]);
let field1_type = self.u8_type.make_pointer();
let field1 = self.context.new_field(None, field1_type, "landing_pad_field_1");
let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_2");
let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]);
self.current_func().new_local(None, struct_type.as_type(), "landing_pad")
.to_rvalue()
// TODO(antoyo): Properly implement unwinding.
// the above is just to make the compilation work as it seems
// rustc_codegen_ssa now calls the unwinding builder methods even on panic=abort.
let value = self.current_func().new_local(None, struct_type.as_type(), "landing_pad");
let ptr = self.cx.context.new_cast(None, ptr, field1_type);
self.block.add_assignment(None, value.access_field(None, field1), ptr);
self.block.add_assignment(None, value.access_field(None, field2), zero); // TODO: set the proper value here (the type of exception?).
// Resume.
let param = self.context.new_parameter(None, ptr.get_type(), "exn");
// TODO: should we call __builtin_unwind_resume instead?
// FIXME: should probably not called resume because it could be executed (I believe) in
// normal (no exception) cases
let unwind_resume = self.context.new_function(None, FunctionType::Extern, self.type_void(), &[param], "_Unwind_Resume", false);
self.block.add_eval(None, self.context.new_call(None, unwind_resume, &[ptr]));
value.to_rvalue()
}
fn resume(&mut self, _exn: RValue<'gcc>) {
// TODO(bjorn3): Properly implement unwinding.
fn resume(&mut self, exn: RValue<'gcc>) {
let param = self.context.new_parameter(None, exn.get_type(), "exn");
// TODO: should we call __builtin_unwind_resume instead?
let unwind_resume = self.context.new_function(None, FunctionType::Extern, self.type_void(), &[param], "_Unwind_Resume", false);
self.llbb().add_eval(None, self.context.new_call(None, unwind_resume, &[exn]));
self.unreachable();
}

View file

@ -1,6 +1,6 @@
#[cfg(feature="master")]
use gccjit::{FnAttribute, Visibility};
use gccjit::{FunctionType, RValue};
use gccjit::{FunctionType, RValue, Function};
use rustc_codegen_ssa::traits::BaseTypeMethods;
use rustc_middle::ty::{self, Instance, TypeVisitable};
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
@ -16,22 +16,31 @@ use crate::context::CodegenCx;
///
/// - `cx`: the crate context
/// - `instance`: the instance to be instantiated
pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> RValue<'gcc> {
pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>, dont_cache: bool) -> Function<'gcc> {
let tcx = cx.tcx();
assert!(!instance.substs.needs_infer());
assert!(!instance.substs.has_escaping_bound_vars());
let sym = tcx.symbol_name(instance).name;
if let Some(&func) = cx.function_instances.borrow().get(&instance) {
if sym == "rust_eh_personality" {
println!("Cached");
}
return func;
}
let sym = tcx.symbol_name(instance).name;
if sym == "rust_eh_personality" {
println!("Not cached");
}
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
let func =
if let Some(func) = cx.get_declared_value(&sym) {
unreachable!();
/*
// Create a fn pointer with the new signature.
let ptrty = fn_abi.ptr_to_gcc_type(cx);
@ -64,11 +73,14 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
}
else {
func
}
}*/
}
else {
cx.linkage.set(FunctionType::Extern);
let func = cx.declare_fn(&sym, &fn_abi);
/*if sym == "rust_eh_personality" {
panic!();
}*/
let func = cx.declare_fn(&sym, &fn_abi, dont_cache);
attributes::from_fn_attrs(cx, func, instance);
@ -163,11 +175,15 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
}
}
// FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
unsafe { std::mem::transmute(func) }
func
};
cx.function_instances.borrow_mut().insert(instance, func);
//if !dont_cache {
if sym == "rust_eh_personality" {
println!("Caching here");
}
cx.function_instances.borrow_mut().insert(instance, func);
//}
func
}

View file

@ -1,9 +1,10 @@
use std::cell::{Cell, RefCell};
use gccjit::{Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, RValue, Type};
use gccjit::{Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, RValue, Type, FnAttribute};
use rustc_codegen_ssa::base::wants_msvc_seh;
use rustc_codegen_ssa::traits::{
BackendTypes,
BaseTypeMethods,
MiscMethods,
};
use rustc_data_structures::base_n;
@ -11,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::span_bug;
use rustc_middle::mir::mono::CodegenUnit;
use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers};
use rustc_middle::ty::layout::{FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers};
use rustc_session::Session;
use rustc_span::Span;
use rustc_target::abi::{call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
@ -82,7 +83,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
/// Cache instances of monomorphic and polymorphic items
pub instances: RefCell<FxHashMap<Instance<'tcx>, LValue<'gcc>>>,
/// Cache function instances of monomorphic and polymorphic items
pub function_instances: RefCell<FxHashMap<Instance<'tcx>, RValue<'gcc>>>,
pub function_instances: RefCell<FxHashMap<Instance<'tcx>, Function<'gcc>>>,
/// Cache generated vtables
pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
@ -109,6 +110,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
local_gen_sym_counter: Cell<usize>,
eh_personality: Cell<Option<RValue<'gcc>>>,
pub rust_try_fn: Cell<Option<(Type<'gcc>, Function<'gcc>)>>,
pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>,
@ -245,6 +247,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
struct_types: Default::default(),
local_gen_sym_counter: Cell::new(0),
eh_personality: Cell::new(None),
rust_try_fn: Cell::new(None),
pointee_infos: Default::default(),
structs_as_pointer: Default::default(),
}
@ -252,8 +255,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> {
let function: Function<'gcc> = unsafe { std::mem::transmute(value) };
debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(),
"{:?} ({:?}) is not a function", value, value.get_type());
// FIXME: seems like self.functions get overwritten for rust_eh_personality.
/*debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(),
"{:?} is not a function", function);*/
function
}
@ -325,9 +329,9 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
fn get_fn(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
let func = get_fn(self, instance);
*self.current_func.borrow_mut() = Some(self.rvalue_as_function(func));
func
let func = get_fn(self, instance, false);
*self.current_func.borrow_mut() = Some(func);
unsafe { std::mem::transmute(func) }
}
fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
@ -338,8 +342,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
self.intrinsics.borrow()[func_name].clone()
}
else {
let func = get_fn(self, instance);
self.rvalue_as_function(func)
get_fn(self, instance, false)
};
let ptr = func.get_address(None);
@ -377,31 +380,68 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
return llpersonality;
}
let tcx = self.tcx;
let llfn = match tcx.lang_items().eh_personality() {
Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
tcx.intern_substs(&[]),
)
.unwrap().unwrap(),
),
_ => {
let _name = if wants_msvc_seh(self.sess()) {
"__CxxFrameHandler3"
} else {
"rust_eh_personality"
};
//let func = self.declare_func(name, self.type_i32(), &[], true);
// FIXME(antoyo): this hack should not be needed. That will probably be removed when
// unwinding support is added.
self.context.new_rvalue_from_int(self.int_type, 0)
}
};
let func =
match tcx.lang_items().eh_personality() {
Some(def_id) if !wants_msvc_seh(self.sess()) => {
// FIXME: this create an instance into self.functions and prevent the creating
// of the function defined in std.
let instance =
ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
tcx.intern_substs(&[]),
)
.unwrap().unwrap();
let symbol_name = tcx.symbol_name(instance).name;
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
self.linkage.set(FunctionType::Extern);
let func = self.declare_fn(symbol_name, &fn_abi, false);
//func.add_attribute(FnAttribute::Weak);
/*let block = func.new_block("eh_personality_block");
// NOTE: it seems this function is overwritten by the standard library, so just
// return a dummy value in this version.
let zero = self.context.new_rvalue_zero(self.type_u32());
block.end_with_return(None, zero);*/
//*self.current_func.borrow_mut() = Some(func);
let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
func
/*self.get_fn(
ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
tcx.intern_substs(&[]),
)
.unwrap().unwrap(),
)*/
},
_ => {
let name = if wants_msvc_seh(self.sess()) {
"__CxxFrameHandler3"
} else {
"rust_eh_personality"
};
let func = self.declare_func(name, self.type_i32(), &[], true);
//*self.current_func.borrow_mut() = Some(func);
// NOTE: this function is created multiple times and is overwritten by the
// standard library, so mark it as weak.
//func.add_attribute(FnAttribute::Weak);
//self.functions.borrow_mut().insert(name.to_string(), func);
/*let block = func.new_block("eh_personality_block");
// NOTE: it seems this function is overwritten by the standard library, so just
// return a dummy value in this version.
let zero = self.context.new_rvalue_zero(self.type_i32());
block.end_with_return(None, zero);*/
unsafe { std::mem::transmute(func) }
}
};
// TODO(antoyo): apply target cpu attributes.
self.eh_personality.set(Some(llfn));
llfn
self.eh_personality.set(Some(func));
func
}
fn sess(&self) -> &Session {

View file

@ -38,12 +38,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
global
}
/*pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> RValue<'gcc> {
self.linkage.set(FunctionType::Exported);
let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic);
// FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
unsafe { std::mem::transmute(func) }
}*/
pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> Function<'gcc> {
self.linkage.set(FunctionType::Extern);
declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic, true)
}
pub fn declare_global(&self, name: &str, ty: Type<'gcc>, global_kind: GlobalKind, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
let global = self.context.new_global(None, global_kind, ty, name);
@ -71,7 +69,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
let return_type = self.type_i32();
let variadic = false;
self.linkage.set(FunctionType::Exported);
let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, &[self.type_i32(), const_string], variadic);
let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, &[self.type_i32(), const_string], variadic, false);
// NOTE: it is needed to set the current_func here as well, because get_fn() is not called
// for the main function.
*self.current_func.borrow_mut() = Some(func);
@ -79,9 +77,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
unsafe { std::mem::transmute(func) }
}
pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Function<'gcc> {
pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, dont_cache: bool) -> Function<'gcc> {
let (return_type, params, variadic, on_stack_param_indices) = fn_abi.gcc_type(self);
let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, &params, variadic);
/*static mut COUNTER: i32 = 0;
if name.contains("personality") {
println!("{}: {}", name, skip_cache);
unsafe {
COUNTER += 1;
if COUNTER == 6 {
panic!("{}", name);
}
}
}*/
let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, &params, variadic, dont_cache);
self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices);
func
}
@ -100,7 +108,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
///
/// If theres a value with the same name already declared, the function will
/// update the declaration and return existing Value instead.
fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool) -> Function<'gcc> {
fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool, dont_cache: bool) -> Function<'gcc> {
if name.starts_with("llvm.") {
let intrinsic = llvm::intrinsic(name, cx);
cx.intrinsics.borrow_mut().insert(name.to_string(), intrinsic);
@ -115,7 +123,9 @@ fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*ll
.map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO(antoyo): set name.
.collect();
let func = cx.context.new_function(None, cx.linkage.get(), return_type, &params, mangle_name(name), variadic);
cx.functions.borrow_mut().insert(name.to_string(), func);
//if !dont_cache {
cx.functions.borrow_mut().insert(name.to_string(), func);
//}
func
};

View file

@ -1,22 +1,24 @@
pub mod llvm;
mod simd;
use std::iter;
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp, FunctionType};
use rustc_codegen_ssa::MemFlags;
use rustc_codegen_ssa::base::wants_msvc_seh;
use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error};
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods, IntrinsicCallMethods, MiscMethods};
use rustc_middle::bug;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf};
use rustc_span::{Span, Symbol, symbol::kw, sym};
use rustc_target::abi::HasDataLayout;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::spec::PanicStrategy;
use rustc_target::spec::{abi::Abi, PanicStrategy};
use crate::abi::GccType;
use crate::abi::{FnAbiGccExt, GccType};
use crate::builder::Builder;
use crate::common::{SignType, TypeReflection};
use crate::context::CodegenCx;
@ -1115,9 +1117,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
}
}
fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
fn try_intrinsic<'a, 'b, 'gcc, 'tcx>(bx: &'b mut Builder<'a, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
// NOTE: the `|| true` here is to use the panic=abort strategy with panic=unwind too
if bx.sess().panic_strategy() == PanicStrategy::Abort || true {
if bx.sess().panic_strategy() == PanicStrategy::Abort {
// TODO(bjorn3): Properly implement unwinding and remove the `|| true` once this is done.
bx.call(bx.type_void(), try_func, &[data], None);
// Return 0 unconditionally from the intrinsic call;
@ -1129,6 +1131,143 @@ fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<
unimplemented!();
}
else {
unimplemented!();
codegen_gnu_try(bx, try_func, data, catch_func, dest);
}
}
// Definition of the standard `try` function for Rust using the GNU-like model
// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
// instructions).
//
// This codegen is a little surprising because we always call a shim
// function instead of inlining the call to `invoke` manually here. This is done
// because in LLVM we're only allowed to have one personality per function
// definition. The call to the `try` intrinsic is being inlined into the
// function calling it, and that function may already have other personality
// functions in play. By calling a shim we're guaranteed that our shim will have
// the right personality function.
fn codegen_gnu_try<'gcc>(bx: &mut Builder<'_, 'gcc, '_>, try_func: RValue<'gcc>, data: RValue<'gcc>, catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
//use std::ops::Deref;
//let cx: &CodegenCx<'gcc, '_> = bx.deref();
let cx: &CodegenCx<'gcc, '_> = bx.cx;
let (llty, func) = get_rust_try_fn(cx, &mut |mut bx| {
// Codegens the shims described above:
//
// bx:
// invoke %try_func(%data) normal %normal unwind %catch
//
// normal:
// ret 0
//
// catch:
// (%ptr, _) = landingpad
// call %catch_func(%data, %ptr)
// ret 1
let then = bx.append_sibling_block("then");
let catch = bx.append_sibling_block("catch");
let func = bx.current_func();
let try_func = func.get_param(0).to_rvalue();
let data = func.get_param(1).to_rvalue();
let catch_func = func.get_param(2).to_rvalue();
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
let current_block = bx.block.clone();
bx.switch_to_block(then);
bx.ret(bx.const_i32(0));
// Type indicator for the exception being thrown.
//
// The first value in this tuple is a pointer to the exception object
// being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
// rust_try ignores the selector.
bx.switch_to_block(catch);
/*let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 1);
let tydesc = bx.const_null(bx.type_i8p());
bx.add_clause(vals, tydesc);
let ptr = bx.extract_value(vals, 0);*/
let eh_pointer_builtin = bx.cx.context.get_target_builtin_function("__builtin_eh_pointer");
let zero = bx.cx.context.new_rvalue_zero(bx.int_type);
let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]);
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
bx.call(catch_ty, catch_func, &[data, ptr], None);
bx.ret(bx.const_i32(1));
// NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not
// generate a try/catch.
// FIXME: add a check in the libgccjit API to prevent this.
bx.switch_to_block(current_block);
bx.invoke(try_func_ty, try_func, &[data], then, catch, None);
});
let func = unsafe { std::mem::transmute(func) };
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bx.call(llty, func, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}
// Helper function used to get a handle to the `__rust_try` function used to
// catch exceptions.
//
// This function is only generated once and is then cached.
fn get_rust_try_fn<'a, 'gcc, 'tcx>(cx: &'a CodegenCx<'gcc, 'tcx>, codegen: &mut dyn FnMut(Builder<'a, 'gcc, 'tcx>)) -> (Type<'gcc>, Function<'gcc>) {
if let Some(llfn) = cx.rust_try_fn.get() {
return llfn;
}
// Define the type up front for the signature of the rust_try function.
let tcx = cx.tcx;
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
// `unsafe fn(*mut i8) -> ()`
let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
iter::once(i8p),
tcx.mk_unit(),
false,
rustc_hir::Unsafety::Unsafe,
Abi::Rust,
)));
// `unsafe fn(*mut i8, *mut i8) -> ()`
let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
[i8p, i8p].iter().cloned(),
tcx.mk_unit(),
false,
rustc_hir::Unsafety::Unsafe,
Abi::Rust,
)));
// `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
[try_fn_ty, i8p, catch_fn_ty].into_iter(),
&tcx.types.i32,
false,
rustc_hir::Unsafety::Unsafe,
Abi::Rust,
));
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
cx.rust_try_fn.set(Some(rust_try));
rust_try
}
// Helper function to give a Block to a closure to codegen a shim function.
// This is currently primarily used for the `try` intrinsic functions above.
fn gen_fn<'a, 'gcc, 'tcx>(cx: &'a CodegenCx<'gcc, 'tcx>, name: &str, rust_fn_sig: ty::PolyFnSig<'tcx>, codegen: &mut dyn FnMut(Builder<'a, 'gcc, 'tcx>)) -> (Type<'gcc>, Function<'gcc>) {
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
let (typ, _, _, _) = fn_abi.gcc_type(cx);
// FIXME(eddyb) find a nicer way to do this.
cx.linkage.set(FunctionType::Internal);
let func = cx.declare_fn(name, fn_abi, false);
let func_val = unsafe { std::mem::transmute(func) };
cx.set_frame_pointer_type(func_val);
cx.apply_target_cpu_attr(func_val);
let block = Builder::append_block(cx, func_val, "entry-block");
let bx = Builder::build(cx, block);
codegen(bx);
(typ, func)
}

View file

@ -35,7 +35,10 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
self.linkage.set(base::linkage_to_gcc(linkage));
let decl = self.declare_fn(symbol_name, &fn_abi);
if symbol_name == "rust_eh_personality" {
println!("********************* Generating real rust_eh_personality: {:?}", base::linkage_to_gcc(linkage));
}
let decl = self.declare_fn(symbol_name, &fn_abi, false);
//let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
attributes::from_fn_attrs(self, decl, instance);
@ -59,5 +62,11 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): call set_link_section() to allow initializing argc/argv.
// TODO(antoyo): set unique comdat.
// TODO(antoyo): use inline attribute from there in linkage.set() above.
self.functions.borrow_mut().insert(symbol_name.to_string(), decl);
if symbol_name == "rust_eh_personality" {
println!("Caching here 2");
}
self.function_instances.borrow_mut().insert(instance, unsafe { std::mem::transmute(decl) });
}
}