WIP: Implement unwinding
This commit is contained in:
parent
0a54f243f6
commit
889a33a500
13 changed files with 379 additions and 87 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
11
src/base.rs
11
src/base.rs
|
|
@ -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);
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
108
src/context.rs
108
src/context.rs
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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, ¶ms, 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, ¶ms, 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 there’s 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, ¶ms, 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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue