From dc6e414e6f85293aceca3357e2466bcc4bcf425c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 15 Nov 2014 20:30:33 -0500 Subject: Move trans, back, driver, and back into a new crate, rustc_trans. Reduces memory usage significantly and opens opportunities for more parallel compilation. --- mk/crates.mk | 11 +- src/driver/driver.rs | 2 +- src/librustc/README.txt | 87 +- src/librustc/back/link.rs | 1313 -------- src/librustc/back/lto.rs | 212 -- src/librustc/back/write.rs | 1053 ------- src/librustc/driver/config.rs | 926 ------ src/librustc/driver/driver.rs | 851 ------ src/librustc/driver/mod.rs | 510 ---- src/librustc/driver/pretty.rs | 598 ---- src/librustc/driver/session.rs | 265 -- src/librustc/lib.rs | 15 +- src/librustc/lint/context.rs | 3 +- src/librustc/metadata/creader.rs | 9 +- src/librustc/metadata/encoder.rs | 2 +- src/librustc/metadata/loader.rs | 2 +- src/librustc/middle/astencode.rs | 2 +- src/librustc/middle/check_loop.rs | 2 +- src/librustc/middle/check_static_recursion.rs | 2 +- src/librustc/middle/dependency_format.rs | 4 +- src/librustc/middle/entry.rs | 3 +- src/librustc/middle/lang_items.rs | 2 +- src/librustc/middle/reachable.rs | 2 +- src/librustc/middle/region.rs | 2 +- src/librustc/middle/resolve.rs | 2 +- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/middle/save/mod.rs | 1558 ---------- src/librustc/middle/save/recorder.rs | 592 ---- src/librustc/middle/save/span_utils.rs | 333 -- src/librustc/middle/trans/_match.rs | 1746 ----------- src/librustc/middle/trans/adt.rs | 1158 ------- src/librustc/middle/trans/asm.rs | 176 -- src/librustc/middle/trans/base.rs | 3196 ------------------- src/librustc/middle/trans/basic_block.rs | 47 - src/librustc/middle/trans/build.rs | 827 ----- src/librustc/middle/trans/builder.rs | 985 ------ src/librustc/middle/trans/cabi.rs | 122 - src/librustc/middle/trans/cabi_arm.rs | 151 - src/librustc/middle/trans/cabi_mips.rs | 184 -- src/librustc/middle/trans/cabi_x86.rs | 85 - src/librustc/middle/trans/cabi_x86_64.rs | 380 --- src/librustc/middle/trans/cabi_x86_win64.rs | 64 - src/librustc/middle/trans/callee.rs | 1167 ------- src/librustc/middle/trans/cleanup.rs | 1218 -------- src/librustc/middle/trans/closure.rs | 659 ---- src/librustc/middle/trans/common.rs | 955 ------ src/librustc/middle/trans/consts.rs | 731 ----- src/librustc/middle/trans/context.rs | 886 ------ src/librustc/middle/trans/controlflow.rs | 535 ---- src/librustc/middle/trans/datum.rs | 685 ----- src/librustc/middle/trans/debuginfo.rs | 4046 ------------------------- src/librustc/middle/trans/doc.rs | 237 -- src/librustc/middle/trans/expr.rs | 2248 -------------- src/librustc/middle/trans/foreign.rs | 974 ------ src/librustc/middle/trans/glue.rs | 593 ---- src/librustc/middle/trans/inline.rs | 197 -- src/librustc/middle/trans/intrinsic.rs | 623 ---- src/librustc/middle/trans/llrepr.rs | 38 - src/librustc/middle/trans/machine.rs | 128 - src/librustc/middle/trans/macros.rs | 31 - src/librustc/middle/trans/meth.rs | 778 ----- src/librustc/middle/trans/mod.rs | 47 - src/librustc/middle/trans/monomorphize.rs | 291 -- src/librustc/middle/trans/tvec.rs | 537 ---- src/librustc/middle/trans/type_.rs | 358 --- src/librustc/middle/trans/type_of.rs | 478 --- src/librustc/middle/trans/value.rs | 167 - src/librustc/middle/ty.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 2 +- src/librustc/middle/typeck/infer/mod.rs | 11 +- src/librustc/middle/typeck/infer/test.rs | 518 ---- src/librustc/middle/typeck/mod.rs | 3 +- src/librustc/middle/weak_lang_items.rs | 4 +- src/librustc/plugin/load.rs | 2 +- src/librustc/session/config.rs | 945 ++++++ src/librustc/session/mod.rs | 277 ++ src/librustc_trans/README.txt | 1 + src/librustc_trans/back/link.rs | 1313 ++++++++ src/librustc_trans/back/lto.rs | 211 ++ src/librustc_trans/back/write.rs | 1048 +++++++ src/librustc_trans/driver/driver.rs | 840 +++++ src/librustc_trans/driver/mod.rs | 502 +++ src/librustc_trans/driver/pretty.rs | 597 ++++ src/librustc_trans/lib.rs | 87 + src/librustc_trans/save/mod.rs | 1558 ++++++++++ src/librustc_trans/save/recorder.rs | 592 ++++ src/librustc_trans/save/span_utils.rs | 333 ++ src/librustc_trans/test.rs | 511 ++++ src/librustc_trans/trans/_match.rs | 1746 +++++++++++ src/librustc_trans/trans/adt.rs | 1158 +++++++ src/librustc_trans/trans/asm.rs | 176 ++ src/librustc_trans/trans/base.rs | 3195 +++++++++++++++++++ src/librustc_trans/trans/basic_block.rs | 47 + src/librustc_trans/trans/build.rs | 827 +++++ src/librustc_trans/trans/builder.rs | 985 ++++++ src/librustc_trans/trans/cabi.rs | 122 + src/librustc_trans/trans/cabi_arm.rs | 151 + src/librustc_trans/trans/cabi_mips.rs | 184 ++ src/librustc_trans/trans/cabi_x86.rs | 85 + src/librustc_trans/trans/cabi_x86_64.rs | 380 +++ src/librustc_trans/trans/cabi_x86_win64.rs | 64 + src/librustc_trans/trans/callee.rs | 1167 +++++++ src/librustc_trans/trans/cleanup.rs | 1218 ++++++++ src/librustc_trans/trans/closure.rs | 659 ++++ src/librustc_trans/trans/common.rs | 955 ++++++ src/librustc_trans/trans/consts.rs | 731 +++++ src/librustc_trans/trans/context.rs | 886 ++++++ src/librustc_trans/trans/controlflow.rs | 535 ++++ src/librustc_trans/trans/datum.rs | 685 +++++ src/librustc_trans/trans/debuginfo.rs | 4045 ++++++++++++++++++++++++ src/librustc_trans/trans/doc.rs | 237 ++ src/librustc_trans/trans/expr.rs | 2248 ++++++++++++++ src/librustc_trans/trans/foreign.rs | 974 ++++++ src/librustc_trans/trans/glue.rs | 593 ++++ src/librustc_trans/trans/inline.rs | 197 ++ src/librustc_trans/trans/intrinsic.rs | 623 ++++ src/librustc_trans/trans/llrepr.rs | 38 + src/librustc_trans/trans/machine.rs | 128 + src/librustc_trans/trans/macros.rs | 31 + src/librustc_trans/trans/meth.rs | 778 +++++ src/librustc_trans/trans/mod.rs | 47 + src/librustc_trans/trans/monomorphize.rs | 291 ++ src/librustc_trans/trans/tvec.rs | 537 ++++ src/librustc_trans/trans/type_.rs | 358 +++ src/librustc_trans/trans/type_of.rs | 478 +++ src/librustc_trans/trans/value.rs | 167 + src/librustdoc/clean/mod.rs | 4 +- src/librustdoc/core.rs | 7 +- src/librustdoc/lib.rs | 3 +- src/librustdoc/test.rs | 8 +- 130 files changed, 36653 insertions(+), 36545 deletions(-) delete mode 100644 src/librustc/back/link.rs delete mode 100644 src/librustc/back/lto.rs delete mode 100644 src/librustc/back/write.rs delete mode 100644 src/librustc/driver/config.rs delete mode 100644 src/librustc/driver/driver.rs delete mode 100644 src/librustc/driver/mod.rs delete mode 100644 src/librustc/driver/pretty.rs delete mode 100644 src/librustc/driver/session.rs delete mode 100644 src/librustc/middle/save/mod.rs delete mode 100644 src/librustc/middle/save/recorder.rs delete mode 100644 src/librustc/middle/save/span_utils.rs delete mode 100644 src/librustc/middle/trans/_match.rs delete mode 100644 src/librustc/middle/trans/adt.rs delete mode 100644 src/librustc/middle/trans/asm.rs delete mode 100644 src/librustc/middle/trans/base.rs delete mode 100644 src/librustc/middle/trans/basic_block.rs delete mode 100644 src/librustc/middle/trans/build.rs delete mode 100644 src/librustc/middle/trans/builder.rs delete mode 100644 src/librustc/middle/trans/cabi.rs delete mode 100644 src/librustc/middle/trans/cabi_arm.rs delete mode 100644 src/librustc/middle/trans/cabi_mips.rs delete mode 100644 src/librustc/middle/trans/cabi_x86.rs delete mode 100644 src/librustc/middle/trans/cabi_x86_64.rs delete mode 100644 src/librustc/middle/trans/cabi_x86_win64.rs delete mode 100644 src/librustc/middle/trans/callee.rs delete mode 100644 src/librustc/middle/trans/cleanup.rs delete mode 100644 src/librustc/middle/trans/closure.rs delete mode 100644 src/librustc/middle/trans/common.rs delete mode 100644 src/librustc/middle/trans/consts.rs delete mode 100644 src/librustc/middle/trans/context.rs delete mode 100644 src/librustc/middle/trans/controlflow.rs delete mode 100644 src/librustc/middle/trans/datum.rs delete mode 100644 src/librustc/middle/trans/debuginfo.rs delete mode 100644 src/librustc/middle/trans/doc.rs delete mode 100644 src/librustc/middle/trans/expr.rs delete mode 100644 src/librustc/middle/trans/foreign.rs delete mode 100644 src/librustc/middle/trans/glue.rs delete mode 100644 src/librustc/middle/trans/inline.rs delete mode 100644 src/librustc/middle/trans/intrinsic.rs delete mode 100644 src/librustc/middle/trans/llrepr.rs delete mode 100644 src/librustc/middle/trans/machine.rs delete mode 100644 src/librustc/middle/trans/macros.rs delete mode 100644 src/librustc/middle/trans/meth.rs delete mode 100644 src/librustc/middle/trans/mod.rs delete mode 100644 src/librustc/middle/trans/monomorphize.rs delete mode 100644 src/librustc/middle/trans/tvec.rs delete mode 100644 src/librustc/middle/trans/type_.rs delete mode 100644 src/librustc/middle/trans/type_of.rs delete mode 100644 src/librustc/middle/trans/value.rs delete mode 100644 src/librustc/middle/typeck/infer/test.rs create mode 100644 src/librustc/session/config.rs create mode 100644 src/librustc/session/mod.rs create mode 100644 src/librustc_trans/README.txt create mode 100644 src/librustc_trans/back/link.rs create mode 100644 src/librustc_trans/back/lto.rs create mode 100644 src/librustc_trans/back/write.rs create mode 100644 src/librustc_trans/driver/driver.rs create mode 100644 src/librustc_trans/driver/mod.rs create mode 100644 src/librustc_trans/driver/pretty.rs create mode 100644 src/librustc_trans/lib.rs create mode 100644 src/librustc_trans/save/mod.rs create mode 100644 src/librustc_trans/save/recorder.rs create mode 100644 src/librustc_trans/save/span_utils.rs create mode 100644 src/librustc_trans/test.rs create mode 100644 src/librustc_trans/trans/_match.rs create mode 100644 src/librustc_trans/trans/adt.rs create mode 100644 src/librustc_trans/trans/asm.rs create mode 100644 src/librustc_trans/trans/base.rs create mode 100644 src/librustc_trans/trans/basic_block.rs create mode 100644 src/librustc_trans/trans/build.rs create mode 100644 src/librustc_trans/trans/builder.rs create mode 100644 src/librustc_trans/trans/cabi.rs create mode 100644 src/librustc_trans/trans/cabi_arm.rs create mode 100644 src/librustc_trans/trans/cabi_mips.rs create mode 100644 src/librustc_trans/trans/cabi_x86.rs create mode 100644 src/librustc_trans/trans/cabi_x86_64.rs create mode 100644 src/librustc_trans/trans/cabi_x86_win64.rs create mode 100644 src/librustc_trans/trans/callee.rs create mode 100644 src/librustc_trans/trans/cleanup.rs create mode 100644 src/librustc_trans/trans/closure.rs create mode 100644 src/librustc_trans/trans/common.rs create mode 100644 src/librustc_trans/trans/consts.rs create mode 100644 src/librustc_trans/trans/context.rs create mode 100644 src/librustc_trans/trans/controlflow.rs create mode 100644 src/librustc_trans/trans/datum.rs create mode 100644 src/librustc_trans/trans/debuginfo.rs create mode 100644 src/librustc_trans/trans/doc.rs create mode 100644 src/librustc_trans/trans/expr.rs create mode 100644 src/librustc_trans/trans/foreign.rs create mode 100644 src/librustc_trans/trans/glue.rs create mode 100644 src/librustc_trans/trans/inline.rs create mode 100644 src/librustc_trans/trans/intrinsic.rs create mode 100644 src/librustc_trans/trans/llrepr.rs create mode 100644 src/librustc_trans/trans/machine.rs create mode 100644 src/librustc_trans/trans/macros.rs create mode 100644 src/librustc_trans/trans/meth.rs create mode 100644 src/librustc_trans/trans/mod.rs create mode 100644 src/librustc_trans/trans/monomorphize.rs create mode 100644 src/librustc_trans/trans/tvec.rs create mode 100644 src/librustc_trans/trans/type_.rs create mode 100644 src/librustc_trans/trans/type_of.rs create mode 100644 src/librustc_trans/trans/value.rs diff --git a/mk/crates.mk b/mk/crates.mk index 771c1821e38..1a9d1e82467 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -53,7 +53,7 @@ TARGET_CRATES := libc std green native flate arena term \ serialize sync getopts collections test time rand \ log regex graphviz core rbml alloc rustrt \ unicode -HOST_CRATES := syntax rustc rustdoc regex_macros fmt_macros \ +HOST_CRATES := syntax rustc rustc_trans rustdoc regex_macros fmt_macros \ rustc_llvm rustc_back CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc @@ -69,11 +69,12 @@ DEPS_graphviz := std DEPS_green := std native:context_switch DEPS_native := std DEPS_syntax := std term serialize log fmt_macros arena libc +DEPS_rustc_trans := rustc rustc_back rustc_llvm libc DEPS_rustc := syntax flate arena serialize getopts rbml \ time log graphviz rustc_llvm rustc_back DEPS_rustc_llvm := native:rustllvm libc std DEPS_rustc_back := std syntax rustc_llvm flate log libc -DEPS_rustdoc := rustc native:hoedown serialize getopts \ +DEPS_rustdoc := rustc rustc_trans native:hoedown serialize getopts \ test time DEPS_flate := std native:miniz DEPS_arena := std @@ -96,7 +97,7 @@ DEPS_fmt_macros = std TOOL_DEPS_compiletest := test getopts native TOOL_DEPS_rustdoc := rustdoc native -TOOL_DEPS_rustc := rustc native +TOOL_DEPS_rustc := rustc_trans native TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs TOOL_SOURCE_rustc := $(S)src/driver/driver.rs @@ -112,8 +113,8 @@ ONLY_RLIB_unicode := 1 # You should not need to edit below this line ################################################################################ -DOC_CRATES := $(filter-out rustc, $(filter-out syntax, $(CRATES))) -COMPILER_DOC_CRATES := rustc syntax +DOC_CRATES := $(filter-out rustc, $(filter-out rustc_trans, $(filter-out syntax, $(CRATES)))) +COMPILER_DOC_CRATES := rustc rustc_trans syntax # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 8ac09ed909b..632d21d7b9c 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -12,6 +12,6 @@ extern crate "rustdoc" as this; #[cfg(rustc)] -extern crate "rustc" as this; +extern crate "rustc_trans" as this; fn main() { this::main() } diff --git a/src/librustc/README.txt b/src/librustc/README.txt index 15e751a283e..e79ee7fa647 100644 --- a/src/librustc/README.txt +++ b/src/librustc/README.txt @@ -13,31 +13,62 @@ https://github.com/rust-lang/rust/issues Your concerns are probably the same as someone else's. - -High-level concepts +The crates of rustc =================== -Rustc consists of the following subdirectories: - -front/ - front-end: attributes, conditional compilation -middle/ - middle-end: name resolution, typechecking, LLVM code +Rustc consists of four crates altogether: `libsyntax`, `librustc`, +`librustc_back`, and `librustc_trans` (the names and divisions are not +set in stone and may change; in general, a finer-grained division of +crates is preferable): + +- `libsyntax` contains those things concerned purely with syntax -- + that is, the AST, parser, pretty-printer, lexer, macro expander, and + utilities for traversing ASTs -- are in a separate crate called + "syntax", whose files are in ./../libsyntax, where . is the current + directory (that is, the parent directory of front/, middle/, back/, + and so on). + +- `librustc` (the current directory) contains the high-level analysis + passes, such as the type checker, borrow checker, and so forth. + It is the heart of the compiler. + +- `librustc_back` contains some very low-level details that are + specific to different LLVM targets and so forth. + +- `librustc_trans` contains the code to convert from Rust IR into LLVM + IR, and then from LLVM IR into machine code, as well as the main + driver that orchestrates all the other passes and various other bits + of miscellany. In general it contains code that runs towards the + end of the compilation process. + +Roughly speaking the "order" of the three crates is as follows: + + libsyntax -> librustc -> librustc_trans + | | + +-----------------+-------------------+ + | + librustc_trans/driver + +Here the role of `librustc_trans/driver` is to invoke the compiler +from libsyntax, then the analysis phases from librustc, and finally +the lowering and codegen passes from librustc_trans. + +Modules in the rustc crate +========================== + +The rustc crate itself consists of the following subdirectories +(mostly, but not entirely, in their own directories): + +session - options and data that pertain to the compilation session as a whole +middle - middle-end: name resolution, typechecking, LLVM code generation -back/ - back-end: linking and ABI -metadata/ - encoder and decoder for data required by +metadata - encoder and decoder for data required by separate compilation -driver/ - command-line processing, main() entrypoint -util/ - ubiquitous types and helper functions -lib/ - bindings to LLVM - -The files concerned purely with syntax -- that is, the AST, parser, -pretty-printer, lexer, macro expander, and utilities for traversing -ASTs -- are in a separate crate called "syntax", whose files are in -./../libsyntax, where . is the current directory (that is, the parent -directory of front/, middle/, back/, and so on). - -The entry-point for the compiler is main() in lib.rs, and -this file sequences the various parts together. +util - ubiquitous types and helper functions +lib - bindings to LLVM +The entry-point for the compiler is main() in the librustc_trans +crate. But the The 3 central data structures: ------------------------------ @@ -66,10 +97,10 @@ The 3 central data structures: compilation. Most variants in the ast::ty tag have a corresponding variant in the ty::sty tag. -#3: lib/llvm.rs defines the exported types ValueRef, TypeRef, - BasicBlockRef, and several others. Each of these is an opaque - pointer to an LLVM type, manipulated through the lib::llvm - interface. +#3: lib/llvm.rs (in librustc_trans) defines the exported types + ValueRef, TypeRef, BasicBlockRef, and several others. Each of + these is an opaque pointer to an LLVM type, manipulated through + the lib::llvm interface. Control and information flow within the compiler: @@ -87,7 +118,7 @@ Control and information flow within the compiler: structures. The driver passes environments to each compiler pass that needs to refer to them. -- Finally middle/trans.rs translates the Rust AST to LLVM bitcode in a - type-directed way. When it's finished synthesizing LLVM values, - rustc asks LLVM to write them out in some form (.bc, .o) and - possibly run the system linker. +- Finally, the `trans` module in `librustc_trans` translates the Rust + AST to LLVM bitcode in a type-directed way. When it's finished + synthesizing LLVM values, rustc asks LLVM to write them out in some + form (.bc, .o) and possibly run the system linker. diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs deleted file mode 100644 index 766715ff251..00000000000 --- a/src/librustc/back/link.rs +++ /dev/null @@ -1,1313 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; -use super::archive; -use super::rpath; -use super::rpath::RPathConfig; -use super::svh::Svh; -use super::write::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; -use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput}; -use driver::config::NoDebugInfo; -use driver::session::Session; -use driver::config; -use metadata::common::LinkMeta; -use metadata::{encoder, cstore, filesearch, csearch, creader}; -use middle::trans::context::CrateContext; -use middle::trans::common::gensym_name; -use middle::ty; -use util::common::time; -use util::ppaux; -use util::sha2::{Digest, Sha256}; - -use std::char; -use std::io::fs::PathExtensions; -use std::io::{fs, TempDir, Command}; -use std::io; -use std::mem; -use std::str; -use std::string::String; -use flate; -use serialize::hex::ToHex; -use syntax::ast; -use syntax::ast_map::{PathElem, PathElems, PathName}; -use syntax::ast_map; -use syntax::attr::AttrMetaMethods; -use syntax::codemap::Span; -use syntax::parse::token; - -// RLIB LLVM-BYTECODE OBJECT LAYOUT -// Version 1 -// Bytes Data -// 0..10 "RUST_OBJECT" encoded in ASCII -// 11..14 format version as little-endian u32 -// 15..22 size in bytes of deflate compressed LLVM bitcode as -// little-endian u64 -// 23.. compressed LLVM bitcode - -// This is the "magic number" expected at the beginning of a LLVM bytecode -// object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; - -// The version number this compiler will write to bytecode objects in rlibs -pub const RLIB_BYTECODE_OBJECT_VERSION: u32 = 1; - -// The offset in bytes the bytecode object format version number can be found at -pub const RLIB_BYTECODE_OBJECT_VERSION_OFFSET: uint = 11; - -// The offset in bytes the size of the compressed bytecode can be found at in -// format version 1 -pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: uint = - RLIB_BYTECODE_OBJECT_VERSION_OFFSET + 4; - -// The offset in bytes the compressed LLVM bytecode can be found at in format -// version 1 -pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint = - RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; - - -/* - * Name mangling and its relationship to metadata. This is complex. Read - * carefully. - * - * The semantic model of Rust linkage is, broadly, that "there's no global - * namespace" between crates. Our aim is to preserve the illusion of this - * model despite the fact that it's not *quite* possible to implement on - * modern linkers. We initially didn't use system linkers at all, but have - * been convinced of their utility. - * - * There are a few issues to handle: - * - * - Linkers operate on a flat namespace, so we have to flatten names. - * We do this using the C++ namespace-mangling technique. Foo::bar - * symbols and such. - * - * - Symbols with the same name but different types need to get different - * linkage-names. We do this by hashing a string-encoding of the type into - * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: - * we use SHA256) to "prevent collisions". This is not airtight but 16 hex - * digits on uniform probability means you're going to need 2**32 same-name - * symbols in the same process before you're even hitting birthday-paradox - * collision probability. - * - * - Symbols in different crates but with same names "within" the crate need - * to get different linkage-names. - * - * - The hash shown in the filename needs to be predictable and stable for - * build tooling integration. It also needs to be using a hash function - * which is easy to use from Python, make, etc. - * - * So here is what we do: - * - * - Consider the package id; every crate has one (specified with crate_id - * attribute). If a package id isn't provided explicitly, we infer a - * versionless one from the output name. The version will end up being 0.0 - * in this case. CNAME and CVERS are taken from this package id. For - * example, github.com/mozilla/CNAME#CVERS. - * - * - Define CMH as SHA256(crateid). - * - * - Define CMH8 as the first 8 characters of CMH. - * - * - Compile our crate to lib CNAME-CMH8-CVERS.so - * - * - Define STH(sym) as SHA256(CMH, type_str(sym)) - * - * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the - * name, non-name metadata, and type sense, and versioned in the way - * system linkers understand. - */ - -pub fn find_crate_name(sess: Option<&Session>, - attrs: &[ast::Attribute], - input: &Input) -> String { - let validate = |s: String, span: Option| { - creader::validate_crate_name(sess, s.as_slice(), span); - s - }; - - // Look in attributes 100% of the time to make sure the attribute is marked - // as used. After doing this, however, we still prioritize a crate name from - // the command line over one found in the #[crate_name] attribute. If we - // find both we ensure that they're the same later on as well. - let attr_crate_name = attrs.iter().find(|at| at.check_name("crate_name")) - .and_then(|at| at.value_str().map(|s| (at, s))); - - match sess { - Some(sess) => { - match sess.opts.crate_name { - Some(ref s) => { - match attr_crate_name { - Some((attr, ref name)) if s.as_slice() != name.get() => { - let msg = format!("--crate-name and #[crate_name] \ - are required to match, but `{}` \ - != `{}`", s, name); - sess.span_err(attr.span, msg.as_slice()); - } - _ => {}, - } - return validate(s.clone(), None); - } - None => {} - } - } - None => {} - } - - match attr_crate_name { - Some((attr, s)) => return validate(s.get().to_string(), Some(attr.span)), - None => {} - } - match *input { - FileInput(ref path) => { - match path.filestem_str() { - Some(s) => return validate(s.to_string(), None), - None => {} - } - } - _ => {} - } - - "rust-out".to_string() -} - -pub fn build_link_meta(sess: &Session, krate: &ast::Crate, - name: String) -> LinkMeta { - let r = LinkMeta { - crate_name: name, - crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate), - }; - info!("{}", r); - return r; -} - -fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String { - let output = symbol_hasher.result_bytes(); - // 64 bits should be enough to avoid collisions. - output.slice_to(8).to_hex().to_string() -} - - -// This calculates STH for a symbol, as defined above -fn symbol_hash(tcx: &ty::ctxt, - symbol_hasher: &mut Sha256, - t: ty::t, - link_meta: &LinkMeta) - -> String { - // NB: do *not* use abbrevs here as we want the symbol names - // to be independent of one another in the crate. - - symbol_hasher.reset(); - symbol_hasher.input_str(link_meta.crate_name.as_slice()); - symbol_hasher.input_str("-"); - symbol_hasher.input_str(link_meta.crate_hash.as_str()); - for meta in tcx.sess.crate_metadata.borrow().iter() { - symbol_hasher.input_str(meta.as_slice()); - } - symbol_hasher.input_str("-"); - symbol_hasher.input_str(encoder::encoded_ty(tcx, t).as_slice()); - // Prefix with 'h' so that it never blends into adjacent digits - let mut hash = String::from_str("h"); - hash.push_str(truncated_hash_result(symbol_hasher).as_slice()); - hash -} - -fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String { - match ccx.type_hashcodes().borrow().get(&t) { - Some(h) => return h.to_string(), - None => {} - } - - let mut symbol_hasher = ccx.symbol_hasher().borrow_mut(); - let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, ccx.link_meta()); - ccx.type_hashcodes().borrow_mut().insert(t, hash.clone()); - hash -} - - -// Name sanitation. LLVM will happily accept identifiers with weird names, but -// gas doesn't! -// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ -pub fn sanitize(s: &str) -> String { - let mut result = String::new(); - for c in s.chars() { - match c { - // Escape these with $ sequences - '@' => result.push_str("$SP$"), - '~' => result.push_str("$UP$"), - '*' => result.push_str("$RP$"), - '&' => result.push_str("$BP$"), - '<' => result.push_str("$LT$"), - '>' => result.push_str("$GT$"), - '(' => result.push_str("$LP$"), - ')' => result.push_str("$RP$"), - ',' => result.push_str("$C$"), - - // '.' doesn't occur in types and functions, so reuse it - // for ':' and '-' - '-' | ':' => result.push('.'), - - // These are legal symbols - 'a' ... 'z' - | 'A' ... 'Z' - | '0' ... '9' - | '_' | '.' | '$' => result.push(c), - - _ => { - let mut tstr = String::new(); - char::escape_unicode(c, |c| tstr.push(c)); - result.push('$'); - result.push_str(tstr.as_slice().slice_from(1)); - } - } - } - - // Underscore-qualify anything that didn't start as an ident. - if result.len() > 0u && - result.as_bytes()[0] != '_' as u8 && - ! char::is_XID_start(result.as_bytes()[0] as char) { - return format!("_{}", result.as_slice()); - } - - return result; -} - -pub fn mangle>(mut path: PI, - hash: Option<&str>) -> String { - // Follow C++ namespace-mangling style, see - // http://en.wikipedia.org/wiki/Name_mangling for more info. - // - // It turns out that on OSX you can actually have arbitrary symbols in - // function names (at least when given to LLVM), but this is not possible - // when using unix's linker. Perhaps one day when we just use a linker from LLVM - // we won't need to do this name mangling. The problem with name mangling is - // that it seriously limits the available characters. For example we can't - // have things like &T or ~[T] in symbol names when one would theoretically - // want them for things like impls of traits on that type. - // - // To be able to work on all platforms and get *some* reasonable output, we - // use C++ name-mangling. - - let mut n = String::from_str("_ZN"); // _Z == Begin name-sequence, N == nested - - fn push(n: &mut String, s: &str) { - let sani = sanitize(s); - n.push_str(format!("{}{}", sani.len(), sani).as_slice()); - } - - // First, connect each component with pairs. - for e in path { - push(&mut n, token::get_name(e.name()).get().as_slice()) - } - - match hash { - Some(s) => push(&mut n, s), - None => {} - } - - n.push('E'); // End name-sequence. - n -} - -pub fn exported_name(path: PathElems, hash: &str) -> String { - mangle(path, Some(hash)) -} - -pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems, - t: ty::t, id: ast::NodeId) -> String { - let mut hash = get_symbol_hash(ccx, t); - - // Paths can be completely identical for different nodes, - // e.g. `fn foo() { { fn a() {} } { fn a() {} } }`, so we - // generate unique characters from the node id. For now - // hopefully 3 characters is enough to avoid collisions. - static EXTRA_CHARS: &'static str = - "abcdefghijklmnopqrstuvwxyz\ - ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - 0123456789"; - let id = id as uint; - let extra1 = id % EXTRA_CHARS.len(); - let id = id / EXTRA_CHARS.len(); - let extra2 = id % EXTRA_CHARS.len(); - let id = id / EXTRA_CHARS.len(); - let extra3 = id % EXTRA_CHARS.len(); - hash.push(EXTRA_CHARS.as_bytes()[extra1] as char); - hash.push(EXTRA_CHARS.as_bytes()[extra2] as char); - hash.push(EXTRA_CHARS.as_bytes()[extra3] as char); - - exported_name(path, hash.as_slice()) -} - -pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext, - t: ty::t, - name: &str) -> String { - let s = ppaux::ty_to_string(ccx.tcx(), t); - let path = [PathName(token::intern(s.as_slice())), - gensym_name(name)]; - let hash = get_symbol_hash(ccx, t); - mangle(ast_map::Values(path.iter()), Some(hash.as_slice())) -} - -pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> String { - mangle(path.chain(Some(gensym_name(flav)).into_iter()), None) -} - -pub fn get_cc_prog(sess: &Session) -> String { - match sess.opts.cg.linker { - Some(ref linker) => return linker.to_string(), - None => sess.target.target.options.linker.clone(), - } -} - -pub fn remove(sess: &Session, path: &Path) { - match fs::unlink(path) { - Ok(..) => {} - Err(e) => { - sess.err(format!("failed to remove {}: {}", - path.display(), - e).as_slice()); - } - } -} - -/// Perform the linkage portion of the compilation phase. This will generate all -/// of the requested outputs for this compilation session. -pub fn link_binary(sess: &Session, - trans: &CrateTranslation, - outputs: &OutputFilenames, - crate_name: &str) -> Vec { - let mut out_filenames = Vec::new(); - for &crate_type in sess.crate_types.borrow().iter() { - if invalid_output_for_target(sess, crate_type) { - sess.bug(format!("invalid output type `{}` for target os `{}`", - crate_type, sess.opts.target_triple).as_slice()); - } - let out_file = link_binary_output(sess, trans, crate_type, outputs, - crate_name); - out_filenames.push(out_file); - } - - // Remove the temporary object file and metadata if we aren't saving temps - if !sess.opts.cg.save_temps { - let obj_filename = outputs.temp_path(OutputTypeObject); - if !sess.opts.output_types.contains(&OutputTypeObject) { - remove(sess, &obj_filename); - } - remove(sess, &obj_filename.with_extension("metadata.o")); - } - - out_filenames -} - - -/// Returns default crate type for target -/// -/// Default crate type is used when crate type isn't provided neither -/// through cmd line arguments nor through crate attributes -/// -/// It is CrateTypeExecutable for all platforms but iOS as there is no -/// way to run iOS binaries anyway without jailbreaking and -/// interaction with Rust code through static library is the only -/// option for now -pub fn default_output_for_target(sess: &Session) -> config::CrateType { - if !sess.target.target.options.executables { - config::CrateTypeStaticlib - } else { - config::CrateTypeExecutable - } -} - -/// Checks if target supports crate_type as output -pub fn invalid_output_for_target(sess: &Session, - crate_type: config::CrateType) -> bool { - match (sess.target.target.options.dynamic_linking, - sess.target.target.options.executables, crate_type) { - (false, _, config::CrateTypeDylib) => true, - (_, false, config::CrateTypeExecutable) => true, - _ => false - } -} - -fn is_writeable(p: &Path) -> bool { - match p.stat() { - Err(..) => true, - Ok(m) => m.perm & io::USER_WRITE == io::USER_WRITE - } -} - -pub fn filename_for_input(sess: &Session, - crate_type: config::CrateType, - name: &str, - out_filename: &Path) -> Path { - let libname = format!("{}{}", name, sess.opts.cg.extra_filename); - match crate_type { - config::CrateTypeRlib => { - out_filename.with_filename(format!("lib{}.rlib", libname)) - } - config::CrateTypeDylib => { - let (prefix, suffix) = (sess.target.target.options.dll_prefix.as_slice(), - sess.target.target.options.dll_suffix.as_slice()); - out_filename.with_filename(format!("{}{}{}", - prefix, - libname, - suffix)) - } - config::CrateTypeStaticlib => { - out_filename.with_filename(format!("lib{}.a", libname)) - } - config::CrateTypeExecutable => { - let suffix = sess.target.target.options.exe_suffix.as_slice(); - out_filename.with_filename(format!("{}{}", libname, suffix)) - } - } -} - -fn link_binary_output(sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) -> Path { - let obj_filename = outputs.temp_path(OutputTypeObject); - let out_filename = match outputs.single_output_file { - Some(ref file) => file.clone(), - None => { - let out_filename = outputs.path(OutputTypeExe); - filename_for_input(sess, crate_type, crate_name, &out_filename) - } - }; - - // Make sure the output and obj_filename are both writeable. - // Mac, FreeBSD, and Windows system linkers check this already -- - // however, the Linux linker will happily overwrite a read-only file. - // We should be consistent. - let obj_is_writeable = is_writeable(&obj_filename); - let out_is_writeable = is_writeable(&out_filename); - if !out_is_writeable { - sess.fatal(format!("output file {} is not writeable -- check its \ - permissions.", - out_filename.display()).as_slice()); - } - else if !obj_is_writeable { - sess.fatal(format!("object file {} is not writeable -- check its \ - permissions.", - obj_filename.display()).as_slice()); - } - - match crate_type { - config::CrateTypeRlib => { - link_rlib(sess, Some(trans), &obj_filename, &out_filename).build(); - } - config::CrateTypeStaticlib => { - link_staticlib(sess, &obj_filename, &out_filename); - } - config::CrateTypeExecutable => { - link_natively(sess, trans, false, &obj_filename, &out_filename); - } - config::CrateTypeDylib => { - link_natively(sess, trans, true, &obj_filename, &out_filename); - } - } - - out_filename -} - -fn archive_search_paths(sess: &Session) -> Vec { - let mut rustpath = filesearch::rust_path(); - rustpath.push(sess.target_filesearch().get_lib_path()); - let mut search: Vec = sess.opts.addl_lib_search_paths.borrow().clone(); - search.push_all(rustpath.as_slice()); - return search; -} - -// Create an 'rlib' -// -// An rlib in its current incarnation is essentially a renamed .a file. The -// rlib primarily contains the object file of the crate, but it also contains -// all of the object files from native libraries. This is done by unzipping -// native libraries and inserting all of the contents into this archive. -fn link_rlib<'a>(sess: &'a Session, - trans: Option<&CrateTranslation>, // None == no metadata/bytecode - obj_filename: &Path, - out_filename: &Path) -> ArchiveBuilder<'a> { - let handler = &sess.diagnostic().handler; - let config = ArchiveConfig { - handler: handler, - dst: out_filename.clone(), - lib_search_paths: archive_search_paths(sess), - slib_prefix: sess.target.target.options.staticlib_prefix.clone(), - slib_suffix: sess.target.target.options.staticlib_suffix.clone(), - maybe_ar_prog: sess.opts.cg.ar.clone() - }; - let mut ab = ArchiveBuilder::create(config); - ab.add_file(obj_filename).unwrap(); - - for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() { - match kind { - cstore::NativeStatic => { - ab.add_native_library(l.as_slice()).unwrap(); - } - cstore::NativeFramework | cstore::NativeUnknown => {} - } - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. - ab.update_symbols(); - - let mut ab = match sess.target.target.options.is_like_osx { - // For OSX/iOS, we must be careful to update symbols only when adding - // object files. We're about to start adding non-object files, so run - // `ar` now to process the object files. - true => ab.build().extend(), - false => ab, - }; - - // Note that it is important that we add all of our non-object "magical - // files" *after* all of the object files in the archive. The reason for - // this is as follows: - // - // * When performing LTO, this archive will be modified to remove - // obj_filename from above. The reason for this is described below. - // - // * When the system linker looks at an archive, it will attempt to - // determine the architecture of the archive in order to see whether its - // linkable. - // - // The algorithm for this detection is: iterate over the files in the - // archive. Skip magical SYMDEF names. Interpret the first file as an - // object file. Read architecture from the object file. - // - // * As one can probably see, if "metadata" and "foo.bc" were placed - // before all of the objects, then the architecture of this archive would - // not be correctly inferred once 'foo.o' is removed. - // - // Basically, all this means is that this code should not move above the - // code above. - match trans { - Some(trans) => { - // Instead of putting the metadata in an object file section, rlibs - // contain the metadata in a separate file. We use a temp directory - // here so concurrent builds in the same directory don't try to use - // the same filename for metadata (stomping over one another) - let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); - let metadata = tmpdir.path().join(METADATA_FILENAME); - match fs::File::create(&metadata).write(trans.metadata - .as_slice()) { - Ok(..) => {} - Err(e) => { - sess.err(format!("failed to write {}: {}", - metadata.display(), - e).as_slice()); - sess.abort_if_errors(); - } - } - ab.add_file(&metadata).unwrap(); - remove(sess, &metadata); - - // For LTO purposes, the bytecode of this library is also inserted - // into the archive. If codegen_units > 1, we insert each of the - // bitcode files. - for i in range(0, sess.opts.cg.codegen_units) { - // Note that we make sure that the bytecode filename in the - // archive is never exactly 16 bytes long by adding a 16 byte - // extension to it. This is to work around a bug in LLDB that - // would cause it to crash if the name of a file in an archive - // was exactly 16 bytes. - let bc_filename = obj_filename.with_extension(format!("{}.bc", i).as_slice()); - let bc_deflated_filename = obj_filename.with_extension( - format!("{}.bytecode.deflate", i).as_slice()); - - let bc_data = match fs::File::open(&bc_filename).read_to_end() { - Ok(buffer) => buffer, - Err(e) => sess.fatal(format!("failed to read bytecode: {}", - e).as_slice()) - }; - - let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) { - Some(compressed) => compressed, - None => sess.fatal(format!("failed to compress bytecode from {}", - bc_filename.display()).as_slice()) - }; - - let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { - Ok(file) => file, - Err(e) => { - sess.fatal(format!("failed to create compressed bytecode \ - file: {}", e).as_slice()) - } - }; - - match write_rlib_bytecode_object_v1(&mut bc_file_deflated, - bc_data_deflated.as_slice()) { - Ok(()) => {} - Err(e) => { - sess.err(format!("failed to write compressed bytecode: \ - {}", e).as_slice()); - sess.abort_if_errors() - } - }; - - ab.add_file(&bc_deflated_filename).unwrap(); - remove(sess, &bc_deflated_filename); - - // See the bottom of back::write::run_passes for an explanation - // of when we do and don't keep .0.bc files around. - let user_wants_numbered_bitcode = - sess.opts.output_types.contains(&OutputTypeBitcode) && - sess.opts.cg.codegen_units > 1; - if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { - remove(sess, &bc_filename); - } - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. This currently dies on OSX (see - // #11162), and isn't necessary there anyway - if !sess.target.target.options.is_like_osx { - ab.update_symbols(); - } - } - - None => {} - } - - ab -} - -fn write_rlib_bytecode_object_v1(writer: &mut T, - bc_data_deflated: &[u8]) - -> ::std::io::IoResult<()> { - let bc_data_deflated_size: u64 = bc_data_deflated.as_slice().len() as u64; - - try! { writer.write(RLIB_BYTECODE_OBJECT_MAGIC) }; - try! { writer.write_le_u32(1) }; - try! { writer.write_le_u64(bc_data_deflated_size) }; - try! { writer.write(bc_data_deflated.as_slice()) }; - - let number_of_bytes_written_so_far = - RLIB_BYTECODE_OBJECT_MAGIC.len() + // magic id - mem::size_of_val(&RLIB_BYTECODE_OBJECT_VERSION) + // version - mem::size_of_val(&bc_data_deflated_size) + // data size field - bc_data_deflated_size as uint; // actual data - - // If the number of bytes written to the object so far is odd, add a - // padding byte to make it even. This works around a crash bug in LLDB - // (see issue #15950) - if number_of_bytes_written_so_far % 2 == 1 { - try! { writer.write_u8(0) }; - } - - return Ok(()); -} - -// Create a static archive -// -// This is essentially the same thing as an rlib, but it also involves adding -// all of the upstream crates' objects into the archive. This will slurp in -// all of the native libraries of upstream dependencies as well. -// -// Additionally, there's no way for us to link dynamic libraries, so we warn -// about all dynamic library dependencies that they're not linked in. -// -// There's no need to include metadata in a static archive, so ensure to not -// link in the metadata object file (and also don't prepare the archive with a -// metadata file). -fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) { - let ab = link_rlib(sess, None, obj_filename, out_filename); - let mut ab = match sess.target.target.options.is_like_osx { - true => ab.build().extend(), - false => ab, - }; - if sess.target.target.options.morestack { - ab.add_native_library("morestack").unwrap(); - } - if !sess.target.target.options.no_compiler_rt { - ab.add_native_library("compiler-rt").unwrap(); - } - - let crates = sess.cstore.get_used_crates(cstore::RequireStatic); - let mut all_native_libs = vec![]; - - for &(cnum, ref path) in crates.iter() { - let ref name = sess.cstore.get_crate_data(cnum).name; - let p = match *path { - Some(ref p) => p.clone(), None => { - sess.err(format!("could not find rlib for: `{}`", - name).as_slice()); - continue - } - }; - ab.add_rlib(&p, name.as_slice(), sess.lto()).unwrap(); - - let native_libs = csearch::get_native_libraries(&sess.cstore, cnum); - all_native_libs.extend(native_libs.into_iter()); - } - - ab.update_symbols(); - let _ = ab.build(); - - if !all_native_libs.is_empty() { - sess.warn("link against the following native artifacts when linking against \ - this static library"); - sess.note("the order and any duplication can be significant on some platforms, \ - and so may need to be preserved"); - } - - for &(kind, ref lib) in all_native_libs.iter() { - let name = match kind { - cstore::NativeStatic => "static library", - cstore::NativeUnknown => "library", - cstore::NativeFramework => "framework", - }; - sess.note(format!("{}: {}", name, *lib).as_slice()); - } -} - -// Create a dynamic library or executable -// -// This will invoke the system linker/cc to create the resulting file. This -// links to all upstream files as well. -fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, - obj_filename: &Path, out_filename: &Path) { - let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); - - // The invocations of cc share some flags across platforms - let pname = get_cc_prog(sess); - let mut cmd = Command::new(pname.as_slice()); - - cmd.args(sess.target.target.options.pre_link_args.as_slice()); - link_args(&mut cmd, sess, dylib, tmpdir.path(), - trans, obj_filename, out_filename); - cmd.args(sess.target.target.options.post_link_args.as_slice()); - if !sess.target.target.options.no_compiler_rt { - cmd.arg("-lcompiler-rt"); - } - - if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { - println!("{}", &cmd); - } - - // May have not found libraries in the right formats. - sess.abort_if_errors(); - - // Invoke the system linker - debug!("{}", &cmd); - let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output()); - match prog { - Ok(prog) => { - if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", - pname, - prog.status).as_slice()); - sess.note(format!("{}", &cmd).as_slice()); - let mut output = prog.error.clone(); - output.push_all(prog.output.as_slice()); - sess.note(str::from_utf8(output.as_slice()).unwrap()); - sess.abort_if_errors(); - } - debug!("linker stderr:\n{}", String::from_utf8(prog.error).unwrap()); - debug!("linker stdout:\n{}", String::from_utf8(prog.output).unwrap()); - }, - Err(e) => { - sess.err(format!("could not exec the linker `{}`: {}", - pname, - e).as_slice()); - sess.abort_if_errors(); - } - } - - - // On OSX, debuggers need this utility to get run to do some munging of - // the symbols - if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo { - match Command::new("dsymutil").arg(out_filename).output() { - Ok(..) => {} - Err(e) => { - sess.err(format!("failed to run dsymutil: {}", e).as_slice()); - sess.abort_if_errors(); - } - } - } -} - -fn link_args(cmd: &mut Command, - sess: &Session, - dylib: bool, - tmpdir: &Path, - trans: &CrateTranslation, - obj_filename: &Path, - out_filename: &Path) { - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch().get_lib_path(); - - // target descriptor - let t = &sess.target.target; - - cmd.arg("-L").arg(&lib_path); - - cmd.arg("-o").arg(out_filename).arg(obj_filename); - - - // Stack growth requires statically linking a __morestack function. Note - // that this is listed *before* all other libraries. Due to the usage of the - // --as-needed flag below, the standard library may only be useful for its - // rust_stack_exhausted function. In this case, we must ensure that the - // libmorestack.a file appears *before* the standard library (so we put it - // at the very front). - // - // Most of the time this is sufficient, except for when LLVM gets super - // clever. If, for example, we have a main function `fn main() {}`, LLVM - // will optimize out calls to `__morestack` entirely because the function - // doesn't need any stack at all! - // - // To get around this snag, we specially tell the linker to always include - // all contents of this library. This way we're guaranteed that the linker - // will include the __morestack symbol 100% of the time, always resolving - // references to it even if the object above didn't use it. - if t.options.morestack { - if t.options.is_like_osx { - let morestack = lib_path.join("libmorestack.a"); - - let mut v = b"-Wl,-force_load,".to_vec(); - v.push_all(morestack.as_vec()); - cmd.arg(v.as_slice()); - } else { - cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]); - } - } - - // When linking a dynamic library, we put the metadata into a section of the - // executable. This metadata is in a separate object file from the main - // object file, so we link that in here. - if dylib { - cmd.arg(obj_filename.with_extension("metadata.o")); - } - - if t.options.is_like_osx { - // The dead_strip option to the linker specifies that functions and data - // unreachable by the entry point will be removed. This is quite useful - // with Rust's compilation model of compiling libraries at a time into - // one object file. For example, this brings hello world from 1.7MB to - // 458K. - // - // Note that this is done for both executables and dynamic libraries. We - // won't get much benefit from dylibs because LLVM will have already - // stripped away as much as it could. This has not been seen to impact - // link times negatively. - // - // -dead_strip can't be part of the pre_link_args because it's also used for partial - // linking when using multiple codegen units (-r). So we insert it here. - cmd.arg("-Wl,-dead_strip"); - } - - // If we're building a dylib, we don't use --gc-sections because LLVM has - // already done the best it can do, and we also don't want to eliminate the - // metadata. If we're building an executable, however, --gc-sections drops - // the size of hello world from 1.8MB to 597K, a 67% reduction. - if !dylib && !t.options.is_like_osx { - cmd.arg("-Wl,--gc-sections"); - } - - let used_link_args = sess.cstore.get_used_link_args().borrow(); - - if t.options.position_independent_executables { - let empty_vec = Vec::new(); - let empty_str = String::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let mut args = args.iter().chain(used_link_args.iter()); - if !dylib - && (t.options.relocation_model.as_slice() == "pic" - || sess.opts.cg.relocation_model.as_ref() - .unwrap_or(&empty_str).as_slice() == "pic") - && !args.any(|x| x.as_slice() == "-static") { - cmd.arg("-pie"); - } - } - - if t.options.linker_is_gnu { - // GNU-style linkers support optimization with -O. GNU ld doesn't need a - // numeric argument, but other linkers do. - if sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive { - cmd.arg("-Wl,-O1"); - } - } - - // We want to prevent the compiler from accidentally leaking in any system - // libraries, so we explicitly ask gcc to not link to any libraries by - // default. Note that this does not happen for windows because windows pulls - // in some large number of libraries and I couldn't quite figure out which - // subset we wanted. - if !t.options.is_like_windows { - cmd.arg("-nodefaultlibs"); - } - - // Mark all dynamic libraries and executables as compatible with ASLR - // FIXME #17098: ASLR breaks gdb - if t.options.is_like_windows && sess.opts.debuginfo == NoDebugInfo { - // cmd.arg("-Wl,--dynamicbase"); - } - - // Take careful note of the ordering of the arguments we pass to the linker - // here. Linkers will assume that things on the left depend on things to the - // right. Things on the right cannot depend on things on the left. This is - // all formally implemented in terms of resolving symbols (libs on the right - // resolve unknown symbols of libs on the left, but not vice versa). - // - // For this reason, we have organized the arguments we pass to the linker as - // such: - // - // 1. The local object that LLVM just generated - // 2. Upstream rust libraries - // 3. Local native libraries - // 4. Upstream native libraries - // - // This is generally fairly natural, but some may expect 2 and 3 to be - // swapped. The reason that all native libraries are put last is that it's - // not recommended for a native library to depend on a symbol from a rust - // crate. If this is the case then a staticlib crate is recommended, solving - // the problem. - // - // Additionally, it is occasionally the case that upstream rust libraries - // depend on a local native library. In the case of libraries such as - // lua/glfw/etc the name of the library isn't the same across all platforms, - // so only the consumer crate of a library knows the actual name. This means - // that downstream crates will provide the #[link] attribute which upstream - // crates will depend on. Hence local native libraries are after out - // upstream rust crates. - // - // In theory this means that a symbol in an upstream native library will be - // shadowed by a local native library when it wouldn't have been before, but - // this kind of behavior is pretty platform specific and generally not - // recommended anyway, so I don't think we're shooting ourself in the foot - // much with that. - add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans); - add_local_native_libraries(cmd, sess); - add_upstream_native_libraries(cmd, sess); - - // # Telling the linker what we're doing - - if dylib { - // On mac we need to tell the linker to let this library be rpathed - if sess.target.target.options.is_like_osx { - cmd.args(&["-dynamiclib", "-Wl,-dylib"]); - - if sess.opts.cg.rpath { - let mut v = "-Wl,-install_name,@rpath/".as_bytes().to_vec(); - v.push_all(out_filename.filename().unwrap()); - cmd.arg(v.as_slice()); - } - } else { - cmd.arg("-shared"); - } - } - - // FIXME (#2397): At some point we want to rpath our guesses as to - // where extern libraries might live, based on the - // addl_lib_search_paths - if sess.opts.cg.rpath { - let sysroot = sess.sysroot(); - let target_triple = sess.opts.target_triple.as_slice(); - let get_install_prefix_lib_path = || { - let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); - let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); - let mut path = Path::new(install_prefix); - path.push(&tlib); - - path - }; - let rpath_config = RPathConfig { - used_crates: sess.cstore.get_used_crates(cstore::RequireDynamic), - out_filename: out_filename.clone(), - has_rpath: sess.target.target.options.has_rpath, - is_like_osx: sess.target.target.options.is_like_osx, - get_install_prefix_lib_path: get_install_prefix_lib_path, - realpath: ::util::fs::realpath - }; - cmd.args(rpath::get_rpath_flags(rpath_config).as_slice()); - } - - // Finally add all the linker arguments provided on the command line along - // with any #[link_args] attributes found inside the crate - let empty = Vec::new(); - cmd.args(sess.opts.cg.link_args.as_ref().unwrap_or(&empty).as_slice()); - cmd.args(used_link_args.as_slice()); -} - -// # Native library linking -// -// User-supplied library search paths (-L on the command line). These are -// the same paths used to find Rust crates, so some of them may have been -// added already by the previous crate linking code. This only allows them -// to be found at compile time so it is still entirely up to outside -// forces to make sure that library can be found at runtime. -// -// Also note that the native libraries linked here are only the ones located -// in the current crate. Upstream crates with native library dependencies -// may have their native library pulled in above. -fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { - for path in sess.opts.addl_lib_search_paths.borrow().iter() { - cmd.arg("-L").arg(path); - } - - let rustpath = filesearch::rust_path(); - for path in rustpath.iter() { - cmd.arg("-L").arg(path); - } - - // Some platforms take hints about whether a library is static or dynamic. - // For those that support this, we ensure we pass the option if the library - // was flagged "static" (most defaults are dynamic) to ensure that if - // libfoo.a and libfoo.so both exist that the right one is chosen. - let takes_hints = !sess.target.target.options.is_like_osx; - - let libs = sess.cstore.get_used_libraries(); - let libs = libs.borrow(); - - let mut staticlibs = libs.iter().filter_map(|&(ref l, kind)| { - if kind == cstore::NativeStatic {Some(l)} else {None} - }); - let mut others = libs.iter().filter(|&&(_, kind)| { - kind != cstore::NativeStatic - }); - - // Platforms that take hints generally also support the --whole-archive - // flag. We need to pass this flag when linking static native libraries to - // ensure the entire library is included. - // - // For more details see #15460, but the gist is that the linker will strip - // away any unused objects in the archive if we don't otherwise explicitly - // reference them. This can occur for libraries which are just providing - // bindings, libraries with generic functions, etc. - if takes_hints { - cmd.arg("-Wl,--whole-archive").arg("-Wl,-Bstatic"); - } - let search_path = archive_search_paths(sess); - for l in staticlibs { - if takes_hints { - cmd.arg(format!("-l{}", l)); - } else { - // -force_load is the OSX equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - let lib = archive::find_library(l.as_slice(), - sess.target.target.options.staticlib_prefix.as_slice(), - sess.target.target.options.staticlib_suffix.as_slice(), - search_path.as_slice(), - &sess.diagnostic().handler); - let mut v = b"-Wl,-force_load,".to_vec(); - v.push_all(lib.as_vec()); - cmd.arg(v.as_slice()); - } - } - if takes_hints { - cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic"); - } - - for &(ref l, kind) in others { - match kind { - cstore::NativeUnknown => { - cmd.arg(format!("-l{}", l)); - } - cstore::NativeFramework => { - cmd.arg("-framework").arg(l.as_slice()); - } - cstore::NativeStatic => unreachable!(), - } - } -} - -// # Rust Crate linking -// -// Rust crates are not considered at all when creating an rlib output. All -// dependencies will be linked when producing the final output (instead of -// the intermediate rlib version) -fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, - dylib: bool, tmpdir: &Path, - trans: &CrateTranslation) { - // All of the heavy lifting has previously been accomplished by the - // dependency_format module of the compiler. This is just crawling the - // output of that module, adding crates as necessary. - // - // Linking to a rlib involves just passing it to the linker (the linker - // will slurp up the object files inside), and linking to a dynamic library - // involves just passing the right -l flag. - - let data = if dylib { - &trans.crate_formats[config::CrateTypeDylib] - } else { - &trans.crate_formats[config::CrateTypeExecutable] - }; - - // Invoke get_used_crates to ensure that we get a topological sorting of - // crates. - let deps = sess.cstore.get_used_crates(cstore::RequireDynamic); - - for &(cnum, _) in deps.iter() { - // We may not pass all crates through to the linker. Some crates may - // appear statically in an existing dylib, meaning we'll pick up all the - // symbols from the dylib. - let kind = match data[cnum as uint - 1] { - Some(t) => t, - None => continue - }; - let src = sess.cstore.get_used_crate_source(cnum).unwrap(); - match kind { - cstore::RequireDynamic => { - add_dynamic_crate(cmd, sess, src.dylib.unwrap()) - } - cstore::RequireStatic => { - add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap()) - } - } - - } - - // Converts a library file-stem into a cc -l argument - fn unlib<'a>(config: &config::Config, stem: &'a [u8]) -> &'a [u8] { - if stem.starts_with("lib".as_bytes()) && !config.target.options.is_like_windows { - stem[3..] - } else { - stem - } - } - - // Adds the static "rlib" versions of all crates to the command line. - fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path, - cratepath: Path) { - // When performing LTO on an executable output, all of the - // bytecode from the upstream libraries has already been - // included in our object file output. We need to modify all of - // the upstream archives to remove their corresponding object - // file to make sure we don't pull the same code in twice. - // - // We must continue to link to the upstream archives to be sure - // to pull in native static dependencies. As the final caveat, - // on Linux it is apparently illegal to link to a blank archive, - // so if an archive no longer has any object files in it after - // we remove `lib.o`, then don't link against it at all. - // - // If we're not doing LTO, then our job is simply to just link - // against the archive. - if sess.lto() { - let name = cratepath.filename_str().unwrap(); - let name = name.slice(3, name.len() - 5); // chop off lib/.rlib - time(sess.time_passes(), - format!("altering {}.rlib", name).as_slice(), - (), |()| { - let dst = tmpdir.join(cratepath.filename().unwrap()); - match fs::copy(&cratepath, &dst) { - Ok(..) => {} - Err(e) => { - sess.err(format!("failed to copy {} to {}: {}", - cratepath.display(), - dst.display(), - e).as_slice()); - sess.abort_if_errors(); - } - } - // Fix up permissions of the copy, as fs::copy() preserves - // permissions, but the original file may have been installed - // by a package manager and may be read-only. - match fs::chmod(&dst, io::USER_READ | io::USER_WRITE) { - Ok(..) => {} - Err(e) => { - sess.err(format!("failed to chmod {} when preparing \ - for LTO: {}", dst.display(), - e).as_slice()); - sess.abort_if_errors(); - } - } - let handler = &sess.diagnostic().handler; - let config = ArchiveConfig { - handler: handler, - dst: dst.clone(), - lib_search_paths: archive_search_paths(sess), - slib_prefix: sess.target.target.options.staticlib_prefix.clone(), - slib_suffix: sess.target.target.options.staticlib_suffix.clone(), - maybe_ar_prog: sess.opts.cg.ar.clone() - }; - let mut archive = Archive::open(config); - archive.remove_file(format!("{}.o", name).as_slice()); - let files = archive.files(); - if files.iter().any(|s| s.as_slice().ends_with(".o")) { - cmd.arg(dst); - } - }); - } else { - cmd.arg(cratepath); - } - } - - // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: Path) { - // If we're performing LTO, then it should have been previously required - // that all upstream rust dependencies were available in an rlib format. - assert!(!sess.lto()); - - // Just need to tell the linker about where the library lives and - // what its name is - let dir = cratepath.dirname(); - if !dir.is_empty() { cmd.arg("-L").arg(dir); } - - let mut v = "-l".as_bytes().to_vec(); - v.push_all(unlib(&sess.target, cratepath.filestem().unwrap())); - cmd.arg(v.as_slice()); - } -} - -// Link in all of our upstream crates' native dependencies. Remember that -// all of these upstream native dependencies are all non-static -// dependencies. We've got two cases then: -// -// 1. The upstream crate is an rlib. In this case we *must* link in the -// native dependency because the rlib is just an archive. -// -// 2. The upstream crate is a dylib. In order to use the dylib, we have to -// have the dependency present on the system somewhere. Thus, we don't -// gain a whole lot from not linking in the dynamic dependency to this -// crate as well. -// -// The use case for this is a little subtle. In theory the native -// dependencies of a crate are purely an implementation detail of the crate -// itself, but the problem arises with generic and inlined functions. If a -// generic function calls a native function, then the generic function must -// be instantiated in the target crate, meaning that the native symbol must -// also be resolved in the target crate. -fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { - // Be sure to use a topological sorting of crates because there may be - // interdependencies between native libraries. When passing -nodefaultlibs, - // for example, almost all native libraries depend on libc, so we have to - // make sure that's all the way at the right (liblibc is near the base of - // the dependency chain). - // - // This passes RequireStatic, but the actual requirement doesn't matter, - // we're just getting an ordering of crate numbers, we're not worried about - // the paths. - let crates = sess.cstore.get_used_crates(cstore::RequireStatic); - for (cnum, _) in crates.into_iter() { - let libs = csearch::get_native_libraries(&sess.cstore, cnum); - for &(kind, ref lib) in libs.iter() { - match kind { - cstore::NativeUnknown => { - cmd.arg(format!("-l{}", *lib)); - } - cstore::NativeFramework => { - cmd.arg("-framework"); - cmd.arg(lib.as_slice()); - } - cstore::NativeStatic => { - sess.bug("statics shouldn't be propagated"); - } - } - } - } -} diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs deleted file mode 100644 index 3fbf830485b..00000000000 --- a/src/librustc/back/lto.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::link; -use super::write; -use driver::session; -use driver::config; -use llvm; -use llvm::archive_ro::ArchiveRO; -use llvm::{ModuleRef, TargetMachineRef, True, False}; -use metadata::cstore; -use util::common::time; - -use libc; -use flate; - -use std::iter; -use std::mem; -use std::num::Int; - -pub fn run(sess: &session::Session, llmod: ModuleRef, - tm: TargetMachineRef, reachable: &[String]) { - if sess.opts.cg.prefer_dynamic { - sess.err("cannot prefer dynamic linking when performing LTO"); - sess.note("only 'staticlib' and 'bin' outputs are supported with LTO"); - sess.abort_if_errors(); - } - - // Make sure we actually can run LTO - for crate_type in sess.crate_types.borrow().iter() { - match *crate_type { - config::CrateTypeExecutable | config::CrateTypeStaticlib => {} - _ => { - sess.fatal("lto can only be run for executables and \ - static library outputs"); - } - } - } - - // For each of our upstream dependencies, find the corresponding rlib and - // load the bitcode from the archive. Then merge it into the current LLVM - // module that we've got. - let crates = sess.cstore.get_used_crates(cstore::RequireStatic); - for (cnum, path) in crates.into_iter() { - let name = sess.cstore.get_crate_data(cnum).name.clone(); - let path = match path { - Some(p) => p, - None => { - sess.fatal(format!("could not find rlib for: `{}`", - name).as_slice()); - } - }; - - let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let file = path.filename_str().unwrap(); - let file = file.slice(3, file.len() - 5); // chop off lib/.rlib - debug!("reading {}", file); - for i in iter::count(0u, 1) { - let bc_encoded = time(sess.time_passes(), - format!("check for {}.{}.bytecode.deflate", name, i).as_slice(), - (), - |_| { - archive.read(format!("{}.{}.bytecode.deflate", - file, i).as_slice()) - }); - let bc_encoded = match bc_encoded { - Some(data) => data, - None => { - if i == 0 { - // No bitcode was found at all. - sess.fatal(format!("missing compressed bytecode in {}", - path.display()).as_slice()); - } - // No more bitcode files to read. - break; - }, - }; - let bc_extractor = if is_versioned_bytecode_format(bc_encoded) { - |_| { - // Read the version - let version = extract_bytecode_format_version(bc_encoded); - - if version == 1 { - // The only version existing so far - let data_size = extract_compressed_bytecode_size_v1(bc_encoded); - let compressed_data = bc_encoded[ - link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET.. - link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint]; - - match flate::inflate_bytes(compressed_data) { - Some(inflated) => inflated, - None => { - sess.fatal(format!("failed to decompress bc of `{}`", - name).as_slice()) - } - } - } else { - sess.fatal(format!("Unsupported bytecode format version {}", - version).as_slice()) - } - } - } else { - // the object must be in the old, pre-versioning format, so simply - // inflate everything and let LLVM decide if it can make sense of it - |_| { - match flate::inflate_bytes(bc_encoded) { - Some(bc) => bc, - None => { - sess.fatal(format!("failed to decompress bc of `{}`", - name).as_slice()) - } - } - } - }; - - let bc_decoded = time(sess.time_passes(), - format!("decode {}.{}.bc", file, i).as_slice(), - (), - bc_extractor); - - let ptr = bc_decoded.as_slice().as_ptr(); - debug!("linking {}, part {}", name, i); - time(sess.time_passes(), - format!("ll link {}.{}", name, i).as_slice(), - (), - |()| unsafe { - if !llvm::LLVMRustLinkInExternalBitcode(llmod, - ptr as *const libc::c_char, - bc_decoded.len() as libc::size_t) { - write::llvm_err(sess.diagnostic().handler(), - format!("failed to load bc of `{}`", - name.as_slice())); - } - }); - } - } - - // Internalize everything but the reachable symbols of the current module - let cstrs: Vec<::std::c_str::CString> = - reachable.iter().map(|s| s.as_slice().to_c_str()).collect(); - let arr: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect(); - let ptr = arr.as_ptr(); - unsafe { - llvm::LLVMRustRunRestrictionPass(llmod, - ptr as *const *const libc::c_char, - arr.len() as libc::size_t); - } - - if sess.no_landing_pads() { - unsafe { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - } - } - - // Now we have one massive module inside of llmod. Time to run the - // LTO-specific optimization passes that LLVM provides. - // - // This code is based off the code found in llvm's LTO code generator: - // tools/lto/LTOCodeGenerator.cpp - debug!("running the pass manager"); - unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); - "verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s)); - - let builder = llvm::LLVMPassManagerBuilderCreate(); - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm, - /* Internalize = */ False, - /* RunInliner = */ True); - llvm::LLVMPassManagerBuilderDispose(builder); - - "verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s)); - - time(sess.time_passes(), "LTO passes", (), |()| - llvm::LLVMRunPassManager(pm, llmod)); - - llvm::LLVMDisposePassManager(pm); - } - debug!("lto done"); -} - -fn is_versioned_bytecode_format(bc: &[u8]) -> bool { - let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len(); - return bc.len() > magic_id_byte_count && - bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC; -} - -fn extract_bytecode_format_version(bc: &[u8]) -> u32 { - return read_from_le_bytes::(bc, link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET); -} - -fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 { - return read_from_le_bytes::(bc, link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET); -} - -fn read_from_le_bytes(bytes: &[u8], position_in_bytes: uint) -> T { - let byte_data = bytes[position_in_bytes.. - position_in_bytes + mem::size_of::()]; - let data = unsafe { - *(byte_data.as_ptr() as *const T) - }; - - Int::from_le(data) -} - diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs deleted file mode 100644 index 19ddb844bec..00000000000 --- a/src/librustc/back/write.rs +++ /dev/null @@ -1,1053 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use self::OutputType::*; - -use back::lto; -use back::link::{get_cc_prog, remove}; -use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames}; -use driver::config::{NoDebugInfo, Passes, SomePasses, AllPasses}; -use driver::session::Session; -use driver::config; -use llvm; -use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; -use llvm::SMDiagnosticRef; -use util::common::time; -use syntax::codemap; -use syntax::diagnostic; -use syntax::diagnostic::{Emitter, Handler, Level, mk_handler}; - -use std::c_str::{ToCStr, CString}; -use std::io::Command; -use std::io::fs; -use std::iter::Unfold; -use std::ptr; -use std::str; -use std::mem; -use std::sync::{Arc, Mutex}; -use std::task::TaskBuilder; -use libc::{c_uint, c_int, c_void}; - -#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] -pub enum OutputType { - OutputTypeBitcode, - OutputTypeAssembly, - OutputTypeLlvmAssembly, - OutputTypeObject, - OutputTypeExe, -} - -pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! { - unsafe { - let cstr = llvm::LLVMRustGetLastError(); - if cstr == ptr::null() { - handler.fatal(msg.as_slice()); - } else { - let err = CString::new(cstr, true); - let err = String::from_utf8_lossy(err.as_bytes()); - handler.fatal(format!("{}: {}", - msg.as_slice(), - err.as_slice()).as_slice()); - } - } -} - -pub fn write_output_file( - handler: &diagnostic::Handler, - target: llvm::TargetMachineRef, - pm: llvm::PassManagerRef, - m: ModuleRef, - output: &Path, - file_type: llvm::FileType) { - unsafe { - output.with_c_str(|output| { - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output, file_type); - if !result { - llvm_err(handler, "could not write output".to_string()); - } - }) - } -} - - -struct Diagnostic { - msg: String, - code: Option, - lvl: Level, -} - -// We use an Arc instead of just returning a list of diagnostics from the -// child task because we need to make sure that the messages are seen even -// if the child task panics (for example, when `fatal` is called). -#[deriving(Clone)] -struct SharedEmitter { - buffer: Arc>>, -} - -impl SharedEmitter { - fn new() -> SharedEmitter { - SharedEmitter { - buffer: Arc::new(Mutex::new(Vec::new())), - } - } - - fn dump(&mut self, handler: &Handler) { - let mut buffer = self.buffer.lock(); - for diag in buffer.iter() { - match diag.code { - Some(ref code) => { - handler.emit_with_code(None, - diag.msg.as_slice(), - code.as_slice(), - diag.lvl); - }, - None => { - handler.emit(None, - diag.msg.as_slice(), - diag.lvl); - }, - } - } - buffer.clear(); - } -} - -impl Emitter for SharedEmitter { - fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>, - msg: &str, code: Option<&str>, lvl: Level) { - assert!(cmsp.is_none(), "SharedEmitter doesn't support spans"); - - self.buffer.lock().push(Diagnostic { - msg: msg.to_string(), - code: code.map(|s| s.to_string()), - lvl: lvl, - }); - } - - fn custom_emit(&mut self, _cm: &codemap::CodeMap, - _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) { - panic!("SharedEmitter doesn't support custom_emit"); - } -} - - -// On android, we by default compile for armv7 processors. This enables -// things like double word CAS instructions (rather than emulating them) -// which are *far* more efficient. This is obviously undesirable in some -// cases, so if any sort of target feature is specified we don't append v7 -// to the feature list. -// -// On iOS only armv7 and newer are supported. So it is useful to -// get all hardware potential via VFP3 (hardware floating point) -// and NEON (SIMD) instructions supported by LLVM. -// Note that without those flags various linking errors might -// arise as some of intrinsics are converted into function calls -// and nobody provides implementations those functions -fn target_feature(sess: &Session) -> String { - format!("{},{}", sess.target.target.options.features, sess.opts.cg.target_feature) -} - -fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { - match optimize { - config::No => llvm::CodeGenLevelNone, - config::Less => llvm::CodeGenLevelLess, - config::Default => llvm::CodeGenLevelDefault, - config::Aggressive => llvm::CodeGenLevelAggressive, - } -} - -fn create_target_machine(sess: &Session) -> TargetMachineRef { - let reloc_model_arg = match sess.opts.cg.relocation_model { - Some(ref s) => s.as_slice(), - None => sess.target.target.options.relocation_model.as_slice() - }; - let reloc_model = match reloc_model_arg { - "pic" => llvm::RelocPIC, - "static" => llvm::RelocStatic, - "default" => llvm::RelocDefault, - "dynamic-no-pic" => llvm::RelocDynamicNoPic, - _ => { - sess.err(format!("{} is not a valid relocation mode", - sess.opts - .cg - .relocation_model).as_slice()); - sess.abort_if_errors(); - unreachable!(); - } - }; - - let opt_level = get_llvm_opt_level(sess.opts.optimize); - let use_softfp = sess.opts.cg.soft_float; - - // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. - let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || - !sess.target.target.options.eliminate_frame_pointer; - - let any_library = sess.crate_types.borrow().iter().any(|ty| { - *ty != config::CrateTypeExecutable - }); - - let ffunction_sections = sess.target.target.options.function_sections; - let fdata_sections = ffunction_sections; - - let code_model_arg = match sess.opts.cg.code_model { - Some(ref s) => s.as_slice(), - None => sess.target.target.options.code_model.as_slice() - }; - - let code_model = match code_model_arg { - "default" => llvm::CodeModelDefault, - "small" => llvm::CodeModelSmall, - "kernel" => llvm::CodeModelKernel, - "medium" => llvm::CodeModelMedium, - "large" => llvm::CodeModelLarge, - _ => { - sess.err(format!("{} is not a valid code model", - sess.opts - .cg - .code_model).as_slice()); - sess.abort_if_errors(); - unreachable!(); - } - }; - - let triple = sess.target.target.llvm_target.as_slice(); - - let tm = unsafe { - triple.with_c_str(|t| { - let cpu = match sess.opts.cg.target_cpu { - Some(ref s) => s.as_slice(), - None => sess.target.target.options.cpu.as_slice() - }; - cpu.with_c_str(|cpu| { - target_feature(sess).with_c_str(|features| { - llvm::LLVMRustCreateTargetMachine( - t, cpu, features, - code_model, - reloc_model, - opt_level, - true /* EnableSegstk */, - use_softfp, - no_fp_elim, - !any_library && reloc_model == llvm::RelocPIC, - ffunction_sections, - fdata_sections, - ) - }) - }) - }) - }; - - if tm.is_null() { - llvm_err(sess.diagnostic().handler(), - format!("Could not create LLVM TargetMachine for triple: {}", - triple).to_string()); - } else { - return tm; - }; -} - - -/// Module-specific configuration for `optimize_and_codegen`. -#[deriving(Clone)] -struct ModuleConfig { - /// LLVM TargetMachine to use for codegen. - tm: TargetMachineRef, - /// Names of additional optimization passes to run. - passes: Vec, - /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). - opt_level: Option, - - // Flags indicating which outputs to produce. - emit_no_opt_bc: bool, - emit_bc: bool, - emit_lto_bc: bool, - emit_ir: bool, - emit_asm: bool, - emit_obj: bool, - - // Miscellaneous flags. These are mostly copied from command-line - // options. - no_verify: bool, - no_prepopulate_passes: bool, - no_builtins: bool, - time_passes: bool, -} - -impl ModuleConfig { - fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { - ModuleConfig { - tm: tm, - passes: passes, - opt_level: None, - - emit_no_opt_bc: false, - emit_bc: false, - emit_lto_bc: false, - emit_ir: false, - emit_asm: false, - emit_obj: false, - - no_verify: false, - no_prepopulate_passes: false, - no_builtins: false, - time_passes: false, - } - } - - fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { - self.no_verify = sess.no_verify(); - self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = trans.no_builtins; - self.time_passes = sess.time_passes(); - } -} - -/// Additional resources used by optimize_and_codegen (not module specific) -struct CodegenContext<'a> { - // Extra resources used for LTO: (sess, reachable). This will be `None` - // when running in a worker thread. - lto_ctxt: Option<(&'a Session, &'a [String])>, - // Handler to use for diagnostics produced during codegen. - handler: &'a Handler, - // LLVM optimizations for which we want to print remarks. - remark: Passes, -} - -impl<'a> CodegenContext<'a> { - fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> { - CodegenContext { - lto_ctxt: Some((sess, reachable)), - handler: sess.diagnostic().handler(), - remark: sess.opts.cg.remark.clone(), - } - } -} - -struct HandlerFreeVars<'a> { - llcx: ContextRef, - cgcx: &'a CodegenContext<'a>, -} - -unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, - user: *const c_void, - cookie: c_uint) { - use syntax::codemap::ExpnId; - - let HandlerFreeVars { cgcx, .. } - = *mem::transmute::<_, *const HandlerFreeVars>(user); - - let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); - - match cgcx.lto_ctxt { - Some((sess, _)) => { - sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info { - Some(ei) => sess.span_err(ei.call_site, msg.as_slice()), - None => sess.err(msg.as_slice()), - }); - } - - None => { - cgcx.handler.err(msg.as_slice()); - cgcx.handler.note("build without -C codegen-units for more exact errors"); - } - } -} - -unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - let HandlerFreeVars { llcx, cgcx } - = *mem::transmute::<_, *const HandlerFreeVars>(user); - - match llvm::diagnostic::Diagnostic::unpack(info) { - llvm::diagnostic::Optimization(opt) => { - let pass_name = CString::new(opt.pass_name, false); - let pass_name = pass_name.as_str().expect("got a non-UTF8 pass name from LLVM"); - let enabled = match cgcx.remark { - AllPasses => true, - SomePasses(ref v) => v.iter().any(|s| s.as_slice() == pass_name), - }; - - if enabled { - let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc); - cgcx.handler.note(format!("optimization {:s} for {:s} at {:s}: {:s}", - opt.kind.describe(), - pass_name, - if loc.is_empty() { "[unknown]" } else { loc.as_slice() }, - llvm::twine_to_string(opt.message)).as_slice()); - } - } - - _ => (), - } -} - -// Unsafe due to LLVM calls. -unsafe fn optimize_and_codegen(cgcx: &CodegenContext, - mtrans: ModuleTranslation, - config: ModuleConfig, - name_extra: String, - output_names: OutputFilenames) { - let ModuleTranslation { llmod, llcx } = mtrans; - let tm = config.tm; - - // llcx doesn't outlive this function, so we can put this on the stack. - let fv = HandlerFreeVars { - llcx: llcx, - cgcx: cgcx, - }; - let fv = &fv as *const HandlerFreeVars as *mut c_void; - - llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); - - if !cgcx.remark.is_empty() { - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); - } - - if config.emit_no_opt_bc { - let ext = format!("{}.no-opt.bc", name_extra); - output_names.with_extension(ext.as_slice()).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - match config.opt_level { - Some(opt_level) => { - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass: &str| { - pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) - }; - if !config.no_verify { assert!(addpass("verify")); } - - if !config.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - populate_llvm_passes(fpm, mpm, llmod, opt_level, - config.no_builtins); - } - - for pass in config.passes.iter() { - pass.as_slice().with_c_str(|s| { - if !llvm::LLVMRustAddPass(mpm, s) { - cgcx.handler.warn(format!("unknown pass {}, ignoring", - *pass).as_slice()); - } - }) - } - - // Finally, run the actual optimization passes - time(config.time_passes, "llvm function passes", (), |()| - llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(config.time_passes, "llvm module passes", (), |()| - llvm::LLVMRunPassManager(mpm, llmod)); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - - match cgcx.lto_ctxt { - Some((sess, reachable)) if sess.lto() => { - time(sess.time_passes(), "all lto passes", (), |()| - lto::run(sess, llmod, tm, reachable)); - - if config.emit_lto_bc { - let name = format!("{}.lto.bc", name_extra); - output_names.with_extension(name.as_slice()).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - }, - _ => {}, - } - }, - None => {}, - } - - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, - no_builtins: bool, f: |PassManagerRef|) { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm); - llvm::LLVMDisposePassManager(cpm); - } - - if config.emit_bc { - let ext = format!("{}.bc", name_extra); - output_names.with_extension(ext.as_slice()).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - time(config.time_passes, "codegen passes", (), |()| { - if config.emit_ir { - let ext = format!("{}.ll", name_extra); - output_names.with_extension(ext.as_slice()).with_c_str(|output| { - with_codegen(tm, llmod, config.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, output); - }) - }) - } - - if config.emit_asm { - let path = output_names.with_extension(format!("{}.s", name_extra).as_slice()); - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFileType); - }); - } - - if config.emit_obj { - let path = output_names.with_extension(format!("{}.o", name_extra).as_slice()); - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); - }); - } - }); - - llvm::LLVMDisposeModule(llmod); - llvm::LLVMContextDispose(llcx); - llvm::LLVMRustDisposeTargetMachine(tm); -} - -pub fn run_passes(sess: &Session, - trans: &CrateTranslation, - output_types: &[OutputType], - crate_output: &OutputFilenames) { - // It's possible that we have `codegen_units > 1` but only one item in - // `trans.modules`. We could theoretically proceed and do LTO in that - // case, but it would be confusing to have the validity of - // `-Z lto -C codegen-units=2` depend on details of the crate being - // compiled, so we complain regardless. - if sess.lto() && sess.opts.cg.codegen_units > 1 { - // This case is impossible to handle because LTO expects to be able - // to combine the entire crate and all its dependencies into a - // single compilation unit, but each codegen unit is in a separate - // LLVM context, so they can't easily be combined. - sess.fatal("can't perform LTO when using multiple codegen units"); - } - - // Sanity check - assert!(trans.modules.len() == sess.opts.cg.codegen_units); - - unsafe { - configure_llvm(sess); - } - - let tm = create_target_machine(sess); - - // Figure out what we actually need to build. - - let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(tm, vec!()); - - modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); - - // Save all versions of the bytecode if we're saving our temporaries. - if sess.opts.cg.save_temps { - modules_config.emit_no_opt_bc = true; - modules_config.emit_bc = true; - modules_config.emit_lto_bc = true; - metadata_config.emit_bc = true; - } - - // Emit bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the - // archive in order to allow LTO against it. - let needs_crate_bitcode = - sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains(&OutputTypeExe); - if needs_crate_bitcode { - modules_config.emit_bc = true; - } - - for output_type in output_types.iter() { - match *output_type { - OutputTypeBitcode => { modules_config.emit_bc = true; }, - OutputTypeLlvmAssembly => { modules_config.emit_ir = true; }, - OutputTypeAssembly => { - modules_config.emit_asm = true; - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, so - // in this case we still want the metadata object file. - if !sess.opts.output_types.contains(&OutputTypeAssembly) { - metadata_config.emit_obj = true; - } - }, - OutputTypeObject => { modules_config.emit_obj = true; }, - OutputTypeExe => { - modules_config.emit_obj = true; - metadata_config.emit_obj = true; - }, - } - } - - modules_config.set_flags(sess, trans); - metadata_config.set_flags(sess, trans); - - - // Populate a buffer with a list of codegen tasks. Items are processed in - // LIFO order, just because it's a tiny bit simpler that way. (The order - // doesn't actually matter.) - let mut work_items = Vec::with_capacity(1 + trans.modules.len()); - - { - let work = build_work_item(sess, - trans.metadata_module, - metadata_config.clone(), - crate_output.clone(), - "metadata".to_string()); - work_items.push(work); - } - - for (index, mtrans) in trans.modules.iter().enumerate() { - let work = build_work_item(sess, - *mtrans, - modules_config.clone(), - crate_output.clone(), - format!("{}", index)); - work_items.push(work); - } - - // Process the work items, optionally using worker threads. - if sess.opts.cg.codegen_units == 1 { - run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items); - } else { - run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units); - } - - // All codegen is finished. - unsafe { - llvm::LLVMRustDisposeTargetMachine(tm); - } - - // Produce final compile outputs. - - let copy_if_one_unit = |ext: &str, output_type: OutputType, keep_numbered: bool| { - // Three cases: - if sess.opts.cg.codegen_units == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - fs::copy(&crate_output.with_extension(ext), - &crate_output.path(output_type)).unwrap(); - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.0.x`. - remove(sess, &crate_output.with_extension(ext)); - } - } else { - if crate_output.single_output_file.is_some() { - // 2) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(format!("ignoring -o because multiple .{} files were produced", - ext).as_slice()); - } else { - // 3) Multiple codegen units, but no `-o some_name`. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } - } - }; - - let link_obj = |output_path: &Path| { - // Running `ld -r` on a single input is kind of pointless. - if sess.opts.cg.codegen_units == 1 { - fs::copy(&crate_output.with_extension("0.o"), - output_path).unwrap(); - // Leave the .0.o file around, to mimic the behavior of the normal - // code path. - return; - } - - // Some builds of MinGW GCC will pass --force-exe-suffix to ld, which - // will automatically add a .exe extension if the extension is not - // already .exe or .dll. To ensure consistent behavior on Windows, we - // add the .exe suffix explicitly and then rename the output file to - // the desired path. This will give the correct behavior whether or - // not GCC adds --force-exe-suffix. - let windows_output_path = - if sess.target.target.options.is_like_windows { - Some(output_path.with_extension("o.exe")) - } else { - None - }; - - let pname = get_cc_prog(sess); - let mut cmd = Command::new(pname.as_slice()); - - cmd.args(sess.target.target.options.pre_link_args.as_slice()); - cmd.arg("-nostdlib"); - - for index in range(0, trans.modules.len()) { - cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice())); - } - - cmd.arg("-r") - .arg("-o") - .arg(windows_output_path.as_ref().unwrap_or(output_path)); - - cmd.args(sess.target.target.options.post_link_args.as_slice()); - - if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { - println!("{}", &cmd); - } - - cmd.stdin(::std::io::process::Ignored) - .stdout(::std::io::process::InheritFd(1)) - .stderr(::std::io::process::InheritFd(2)); - match cmd.status() { - Ok(status) => { - if !status.success() { - sess.err(format!("linking of {} with `{}` failed", - output_path.display(), cmd).as_slice()); - sess.abort_if_errors(); - } - }, - Err(e) => { - sess.err(format!("could not exec the linker `{}`: {}", - pname, - e).as_slice()); - sess.abort_if_errors(); - }, - } - - match windows_output_path { - Some(ref windows_path) => { - fs::rename(windows_path, output_path).unwrap(); - }, - None => { - // The file is already named according to `output_path`. - } - } - }; - - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. - let mut user_wants_bitcode = false; - for output_type in output_types.iter() { - match *output_type { - OutputTypeBitcode => { - user_wants_bitcode = true; - // Copy to .bc, but always keep the .0.bc. There is a later - // check to figure out if we should delete .0.bc files, or keep - // them for making an rlib. - copy_if_one_unit("0.bc", OutputTypeBitcode, true); - }, - OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly, false); }, - OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly, false); }, - OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); }, - OutputTypeExe => { - // If OutputTypeObject is already in the list, then - // `crate.o` will be handled by the OutputTypeObject case. - // Otherwise, we need to create the temporary object so we - // can run the linker. - if !sess.opts.output_types.contains(&OutputTypeObject) { - link_obj(&crate_output.temp_path(OutputTypeObject)); - } - }, - } - } - let user_wants_bitcode = user_wants_bitcode; - - // Clean up unwanted temporary files. - - // We create the following files by default: - // - crate.0.bc - // - crate.0.o - // - crate.metadata.bc - // - crate.metadata.o - // - crate.o (linked from crate.##.o) - // - crate.bc (copied from crate.0.bc) - // We may create additional files if requested by the user (through - // `-C save-temps` or `--emit=` flags). - - if !sess.opts.cg.save_temps { - // Remove the temporary .0.o objects. If the user didn't - // explicitly request bitcode (with --emit=bc), and the bitcode is not - // needed for building an rlib, then we must remove .0.bc as well. - - // Specific rules for keeping .0.bc: - // - If we're building an rlib (`needs_crate_bitcode`), then keep - // it. - // - If the user requested bitcode (`user_wants_bitcode`), and - // codegen_units > 1, then keep it. - // - If the user requested bitcode but codegen_units == 1, then we - // can toss .0.bc because we copied it to .bc earlier. - // - If we're not building an rlib and the user didn't request - // bitcode, then delete .0.bc. - // If you change how this works, also update back::link::link_rlib, - // where .0.bc files are (maybe) deleted after making an rlib. - let keep_numbered_bitcode = needs_crate_bitcode || - (user_wants_bitcode && sess.opts.cg.codegen_units > 1); - - for i in range(0, trans.modules.len()) { - if modules_config.emit_obj { - let ext = format!("{}.o", i); - remove(sess, &crate_output.with_extension(ext.as_slice())); - } - - if modules_config.emit_bc && !keep_numbered_bitcode { - let ext = format!("{}.bc", i); - remove(sess, &crate_output.with_extension(ext.as_slice())); - } - } - - if metadata_config.emit_bc && !user_wants_bitcode { - remove(sess, &crate_output.with_extension("metadata.bc")); - } - } - - // We leave the following files around by default: - // - crate.o - // - crate.metadata.o - // - crate.bc - // These are used in linking steps and will be cleaned up afterward. - - // FIXME: time_llvm_passes support - does this use a global context or - // something? - //if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } -} - -type WorkItem = proc(&CodegenContext):Send; - -fn build_work_item(sess: &Session, - mtrans: ModuleTranslation, - config: ModuleConfig, - output_names: OutputFilenames, - name_extra: String) -> WorkItem { - let mut config = config; - config.tm = create_target_machine(sess); - - proc(cgcx) unsafe { - optimize_and_codegen(cgcx, mtrans, config, name_extra, output_names); - } -} - -fn run_work_singlethreaded(sess: &Session, - reachable: &[String], - work_items: Vec) { - let cgcx = CodegenContext::new_with_session(sess, reachable); - let mut work_items = work_items; - - // Since we're running single-threaded, we can pass the session to - // the proc, allowing `optimize_and_codegen` to perform LTO. - for work in Unfold::new((), |_| work_items.pop()) { - work(&cgcx); - } -} - -fn run_work_multithreaded(sess: &Session, - work_items: Vec, - num_workers: uint) { - // Run some workers to process the work items. - let work_items_arc = Arc::new(Mutex::new(work_items)); - let mut diag_emitter = SharedEmitter::new(); - let mut futures = Vec::with_capacity(num_workers); - - for i in range(0, num_workers) { - let work_items_arc = work_items_arc.clone(); - let diag_emitter = diag_emitter.clone(); - let remark = sess.opts.cg.remark.clone(); - - let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() { - let diag_handler = mk_handler(box diag_emitter); - - // Must construct cgcx inside the proc because it has non-Send - // fields. - let cgcx = CodegenContext { - lto_ctxt: None, - handler: &diag_handler, - remark: remark, - }; - - loop { - // Avoid holding the lock for the entire duration of the match. - let maybe_work = work_items_arc.lock().pop(); - match maybe_work { - Some(work) => { - work(&cgcx); - - // Make sure to fail the worker so the main thread can - // tell that there were errors. - cgcx.handler.abort_if_errors(); - } - None => break, - } - } - }); - futures.push(future); - } - - let mut panicked = false; - for future in futures.into_iter() { - match future.unwrap() { - Ok(()) => {}, - Err(_) => { - panicked = true; - }, - } - // Display any new diagnostics. - diag_emitter.dump(sess.diagnostic().handler()); - } - if panicked { - sess.fatal("aborting due to worker thread panic"); - } -} - -pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { - let pname = get_cc_prog(sess); - let mut cmd = Command::new(pname.as_slice()); - - cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject)) - .arg(outputs.temp_path(OutputTypeAssembly)); - debug!("{}", &cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", - pname, - prog.status).as_slice()); - sess.note(format!("{}", &cmd).as_slice()); - let mut note = prog.error.clone(); - note.push_all(prog.output.as_slice()); - sess.note(str::from_utf8(note.as_slice()).unwrap()); - sess.abort_if_errors(); - } - }, - Err(e) => { - sess.err(format!("could not exec the linker `{}`: {}", - pname, - e).as_slice()); - sess.abort_if_errors(); - } - } -} - -unsafe fn configure_llvm(sess: &Session) { - use std::sync::{Once, ONCE_INIT}; - static INIT: Once = ONCE_INIT; - - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3 - let vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive); - let vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::Aggressive; - - let mut llvm_c_strs = Vec::new(); - let mut llvm_args = Vec::new(); - { - let add = |arg: &str| { - let s = arg.to_c_str(); - llvm_args.push(s.as_ptr()); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - if vectorize_loop { add("-vectorize-loops"); } - if vectorize_slp { add("-vectorize-slp"); } - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - - for arg in sess.opts.cg.llvm_args.iter() { - add((*arg).as_slice()); - } - } - - INIT.doit(|| { - llvm::LLVMInitializePasses(); - - // Only initialize the platforms supported by Rust here, because - // using --llvm-root will have multiple platforms that rustllvm - // doesn't actually link to and it's pointless to put target info - // into the registry that Rust cannot generate machine code for. - llvm::LLVMInitializeX86TargetInfo(); - llvm::LLVMInitializeX86Target(); - llvm::LLVMInitializeX86TargetMC(); - llvm::LLVMInitializeX86AsmPrinter(); - llvm::LLVMInitializeX86AsmParser(); - - llvm::LLVMInitializeARMTargetInfo(); - llvm::LLVMInitializeARMTarget(); - llvm::LLVMInitializeARMTargetMC(); - llvm::LLVMInitializeARMAsmPrinter(); - llvm::LLVMInitializeARMAsmParser(); - - llvm::LLVMInitializeMipsTargetInfo(); - llvm::LLVMInitializeMipsTarget(); - llvm::LLVMInitializeMipsTargetMC(); - llvm::LLVMInitializeMipsAsmPrinter(); - llvm::LLVMInitializeMipsAsmParser(); - - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, - llvm_args.as_ptr()); - }); -} - -unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, - mpm: llvm::PassManagerRef, - llmod: ModuleRef, - opt: llvm::CodeGenOptLevel, - no_builtins: bool) { - // Create the PassManagerBuilder for LLVM. We configure it with - // reasonable defaults and prepare it to actually populate the pass - // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); - match opt { - llvm::CodeGenLevelNone => { - // Don't add lifetime intrinsics at O0 - llvm::LLVMRustAddAlwaysInlinePass(builder, false); - } - llvm::CodeGenLevelLess => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); - } - // numeric values copied from clang - llvm::CodeGenLevelDefault => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 225); - } - llvm::CodeGenLevelAggressive => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 275); - } - } - llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); - - // Use the builder to populate the function/module pass managers. - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); - llvm::LLVMPassManagerBuilderDispose(builder); - - match opt { - llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { - "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); - } - _ => {} - }; -} diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs deleted file mode 100644 index 1a40a04350d..00000000000 --- a/src/librustc/driver/config.rs +++ /dev/null @@ -1,926 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Contains infrastructure for configuring the compiler, including parsing -//! command line options. - -pub use self::EntryFnType::*; -pub use self::CrateType::*; -pub use self::Passes::*; -pub use self::OptLevel::*; -pub use self::DebugInfoLevel::*; - -use driver::{early_error, early_warn}; -use driver::driver; -use driver::session::Session; - -use back; -use back::write; -use rustc_back::target::Target; -use lint; -use metadata::cstore; - -use syntax::ast; -use syntax::ast::{IntTy, UintTy}; -use syntax::attr; -use syntax::attr::AttrMetaMethods; -use syntax::diagnostic::{ColorConfig, Auto, Always, Never, SpanHandler}; -use syntax::parse; -use syntax::parse::token::InternedString; - -use std::collections::HashMap; -use std::collections::hash_map::{Occupied, Vacant}; -use getopts::{optopt, optmulti, optflag, optflagopt}; -use getopts; -use std::cell::{RefCell}; -use std::fmt; - -use llvm; - -pub struct Config { - pub target: Target, - pub int_type: IntTy, - pub uint_type: UintTy, -} - -#[deriving(Clone, PartialEq)] -pub enum OptLevel { - No, // -O0 - Less, // -O1 - Default, // -O2 - Aggressive // -O3 -} - -#[deriving(Clone, PartialEq)] -pub enum DebugInfoLevel { - NoDebugInfo, - LimitedDebugInfo, - FullDebugInfo, -} - -#[deriving(Clone)] -pub struct Options { - // The crate config requested for the session, which may be combined - // with additional crate configurations during the compile process - pub crate_types: Vec, - - pub gc: bool, - pub optimize: OptLevel, - pub debuginfo: DebugInfoLevel, - pub lint_opts: Vec<(String, lint::Level)>, - pub describe_lints: bool, - pub output_types: Vec , - // This was mutable for rustpkg, which updates search paths based on the - // parsed code. It remains mutable in case its replacements wants to use - // this. - pub addl_lib_search_paths: RefCell>, - pub libs: Vec<(String, cstore::NativeLibaryKind)>, - pub maybe_sysroot: Option, - pub target_triple: String, - // User-specified cfg meta items. The compiler itself will add additional - // items to the crate config, and during parsing the entire crate config - // will be added to the crate AST node. This should not be used for - // anything except building the full crate config prior to parsing. - pub cfg: ast::CrateConfig, - pub test: bool, - pub parse_only: bool, - pub no_trans: bool, - pub no_analysis: bool, - pub debugging_opts: u64, - /// Whether to write dependency files. It's (enabled, optional filename). - pub write_dependency_info: (bool, Option), - /// Crate id-related things to maybe print. It's (crate_name, crate_file_name). - pub print_metas: (bool, bool), - pub cg: CodegenOptions, - pub color: ColorConfig, - pub externs: HashMap>, - pub crate_name: Option, - /// An optional name to use as the crate for std during std injection, - /// written `extern crate std = "name"`. Default to "std". Used by - /// out-of-tree drivers. - pub alt_std_name: Option -} - -/// Some reasonable defaults -pub fn basic_options() -> Options { - Options { - crate_types: Vec::new(), - gc: false, - optimize: No, - debuginfo: NoDebugInfo, - lint_opts: Vec::new(), - describe_lints: false, - output_types: Vec::new(), - addl_lib_search_paths: RefCell::new(Vec::new()), - maybe_sysroot: None, - target_triple: driver::host_triple().to_string(), - cfg: Vec::new(), - test: false, - parse_only: false, - no_trans: false, - no_analysis: false, - debugging_opts: 0, - write_dependency_info: (false, None), - print_metas: (false, false), - cg: basic_codegen_options(), - color: Auto, - externs: HashMap::new(), - crate_name: None, - alt_std_name: None, - libs: Vec::new(), - } -} - -// The type of entry function, so -// users can have their own entry -// functions that don't start a -// scheduler -#[deriving(PartialEq)] -pub enum EntryFnType { - EntryMain, - EntryStart, - EntryNone, -} - -#[deriving(PartialEq, PartialOrd, Clone, Ord, Eq, Hash)] -pub enum CrateType { - CrateTypeExecutable, - CrateTypeDylib, - CrateTypeRlib, - CrateTypeStaticlib, -} - -macro_rules! debugging_opts( - ([ $opt:ident ] $cnt:expr ) => ( - pub const $opt: u64 = 1 << $cnt; - ); - ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => ( - pub const $opt: u64 = 1 << $cnt; - debugging_opts!([ $($rest),* ] $cnt + 1) - ) -) - -debugging_opts!( - [ - VERBOSE, - TIME_PASSES, - COUNT_LLVM_INSNS, - TIME_LLVM_PASSES, - TRANS_STATS, - ASM_COMMENTS, - NO_VERIFY, - BORROWCK_STATS, - NO_LANDING_PADS, - DEBUG_LLVM, - SHOW_SPAN, - COUNT_TYPE_SIZES, - META_STATS, - GC, - PRINT_LINK_ARGS, - PRINT_LLVM_PASSES, - AST_JSON, - AST_JSON_NOEXPAND, - LS, - SAVE_ANALYSIS, - FLOWGRAPH_PRINT_LOANS, - FLOWGRAPH_PRINT_MOVES, - FLOWGRAPH_PRINT_ASSIGNS, - FLOWGRAPH_PRINT_ALL - ] - 0 -) - -pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { - vec!(("verbose", "in general, enable more debug printouts", VERBOSE), - ("time-passes", "measure time of each rustc pass", TIME_PASSES), - ("count-llvm-insns", "count where LLVM \ - instrs originate", COUNT_LLVM_INSNS), - ("time-llvm-passes", "measure time of each LLVM pass", - TIME_LLVM_PASSES), - ("trans-stats", "gather trans statistics", TRANS_STATS), - ("asm-comments", "generate comments into the assembly (may change behavior)", - ASM_COMMENTS), - ("no-verify", "skip LLVM verification", NO_VERIFY), - ("borrowck-stats", "gather borrowck statistics", BORROWCK_STATS), - ("no-landing-pads", "omit landing pads for unwinding", - NO_LANDING_PADS), - ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM), - ("show-span", "show spans for compiler debugging", SHOW_SPAN), - ("count-type-sizes", "count the sizes of aggregate types", - COUNT_TYPE_SIZES), - ("meta-stats", "gather metadata statistics", META_STATS), - ("print-link-args", "Print the arguments passed to the linker", - PRINT_LINK_ARGS), - ("gc", "Garbage collect shared data (experimental)", GC), - ("print-llvm-passes", - "Prints the llvm optimization passes being run", - PRINT_LLVM_PASSES), - ("ast-json", "Print the AST as JSON and halt", AST_JSON), - ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND), - ("ls", "List the symbols defined by a library crate", LS), - ("save-analysis", "Write syntax and type analysis information \ - in addition to normal output", SAVE_ANALYSIS), - ("flowgraph-print-loans", "Include loan analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS), - ("flowgraph-print-moves", "Include move analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES), - ("flowgraph-print-assigns", "Include assignment analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS), - ("flowgraph-print-all", "Include all dataflow analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_ALL)) -} - -#[deriving(Clone)] -pub enum Passes { - SomePasses(Vec), - AllPasses, -} - -impl Passes { - pub fn is_empty(&self) -> bool { - match *self { - SomePasses(ref v) => v.is_empty(), - AllPasses => false, - } - } -} - -/// Declare a macro that will define all CodegenOptions fields and parsers all -/// at once. The goal of this macro is to define an interface that can be -/// programmatically used by the option parser in order to initialize the struct -/// without hardcoding field names all over the place. -/// -/// The goal is to invoke this macro once with the correct fields, and then this -/// macro generates all necessary code. The main gotcha of this macro is the -/// cgsetters module which is a bunch of generated code to parse an option into -/// its respective field in the struct. There are a few hand-written parsers for -/// parsing specific types of values in this module. -macro_rules! cgoptions( - ($($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) => -( - #[deriving(Clone)] - pub struct CodegenOptions { $(pub $opt: $t),* } - - pub fn basic_codegen_options() -> CodegenOptions { - CodegenOptions { $($opt: $init),* } - } - - pub type CodegenSetter = fn(&mut CodegenOptions, v: Option<&str>) -> bool; - pub const CG_OPTIONS: &'static [(&'static str, CodegenSetter, - Option<&'static str>, &'static str)] = - &[ $( (stringify!($opt), cgsetters::$opt, cg_type_descs::$parse, $desc) ),* ]; - - #[allow(non_upper_case_globals)] - mod cg_type_descs { - pub const parse_bool: Option<&'static str> = None; - pub const parse_opt_bool: Option<&'static str> = None; - pub const parse_string: Option<&'static str> = Some("a string"); - pub const parse_opt_string: Option<&'static str> = Some("a string"); - pub const parse_list: Option<&'static str> = Some("a space-separated list of strings"); - pub const parse_opt_list: Option<&'static str> = Some("a space-separated list of strings"); - pub const parse_uint: Option<&'static str> = Some("a number"); - pub const parse_passes: Option<&'static str> = - Some("a space-separated list of passes, or `all`"); - } - - mod cgsetters { - use super::{CodegenOptions, Passes, SomePasses, AllPasses}; - - $( - pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool { - $parse(&mut cg.$opt, v) - } - )* - - fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { - match v { - Some(..) => false, - None => { *slot = true; true } - } - } - - fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(..) => false, - None => { *slot = Some(true); true } - } - } - - fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = Some(s.to_string()); true }, - None => false, - } - } - - fn parse_string(slot: &mut String, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = s.to_string(); true }, - None => false, - } - } - - fn parse_list(slot: &mut Vec, v: Option<&str>) - -> bool { - match v { - Some(s) => { - for s in s.words() { - slot.push(s.to_string()); - } - true - }, - None => false, - } - } - - fn parse_opt_list(slot: &mut Option>, v: Option<&str>) - -> bool { - match v { - Some(s) => { - let v = s.words().map(|s| s.to_string()).collect(); - *slot = Some(v); - true - }, - None => false, - } - } - - fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool { - match v.and_then(from_str) { - Some(i) => { *slot = i; true }, - None => false - } - } - - fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { - match v { - Some("all") => { - *slot = AllPasses; - true - } - v => { - let mut passes = vec!(); - if parse_list(&mut passes, v) { - *slot = SomePasses(passes); - true - } else { - false - } - } - } - } - } -) ) - -cgoptions!( - ar: Option = (None, parse_opt_string, - "tool to assemble archives with"), - linker: Option = (None, parse_opt_string, - "system linker to link outputs with"), - link_args: Option> = (None, parse_opt_list, - "extra arguments to pass to the linker (space separated)"), - lto: bool = (false, parse_bool, - "perform LLVM link-time optimizations"), - target_cpu: Option = (None, parse_opt_string, - "select target processor (llc -mcpu=help for details)"), - target_feature: String = ("".to_string(), parse_string, - "target specific attributes (llc -mattr=help for details)"), - passes: Vec = (Vec::new(), parse_list, - "a list of extra LLVM passes to run (space separated)"), - llvm_args: Vec = (Vec::new(), parse_list, - "a list of arguments to pass to llvm (space separated)"), - save_temps: bool = (false, parse_bool, - "save all temporary output files during compilation"), - rpath: bool = (false, parse_bool, - "set rpath values in libs/exes"), - no_prepopulate_passes: bool = (false, parse_bool, - "don't pre-populate the pass manager with a list of passes"), - no_vectorize_loops: bool = (false, parse_bool, - "don't run the loop vectorization optimization passes"), - no_vectorize_slp: bool = (false, parse_bool, - "don't run LLVM's SLP vectorization pass"), - soft_float: bool = (false, parse_bool, - "generate software floating point library calls"), - prefer_dynamic: bool = (false, parse_bool, - "prefer dynamic linking to static linking"), - no_integrated_as: bool = (false, parse_bool, - "use an external assembler rather than LLVM's integrated one"), - no_redzone: Option = (None, parse_opt_bool, - "disable the use of the redzone"), - relocation_model: Option = (None, parse_opt_string, - "choose the relocation model to use (llc -relocation-model for details)"), - code_model: Option = (None, parse_opt_string, - "choose the code model to use (llc -code-model for details)"), - metadata: Vec = (Vec::new(), parse_list, - "metadata to mangle symbol names with"), - extra_filename: String = ("".to_string(), parse_string, - "extra data to put in each output filename"), - codegen_units: uint = (1, parse_uint, - "divide crate into N units to optimize in parallel"), - remark: Passes = (SomePasses(Vec::new()), parse_passes, - "print remarks for these optimization passes (space separated, or \"all\")"), - no_stack_check: bool = (false, parse_bool, - "disable checks for stack exhaustion (a memory-safety hazard!)"), -) - -pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions -{ - let mut cg = basic_codegen_options(); - for option in matches.opt_strs("C").into_iter() { - let mut iter = option.as_slice().splitn(1, '='); - let key = iter.next().unwrap(); - let value = iter.next(); - let option_to_lookup = key.replace("-", "_"); - let mut found = false; - for &(candidate, setter, opt_type_desc, _) in CG_OPTIONS.iter() { - if option_to_lookup.as_slice() != candidate { continue } - if !setter(&mut cg, value) { - match (value, opt_type_desc) { - (Some(..), None) => { - early_error(format!("codegen option `{}` takes no \ - value", key).as_slice()) - } - (None, Some(type_desc)) => { - early_error(format!("codegen option `{0}` requires \ - {1} (-C {0}=)", - key, type_desc).as_slice()) - } - (Some(value), Some(type_desc)) => { - early_error(format!("incorrect value `{}` for codegen \ - option `{}` - {} was expected", - value, key, type_desc).as_slice()) - } - (None, None) => unreachable!() - } - } - found = true; - break; - } - if !found { - early_error(format!("unknown codegen option: `{}`", - key).as_slice()); - } - } - return cg; -} - -pub fn default_lib_output() -> CrateType { - CrateTypeRlib -} - -pub fn default_configuration(sess: &Session) -> ast::CrateConfig { - use syntax::parse::token::intern_and_get_ident as intern; - - let end = sess.target.target.target_endian.as_slice(); - let arch = sess.target.target.arch.as_slice(); - let wordsz = sess.target.target.target_word_size.as_slice(); - let os = sess.target.target.target_os.as_slice(); - - let fam = match sess.target.target.options.is_like_windows { - true => InternedString::new("windows"), - false => InternedString::new("unix") - }; - - let mk = attr::mk_name_value_item_str; - return vec!(// Target bindings. - attr::mk_word_item(fam.clone()), - mk(InternedString::new("target_os"), intern(os)), - mk(InternedString::new("target_family"), fam), - mk(InternedString::new("target_arch"), intern(arch)), - mk(InternedString::new("target_endian"), intern(end)), - mk(InternedString::new("target_word_size"), - intern(wordsz)) - ); -} - -pub fn append_configuration(cfg: &mut ast::CrateConfig, - name: InternedString) { - if !cfg.iter().any(|mi| mi.name() == name) { - cfg.push(attr::mk_word_item(name)) - } -} - -pub fn build_configuration(sess: &Session) -> ast::CrateConfig { - // Combine the configuration requested by the session (command line) with - // some default and generated configuration items - let default_cfg = default_configuration(sess); - let mut user_cfg = sess.opts.cfg.clone(); - // If the user wants a test runner, then add the test cfg - if sess.opts.test { - append_configuration(&mut user_cfg, InternedString::new("test")) - } - let mut v = user_cfg.into_iter().collect::>(); - v.push_all(default_cfg.as_slice()); - v -} - -pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config { - let target = match Target::search(opts.target_triple.as_slice()) { - Ok(t) => t, - Err(e) => { - sp.handler().fatal((format!("Error loading target specification: {}", e)).as_slice()); - } - }; - - let (int_type, uint_type) = match target.target_word_size.as_slice() { - "32" => (ast::TyI32, ast::TyU32), - "64" => (ast::TyI64, ast::TyU64), - w => sp.handler().fatal((format!("target specification was invalid: unrecognized \ - target-word-size {}", w)).as_slice()) - }; - - Config { - target: target, - int_type: int_type, - uint_type: uint_type, - } -} - -// rustc command line options -pub fn optgroups() -> Vec { - vec!( - optflag("h", "help", "Display this message"), - optmulti("", "cfg", "Configure the compilation environment", "SPEC"), - optmulti("L", "", "Add a directory to the library search path", "PATH"), - optmulti("l", "", "Link the generated crate(s) to the specified native - library NAME. The optional KIND can be one of, - static, dylib, or framework. If omitted, dylib is - assumed.", "NAME[:KIND]"), - optmulti("", "crate-type", "Comma separated list of types of crates - for the compiler to emit", - "[bin|lib|rlib|dylib|staticlib]"), - optmulti("", "emit", "Comma separated list of types of output for the compiler to emit", - "[asm|bc|ir|obj|link]"), - optopt("", "crate-name", "Specify the name of the crate being built", - "NAME"), - optflag("", "print-crate-name", "Output the crate name and exit"), - optflag("", "print-file-name", "Output the file(s) that would be written if compilation \ - continued and exit"), - optflag("", "crate-file-name", "deprecated in favor of --print-file-name"), - optflag("g", "", "Equivalent to --debuginfo=2"), - optopt("", "debuginfo", "Emit DWARF debug info to the objects created: - 0 = no debug info, - 1 = line-tables only (for stacktraces and breakpoints), - 2 = full debug info with variable and type information (same as -g)", "LEVEL"), - optflag("", "no-trans", "Run all passes except translation; no output"), - optflag("", "no-analysis", - "Parse and expand the source, but run no analysis and produce no output"), - optflag("O", "", "Equivalent to --opt-level=2"), - optopt("o", "", "Write output to ", "FILENAME"), - optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"), - optopt( "", "out-dir", "Write output to compiler-chosen filename in ", "DIR"), - optflag("", "parse-only", "Parse only; do not compile, assemble, or link"), - optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"), - optflagopt("", "pretty", - "Pretty-print the input instead of compiling; - valid types are: `normal` (un-annotated source), - `expanded` (crates expanded), - `typed` (crates expanded, with type annotations), - `expanded,identified` (fully parenthesized, AST nodes with IDs), or - `flowgraph=` (graphviz formatted flowgraph for node)", - "TYPE"), - optflagopt("", "dep-info", - "Output dependency info to after compiling, \ - in a format suitable for use by Makefiles", "FILENAME"), - optopt("", "sysroot", "Override the system root", "PATH"), - optflag("", "test", "Build a test harness"), - optopt("", "target", "Target triple cpu-manufacturer-kernel[-os] - to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/ - for details)", "TRIPLE"), - optmulti("W", "warn", "Set lint warnings", "OPT"), - optmulti("A", "allow", "Set lint allowed", "OPT"), - optmulti("D", "deny", "Set lint denied", "OPT"), - optmulti("F", "forbid", "Set lint forbidden", "OPT"), - optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), - optmulti("Z", "", "Set internal debugging options", "FLAG"), - optflagopt("v", "version", "Print version info and exit", "verbose"), - optopt("", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", "auto|always|never"), - optmulti("", "extern", "Specify where an external rust library is located", - "NAME=PATH"), - ) -} - - -// Convert strings provided as --cfg [cfgspec] into a crate_cfg -pub fn parse_cfgspecs(cfgspecs: Vec ) -> ast::CrateConfig { - cfgspecs.into_iter().map(|s| { - parse::parse_meta_from_source_str("cfgspec".to_string(), - s.to_string(), - Vec::new(), - &parse::new_parse_sess()) - }).collect::() -} - -pub fn build_session_options(matches: &getopts::Matches) -> Options { - - let unparsed_crate_types = matches.opt_strs("crate-type"); - let crate_types = parse_crate_types_from_list(unparsed_crate_types) - .unwrap_or_else(|e| early_error(e.as_slice())); - - let parse_only = matches.opt_present("parse-only"); - let no_trans = matches.opt_present("no-trans"); - let no_analysis = matches.opt_present("no-analysis"); - - let mut lint_opts = vec!(); - let mut describe_lints = false; - - for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() { - for lint_name in matches.opt_strs(level.as_str()).into_iter() { - if lint_name.as_slice() == "help" { - describe_lints = true; - } else { - lint_opts.push((lint_name.replace("-", "_").into_string(), level)); - } - } - } - - let mut debugging_opts = 0; - let debug_flags = matches.opt_strs("Z"); - let debug_map = debugging_opts_map(); - for debug_flag in debug_flags.iter() { - let mut this_bit = 0; - for tuple in debug_map.iter() { - let (name, bit) = match *tuple { (ref a, _, b) => (a, b) }; - if *name == debug_flag.as_slice() { - this_bit = bit; - break; - } - } - if this_bit == 0 { - early_error(format!("unknown debug flag: {}", - *debug_flag).as_slice()) - } - debugging_opts |= this_bit; - } - - if debugging_opts & DEBUG_LLVM != 0 { - unsafe { llvm::LLVMSetDebug(1); } - } - - let mut output_types = Vec::new(); - if !parse_only && !no_trans { - let unparsed_output_types = matches.opt_strs("emit"); - for unparsed_output_type in unparsed_output_types.iter() { - for part in unparsed_output_type.as_slice().split(',') { - let output_type = match part.as_slice() { - "asm" => write::OutputTypeAssembly, - "ir" => write::OutputTypeLlvmAssembly, - "bc" => write::OutputTypeBitcode, - "obj" => write::OutputTypeObject, - "link" => write::OutputTypeExe, - _ => { - early_error(format!("unknown emission type: `{}`", - part).as_slice()) - } - }; - output_types.push(output_type) - } - } - }; - output_types.as_mut_slice().sort(); - output_types.dedup(); - if output_types.len() == 0 { - output_types.push(write::OutputTypeExe); - } - - let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m)); - let target = matches.opt_str("target").unwrap_or( - driver::host_triple().to_string()); - let opt_level = { - if matches.opt_present("O") { - if matches.opt_present("opt-level") { - early_error("-O and --opt-level both provided"); - } - Default - } else if matches.opt_present("opt-level") { - match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) { - None | - Some("0") => No, - Some("1") => Less, - Some("2") => Default, - Some("3") => Aggressive, - Some(arg) => { - early_error(format!("optimization level needs to be \ - between 0-3 (instead was `{}`)", - arg).as_slice()); - } - } - } else { - No - } - }; - let gc = debugging_opts & GC != 0; - let debuginfo = if matches.opt_present("g") { - if matches.opt_present("debuginfo") { - early_error("-g and --debuginfo both provided"); - } - FullDebugInfo - } else if matches.opt_present("debuginfo") { - match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) { - Some("0") => NoDebugInfo, - Some("1") => LimitedDebugInfo, - None | - Some("2") => FullDebugInfo, - Some(arg) => { - early_error(format!("debug info level needs to be between \ - 0-2 (instead was `{}`)", - arg).as_slice()); - } - } - } else { - NoDebugInfo - }; - - let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| { - Path::new(s.as_slice()) - }).collect(); - - let libs = matches.opt_strs("l").into_iter().map(|s| { - let mut parts = s.as_slice().rsplitn(1, ':'); - let kind = parts.next().unwrap(); - let (name, kind) = match (parts.next(), kind) { - (None, name) | - (Some(name), "dylib") => (name, cstore::NativeUnknown), - (Some(name), "framework") => (name, cstore::NativeFramework), - (Some(name), "static") => (name, cstore::NativeStatic), - (_, s) => { - early_error(format!("unknown library kind `{}`, expected \ - one of dylib, framework, or static", - s).as_slice()); - } - }; - (name.to_string(), kind) - }).collect(); - - let cfg = parse_cfgspecs(matches.opt_strs("cfg")); - let test = matches.opt_present("test"); - let write_dependency_info = (matches.opt_present("dep-info"), - matches.opt_str("dep-info") - .map(|p| Path::new(p))); - - let print_metas = (matches.opt_present("print-crate-name"), - matches.opt_present("print-file-name") || - matches.opt_present("crate-file-name")); - if matches.opt_present("crate-file-name") { - early_warn("the --crate-file-name argument has been renamed to \ - --print-file-name"); - } - let cg = build_codegen_options(matches); - - if !cg.remark.is_empty() && debuginfo == NoDebugInfo { - early_warn("-C remark will not show source locations without --debuginfo"); - } - - let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) { - Some("auto") => Auto, - Some("always") => Always, - Some("never") => Never, - - None => Auto, - - Some(arg) => { - early_error(format!("argument for --color must be auto, always \ - or never (instead was `{}`)", - arg).as_slice()) - } - }; - - let mut externs = HashMap::new(); - for arg in matches.opt_strs("extern").iter() { - let mut parts = arg.as_slice().splitn(1, '='); - let name = match parts.next() { - Some(s) => s, - None => early_error("--extern value must not be empty"), - }; - let location = match parts.next() { - Some(s) => s, - None => early_error("--extern value must be of the format `foo=bar`"), - }; - - match externs.entry(name.to_string()) { - Vacant(entry) => { entry.set(vec![location.to_string()]); }, - Occupied(mut entry) => { entry.get_mut().push(location.to_string()); }, - } - } - - let crate_name = matches.opt_str("crate-name"); - - Options { - crate_types: crate_types, - gc: gc, - optimize: opt_level, - debuginfo: debuginfo, - lint_opts: lint_opts, - describe_lints: describe_lints, - output_types: output_types, - addl_lib_search_paths: RefCell::new(addl_lib_search_paths), - maybe_sysroot: sysroot_opt, - target_triple: target, - cfg: cfg, - test: test, - parse_only: parse_only, - no_trans: no_trans, - no_analysis: no_analysis, - debugging_opts: debugging_opts, - write_dependency_info: write_dependency_info, - print_metas: print_metas, - cg: cg, - color: color, - externs: externs, - crate_name: crate_name, - alt_std_name: None, - libs: libs, - } -} - -pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { - - let mut crate_types: Vec = Vec::new(); - for unparsed_crate_type in list_list.iter() { - for part in unparsed_crate_type.as_slice().split(',') { - let new_part = match part { - "lib" => default_lib_output(), - "rlib" => CrateTypeRlib, - "staticlib" => CrateTypeStaticlib, - "dylib" => CrateTypeDylib, - "bin" => CrateTypeExecutable, - _ => { - return Err(format!("unknown crate type: `{}`", - part)); - } - }; - crate_types.push(new_part) - } - } - - return Ok(crate_types); -} - -impl fmt::Show for CrateType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - CrateTypeExecutable => "bin".fmt(f), - CrateTypeDylib => "dylib".fmt(f), - CrateTypeRlib => "rlib".fmt(f), - CrateTypeStaticlib => "staticlib".fmt(f) - } - } -} - -#[cfg(test)] -mod test { - - use driver::config::{build_configuration, optgroups, build_session_options}; - use driver::session::build_session; - - use getopts::getopts; - use syntax::attr; - use syntax::attr::AttrMetaMethods; - use syntax::diagnostics; - - // When the user supplies --test we should implicitly supply --cfg test - #[test] - fn test_switch_implies_cfg_test() { - let matches = - &match getopts(&["--test".to_string()], optgroups().as_slice()) { - Ok(m) => m, - Err(f) => panic!("test_switch_implies_cfg_test: {}", f) - }; - let registry = diagnostics::registry::Registry::new(&[]); - let sessopts = build_session_options(matches); - let sess = build_session(sessopts, None, registry); - let cfg = build_configuration(&sess); - assert!((attr::contains_name(cfg.as_slice(), "test"))); - } - - // When the user supplies --test and --cfg test, don't implicitly add - // another --cfg test - #[test] - fn test_switch_implies_cfg_test_unless_cfg_test() { - let matches = - &match getopts(&["--test".to_string(), "--cfg=test".to_string()], - optgroups().as_slice()) { - Ok(m) => m, - Err(f) => { - panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f) - } - }; - let registry = diagnostics::registry::Registry::new(&[]); - let sessopts = build_session_options(matches); - let sess = build_session(sessopts, None, registry); - let cfg = build_configuration(&sess); - let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test"))); - assert!(test_items.next().is_some()); - assert!(test_items.next().is_none()); - } -} diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs deleted file mode 100644 index 498e676474c..00000000000 --- a/src/librustc/driver/driver.rs +++ /dev/null @@ -1,851 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use self::Input::*; - -use back::link; -use back::write; -use driver::session::Session; -use driver::config; -use lint; -use llvm::{ContextRef, ModuleRef}; -use metadata::common::LinkMeta; -use metadata::creader; -use middle::{trans, stability, ty, typeck, reachable}; -use middle::dependency_format; -use middle; -use plugin::load::Plugins; -use plugin::registry::Registry; -use plugin; - -use util::common::time; -use util::nodemap::{NodeSet}; - -use serialize::{json, Encodable}; - -use std::io; -use std::io::fs; -use std::os; -use arena::TypedArena; -use syntax::ast; -use syntax::ast_map; -use syntax::attr; -use syntax::attr::{AttrMetaMethods}; -use syntax::diagnostics; -use syntax::parse; -use syntax::parse::token; -use syntax; - -pub fn host_triple() -> &'static str { - // Get the host triple out of the build environment. This ensures that our - // idea of the host triple is the same as for the set of libraries we've - // actually built. We can't just take LLVM's host triple because they - // normalize all ix86 architectures to i386. - // - // Instead of grabbing the host triple (for the current host), we grab (at - // compile time) the target triple that this rustc is built with and - // calling that (at runtime) the host triple. - (option_env!("CFG_COMPILER_HOST_TRIPLE")). - expect("CFG_COMPILER_HOST_TRIPLE") -} - -pub fn compile_input(sess: Session, - cfg: ast::CrateConfig, - input: &Input, - outdir: &Option, - output: &Option, - addl_plugins: Option) { - // We need nested scopes here, because the intermediate results can keep - // large chunks of memory alive and we want to free them as soon as - // possible to keep the peak memory usage low - let (outputs, trans, sess) = { - let (outputs, expanded_crate, id) = { - let krate = phase_1_parse_input(&sess, cfg, input); - if stop_after_phase_1(&sess) { return; } - let outputs = build_output_filenames(input, - outdir, - output, - krate.attrs.as_slice(), - &sess); - let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), - input); - let expanded_crate - = match phase_2_configure_and_expand(&sess, krate, id.as_slice(), - addl_plugins) { - None => return, - Some(k) => k - }; - - (outputs, expanded_crate, id) - }; - - let mut forest = ast_map::Forest::new(expanded_crate); - let ast_map = assign_node_ids_and_map(&sess, &mut forest); - - write_out_deps(&sess, input, &outputs, id.as_slice()); - - if stop_after_phase_2(&sess) { return; } - - let type_arena = TypedArena::new(); - let analysis = phase_3_run_analysis_passes(sess, ast_map, &type_arena, id); - phase_save_analysis(&analysis.ty_cx.sess, analysis.ty_cx.map.krate(), &analysis, outdir); - if stop_after_phase_3(&analysis.ty_cx.sess) { return; } - let (tcx, trans) = phase_4_translate_to_llvm(analysis); - - // Discard interned strings as they are no longer required. - token::get_ident_interner().clear(); - - (outputs, trans, tcx.sess) - }; - phase_5_run_llvm_passes(&sess, &trans, &outputs); - if stop_after_phase_5(&sess) { return; } - phase_6_link_output(&sess, &trans, &outputs); -} - -/** - * The name used for source code that doesn't originate in a file - * (e.g. source from stdin or a string) - */ -pub fn anon_src() -> String { - "".to_string() -} - -pub fn source_name(input: &Input) -> String { - match *input { - // FIXME (#9639): This needs to handle non-utf8 paths - FileInput(ref ifile) => ifile.as_str().unwrap().to_string(), - StrInput(_) => anon_src() - } -} - -pub enum Input { - /// Load source from file - FileInput(Path), - /// The string is the source - StrInput(String) -} - -impl Input { - fn filestem(&self) -> String { - match *self { - FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(), - StrInput(_) => "rust_out".to_string(), - } - } -} - - -pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) - -> ast::Crate { - let krate = time(sess.time_passes(), "parsing", (), |_| { - match *input { - FileInput(ref file) => { - parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess) - } - StrInput(ref src) => { - parse::parse_crate_from_source_str(anon_src().to_string(), - src.to_string(), - cfg.clone(), - &sess.parse_sess) - } - } - }); - - if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 { - let mut stdout = io::BufferedWriter::new(io::stdout()); - let mut json = json::PrettyEncoder::new(&mut stdout); - // unwrapping so IoError isn't ignored - krate.encode(&mut json).unwrap(); - } - - if sess.show_span() { - syntax::show_span::run(sess.diagnostic(), &krate); - } - - krate -} - -// For continuing compilation after a parsed crate has been -// modified - -/// Run the "early phases" of the compiler: initial `cfg` processing, -/// loading compiler plugins (including those from `addl_plugins`), -/// syntax expansion, secondary `cfg` expansion, synthesis of a test -/// harness if one is to be provided and injection of a dependency on the -/// standard library and prelude. -/// -/// Returns `None` if we're aborting after handling -W help. -pub fn phase_2_configure_and_expand(sess: &Session, - mut krate: ast::Crate, - crate_name: &str, - addl_plugins: Option) - -> Option { - let time_passes = sess.time_passes(); - - *sess.crate_types.borrow_mut() = - collect_crate_types(sess, krate.attrs.as_slice()); - *sess.crate_metadata.borrow_mut() = - collect_crate_metadata(sess, krate.attrs.as_slice()); - - time(time_passes, "gated feature checking", (), |_| { - let (features, unknown_features) = - syntax::feature_gate::check_crate(&sess.parse_sess.span_diagnostic, &krate); - - for uf in unknown_features.iter() { - sess.add_lint(lint::builtin::UNKNOWN_FEATURES, - ast::CRATE_NODE_ID, - *uf, - "unknown feature".to_string()); - } - - sess.abort_if_errors(); - *sess.features.borrow_mut() = features; - }); - - let any_exe = sess.crate_types.borrow().iter().any(|ty| { - *ty == config::CrateTypeExecutable - }); - - // strip before expansion to allow macros to depend on - // configuration variables e.g/ in - // - // #[macro_escape] #[cfg(foo)] - // mod bar { macro_rules! baz!(() => {{}}) } - // - // baz! should not use this definition unless foo is enabled. - - krate = time(time_passes, "configuration 1", krate, |krate| - syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); - - krate = time(time_passes, "crate injection", krate, |krate| - syntax::std_inject::maybe_inject_crates_ref(krate, - sess.opts.alt_std_name.clone(), - any_exe)); - - let mut addl_plugins = Some(addl_plugins); - let Plugins { macros, registrars } - = time(time_passes, "plugin loading", (), |_| - plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap())); - - let mut registry = Registry::new(&krate); - - time(time_passes, "plugin registration", (), |_| { - if sess.features.borrow().rustc_diagnostic_macros { - registry.register_macro("__diagnostic_used", - diagnostics::plugin::expand_diagnostic_used); - registry.register_macro("__register_diagnostic", - diagnostics::plugin::expand_register_diagnostic); - registry.register_macro("__build_diagnostic_array", - diagnostics::plugin::expand_build_diagnostic_array); - } - - for ®istrar in registrars.iter() { - registrar(&mut registry); - } - }); - - let Registry { syntax_exts, lint_passes, lint_groups, .. } = registry; - - { - let mut ls = sess.lint_store.borrow_mut(); - for pass in lint_passes.into_iter() { - ls.register_pass(Some(sess), true, pass); - } - - for (name, to) in lint_groups.into_iter() { - ls.register_group(Some(sess), true, name, to); - } - } - - // Lint plugins are registered; now we can process command line flags. - if sess.opts.describe_lints { - super::describe_lints(&*sess.lint_store.borrow(), true); - return None; - } - sess.lint_store.borrow_mut().process_command_line(sess); - - // Abort if there are errors from lint processing or a plugin registrar. - sess.abort_if_errors(); - - krate = time(time_passes, "expansion", (krate, macros, syntax_exts), - |(krate, macros, syntax_exts)| { - // Windows dlls do not have rpaths, so they don't know how to find their - // dependencies. It's up to us to tell the system where to find all the - // dependent dlls. Note that this uses cfg!(windows) as opposed to - // targ_cfg because syntax extensions are always loaded for the host - // compiler, not for the target. - let mut _old_path = String::new(); - if cfg!(windows) { - _old_path = os::getenv("PATH").unwrap_or(_old_path); - let mut new_path = sess.host_filesearch().get_dylib_search_paths(); - new_path.extend(os::split_paths(_old_path.as_slice()).into_iter()); - os::setenv("PATH", os::join_paths(new_path.as_slice()).unwrap()); - } - let cfg = syntax::ext::expand::ExpansionConfig { - crate_name: crate_name.to_string(), - deriving_hash_type_parameter: sess.features.borrow().default_type_params, - enable_quotes: sess.features.borrow().quote, - recursion_limit: sess.recursion_limit.get(), - }; - let ret = syntax::ext::expand::expand_crate(&sess.parse_sess, - cfg, - macros, - syntax_exts, - krate); - if cfg!(windows) { - os::setenv("PATH", _old_path); - } - ret - } - ); - - // JBC: make CFG processing part of expansion to avoid this problem: - - // strip again, in case expansion added anything with a #[cfg]. - krate = time(time_passes, "configuration 2", krate, |krate| - syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); - - krate = time(time_passes, "maybe building test harness", krate, |krate| - syntax::test::modify_for_testing(&sess.parse_sess, - &sess.opts.cfg, - krate, - sess.diagnostic())); - - krate = time(time_passes, "prelude injection", krate, |krate| - syntax::std_inject::maybe_inject_prelude(krate)); - - time(time_passes, "checking that all macro invocations are gone", &krate, |krate| - syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); - - Some(krate) -} - -pub fn assign_node_ids_and_map<'ast>(sess: &Session, - forest: &'ast mut ast_map::Forest) - -> ast_map::Map<'ast> { - struct NodeIdAssigner<'a> { - sess: &'a Session - } - - impl<'a> ast_map::FoldOps for NodeIdAssigner<'a> { - fn new_id(&self, old_id: ast::NodeId) -> ast::NodeId { - assert_eq!(old_id, ast::DUMMY_NODE_ID); - self.sess.next_node_id() - } - } - - let map = time(sess.time_passes(), "assigning node ids and indexing ast", forest, |forest| - ast_map::map_crate(forest, NodeIdAssigner { sess: sess })); - - if sess.opts.debugging_opts & config::AST_JSON != 0 { - let mut stdout = io::BufferedWriter::new(io::stdout()); - let mut json = json::PrettyEncoder::new(&mut stdout); - // unwrapping so IoError isn't ignored - map.krate().encode(&mut json).unwrap(); - } - - map -} - -pub struct CrateAnalysis<'tcx> { - pub exp_map2: middle::resolve::ExportMap2, - pub exported_items: middle::privacy::ExportedItems, - pub public_items: middle::privacy::PublicItems, - pub ty_cx: ty::ctxt<'tcx>, - pub reachable: NodeSet, - pub name: String, -} - - -/// Run the resolution, typechecking, region checking and other -/// miscellaneous analysis passes on the crate. Return various -/// structures carrying the results of the analysis. -pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, - ast_map: ast_map::Map<'tcx>, - type_arena: &'tcx TypedArena, - name: String) -> CrateAnalysis<'tcx> { - let time_passes = sess.time_passes(); - let krate = ast_map.krate(); - - time(time_passes, "external crate/lib resolution", (), |_| - creader::read_crates(&sess, krate)); - - let lang_items = time(time_passes, "language item collection", (), |_| - middle::lang_items::collect_language_items(krate, &sess)); - - let middle::resolve::CrateMap { - def_map, - freevars, - capture_mode_map, - exp_map2, - trait_map, - external_exports, - last_private_map - } = - time(time_passes, "resolution", (), |_| - middle::resolve::resolve_crate(&sess, &lang_items, krate)); - - // Discard MTWT tables that aren't required past resolution. - syntax::ext::mtwt::clear_tables(); - - let named_region_map = time(time_passes, "lifetime resolution", (), - |_| middle::resolve_lifetime::krate(&sess, krate)); - - time(time_passes, "looking for entry point", (), - |_| middle::entry::find_entry_point(&sess, &ast_map)); - - sess.plugin_registrar_fn.set( - time(time_passes, "looking for plugin registrar", (), |_| - plugin::build::find_plugin_registrar( - sess.diagnostic(), krate))); - - let region_map = time(time_passes, "region resolution", (), |_| - middle::region::resolve_crate(&sess, krate)); - - time(time_passes, "loop checking", (), |_| - middle::check_loop::check_crate(&sess, krate)); - - let stability_index = time(time_passes, "stability index", (), |_| - stability::Index::build(krate)); - - time(time_passes, "static item recursion checking", (), |_| - middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map)); - - let ty_cx = ty::mk_ctxt(sess, - type_arena, - def_map, - named_region_map, - ast_map, - freevars, - capture_mode_map, - region_map, - lang_items, - stability_index); - - // passes are timed inside typeck - typeck::check_crate(&ty_cx, trait_map); - - time(time_passes, "check static items", (), |_| - middle::check_static::check_crate(&ty_cx)); - - // These next two const passes can probably be merged - time(time_passes, "const marking", (), |_| - middle::const_eval::process_crate(&ty_cx)); - - time(time_passes, "const checking", (), |_| - middle::check_const::check_crate(&ty_cx)); - - let maps = (external_exports, last_private_map); - let (exported_items, public_items) = - time(time_passes, "privacy checking", maps, |(a, b)| - middle::privacy::check_crate(&ty_cx, &exp_map2, a, b)); - - time(time_passes, "intrinsic checking", (), |_| - middle::intrinsicck::check_crate(&ty_cx)); - - time(time_passes, "effect checking", (), |_| - middle::effect::check_crate(&ty_cx)); - - time(time_passes, "match checking", (), |_| - middle::check_match::check_crate(&ty_cx)); - - time(time_passes, "liveness checking", (), |_| - middle::liveness::check_crate(&ty_cx)); - - time(time_passes, "borrow checking", (), |_| - middle::borrowck::check_crate(&ty_cx)); - - time(time_passes, "rvalue checking", (), |_| - middle::check_rvalues::check_crate(&ty_cx, krate)); - - // Avoid overwhelming user with errors if type checking failed. - // I'm not sure how helpful this is, to be honest, but it avoids a - // lot of annoying errors in the compile-fail tests (basically, - // lint warnings and so on -- kindck used to do this abort, but - // kindck is gone now). -nmatsakis - ty_cx.sess.abort_if_errors(); - - let reachable_map = - time(time_passes, "reachability checking", (), |_| - reachable::find_reachable(&ty_cx, &exported_items)); - - time(time_passes, "death checking", (), |_| { - middle::dead::check_crate(&ty_cx, - &exported_items, - &reachable_map) - }); - - time(time_passes, "lint checking", (), |_| - lint::check_crate(&ty_cx, &exported_items)); - - CrateAnalysis { - exp_map2: exp_map2, - ty_cx: ty_cx, - exported_items: exported_items, - public_items: public_items, - reachable: reachable_map, - name: name, - } -} - -pub fn phase_save_analysis(sess: &Session, - krate: &ast::Crate, - analysis: &CrateAnalysis, - odir: &Option) { - if (sess.opts.debugging_opts & config::SAVE_ANALYSIS) == 0 { - return; - } - time(sess.time_passes(), "save analysis", krate, |krate| - middle::save::process_crate(sess, krate, analysis, odir)); -} - -pub struct ModuleTranslation { - pub llcx: ContextRef, - pub llmod: ModuleRef, -} - -pub struct CrateTranslation { - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub link: LinkMeta, - pub metadata: Vec, - pub reachable: Vec, - pub crate_formats: dependency_format::Dependencies, - pub no_builtins: bool, -} - -/// Run the translation phase to LLVM, after which the AST and analysis can -/// be discarded. -pub fn phase_4_translate_to_llvm<'tcx>(analysis: CrateAnalysis<'tcx>) - -> (ty::ctxt<'tcx>, CrateTranslation) { - let time_passes = analysis.ty_cx.sess.time_passes(); - - time(time_passes, "resolving dependency formats", (), |_| - dependency_format::calculate(&analysis.ty_cx)); - - // Option dance to work around the lack of stack once closures. - time(time_passes, "translation", analysis, |analysis| - trans::base::trans_crate(analysis)) -} - -/// Run LLVM itself, producing a bitcode file, assembly file or object file -/// as a side effect. -pub fn phase_5_run_llvm_passes(sess: &Session, - trans: &CrateTranslation, - outputs: &OutputFilenames) { - if sess.opts.cg.no_integrated_as { - let output_type = write::OutputTypeAssembly; - - time(sess.time_passes(), "LLVM passes", (), |_| - write::run_passes(sess, trans, &[output_type], outputs)); - - write::run_assembler(sess, outputs); - - // Remove assembly source, unless --save-temps was specified - if !sess.opts.cg.save_temps { - fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap(); - } - } else { - time(sess.time_passes(), "LLVM passes", (), |_| - write::run_passes(sess, - trans, - sess.opts.output_types.as_slice(), - outputs)); - } - - sess.abort_if_errors(); -} - -/// Run the linker on any artifacts that resulted from the LLVM run. -/// This should produce either a finished executable or library. -pub fn phase_6_link_output(sess: &Session, - trans: &CrateTranslation, - outputs: &OutputFilenames) { - let old_path = os::getenv("PATH").unwrap_or_else(||String::new()); - let mut new_path = sess.host_filesearch().get_tools_search_paths(); - new_path.extend(os::split_paths(old_path.as_slice()).into_iter()); - os::setenv("PATH", os::join_paths(new_path.as_slice()).unwrap()); - - time(sess.time_passes(), "linking", (), |_| - link::link_binary(sess, - trans, - outputs, - trans.link.crate_name.as_slice())); - - os::setenv("PATH", old_path); -} - -pub fn stop_after_phase_3(sess: &Session) -> bool { - if sess.opts.no_trans { - debug!("invoked with --no-trans, returning early from compile_input"); - return true; - } - return false; -} - -pub fn stop_after_phase_1(sess: &Session) -> bool { - if sess.opts.parse_only { - debug!("invoked with --parse-only, returning early from compile_input"); - return true; - } - if sess.show_span() { - return true; - } - return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0; -} - -pub fn stop_after_phase_2(sess: &Session) -> bool { - if sess.opts.no_analysis { - debug!("invoked with --no-analysis, returning early from compile_input"); - return true; - } - return sess.opts.debugging_opts & config::AST_JSON != 0; -} - -pub fn stop_after_phase_5(sess: &Session) -> bool { - if !sess.opts.output_types.iter().any(|&i| i == write::OutputTypeExe) { - debug!("not building executable, returning early from compile_input"); - return true; - } - return false; -} - -fn escape_dep_filename(filename: &str) -> String { - // Apparently clang and gcc *only* escape spaces: - // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 - filename.replace(" ", "\\ ") -} - -fn write_out_deps(sess: &Session, - input: &Input, - outputs: &OutputFilenames, - id: &str) { - - let mut out_filenames = Vec::new(); - for output_type in sess.opts.output_types.iter() { - let file = outputs.path(*output_type); - match *output_type { - write::OutputTypeExe => { - for output in sess.crate_types.borrow().iter() { - let p = link::filename_for_input(sess, *output, - id, &file); - out_filenames.push(p); - } - } - _ => { out_filenames.push(file); } - } - } - - // Write out dependency rules to the dep-info file if requested with - // --dep-info - let deps_filename = match sess.opts.write_dependency_info { - // Use filename from --dep-file argument if given - (true, Some(ref filename)) => filename.clone(), - // Use default filename: crate source filename with extension replaced - // by ".d" - (true, None) => match *input { - FileInput(..) => outputs.with_extension("d"), - StrInput(..) => { - sess.warn("can not write --dep-info without a filename \ - when compiling stdin."); - return - }, - }, - _ => return, - }; - - let result = (|| -> io::IoResult<()> { - // Build a list of files used to compile the output and - // write Makefile-compatible dependency rules - let files: Vec = sess.codemap().files.borrow() - .iter().filter(|fmap| fmap.is_real_file()) - .map(|fmap| escape_dep_filename(fmap.name.as_slice())) - .collect(); - let mut file = try!(io::File::create(&deps_filename)); - for path in out_filenames.iter() { - try!(write!(&mut file as &mut Writer, - "{}: {}\n\n", path.display(), files.connect(" "))); - } - Ok(()) - })(); - - match result { - Ok(()) => {} - Err(e) => { - sess.fatal(format!("error writing dependencies to `{}`: {}", - deps_filename.display(), e).as_slice()); - } - } -} - -pub fn collect_crate_types(session: &Session, - attrs: &[ast::Attribute]) -> Vec { - // Unconditionally collect crate types from attributes to make them used - let attr_types: Vec = attrs.iter().filter_map(|a| { - if a.check_name("crate_type") { - match a.value_str() { - Some(ref n) if n.equiv(&("rlib")) => { - Some(config::CrateTypeRlib) - } - Some(ref n) if n.equiv(&("dylib")) => { - Some(config::CrateTypeDylib) - } - Some(ref n) if n.equiv(&("lib")) => { - Some(config::default_lib_output()) - } - Some(ref n) if n.equiv(&("staticlib")) => { - Some(config::CrateTypeStaticlib) - } - Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), - Some(_) => { - session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - a.span, - "invalid `crate_type` \ - value".to_string()); - None - } - _ => { - session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - a.span, - "`crate_type` requires a \ - value".to_string()); - None - } - } - } else { - None - } - }).collect(); - - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec!(config::CrateTypeExecutable) - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - let mut base = session.opts.crate_types.clone(); - if base.len() == 0 { - base.extend(attr_types.into_iter()); - if base.len() == 0 { - base.push(link::default_output_for_target(session)); - } - base.as_mut_slice().sort(); - base.dedup(); - } - - base.into_iter().filter(|crate_type| { - let res = !link::invalid_output_for_target(session, *crate_type); - - if !res { - session.warn(format!("dropping unsupported crate type `{}` \ - for target `{}`", - *crate_type, session.opts.target_triple).as_slice()); - } - - res - }).collect() -} - -pub fn collect_crate_metadata(session: &Session, - _attrs: &[ast::Attribute]) -> Vec { - session.opts.cg.metadata.clone() -} - -#[deriving(Clone)] -pub struct OutputFilenames { - pub out_directory: Path, - pub out_filestem: String, - pub single_output_file: Option, - extra: String, -} - -impl OutputFilenames { - pub fn path(&self, flavor: write::OutputType) -> Path { - match self.single_output_file { - Some(ref path) => return path.clone(), - None => {} - } - self.temp_path(flavor) - } - - pub fn temp_path(&self, flavor: write::OutputType) -> Path { - let base = self.out_directory.join(self.filestem()); - match flavor { - write::OutputTypeBitcode => base.with_extension("bc"), - write::OutputTypeAssembly => base.with_extension("s"), - write::OutputTypeLlvmAssembly => base.with_extension("ll"), - write::OutputTypeObject => base.with_extension("o"), - write::OutputTypeExe => base, - } - } - - pub fn with_extension(&self, extension: &str) -> Path { - self.out_directory.join(self.filestem()).with_extension(extension) - } - - fn filestem(&self) -> String { - format!("{}{}", self.out_filestem, self.extra) - } -} - -pub fn build_output_filenames(input: &Input, - odir: &Option, - ofile: &Option, - attrs: &[ast::Attribute], - sess: &Session) - -> OutputFilenames { - match *ofile { - None => { - // "-" as input file will cause the parser to read from stdin so we - // have to make up a name - // We want to toss everything after the final '.' - let dirpath = match *odir { - Some(ref d) => d.clone(), - None => Path::new(".") - }; - - // If a crate name is present, we use it as the link name - let stem = sess.opts.crate_name.clone().or_else(|| { - attr::find_crate_name(attrs).map(|n| n.get().to_string()) - }).unwrap_or(input.filestem()); - - OutputFilenames { - out_directory: dirpath, - out_filestem: stem, - single_output_file: None, - extra: sess.opts.cg.extra_filename.clone(), - } - } - - Some(ref out_file) => { - let ofile = if sess.opts.output_types.len() > 1 { - sess.warn("ignoring specified output filename because multiple \ - outputs were requested"); - None - } else { - Some(out_file.clone()) - }; - if *odir != None { - sess.warn("ignoring --out-dir flag due to -o flag."); - } - OutputFilenames { - out_directory: out_file.dir_path(), - out_filestem: out_file.filestem_str().unwrap().to_string(), - single_output_file: ofile, - extra: sess.opts.cg.extra_filename.clone(), - } - } - } -} diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs deleted file mode 100644 index 2087434ccd9..00000000000 --- a/src/librustc/driver/mod.rs +++ /dev/null @@ -1,510 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use syntax::diagnostic; - -use back::link; -use driver::driver::{Input, FileInput, StrInput}; -use driver::session::{Session, build_session}; -use lint::Lint; -use lint; -use metadata; - -use std::any::AnyRefExt; -use std::io; -use std::os; -use std::task::TaskBuilder; - -use syntax::ast; -use syntax::parse; -use syntax::diagnostic::Emitter; -use syntax::diagnostics; - -use getopts; - -pub mod driver; -pub mod session; -pub mod config; -pub mod pretty; - -pub fn run(args: Vec) -> int { - monitor(proc() run_compiler(args.as_slice())); - 0 -} - -static BUG_REPORT_URL: &'static str = - "http://doc.rust-lang.org/complement-bugreport.html"; - -fn run_compiler(args: &[String]) { - let matches = match handle_options(args.to_vec()) { - Some(matches) => matches, - None => return - }; - - let descriptions = diagnostics::registry::Registry::new(&super::DIAGNOSTICS); - match matches.opt_str("explain") { - Some(ref code) => { - match descriptions.find_description(code.as_slice()) { - Some(ref description) => { - println!("{}", description); - } - None => { - early_error(format!("no extended information for {}", code).as_slice()); - } - } - return; - }, - None => () - } - - let sopts = config::build_session_options(&matches); - let (input, input_file_path) = match matches.free.len() { - 0u => { - if sopts.describe_lints { - let mut ls = lint::LintStore::new(); - ls.register_builtin(None); - describe_lints(&ls, false); - return; - } - early_error("no input filename given"); - } - 1u => { - let ifile = matches.free[0].as_slice(); - if ifile == "-" { - let contents = io::stdin().read_to_end().unwrap(); - let src = String::from_utf8(contents).unwrap(); - (StrInput(src), None) - } else { - (FileInput(Path::new(ifile)), Some(Path::new(ifile))) - } - } - _ => early_error("multiple input filenames provided") - }; - - let sess = build_session(sopts, input_file_path, descriptions); - let cfg = config::build_configuration(&sess); - let odir = matches.opt_str("out-dir").map(|o| Path::new(o)); - let ofile = matches.opt_str("o").map(|o| Path::new(o)); - - let pretty = matches.opt_default("pretty", "normal").map(|a| { - pretty::parse_pretty(&sess, a.as_slice()) - }); - match pretty { - Some((ppm, opt_uii)) => { - pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile); - return; - } - None => {/* continue */ } - } - - let r = matches.opt_strs("Z"); - if r.contains(&("ls".to_string())) { - match input { - FileInput(ref ifile) => { - let mut stdout = io::stdout(); - list_metadata(&sess, &(*ifile), &mut stdout).unwrap(); - } - StrInput(_) => { - early_error("can not list metadata for stdin"); - } - } - return; - } - - if print_crate_info(&sess, &input, &odir, &ofile) { - return; - } - - driver::compile_input(sess, cfg, &input, &odir, &ofile, None); -} - -/// Returns a version string such as "0.12.0-dev". -pub fn release_str() -> Option<&'static str> { - option_env!("CFG_RELEASE") -} - -/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built. -pub fn commit_hash_str() -> Option<&'static str> { - option_env!("CFG_VER_HASH") -} - -/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string. -pub fn commit_date_str() -> Option<&'static str> { - option_env!("CFG_VER_DATE") -} - -/// Prints version information and returns None on success or an error -/// message on failure. -pub fn version(binary: &str, matches: &getopts::Matches) -> Option { - let verbose = match matches.opt_str("version").as_ref().map(|s| s.as_slice()) { - None => false, - Some("verbose") => true, - Some(s) => return Some(format!("Unrecognized argument: {}", s)) - }; - - println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version")); - if verbose { - fn unw(x: Option<&str>) -> &str { x.unwrap_or("unknown") } - println!("binary: {}", binary); - println!("commit-hash: {}", unw(commit_hash_str())); - println!("commit-date: {}", unw(commit_date_str())); - println!("host: {}", driver::host_triple()); - println!("release: {}", unw(release_str())); - } - None -} - -fn usage() { - let message = format!("Usage: rustc [OPTIONS] INPUT"); - println!("{}\n\ -Additional help: - -C help Print codegen options - -W help Print 'lint' options and default settings - -Z help Print internal options for debugging rustc\n", - getopts::usage(message.as_slice(), - config::optgroups().as_slice())); -} - -fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) { - println!(" -Available lint options: - -W Warn about - -A Allow - -D Deny - -F Forbid (deny, and deny all overrides) - -"); - - fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { - let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect(); - lints.sort_by(|x: &&Lint, y: &&Lint| { - match x.default_level.cmp(&y.default_level) { - // The sort doesn't case-fold but it's doubtful we care. - Equal => x.name.cmp(y.name), - r => r, - } - }); - lints - } - - fn sort_lint_groups(lints: Vec<(&'static str, Vec, bool)>) - -> Vec<(&'static str, Vec)> { - let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect(); - lints.sort_by(|&(x, _): &(&'static str, Vec), - &(y, _): &(&'static str, Vec)| { - x.cmp(y) - }); - lints - } - - let (plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p); - let plugin = sort_lints(plugin); - let builtin = sort_lints(builtin); - - let (plugin_groups, builtin_groups) = lint_store.get_lint_groups().partitioned(|&(_, _, p)| p); - let plugin_groups = sort_lint_groups(plugin_groups); - let builtin_groups = sort_lint_groups(builtin_groups); - - let max_name_len = plugin.iter().chain(builtin.iter()) - .map(|&s| s.name.width(true)) - .max().unwrap_or(0); - let padded = |x: &str| { - let mut s = " ".repeat(max_name_len - x.char_len()); - s.push_str(x); - s - }; - - println!("Lint checks provided by rustc:\n"); - println!(" {} {:7.7s} {}", padded("name"), "default", "meaning"); - println!(" {} {:7.7s} {}", padded("----"), "-------", "-------"); - - let print_lints = |lints: Vec<&Lint>| { - for lint in lints.into_iter() { - let name = lint.name_lower().replace("_", "-"); - println!(" {} {:7.7s} {}", - padded(name.as_slice()), lint.default_level.as_str(), lint.desc); - } - println!("\n"); - }; - - print_lints(builtin); - - - - let max_name_len = plugin_groups.iter().chain(builtin_groups.iter()) - .map(|&(s, _)| s.width(true)) - .max().unwrap_or(0); - let padded = |x: &str| { - let mut s = " ".repeat(max_name_len - x.char_len()); - s.push_str(x); - s - }; - - println!("Lint groups provided by rustc:\n"); - println!(" {} {}", padded("name"), "sub-lints"); - println!(" {} {}", padded("----"), "---------"); - - let print_lint_groups = |lints: Vec<(&'static str, Vec)>| { - for (name, to) in lints.into_iter() { - let name = name.chars().map(|x| x.to_lowercase()) - .collect::().replace("_", "-"); - let desc = to.into_iter().map(|x| x.as_str().replace("_", "-")) - .collect::>().connect(", "); - println!(" {} {}", - padded(name.as_slice()), desc); - } - println!("\n"); - }; - - print_lint_groups(builtin_groups); - - match (loaded_plugins, plugin.len(), plugin_groups.len()) { - (false, 0, _) | (false, _, 0) => { - println!("Compiler plugins can provide additional lints and lint groups. To see a \ - listing of these, re-run `rustc -W help` with a crate filename."); - } - (false, _, _) => panic!("didn't load lint plugins but got them anyway!"), - (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."), - (true, l, g) => { - if l > 0 { - println!("Lint checks provided by plugins loaded by this crate:\n"); - print_lints(plugin); - } - if g > 0 { - println!("Lint groups provided by plugins loaded by this crate:\n"); - print_lint_groups(plugin_groups); - } - } - } -} - -fn describe_debug_flags() { - println!("\nAvailable debug options:\n"); - let r = config::debugging_opts_map(); - for tuple in r.iter() { - match *tuple { - (ref name, ref desc, _) => { - println!(" -Z {:>20s} -- {}", *name, *desc); - } - } - } -} - -fn describe_codegen_flags() { - println!("\nAvailable codegen options:\n"); - for &(name, _, opt_type_desc, desc) in config::CG_OPTIONS.iter() { - let (width, extra) = match opt_type_desc { - Some(..) => (21, "=val"), - None => (25, "") - }; - println!(" -C {:>width$s}{} -- {}", name.replace("_", "-"), - extra, desc, width=width); - } -} - -/// Process command line options. Emits messages as appropriate. If compilation -/// should continue, returns a getopts::Matches object parsed from args, otherwise -/// returns None. -pub fn handle_options(mut args: Vec) -> Option { - // Throw away the first argument, the name of the binary - let _binary = args.remove(0).unwrap(); - - if args.is_empty() { - usage(); - return None; - } - - let matches = - match getopts::getopts(args.as_slice(), config::optgroups().as_slice()) { - Ok(m) => m, - Err(f) => { - early_error(f.to_string().as_slice()); - } - }; - - if matches.opt_present("h") || matches.opt_present("help") { - usage(); - return None; - } - - // Don't handle -W help here, because we might first load plugins. - - let r = matches.opt_strs("Z"); - if r.iter().any(|x| x.as_slice() == "help") { - describe_debug_flags(); - return None; - } - - let cg_flags = matches.opt_strs("C"); - if cg_flags.iter().any(|x| x.as_slice() == "help") { - describe_codegen_flags(); - return None; - } - - if cg_flags.contains(&"passes=list".to_string()) { - unsafe { ::llvm::LLVMRustPrintPasses(); } - return None; - } - - if matches.opt_present("version") { - match version("rustc", &matches) { - Some(err) => early_error(err.as_slice()), - None => return None - } - } - - Some(matches) -} - -fn print_crate_info(sess: &Session, - input: &Input, - odir: &Option, - ofile: &Option) - -> bool { - let (crate_name, crate_file_name) = sess.opts.print_metas; - // these nasty nested conditions are to avoid doing extra work - if crate_name || crate_file_name { - let attrs = parse_crate_attrs(sess, input); - let t_outputs = driver::build_output_filenames(input, - odir, - ofile, - attrs.as_slice(), - sess); - let id = link::find_crate_name(Some(sess), attrs.as_slice(), input); - - if crate_name { - println!("{}", id); - } - if crate_file_name { - let crate_types = driver::collect_crate_types(sess, attrs.as_slice()); - let metadata = driver::collect_crate_metadata(sess, attrs.as_slice()); - *sess.crate_metadata.borrow_mut() = metadata; - for &style in crate_types.iter() { - let fname = link::filename_for_input(sess, style, id.as_slice(), - &t_outputs.with_extension("")); - println!("{}", fname.filename_display()); - } - } - - true - } else { - false - } -} - -fn parse_crate_attrs(sess: &Session, input: &Input) -> - Vec { - let result = match *input { - FileInput(ref ifile) => { - parse::parse_crate_attrs_from_file(ifile, - Vec::new(), - &sess.parse_sess) - } - StrInput(ref src) => { - parse::parse_crate_attrs_from_source_str( - driver::anon_src().to_string(), - src.to_string(), - Vec::new(), - &sess.parse_sess) - } - }; - result.into_iter().collect() -} - -pub fn early_error(msg: &str) -> ! { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); - emitter.emit(None, msg, None, diagnostic::Fatal); - panic!(diagnostic::FatalError); -} - -pub fn early_warn(msg: &str) { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); - emitter.emit(None, msg, None, diagnostic::Warning); -} - -pub fn list_metadata(sess: &Session, path: &Path, - out: &mut io::Writer) -> io::IoResult<()> { - metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, path, out) -} - -/// Run a procedure which will detect failures in the compiler and print nicer -/// error messages rather than just failing the test. -/// -/// The diagnostic emitter yielded to the procedure should be used for reporting -/// errors of the compiler. -pub fn monitor(f: proc():Send) { - // FIXME: This is a hack for newsched since it doesn't support split stacks. - // rustc needs a lot of stack! When optimizations are disabled, it needs - // even *more* stack than usual as well. - #[cfg(rtopt)] - static STACK_SIZE: uint = 6000000; // 6MB - #[cfg(not(rtopt))] - static STACK_SIZE: uint = 20000000; // 20MB - - let (tx, rx) = channel(); - let w = io::ChanWriter::new(tx); - let mut r = io::ChanReader::new(rx); - - let mut task = TaskBuilder::new().named("rustc").stderr(box w); - - // FIXME: Hacks on hacks. If the env is trying to override the stack size - // then *don't* set it explicitly. - if os::getenv("RUST_MIN_STACK").is_none() { - task = task.stack_size(STACK_SIZE); - } - - match task.try(f) { - Ok(()) => { /* fallthrough */ } - Err(value) => { - // Task panicked without emitting a fatal diagnostic - if !value.is::() { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); - - // a .span_bug or .bug call has already printed what - // it wants to print. - if !value.is::() { - emitter.emit( - None, - "unexpected panic", - None, - diagnostic::Bug); - } - - let xs = [ - "the compiler unexpectedly panicked. this is a bug.".to_string(), - format!("we would appreciate a bug report: {}", - BUG_REPORT_URL), - "run with `RUST_BACKTRACE=1` for a backtrace".to_string(), - ]; - for note in xs.iter() { - emitter.emit(None, note.as_slice(), None, diagnostic::Note) - } - - match r.read_to_string() { - Ok(s) => println!("{}", s), - Err(e) => { - emitter.emit(None, - format!("failed to read internal \ - stderr: {}", - e).as_slice(), - None, - diagnostic::Error) - } - } - } - - // Panic so the process returns a failure code, but don't pollute the - // output with some unnecessary failure messages, we've already - // printed everything that we needed to. - io::stdio::set_stderr(box io::util::NullWriter); - panic!(); - } - } -} diff --git a/src/librustc/driver/pretty.rs b/src/librustc/driver/pretty.rs deleted file mode 100644 index 8099bf314c2..00000000000 --- a/src/librustc/driver/pretty.rs +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The various pretty print routines. - -pub use self::UserIdentifiedItem::*; -pub use self::PpSourceMode::*; -pub use self::PpMode::*; -use self::NodesMatchingUII::*; - -use back::link; - -use driver::config; -use driver::driver::{mod, CrateAnalysis}; -use driver::session::Session; - -use middle::ty; -use middle::borrowck::{mod, FnPartsWithCFG}; -use middle::borrowck::graphviz as borrowck_dot; -use middle::cfg; -use middle::cfg::graphviz::LabelledCFG; - -use util::ppaux; - -use syntax::ast; -use syntax::ast_map::{mod, blocks, NodePrinter}; -use syntax::print::{pp, pprust}; - -use graphviz as dot; - -use std::io::{mod, MemReader}; -use std::option; -use std::str::FromStr; -use arena::TypedArena; - -#[deriving(PartialEq, Show)] -pub enum PpSourceMode { - PpmNormal, - PpmExpanded, - PpmTyped, - PpmIdentified, - PpmExpandedIdentified, - PpmExpandedHygiene, -} - -#[deriving(PartialEq, Show)] -pub enum PpMode { - PpmSource(PpSourceMode), - PpmFlowGraph, -} - -pub fn parse_pretty(sess: &Session, name: &str) -> (PpMode, Option) { - let mut split = name.splitn(1, '='); - let first = split.next().unwrap(); - let opt_second = split.next(); - let first = match first { - "normal" => PpmSource(PpmNormal), - "expanded" => PpmSource(PpmExpanded), - "typed" => PpmSource(PpmTyped), - "expanded,identified" => PpmSource(PpmExpandedIdentified), - "expanded,hygiene" => PpmSource(PpmExpandedHygiene), - "identified" => PpmSource(PpmIdentified), - "flowgraph" => PpmFlowGraph, - _ => { - sess.fatal(format!( - "argument to `pretty` must be one of `normal`, \ - `expanded`, `flowgraph=`, `typed`, `identified`, \ - or `expanded,identified`; got {}", name).as_slice()); - } - }; - let opt_second = opt_second.and_then::(from_str); - (first, opt_second) -} - - - -// This slightly awkward construction is to allow for each PpMode to -// choose whether it needs to do analyses (which can consume the -// Session) and then pass through the session (now attached to the -// analysis results) on to the chosen pretty-printer, along with the -// `&PpAnn` object. -// -// Note that since the `&PrinterSupport` is freshly constructed on each -// call, it would not make sense to try to attach the lifetime of `self` -// to the lifetime of the `&PrinterObject`. -// -// (The `use_once_payload` is working around the current lack of once -// functions in the compiler.) - -impl PpSourceMode { - /// Constructs a `PrinterSupport` object and passes it to `f`. - fn call_with_pp_support<'tcx, A, B>(&self, - sess: Session, - ast_map: Option>, - type_arena: &'tcx TypedArena, - id: String, - payload: B, - f: |&PrinterSupport, B| -> A) -> A { - match *self { - PpmNormal | PpmExpanded => { - let annotation = NoAnn { sess: sess, ast_map: ast_map }; - f(&annotation, payload) - } - - PpmIdentified | PpmExpandedIdentified => { - let annotation = IdentifiedAnnotation { sess: sess, ast_map: ast_map }; - f(&annotation, payload) - } - PpmExpandedHygiene => { - let annotation = HygieneAnnotation { sess: sess, ast_map: ast_map }; - f(&annotation, payload) - } - PpmTyped => { - let ast_map = ast_map.expect("--pretty=typed missing ast_map"); - let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, - type_arena, id); - let annotation = TypedAnnotation { analysis: analysis }; - f(&annotation, payload) - } - } - } -} - -trait PrinterSupport<'ast>: pprust::PpAnn { - /// Provides a uniform interface for re-extracting a reference to a - /// `Session` from a value that now owns it. - fn sess<'a>(&'a self) -> &'a Session; - - /// Provides a uniform interface for re-extracting a reference to an - /// `ast_map::Map` from a value that now owns it. - fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>>; - - /// Produces the pretty-print annotation object. - /// - /// (Rust does not yet support upcasting from a trait object to - /// an object for one of its super-traits.) - fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self as &pprust::PpAnn } -} - -struct NoAnn<'ast> { - sess: Session, - ast_map: Option> -} - -impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> { - fn sess<'a>(&'a self) -> &'a Session { &self.sess } - - fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { - self.ast_map.as_ref() - } -} - -impl<'ast> pprust::PpAnn for NoAnn<'ast> {} - -struct IdentifiedAnnotation<'ast> { - sess: Session, - ast_map: Option>, -} - -impl<'ast> PrinterSupport<'ast> for IdentifiedAnnotation<'ast> { - fn sess<'a>(&'a self) -> &'a Session { &self.sess } - - fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { - self.ast_map.as_ref() - } -} - -impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> { - fn pre(&self, - s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { - match node { - pprust::NodeExpr(_) => s.popen(), - _ => Ok(()) - } - } - fn post(&self, - s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { - match node { - pprust::NodeIdent(_) | pprust::NodeName(_) => Ok(()), - - pprust::NodeItem(item) => { - try!(pp::space(&mut s.s)); - s.synth_comment(item.id.to_string()) - } - pprust::NodeBlock(blk) => { - try!(pp::space(&mut s.s)); - s.synth_comment(format!("block {}", blk.id)) - } - pprust::NodeExpr(expr) => { - try!(pp::space(&mut s.s)); - try!(s.synth_comment(expr.id.to_string())); - s.pclose() - } - pprust::NodePat(pat) => { - try!(pp::space(&mut s.s)); - s.synth_comment(format!("pat {}", pat.id)) - } - } - } -} - -struct HygieneAnnotation<'ast> { - sess: Session, - ast_map: Option>, -} - -impl<'ast> PrinterSupport<'ast> for HygieneAnnotation<'ast> { - fn sess<'a>(&'a self) -> &'a Session { &self.sess } - - fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { - self.ast_map.as_ref() - } -} - -impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { - fn post(&self, - s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { - match node { - pprust::NodeIdent(&ast::Ident { name: ast::Name(nm), ctxt }) => { - try!(pp::space(&mut s.s)); - // FIXME #16420: this doesn't display the connections - // between syntax contexts - s.synth_comment(format!("{}#{}", nm, ctxt)) - } - pprust::NodeName(&ast::Name(nm)) => { - try!(pp::space(&mut s.s)); - s.synth_comment(nm.to_string()) - } - _ => Ok(()) - } - } -} - - -struct TypedAnnotation<'tcx> { - analysis: CrateAnalysis<'tcx>, -} - -impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> { - fn sess<'a>(&'a self) -> &'a Session { &self.analysis.ty_cx.sess } - - fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'tcx>> { - Some(&self.analysis.ty_cx.map) - } -} - -impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> { - fn pre(&self, - s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { - match node { - pprust::NodeExpr(_) => s.popen(), - _ => Ok(()) - } - } - fn post(&self, - s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { - let tcx = &self.analysis.ty_cx; - match node { - pprust::NodeExpr(expr) => { - try!(pp::space(&mut s.s)); - try!(pp::word(&mut s.s, "as")); - try!(pp::space(&mut s.s)); - try!(pp::word(&mut s.s, - ppaux::ty_to_string( - tcx, - ty::expr_ty(tcx, expr)).as_slice())); - s.pclose() - } - _ => Ok(()) - } - } -} - -fn gather_flowgraph_variants(sess: &Session) -> Vec { - let print_loans = config::FLOWGRAPH_PRINT_LOANS; - let print_moves = config::FLOWGRAPH_PRINT_MOVES; - let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS; - let print_all = config::FLOWGRAPH_PRINT_ALL; - let opt = |print_which| sess.debugging_opt(print_which); - let mut variants = Vec::new(); - if opt(print_all) || opt(print_loans) { - variants.push(borrowck_dot::Loans); - } - if opt(print_all) || opt(print_moves) { - variants.push(borrowck_dot::Moves); - } - if opt(print_all) || opt(print_assigns) { - variants.push(borrowck_dot::Assigns); - } - variants -} - -#[deriving(Clone, Show)] -pub enum UserIdentifiedItem { - ItemViaNode(ast::NodeId), - ItemViaPath(Vec), -} - -impl FromStr for UserIdentifiedItem { - fn from_str(s: &str) -> Option { - let extract_path_parts = || { - let v : Vec<_> = s.split_str("::") - .map(|x|x.to_string()) - .collect(); - Some(ItemViaPath(v)) - }; - - from_str(s).map(ItemViaNode).or_else(extract_path_parts) - } -} - -enum NodesMatchingUII<'a, 'ast: 'a> { - NodesMatchingDirect(option::Item), - NodesMatchingSuffix(ast_map::NodesMatchingSuffix<'a, 'ast, String>), -} - -impl<'a, 'ast> Iterator for NodesMatchingUII<'a, 'ast> { - fn next(&mut self) -> Option { - match self { - &NodesMatchingDirect(ref mut iter) => iter.next(), - &NodesMatchingSuffix(ref mut iter) => iter.next(), - } - } -} - -impl UserIdentifiedItem { - fn reconstructed_input(&self) -> String { - match *self { - ItemViaNode(node_id) => node_id.to_string(), - ItemViaPath(ref parts) => parts.connect("::"), - } - } - - fn all_matching_node_ids<'a, 'ast>(&'a self, map: &'a ast_map::Map<'ast>) - -> NodesMatchingUII<'a, 'ast> { - match *self { - ItemViaNode(node_id) => - NodesMatchingDirect(Some(node_id).into_iter()), - ItemViaPath(ref parts) => - NodesMatchingSuffix(map.nodes_matching_suffix(parts.as_slice())), - } - } - - fn to_one_node_id(self, user_option: &str, sess: &Session, map: &ast_map::Map) -> ast::NodeId { - let fail_because = |is_wrong_because| -> ast::NodeId { - let message = - format!("{:s} needs NodeId (int) or unique \ - path suffix (b::c::d); got {:s}, which {:s}", - user_option, - self.reconstructed_input(), - is_wrong_because); - sess.fatal(message.as_slice()) - }; - - let mut saw_node = ast::DUMMY_NODE_ID; - let mut seen = 0u; - for node in self.all_matching_node_ids(map) { - saw_node = node; - seen += 1; - if seen > 1 { - fail_because("does not resolve uniquely"); - } - } - if seen == 0 { - fail_because("does not resolve to any item"); - } - - assert!(seen == 1); - return saw_node; - } -} - -fn needs_ast_map(ppm: &PpMode, opt_uii: &Option) -> bool { - match *ppm { - PpmSource(PpmNormal) | - PpmSource(PpmIdentified) => opt_uii.is_some(), - - PpmSource(PpmExpanded) | - PpmSource(PpmExpandedIdentified) | - PpmSource(PpmExpandedHygiene) | - PpmSource(PpmTyped) | - PpmFlowGraph => true - } -} - -fn needs_expansion(ppm: &PpMode) -> bool { - match *ppm { - PpmSource(PpmNormal) | - PpmSource(PpmIdentified) => false, - - PpmSource(PpmExpanded) | - PpmSource(PpmExpandedIdentified) | - PpmSource(PpmExpandedHygiene) | - PpmSource(PpmTyped) | - PpmFlowGraph => true - } -} - -pub fn pretty_print_input(sess: Session, - cfg: ast::CrateConfig, - input: &driver::Input, - ppm: PpMode, - opt_uii: Option, - ofile: Option) { - let krate = driver::phase_1_parse_input(&sess, cfg, input); - let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), input); - - let is_expanded = needs_expansion(&ppm); - let compute_ast_map = needs_ast_map(&ppm, &opt_uii); - let krate = if compute_ast_map { - match driver::phase_2_configure_and_expand(&sess, krate, id.as_slice(), None) { - None => return, - Some(k) => k - } - } else { - krate - }; - - let mut forest = ast_map::Forest::new(krate); - let type_arena = TypedArena::new(); - - let (krate, ast_map) = if compute_ast_map { - let map = driver::assign_node_ids_and_map(&sess, &mut forest); - (map.krate(), Some(map)) - } else { - (forest.krate(), None) - }; - - let src_name = driver::source_name(input); - let src = sess.codemap().get_filemap(src_name.as_slice()) - .src.as_bytes().to_vec(); - let mut rdr = MemReader::new(src); - - let out = match ofile { - None => box io::stdout() as Box, - Some(p) => { - let r = io::File::create(&p); - match r { - Ok(w) => box w as Box, - Err(e) => panic!("print-print failed to open {} due to {}", - p.display(), e), - } - } - }; - - match (ppm, opt_uii) { - (PpmSource(s), None) => - s.call_with_pp_support( - sess, ast_map, &type_arena, id, out, |annotation, out| { - debug!("pretty printing source code {}", s); - let sess = annotation.sess(); - pprust::print_crate(sess.codemap(), - sess.diagnostic(), - krate, - src_name.to_string(), - &mut rdr, - out, - annotation.pp_ann(), - is_expanded) - }), - - (PpmSource(s), Some(uii)) => - s.call_with_pp_support( - sess, ast_map, &type_arena, id, (out,uii), |annotation, (out,uii)| { - debug!("pretty printing source code {}", s); - let sess = annotation.sess(); - let ast_map = annotation.ast_map() - .expect("--pretty missing ast_map"); - let mut pp_state = - pprust::State::new_from_input(sess.codemap(), - sess.diagnostic(), - src_name.to_string(), - &mut rdr, - out, - annotation.pp_ann(), - is_expanded); - for node_id in uii.all_matching_node_ids(ast_map) { - let node = ast_map.get(node_id); - try!(pp_state.print_node(&node)); - try!(pp::space(&mut pp_state.s)); - try!(pp_state.synth_comment(ast_map.path_to_string(node_id))); - try!(pp::hardbreak(&mut pp_state.s)); - } - pp::eof(&mut pp_state.s) - }), - - (PpmFlowGraph, opt_uii) => { - debug!("pretty printing flow graph for {}", opt_uii); - let uii = opt_uii.unwrap_or_else(|| { - sess.fatal(format!("`pretty flowgraph=..` needs NodeId (int) or - unique path suffix (b::c::d)").as_slice()) - - }); - let ast_map = ast_map.expect("--pretty flowgraph missing ast_map"); - let nodeid = uii.to_one_node_id("--pretty", &sess, &ast_map); - - let node = ast_map.find(nodeid).unwrap_or_else(|| { - sess.fatal(format!("--pretty flowgraph couldn't find id: {}", - nodeid).as_slice()) - }); - - let code = blocks::Code::from_node(node); - match code { - Some(code) => { - let variants = gather_flowgraph_variants(&sess); - let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, - &type_arena, id); - print_flowgraph(variants, analysis, code, out) - } - None => { - let message = format!("--pretty=flowgraph needs \ - block, fn, or method; got {}", - node); - - // point to what was found, if there's an - // accessible span. - match ast_map.opt_span(nodeid) { - Some(sp) => sess.span_fatal(sp, message.as_slice()), - None => sess.fatal(message.as_slice()) - } - } - } - } - }.unwrap() -} - -fn print_flowgraph(variants: Vec, - analysis: CrateAnalysis, - code: blocks::Code, - mut out: W) -> io::IoResult<()> { - let ty_cx = &analysis.ty_cx; - let cfg = match code { - blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block), - blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, &*fn_like.body()), - }; - - match code { - _ if variants.len() == 0 => { - let lcfg = LabelledCFG { - ast_map: &ty_cx.map, - cfg: &cfg, - name: format!("node_{}", code.id()), - }; - let r = dot::render(&lcfg, &mut out); - return expand_err_details(r); - } - blocks::BlockCode(_) => { - ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \ - annotations requires fn-like node id."); - return Ok(()) - } - blocks::FnLikeCode(fn_like) => { - let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg); - let (bccx, analysis_data) = - borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts); - - let lcfg = LabelledCFG { - ast_map: &ty_cx.map, - cfg: &cfg, - name: format!("node_{}", code.id()), - }; - let lcfg = borrowck_dot::DataflowLabeller { - inner: lcfg, - variants: variants, - borrowck_ctxt: &bccx, - analysis_data: &analysis_data, - }; - let r = dot::render(&lcfg, &mut out); - return expand_err_details(r); - } - } - - fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> { - r.map_err(|ioerr| { - let orig_detail = ioerr.detail.clone(); - let m = "graphviz::render failed"; - io::IoError { - detail: Some(match orig_detail { - None => m.into_string(), - Some(d) => format!("{}: {}", m, d) - }), - ..ioerr - } - }) - } -} diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs deleted file mode 100644 index 35ccbb4c7b4..00000000000 --- a/src/librustc/driver/session.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use driver::config; -use driver::driver; -use metadata::cstore::CStore; -use metadata::filesearch; -use lint; -use util::nodemap::NodeMap; - -use syntax::ast::NodeId; -use syntax::codemap::Span; -use syntax::diagnostic; -use syntax::diagnostics; -use syntax::feature_gate; -use syntax::parse; -use syntax::parse::token; -use syntax::parse::ParseSess; -use syntax::{ast, codemap}; - -use std::os; -use std::cell::{Cell, RefCell}; - -// Represents the data associated with a compilation -// session for a single crate. -pub struct Session { - pub target: config::Config, - pub opts: config::Options, - pub cstore: CStore, - pub parse_sess: ParseSess, - // For a library crate, this is always none - pub entry_fn: RefCell>, - pub entry_type: Cell>, - pub plugin_registrar_fn: Cell>, - pub default_sysroot: Option, - // The name of the root source file of the crate, in the local file system. The path is always - // expected to be absolute. `None` means that there is no source file. - pub local_crate_source_file: Option, - pub working_dir: Path, - pub lint_store: RefCell, - pub lints: RefCell>>, - pub crate_types: RefCell>, - pub crate_metadata: RefCell>, - pub features: RefCell, - - /// The maximum recursion limit for potentially infinitely recursive - /// operations such as auto-dereference and monomorphization. - pub recursion_limit: Cell, -} - -impl Session { - pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { - self.diagnostic().span_fatal(sp, msg) - } - pub fn fatal(&self, msg: &str) -> ! { - self.diagnostic().handler().fatal(msg) - } - pub fn span_err(&self, sp: Span, msg: &str) { - self.diagnostic().span_err(sp, msg) - } - pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) { - self.diagnostic().span_err_with_code(sp, msg, code) - } - pub fn err(&self, msg: &str) { - self.diagnostic().handler().err(msg) - } - pub fn err_count(&self) -> uint { - self.diagnostic().handler().err_count() - } - pub fn has_errors(&self) -> bool { - self.diagnostic().handler().has_errors() - } - pub fn abort_if_errors(&self) { - self.diagnostic().handler().abort_if_errors() - } - pub fn span_warn(&self, sp: Span, msg: &str) { - self.diagnostic().span_warn(sp, msg) - } - pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { - self.diagnostic().span_warn_with_code(sp, msg, code) - } - pub fn warn(&self, msg: &str) { - self.diagnostic().handler().warn(msg) - } - pub fn span_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_note(sp, msg) - } - pub fn span_end_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_end_note(sp, msg) - } - pub fn span_help(&self, sp: Span, msg: &str) { - self.diagnostic().span_help(sp, msg) - } - pub fn fileline_note(&self, sp: Span, msg: &str) { - self.diagnostic().fileline_note(sp, msg) - } - pub fn note(&self, msg: &str) { - self.diagnostic().handler().note(msg) - } - pub fn help(&self, msg: &str) { - self.diagnostic().handler().note(msg) - } - pub fn span_bug(&self, sp: Span, msg: &str) -> ! { - self.diagnostic().span_bug(sp, msg) - } - pub fn bug(&self, msg: &str) -> ! { - self.diagnostic().handler().bug(msg) - } - pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { - self.diagnostic().span_unimpl(sp, msg) - } - pub fn unimpl(&self, msg: &str) -> ! { - self.diagnostic().handler().unimpl(msg) - } - pub fn add_lint(&self, - lint: &'static lint::Lint, - id: ast::NodeId, - sp: Span, - msg: String) { - let lint_id = lint::LintId::of(lint); - let mut lints = self.lints.borrow_mut(); - match lints.get_mut(&id) { - Some(arr) => { arr.push((lint_id, sp, msg)); return; } - None => {} - } - lints.insert(id, vec!((lint_id, sp, msg))); - } - pub fn next_node_id(&self) -> ast::NodeId { - self.parse_sess.next_node_id() - } - pub fn reserve_node_ids(&self, count: ast::NodeId) -> ast::NodeId { - self.parse_sess.reserve_node_ids(count) - } - pub fn diagnostic<'a>(&'a self) -> &'a diagnostic::SpanHandler { - &self.parse_sess.span_diagnostic - } - pub fn debugging_opt(&self, opt: u64) -> bool { - (self.opts.debugging_opts & opt) != 0 - } - pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap { - &self.parse_sess.span_diagnostic.cm - } - // This exists to help with refactoring to eliminate impossible - // cases later on - pub fn impossible_case(&self, sp: Span, msg: &str) -> ! { - self.span_bug(sp, - format!("impossible case reached: {}", msg).as_slice()); - } - pub fn verbose(&self) -> bool { self.debugging_opt(config::VERBOSE) } - pub fn time_passes(&self) -> bool { self.debugging_opt(config::TIME_PASSES) } - pub fn count_llvm_insns(&self) -> bool { - self.debugging_opt(config::COUNT_LLVM_INSNS) - } - pub fn count_type_sizes(&self) -> bool { - self.debugging_opt(config::COUNT_TYPE_SIZES) - } - pub fn time_llvm_passes(&self) -> bool { - self.debugging_opt(config::TIME_LLVM_PASSES) - } - pub fn trans_stats(&self) -> bool { self.debugging_opt(config::TRANS_STATS) } - pub fn meta_stats(&self) -> bool { self.debugging_opt(config::META_STATS) } - pub fn asm_comments(&self) -> bool { self.debugging_opt(config::ASM_COMMENTS) } - pub fn no_verify(&self) -> bool { self.debugging_opt(config::NO_VERIFY) } - pub fn borrowck_stats(&self) -> bool { self.debugging_opt(config::BORROWCK_STATS) } - pub fn print_llvm_passes(&self) -> bool { - self.debugging_opt(config::PRINT_LLVM_PASSES) - } - pub fn lto(&self) -> bool { - self.opts.cg.lto - } - pub fn no_landing_pads(&self) -> bool { - self.debugging_opt(config::NO_LANDING_PADS) - } - pub fn show_span(&self) -> bool { - self.debugging_opt(config::SHOW_SPAN) - } - pub fn sysroot<'a>(&'a self) -> &'a Path { - match self.opts.maybe_sysroot { - Some (ref sysroot) => sysroot, - None => self.default_sysroot.as_ref() - .expect("missing sysroot and default_sysroot in Session") - } - } - pub fn target_filesearch<'a>(&'a self) -> filesearch::FileSearch<'a> { - filesearch::FileSearch::new(self.sysroot(), - self.opts.target_triple.as_slice(), - &self.opts.addl_lib_search_paths) - } - pub fn host_filesearch<'a>(&'a self) -> filesearch::FileSearch<'a> { - filesearch::FileSearch::new( - self.sysroot(), - driver::host_triple(), - &self.opts.addl_lib_search_paths) - } -} - -pub fn build_session(sopts: config::Options, - local_crate_source_file: Option, - registry: diagnostics::registry::Registry) - -> Session { - let codemap = codemap::CodeMap::new(); - let diagnostic_handler = - diagnostic::default_handler(sopts.color, Some(registry)); - let span_diagnostic_handler = - diagnostic::mk_span_handler(diagnostic_handler, codemap); - - build_session_(sopts, local_crate_source_file, span_diagnostic_handler) -} - -pub fn build_session_(sopts: config::Options, - local_crate_source_file: Option, - span_diagnostic: diagnostic::SpanHandler) - -> Session { - let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - let p_s = parse::new_parse_sess_special_handler(span_diagnostic); - let default_sysroot = match sopts.maybe_sysroot { - Some(_) => None, - None => Some(filesearch::get_or_default_sysroot()) - }; - - // Make the path absolute, if necessary - let local_crate_source_file = local_crate_source_file.map(|path| - if path.is_absolute() { - path.clone() - } else { - os::getcwd().join(&path) - } - ); - - let sess = Session { - target: target_cfg, - opts: sopts, - cstore: CStore::new(token::get_ident_interner()), - parse_sess: p_s, - // For a library crate, this is always none - entry_fn: RefCell::new(None), - entry_type: Cell::new(None), - plugin_registrar_fn: Cell::new(None), - default_sysroot: default_sysroot, - local_crate_source_file: local_crate_source_file, - working_dir: os::getcwd(), - lint_store: RefCell::new(lint::LintStore::new()), - lints: RefCell::new(NodeMap::new()), - crate_types: RefCell::new(Vec::new()), - crate_metadata: RefCell::new(Vec::new()), - features: RefCell::new(feature_gate::Features::new()), - recursion_limit: Cell::new(64), - }; - - sess.lint_store.borrow_mut().register_builtin(Some(&sess)); - sess -} - -// Seems out of place, but it uses session, so I'm putting it here -pub fn expect(sess: &Session, opt: Option, msg: || -> String) -> T { - diagnostic::expect(sess.diagnostic(), opt, msg) -} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index c9a1d47f558..0da9b05d4f8 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -62,11 +62,6 @@ pub mod back { pub use rustc_back::target_strs; pub use rustc_back::x86; pub use rustc_back::x86_64; - - pub mod link; - pub mod lto; - pub mod write; - } pub mod middle { @@ -99,11 +94,9 @@ pub mod middle { pub mod region; pub mod resolve; pub mod resolve_lifetime; - pub mod save; pub mod stability; pub mod subst; pub mod traits; - pub mod trans; pub mod ty; pub mod ty_fold; pub mod typeck; @@ -112,7 +105,7 @@ pub mod middle { pub mod metadata; -pub mod driver; +pub mod session; pub mod plugin; @@ -142,9 +135,3 @@ __build_diagnostic_array!(DIAGNOSTICS) mod rustc { pub use lint; } - -pub fn main() { - let args = std::os::args(); - let result = driver::run(args); - std::os::set_exit_status(result); -} diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index aa6a021a210..e9b235a2fe3 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -30,8 +30,7 @@ use middle::subst; use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::infer; -use driver::session::Session; -use driver::early_error; +use session::{early_error, Session}; use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject}; use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; use lint::builtin; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index d9e7437216e..7f0941db6b2 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -13,8 +13,7 @@ //! Validates all used crates and extern libraries and loads their metadata use back::svh::Svh; -use driver::session::Session; -use driver::driver; +use session::{config, Session}; use metadata::cstore; use metadata::cstore::{CStore, CrateSource}; use metadata::decoder; @@ -455,7 +454,7 @@ impl<'a> PluginMetadataReader<'a> { pub fn read_plugin_metadata(&mut self, krate: &ast::ViewItem) -> PluginMetadata { let info = extract_crate_info(&self.env, krate).unwrap(); let target_triple = self.env.sess.opts.target_triple.as_slice(); - let is_cross = target_triple != driver::host_triple(); + let is_cross = target_triple != config::host_triple(); let mut should_link = info.should_link && !is_cross; let mut load_ctxt = loader::Context { sess: self.env.sess, @@ -464,7 +463,7 @@ impl<'a> PluginMetadataReader<'a> { crate_name: info.name.as_slice(), hash: None, filesearch: self.env.sess.host_filesearch(), - triple: driver::host_triple(), + triple: config::host_triple(), root: &None, rejected_via_hash: vec!(), rejected_via_triple: vec!(), @@ -481,7 +480,7 @@ impl<'a> PluginMetadataReader<'a> { if decoder::get_plugin_registrar_fn(lib.metadata.as_slice()).is_some() { let message = format!("crate `{}` contains a plugin_registrar fn but \ only a version for triple `{}` could be found (need {})", - info.ident, target_triple, driver::host_triple()); + info.ident, target_triple, config::host_triple()); self.env.sess.span_err(krate.span, message.as_slice()); // need to abort now because the syntax expansion // code will shortly attempt to load and execute diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index e7a31520915..e90bf1e2b33 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -16,7 +16,7 @@ pub use self::InlinedItemRef::*; use back::svh::Svh; -use driver::config; +use session::config; use metadata::common::*; use metadata::cstore; use metadata::decoder; diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 30bef248b47..269c15e7d81 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -214,7 +214,7 @@ use back::archive::{METADATA_FILENAME}; use back::svh::Svh; -use driver::session::Session; +use session::Session; use llvm; use llvm::{False, ObjectFile, mk_section_iter}; use llvm::archive_ro::ArchiveRO; diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 926197ad7d2..20dcf094b66 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -14,7 +14,7 @@ use metadata::common as c; use metadata::cstore as cstore; -use driver::session::Session; +use session::Session; use metadata::decoder; use middle::def; use metadata::encoder as e; diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 9fc44744cf9..01ec9bbc13d 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -9,7 +9,7 @@ // except according to those terms. use self::Context::*; -use driver::session::Session; +use session::Session; use syntax::ast; use syntax::codemap::Span; diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs index a3738f031a9..b32eb64025f 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc/middle/check_static_recursion.rs @@ -11,7 +11,7 @@ // This compiler pass detects static items that refer to themselves // recursively. -use driver::session::Session; +use session::Session; use middle::resolve; use middle::def::{DefStatic, DefConst}; diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 75e61690685..e8e90e74989 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -63,8 +63,8 @@ use syntax::ast; -use driver::session; -use driver::config; +use session; +use session::config; use metadata::cstore; use metadata::csearch; use middle::ty; diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 01854564c05..fd0162b30d8 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -9,8 +9,7 @@ // except according to those terms. -use driver::config; -use driver::session::Session; +use session::{config, Session}; use syntax::ast::{Name, NodeId, Item, ItemFn}; use syntax::ast_map; use syntax::attr; diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index ffc8a83e42e..da1c0bd649a 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -21,7 +21,7 @@ pub use self::LangItem::*; -use driver::session::Session; +use session::Session; use metadata::csearch::each_lang_item; use middle::ty; use middle::weak_lang_items; diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index dbeb2e289fb..7dcc0510a6a 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,11 +15,11 @@ // makes all other generics or inline functions that it references // reachable as well. -use driver::config; use middle::def; use middle::ty; use middle::typeck; use middle::privacy; +use session::config; use util::nodemap::NodeSet; use std::collections::HashSet; diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 2cc8f20b562..f3d62988cc0 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -21,7 +21,7 @@ Most of the documentation on regions can be found in */ -use driver::session::Session; +use session::Session; use middle::ty::{FreeRegion}; use middle::ty; use util::nodemap::{FnvHashMap, NodeMap, NodeSet}; diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 134e4bc8d18..94b52532a94 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -36,7 +36,7 @@ use self::ModuleKind::*; use self::TraitReferenceType::*; use self::FallbackChecks::*; -use driver::session::Session; +use session::Session; use lint; use metadata::csearch; use metadata::decoder::{DefLike, DlDef, DlField, DlImpl}; diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 359f58a1100..4077629f76d 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -20,7 +20,7 @@ pub use self::DefRegion::*; use self::ScopeChain::*; -use driver::session::Session; +use session::Session; use middle::subst; use std::fmt; use syntax::ast; diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs deleted file mode 100644 index 367fe2845dd..00000000000 --- a/src/librustc/middle/save/mod.rs +++ /dev/null @@ -1,1558 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Output a CSV file containing the output from rustc's analysis. The data is -//! primarily designed to be used as input to the DXR tool, specifically its -//! Rust plugin. It could also be used by IDEs or other code browsing, search, or -//! cross-referencing tools. -//! -//! Dumping the analysis is implemented by walking the AST and getting a bunch of -//! info out from all over the place. We use Def IDs to identify objects. The -//! tricky part is getting syntactic (span, source text) and semantic (reference -//! Def IDs) information for parts of expressions which the compiler has discarded. -//! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole -//! path and a reference to `baz`, but we want spans and references for all three -//! idents. -//! -//! SpanUtils is used to manipulate spans. In particular, to extract sub-spans -//! from spans (e.g., the span for `bar` from the above example path). -//! Recorder is used for recording the output in csv format. FmtStrs separates -//! the format of the output away from extracting it from the compiler. -//! DxrVisitor walks the AST and processes it. - -use driver::driver::CrateAnalysis; -use driver::session::Session; - -use middle::def; -use middle::ty; -use middle::typeck; - -use std::cell::Cell; -use std::io; -use std::io::File; -use std::io::fs; -use std::os; - -use syntax::ast; -use syntax::ast_util; -use syntax::ast_util::PostExpansionMethod; -use syntax::ast::{NodeId,DefId}; -use syntax::ast_map::NodeItem; -use syntax::attr; -use syntax::codemap::*; -use syntax::parse::token; -use syntax::parse::token::{get_ident,keywords}; -use syntax::owned_slice::OwnedSlice; -use syntax::visit; -use syntax::visit::Visitor; -use syntax::print::pprust::{path_to_string,ty_to_string}; -use syntax::ptr::P; - -use middle::save::span_utils::SpanUtils; -use middle::save::recorder::Recorder; -use middle::save::recorder::FmtStrs; - -use util::ppaux; - -mod span_utils; -mod recorder; - -// Helper function to escape quotes in a string -fn escape(s: String) -> String { - s.replace("\"", "\"\"") -} - -// If the expression is a macro expansion or other generated code, run screaming and don't index. -fn generated_code(span: Span) -> bool { - span.expn_id != NO_EXPANSION || span == DUMMY_SP -} - -struct DxrVisitor<'l, 'tcx: 'l> { - sess: &'l Session, - analysis: &'l CrateAnalysis<'tcx>, - - collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, - collecting: bool, - - span: SpanUtils<'l>, - fmt: FmtStrs<'l>, - - cur_scope: NodeId -} - -impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { - fn nest(&mut self, scope_id: NodeId, f: |&mut DxrVisitor<'l, 'tcx>|) { - let parent_scope = self.cur_scope; - self.cur_scope = scope_id; - f(self); - self.cur_scope = parent_scope; - } - - fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { - // the current crate - self.fmt.crate_str(krate.span, name); - - // dump info about all the external crates referenced from this crate - self.sess.cstore.iter_crate_data(|n, cmd| { - self.fmt.external_crate_str(krate.span, cmd.name.as_slice(), n); - }); - self.fmt.recorder.record("end_external_crates\n"); - } - - // Return all non-empty prefixes of a path. - // For each prefix, we return the span for the last segment in the prefix and - // a str representation of the entire prefix. - fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> { - let spans = self.span.spans_for_path_segments(path); - - // Paths to enums seem to not match their spans - the span includes all the - // variants too. But they seem to always be at the end, so I hope we can cope with - // always using the first ones. So, only error out if we don't have enough spans. - // What could go wrong...? - if spans.len() < path.segments.len() { - error!("Mis-calculated spans for path '{}'. \ - Found {} spans, expected {}. Found spans:", - path_to_string(path), spans.len(), path.segments.len()); - for s in spans.iter() { - let loc = self.sess.codemap().lookup_char_pos(s.lo); - error!(" '{}' in {}, line {}", - self.span.snippet(*s), loc.file.name, loc.line); - } - return vec!(); - } - - let mut result: Vec<(Span, String)> = vec!(); - - - let mut segs = vec!(); - for (seg, span) in path.segments.iter().zip(spans.iter()) { - segs.push(seg.clone()); - let sub_path = ast::Path{span: *span, // span for the last segment - global: path.global, - segments: segs}; - let qualname = path_to_string(&sub_path); - result.push((*span, qualname)); - segs = sub_path.segments; - } - - result - } - - fn write_sub_paths(&mut self, path: &ast::Path) { - let sub_paths = self.process_path_prefixes(path); - for &(ref span, ref qualname) in sub_paths.iter() { - self.fmt.sub_mod_ref_str(path.span, - *span, - qualname.as_slice(), - self.cur_scope); - } - } - - // As write_sub_paths, but does not process the last ident in the path (assuming it - // will be processed elsewhere). - fn write_sub_paths_truncated(&mut self, path: &ast::Path) { - let sub_paths = self.process_path_prefixes(path); - let len = sub_paths.len(); - if len <= 1 { - return; - } - - let sub_paths = sub_paths.slice(0, len-1); - for &(ref span, ref qualname) in sub_paths.iter() { - self.fmt.sub_mod_ref_str(path.span, - *span, - qualname.as_slice(), - self.cur_scope); - } - } - - // As write_sub_paths, but expects a path of the form module_path::trait::method - // Where trait could actually be a struct too. - fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) { - let sub_paths = self.process_path_prefixes(path); - let len = sub_paths.len(); - if len <= 1 { - return; - } - let sub_paths = sub_paths.slice_to(len-1); - - // write the trait part of the sub-path - let (ref span, ref qualname) = sub_paths[len-2]; - self.fmt.sub_type_ref_str(path.span, - *span, - qualname.as_slice()); - - // write the other sub-paths - if len <= 2 { - return; - } - let sub_paths = sub_paths[..len-2]; - for &(ref span, ref qualname) in sub_paths.iter() { - self.fmt.sub_mod_ref_str(path.span, - *span, - qualname.as_slice(), - self.cur_scope); - } - } - - // looks up anything, not just a type - fn lookup_type_ref(&self, ref_id: NodeId) -> Option { - if !self.analysis.ty_cx.def_map.borrow().contains_key(&ref_id) { - self.sess.bug(format!("def_map has no key for {} in lookup_type_ref", - ref_id).as_slice()); - } - let def = (*self.analysis.ty_cx.def_map.borrow())[ref_id]; - match def { - def::DefPrimTy(_) => None, - _ => Some(def.def_id()), - } - } - - fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option { - let def_map = self.analysis.ty_cx.def_map.borrow(); - if !def_map.contains_key(&ref_id) { - self.sess.span_bug(span, format!("def_map has no key for {} in lookup_def_kind", - ref_id).as_slice()); - } - let def = (*def_map)[ref_id]; - match def { - def::DefMod(_) | - def::DefForeignMod(_) => Some(recorder::ModRef), - def::DefStruct(_) => Some(recorder::StructRef), - def::DefTy(..) | - def::DefAssociatedTy(..) | - def::DefTrait(_) => Some(recorder::TypeRef), - def::DefStatic(_, _) | - def::DefConst(_) | - def::DefLocal(_) | - def::DefVariant(_, _, _) | - def::DefUpvar(..) => Some(recorder::VarRef), - - def::DefFn(..) => Some(recorder::FnRef), - - def::DefSelfTy(_) | - def::DefRegion(_) | - def::DefTyParamBinder(_) | - def::DefLabel(_) | - def::DefStaticMethod(..) | - def::DefTyParam(..) | - def::DefUse(_) | - def::DefMethod(..) | - def::DefPrimTy(_) => { - self.sess.span_bug(span, format!("lookup_def_kind for unexpected item: {}", - def).as_slice()); - }, - } - } - - fn process_formals(&mut self, formals: &Vec, qualname: &str) { - for arg in formals.iter() { - assert!(self.collected_paths.len() == 0 && !self.collecting); - self.collecting = true; - self.visit_pat(&*arg.pat); - self.collecting = false; - let span_utils = self.span; - for &(id, ref p, _, _) in self.collected_paths.iter() { - let typ = ppaux::ty_to_string(&self.analysis.ty_cx, - (*self.analysis.ty_cx.node_types.borrow())[id]); - // get the span only for the name of the variable (I hope the path is only ever a - // variable name, but who knows?) - self.fmt.formal_str(p.span, - span_utils.span_for_last_ident(p.span), - id, - qualname, - path_to_string(p).as_slice(), - typ.as_slice()); - } - self.collected_paths.clear(); - } - } - - fn process_method(&mut self, method: &ast::Method) { - if generated_code(method.span) { - return; - } - - let mut scope_id; - // The qualname for a method is the trait name or name of the struct in an impl in - // which the method is declared in followed by the method's name. - let mut qualname = match ty::impl_of_method(&self.analysis.ty_cx, - ast_util::local_def(method.id)) { - Some(impl_id) => match self.analysis.ty_cx.map.get(impl_id.node) { - NodeItem(item) => { - scope_id = item.id; - match item.node { - ast::ItemImpl(_, _, ref ty, _) => { - let mut result = String::from_str("<"); - result.push_str(ty_to_string(&**ty).as_slice()); - - match ty::trait_of_item(&self.analysis.ty_cx, - ast_util::local_def(method.id)) { - Some(def_id) => { - result.push_str(" as "); - result.push_str( - ty::item_path_str(&self.analysis.ty_cx, def_id).as_slice()); - }, - None => {} - } - result.push_str(">::"); - result - } - _ => { - self.sess.span_bug(method.span, - format!("Container {} for method {} not an impl?", - impl_id.node, method.id).as_slice()); - }, - } - }, - _ => { - self.sess.span_bug(method.span, - format!("Container {} for method {} is not a node item {}", - impl_id.node, - method.id, - self.analysis.ty_cx.map.get(impl_id.node) - ).as_slice()); - }, - }, - None => match ty::trait_of_item(&self.analysis.ty_cx, - ast_util::local_def(method.id)) { - Some(def_id) => { - scope_id = def_id.node; - match self.analysis.ty_cx.map.get(def_id.node) { - NodeItem(_) => { - let mut result = ty::item_path_str(&self.analysis.ty_cx, def_id); - result.push_str("::"); - result - } - _ => { - self.sess.span_bug(method.span, - format!("Could not find container {} for method {}", - def_id.node, method.id).as_slice()); - } - } - }, - None => { - self.sess.span_bug(method.span, - format!("Could not find container for method {}", - method.id).as_slice()); - }, - }, - }; - - qualname.push_str(get_ident(method.pe_ident()).get()); - let qualname = qualname.as_slice(); - - // record the decl for this def (if it has one) - let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx, - ast_util::local_def(method.id)) - .and_then(|def_id| { - if match def_id { - ty::MethodTraitItemId(def_id) => { - method.id != 0 && def_id.node == 0 - } - ty::TypeTraitItemId(_) => false, - } { - Some(def_id) - } else { - None - } - }); - let decl_id = match decl_id { - None => None, - Some(id) => Some(id.def_id()), - }; - - let sub_span = self.span.sub_span_after_keyword(method.span, keywords::Fn); - self.fmt.method_str(method.span, - sub_span, - method.id, - qualname, - decl_id, - scope_id); - - self.process_formals(&method.pe_fn_decl().inputs, qualname); - - // walk arg and return types - for arg in method.pe_fn_decl().inputs.iter() { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = method.pe_fn_decl().output { - self.visit_ty(&**ret_ty); - } - - // walk the fn body - self.nest(method.id, |v| v.visit_block(&*method.pe_body())); - - self.process_generic_params(method.pe_generics(), - method.span, - qualname, - method.id); - } - - fn process_trait_ref(&mut self, - trait_ref: &ast::TraitRef, - impl_id: Option) { - match self.lookup_type_ref(trait_ref.ref_id) { - Some(id) => { - let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span); - self.fmt.ref_str(recorder::TypeRef, - trait_ref.path.span, - sub_span, - id, - self.cur_scope); - match impl_id { - Some(impl_id) => self.fmt.impl_str(trait_ref.path.span, - sub_span, - impl_id, - id, - self.cur_scope), - None => (), - } - visit::walk_path(self, &trait_ref.path); - }, - None => () - } - } - - fn process_struct_field_def(&mut self, - field: &ast::StructField, - qualname: &str, - scope_id: NodeId) { - match field.node.kind { - ast::NamedField(ident, _) => { - let name = get_ident(ident); - let qualname = format!("{}::{}", qualname, name); - let typ = ppaux::ty_to_string(&self.analysis.ty_cx, - (*self.analysis.ty_cx.node_types.borrow())[field.node.id]); - match self.span.sub_span_before_token(field.span, token::Colon) { - Some(sub_span) => self.fmt.field_str(field.span, - Some(sub_span), - field.node.id, - name.get().as_slice(), - qualname.as_slice(), - typ.as_slice(), - scope_id), - None => self.sess.span_bug(field.span, - format!("Could not find sub-span for field {}", - qualname).as_slice()), - } - }, - _ => (), - } - } - - // Dump generic params bindings, then visit_generics - fn process_generic_params(&mut self, generics:&ast::Generics, - full_span: Span, - prefix: &str, - id: NodeId) { - // We can't only use visit_generics since we don't have spans for param - // bindings, so we reparse the full_span to get those sub spans. - // However full span is the entire enum/fn/struct block, so we only want - // the first few to match the number of generics we're looking for. - let param_sub_spans = self.span.spans_for_ty_params(full_span, - (generics.ty_params.len() as int)); - for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans.iter()) { - // Append $id to name to make sure each one is unique - let name = format!("{}::{}${}", - prefix, - escape(self.span.snippet(*param_ss)), - id); - self.fmt.typedef_str(full_span, - Some(*param_ss), - param.id, - name.as_slice(), - ""); - } - self.visit_generics(generics); - } - - fn process_fn(&mut self, - item: &ast::Item, - decl: &ast::FnDecl, - ty_params: &ast::Generics, - body: &ast::Block) { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn); - self.fmt.fn_str(item.span, - sub_span, - item.id, - qualname.as_slice(), - self.cur_scope); - - self.process_formals(&decl.inputs, qualname.as_slice()); - - // walk arg and return types - for arg in decl.inputs.iter() { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = decl.output { - self.visit_ty(&**ret_ty); - } - - // walk the body - self.nest(item.id, |v| v.visit_block(&*body)); - - self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); - } - - fn process_static(&mut self, - item: &ast::Item, - typ: &ast::Ty, - mt: ast::Mutability, - expr: &ast::Expr) - { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - // If the variable is immutable, save the initialising expression. - let value = match mt { - ast::MutMutable => String::from_str(""), - ast::MutImmutable => self.span.snippet(expr.span), - }; - - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Static); - self.fmt.static_str(item.span, - sub_span, - item.id, - get_ident(item.ident).get(), - qualname.as_slice(), - value.as_slice(), - ty_to_string(&*typ).as_slice(), - self.cur_scope); - - // walk type and init value - self.visit_ty(&*typ); - self.visit_expr(expr); - } - - fn process_const(&mut self, - item: &ast::Item, - typ: &ast::Ty, - expr: &ast::Expr) - { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - let sub_span = self.span.sub_span_after_keyword(item.span, - keywords::Const); - self.fmt.static_str(item.span, - sub_span, - item.id, - get_ident(item.ident).get(), - qualname.as_slice(), - "", - ty_to_string(&*typ).as_slice(), - self.cur_scope); - - // walk type and init value - self.visit_ty(&*typ); - self.visit_expr(expr); - } - - fn process_struct(&mut self, - item: &ast::Item, - def: &ast::StructDef, - ty_params: &ast::Generics) { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - let ctor_id = match def.ctor_id { - Some(node_id) => node_id, - None => -1, - }; - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct); - self.fmt.struct_str(item.span, - sub_span, - item.id, - ctor_id, - qualname.as_slice(), - self.cur_scope); - - // fields - for field in def.fields.iter() { - self.process_struct_field_def(field, qualname.as_slice(), item.id); - self.visit_ty(&*field.node.ty); - } - - self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); - } - - fn process_enum(&mut self, - item: &ast::Item, - enum_definition: &ast::EnumDef, - ty_params: &ast::Generics) { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - match self.span.sub_span_after_keyword(item.span, keywords::Enum) { - Some(sub_span) => self.fmt.enum_str(item.span, - Some(sub_span), - item.id, - qualname.as_slice(), - self.cur_scope), - None => self.sess.span_bug(item.span, - format!("Could not find subspan for enum {}", - qualname).as_slice()), - } - for variant in enum_definition.variants.iter() { - let name = get_ident(variant.node.name); - let name = name.get(); - let mut qualname = qualname.clone(); - qualname.push_str("::"); - qualname.push_str(name); - let val = self.span.snippet(variant.span); - match variant.node.kind { - ast::TupleVariantKind(ref args) => { - // first ident in span is the variant's name - self.fmt.tuple_variant_str(variant.span, - self.span.span_for_first_ident(variant.span), - variant.node.id, - name, - qualname.as_slice(), - val.as_slice(), - item.id); - for arg in args.iter() { - self.visit_ty(&*arg.ty); - } - } - ast::StructVariantKind(ref struct_def) => { - let ctor_id = match struct_def.ctor_id { - Some(node_id) => node_id, - None => -1, - }; - self.fmt.struct_variant_str( - variant.span, - self.span.span_for_first_ident(variant.span), - variant.node.id, - ctor_id, - qualname.as_slice(), - val.as_slice(), - item.id); - - for field in struct_def.fields.iter() { - self.process_struct_field_def(field, qualname.as_slice(), variant.node.id); - self.visit_ty(&*field.node.ty); - } - } - } - } - - self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); - } - - fn process_impl(&mut self, - item: &ast::Item, - type_parameters: &ast::Generics, - trait_ref: &Option, - typ: &ast::Ty, - impl_items: &Vec) { - match typ.node { - ast::TyPath(ref path, _, id) => { - match self.lookup_type_ref(id) { - Some(id) => { - let sub_span = self.span.sub_span_for_type_name(path.span); - self.fmt.ref_str(recorder::TypeRef, - path.span, - sub_span, - id, - self.cur_scope); - self.fmt.impl_str(path.span, - sub_span, - item.id, - id, - self.cur_scope); - }, - None => () - } - }, - _ => self.visit_ty(&*typ), - } - - match *trait_ref { - Some(ref trait_ref) => self.process_trait_ref(trait_ref, Some(item.id)), - None => (), - } - - self.process_generic_params(type_parameters, item.span, "", item.id); - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - visit::walk_method_helper(self, &**method) - } - ast::TypeImplItem(ref typedef) => { - visit::walk_ty(self, &*typedef.typ) - } - } - } - } - - fn process_trait(&mut self, - item: &ast::Item, - generics: &ast::Generics, - trait_refs: &OwnedSlice, - methods: &Vec) { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait); - self.fmt.trait_str(item.span, - sub_span, - item.id, - qualname.as_slice(), - self.cur_scope); - - // super-traits - for super_bound in trait_refs.iter() { - let trait_ref = match *super_bound { - ast::TraitTyParamBound(ref trait_ref) => { - trait_ref - } - ast::RegionTyParamBound(..) => { - continue; - } - }; - - let trait_ref = &trait_ref.trait_ref; - match self.lookup_type_ref(trait_ref.ref_id) { - Some(id) => { - let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span); - self.fmt.ref_str(recorder::TypeRef, - trait_ref.path.span, - sub_span, - id, - self.cur_scope); - self.fmt.inherit_str(trait_ref.path.span, - sub_span, - id, - item.id); - }, - None => () - } - } - - // walk generics and methods - self.process_generic_params(generics, item.span, qualname.as_slice(), item.id); - for method in methods.iter() { - self.visit_trait_item(method) - } - } - - fn process_mod(&mut self, - item: &ast::Item, // The module in question, represented as an item. - m: &ast::Mod) { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - - let cm = self.sess.codemap(); - let filename = cm.span_to_filename(m.inner); - - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Mod); - self.fmt.mod_str(item.span, - sub_span, - item.id, - qualname.as_slice(), - self.cur_scope, - filename.as_slice()); - - self.nest(item.id, |v| visit::walk_mod(v, m)); - } - - fn process_path(&mut self, - ex: &ast::Expr, - path: &ast::Path) { - if generated_code(path.span) { - return - } - - let def_map = self.analysis.ty_cx.def_map.borrow(); - if !def_map.contains_key(&ex.id) { - self.sess.span_bug(ex.span, - format!("def_map has no key for {} in visit_expr", - ex.id).as_slice()); - } - let def = &(*def_map)[ex.id]; - let sub_span = self.span.span_for_last_ident(ex.span); - match *def { - def::DefUpvar(..) | - def::DefLocal(..) | - def::DefStatic(..) | - def::DefConst(..) | - def::DefVariant(..) => self.fmt.ref_str(recorder::VarRef, - ex.span, - sub_span, - def.def_id(), - self.cur_scope), - def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef, - ex.span, - sub_span, - def_id, - self.cur_scope), - def::DefStaticMethod(declid, provenence) => { - let sub_span = self.span.sub_span_for_meth_name(ex.span); - let defid = if declid.krate == ast::LOCAL_CRATE { - let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, - declid); - match provenence { - def::FromTrait(def_id) => { - Some(ty::trait_items(&self.analysis.ty_cx, - def_id) - .iter() - .find(|mr| { - mr.name() == ti.name() - }) - .unwrap() - .def_id()) - } - def::FromImpl(def_id) => { - let impl_items = self.analysis - .ty_cx - .impl_items - .borrow(); - Some((*impl_items)[def_id] - .iter() - .find(|mr| { - ty::impl_or_trait_item( - &self.analysis.ty_cx, - mr.def_id() - ).name() == ti.name() - }) - .unwrap() - .def_id()) - } - } - } else { - None - }; - self.fmt.meth_call_str(ex.span, - sub_span, - defid, - Some(declid), - self.cur_scope); - }, - def::DefFn(def_id, _) => self.fmt.fn_call_str(ex.span, - sub_span, - def_id, - self.cur_scope), - _ => self.sess.span_bug(ex.span, - format!("Unexpected def kind while looking up path in '{}'", - self.span.snippet(ex.span)).as_slice()), - } - // modules or types in the path prefix - match *def { - def::DefStaticMethod(..) => { - self.write_sub_path_trait_truncated(path); - }, - def::DefLocal(_) | - def::DefStatic(_,_) | - def::DefConst(..) | - def::DefStruct(_) | - def::DefFn(..) => self.write_sub_paths_truncated(path), - _ => {}, - } - - visit::walk_path(self, path); - } - - fn process_struct_lit(&mut self, - ex: &ast::Expr, - path: &ast::Path, - fields: &Vec, - base: &Option>) { - if generated_code(path.span) { - return - } - - let mut struct_def: Option = None; - match self.lookup_type_ref(ex.id) { - Some(id) => { - struct_def = Some(id); - let sub_span = self.span.span_for_last_ident(path.span); - self.fmt.ref_str(recorder::StructRef, - path.span, - sub_span, - id, - self.cur_scope); - }, - None => () - } - - self.write_sub_paths_truncated(path); - - for field in fields.iter() { - match struct_def { - Some(struct_def) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); - for f in fields.iter() { - if generated_code(field.ident.span) { - continue; - } - if f.name == field.ident.node.name { - // We don't really need a sub-span here, but no harm done - let sub_span = self.span.span_for_last_ident(field.ident.span); - self.fmt.ref_str(recorder::VarRef, - field.ident.span, - sub_span, - f.id, - self.cur_scope); - } - } - } - None => {} - } - - self.visit_expr(&*field.expr) - } - visit::walk_expr_opt(self, base) - } - - fn process_method_call(&mut self, - ex: &ast::Expr, - args: &Vec>) { - let method_map = self.analysis.ty_cx.method_map.borrow(); - let method_callee = &(*method_map)[typeck::MethodCall::expr(ex.id)]; - let (def_id, decl_id) = match method_callee.origin { - typeck::MethodStatic(def_id) | - typeck::MethodStaticUnboxedClosure(def_id) => { - // method invoked on an object with a concrete type (not a static method) - let decl_id = - match ty::trait_item_of_item(&self.analysis.ty_cx, - def_id) { - None => None, - Some(decl_id) => Some(decl_id.def_id()), - }; - - // This incantation is required if the method referenced is a - // trait's default implementation. - let def_id = match ty::impl_or_trait_item(&self.analysis - .ty_cx, - def_id) { - ty::MethodTraitItem(method) => { - method.provided_source.unwrap_or(def_id) - } - ty::TypeTraitItem(_) => def_id, - }; - (Some(def_id), decl_id) - } - typeck::MethodTypeParam(ref mp) => { - // method invoked on a type parameter - let trait_item = ty::trait_item(&self.analysis.ty_cx, - mp.trait_ref.def_id, - mp.method_num); - (None, Some(trait_item.def_id())) - } - typeck::MethodTraitObject(ref mo) => { - // method invoked on a trait instance - let trait_item = ty::trait_item(&self.analysis.ty_cx, - mo.trait_ref.def_id, - mo.method_num); - (None, Some(trait_item.def_id())) - } - }; - let sub_span = self.span.sub_span_for_meth_name(ex.span); - self.fmt.meth_call_str(ex.span, - sub_span, - def_id, - decl_id, - self.cur_scope); - - // walk receiver and args - visit::walk_exprs(self, args.as_slice()); - } - - fn process_pat(&mut self, p:&ast::Pat) { - if generated_code(p.span) { - return - } - - match p.node { - ast::PatStruct(ref path, ref fields, _) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef)); - visit::walk_path(self, path); - let struct_def = match self.lookup_type_ref(p.id) { - Some(sd) => sd, - None => { - self.sess.span_bug(p.span, - format!("Could not find struct_def for `{}`", - self.span.snippet(p.span)).as_slice()); - } - }; - for &Spanned { node: ref field, span } in fields.iter() { - self.visit_pat(&*field.pat); - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); - for f in fields.iter() { - if f.name == field.ident.name { - self.fmt.ref_str(recorder::VarRef, - p.span, - Some(span), - f.id, - self.cur_scope); - break; - } - } - } - } - ast::PatEnum(ref path, _) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); - visit::walk_pat(self, p); - } - ast::PatIdent(bm, ref path1, ref optional_subpattern) => { - let immut = match bm { - // Even if the ref is mut, you can't change the ref, only - // the data pointed at, so showing the initialising expression - // is still worthwhile. - ast::BindByRef(_) => true, - ast::BindByValue(mt) => { - match mt { - ast::MutMutable => false, - ast::MutImmutable => true, - } - } - }; - // collect path for either visit_local or visit_arm - let path = ast_util::ident_to_path(path1.span,path1.node); - self.collected_paths.push((p.id, path, immut, recorder::VarRef)); - match *optional_subpattern { - None => {} - Some(ref subpattern) => self.visit_pat(&**subpattern) - } - } - _ => visit::walk_pat(self, p) - } - } -} - -impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - if generated_code(item.span) { - return - } - - match item.node { - ast::ItemFn(ref decl, _, _, ref ty_params, ref body) => - self.process_fn(item, &**decl, ty_params, &**body), - ast::ItemStatic(ref typ, mt, ref expr) => - self.process_static(item, &**typ, mt, &**expr), - ast::ItemConst(ref typ, ref expr) => - self.process_const(item, &**typ, &**expr), - ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), - ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), - ast::ItemImpl(ref ty_params, - ref trait_ref, - ref typ, - ref impl_items) => { - self.process_impl(item, - ty_params, - trait_ref, - &**typ, - impl_items) - } - ast::ItemTrait(ref generics, _, ref trait_refs, ref methods) => - self.process_trait(item, generics, trait_refs, methods), - ast::ItemMod(ref m) => self.process_mod(item, m), - ast::ItemTy(ref ty, ref ty_params) => { - let qualname = self.analysis.ty_cx.map.path_to_string(item.id); - let value = ty_to_string(&**ty); - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type); - self.fmt.typedef_str(item.span, - sub_span, - item.id, - qualname.as_slice(), - value.as_slice()); - - self.visit_ty(&**ty); - self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); - }, - ast::ItemMac(_) => (), - _ => visit::walk_item(self, item), - } - } - - fn visit_generics(&mut self, generics: &ast::Generics) { - for param in generics.ty_params.iter() { - for bound in param.bounds.iter() { - match *bound { - ast::TraitTyParamBound(ref trait_ref) => { - self.process_trait_ref(&trait_ref.trait_ref, None); - } - _ => {} - } - } - match param.default { - Some(ref ty) => self.visit_ty(&**ty), - None => {} - } - } - } - - // We don't actually index functions here, that is done in visit_item/ItemFn. - // Here we just visit methods. - fn visit_fn(&mut self, - fk: visit::FnKind<'v>, - fd: &'v ast::FnDecl, - b: &'v ast::Block, - s: Span, - _: NodeId) { - if generated_code(s) { - return; - } - - match fk { - visit::FkMethod(_, _, method) => self.process_method(method), - _ => visit::walk_fn(self, fk, fd, b, s), - } - } - - fn visit_trait_item(&mut self, tm: &ast::TraitItem) { - match *tm { - ast::RequiredMethod(ref method_type) => { - if generated_code(method_type.span) { - return; - } - - let mut scope_id; - let mut qualname = match ty::trait_of_item(&self.analysis.ty_cx, - ast_util::local_def(method_type.id)) { - Some(def_id) => { - scope_id = def_id.node; - let mut s = ty::item_path_str(&self.analysis.ty_cx, def_id); - s.push_str("::"); - s - }, - None => { - self.sess.span_bug(method_type.span, - format!("Could not find trait for method {}", - method_type.id).as_slice()); - }, - }; - - qualname.push_str(get_ident(method_type.ident).get()); - let qualname = qualname.as_slice(); - - let sub_span = self.span.sub_span_after_keyword(method_type.span, keywords::Fn); - self.fmt.method_decl_str(method_type.span, - sub_span, - method_type.id, - qualname, - scope_id); - - // walk arg and return types - for arg in method_type.decl.inputs.iter() { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = method_type.decl.output { - self.visit_ty(&**ret_ty); - } - - self.process_generic_params(&method_type.generics, - method_type.span, - qualname, - method_type.id); - } - ast::ProvidedMethod(ref method) => self.process_method(&**method), - ast::TypeTraitItem(_) => {} - } - } - - fn visit_view_item(&mut self, i: &ast::ViewItem) { - if generated_code(i.span) { - return - } - - match i.node { - ast::ViewItemUse(ref path) => { - match path.node { - ast::ViewPathSimple(ident, ref path, id) => { - let sub_span = self.span.span_for_last_ident(path.span); - let mod_id = match self.lookup_type_ref(id) { - Some(def_id) => { - match self.lookup_def_kind(id, path.span) { - Some(kind) => self.fmt.ref_str(kind, - path.span, - sub_span, - def_id, - self.cur_scope), - None => {}, - } - Some(def_id) - }, - None => None, - }; - - // 'use' always introduces an alias, if there is not an explicit - // one, there is an implicit one. - let sub_span = - match self.span.sub_span_before_token(path.span, token::Eq) { - Some(sub_span) => Some(sub_span), - None => sub_span, - }; - - self.fmt.use_alias_str(path.span, - sub_span, - id, - mod_id, - get_ident(ident).get(), - self.cur_scope); - self.write_sub_paths_truncated(path); - } - ast::ViewPathGlob(ref path, _) => { - self.write_sub_paths(path); - } - ast::ViewPathList(ref path, ref list, _) => { - for plid in list.iter() { - match plid.node { - ast::PathListIdent { id, .. } => { - match self.lookup_type_ref(id) { - Some(def_id) => - match self.lookup_def_kind(id, plid.span) { - Some(kind) => { - self.fmt.ref_str( - kind, plid.span, - Some(plid.span), - def_id, self.cur_scope); - } - None => () - }, - None => () - } - }, - ast::PathListMod { .. } => () - } - } - - self.write_sub_paths(path); - } - } - }, - ast::ViewItemExternCrate(ident, ref s, id) => { - let name = get_ident(ident); - let name = name.get(); - let s = match *s { - Some((ref s, _)) => s.get().to_string(), - None => name.to_string(), - }; - let sub_span = self.span.sub_span_after_keyword(i.span, keywords::Crate); - let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(id) { - Some(cnum) => cnum, - None => 0, - }; - self.fmt.extern_crate_str(i.span, - sub_span, - id, - cnum, - name, - s.as_slice(), - self.cur_scope); - }, - } - } - - fn visit_ty(&mut self, t: &ast::Ty) { - if generated_code(t.span) { - return - } - - match t.node { - ast::TyPath(ref path, _, id) => { - match self.lookup_type_ref(id) { - Some(id) => { - let sub_span = self.span.sub_span_for_type_name(t.span); - self.fmt.ref_str(recorder::TypeRef, - t.span, - sub_span, - id, - self.cur_scope); - }, - None => () - } - - self.write_sub_paths_truncated(path); - - visit::walk_path(self, path); - }, - _ => visit::walk_ty(self, t), - } - } - - fn visit_expr(&mut self, ex: &ast::Expr) { - if generated_code(ex.span) { - return - } - - match ex.node { - ast::ExprCall(ref _f, ref _args) => { - // Don't need to do anything for function calls, - // because just walking the callee path does what we want. - visit::walk_expr(self, ex); - }, - ast::ExprPath(ref path) => self.process_path(ex, path), - ast::ExprStruct(ref path, ref fields, ref base) => - self.process_struct_lit(ex, path, fields, base), - ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args), - ast::ExprField(ref sub_ex, ident, _) => { - if generated_code(sub_ex.span) { - return - } - - self.visit_expr(&**sub_ex); - - let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex); - let t_box = ty::get(t); - match t_box.sty { - ty::ty_struct(def_id, _) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); - for f in fields.iter() { - if f.name == ident.node.name { - let sub_span = self.span.span_for_last_ident(ex.span); - self.fmt.ref_str(recorder::VarRef, - ex.span, - sub_span, - f.id, - self.cur_scope); - break; - } - } - }, - _ => self.sess.span_bug(ex.span, - "Expected struct type, but not ty_struct"), - } - }, - ast::ExprTupField(ref sub_ex, idx, _) => { - if generated_code(sub_ex.span) { - return - } - - self.visit_expr(&**sub_ex); - - let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex); - let t_box = ty::get(t); - match t_box.sty { - ty::ty_struct(def_id, _) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); - for (i, f) in fields.iter().enumerate() { - if i == idx.node { - let sub_span = self.span.span_for_last_ident(ex.span); - self.fmt.ref_str(recorder::VarRef, - ex.span, - sub_span, - f.id, - self.cur_scope); - break; - } - } - }, - _ => self.sess.span_bug(ex.span, - "Expected struct type, but not ty_struct"), - } - }, - ast::ExprFnBlock(_, ref decl, ref body) => { - if generated_code(body.span) { - return - } - - let mut id = String::from_str("$"); - id.push_str(ex.id.to_string().as_slice()); - self.process_formals(&decl.inputs, id.as_slice()); - - // walk arg and return types - for arg in decl.inputs.iter() { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = decl.output { - self.visit_ty(&**ret_ty); - } - - // walk the body - self.nest(ex.id, |v| v.visit_block(&**body)); - }, - _ => { - visit::walk_expr(self, ex) - }, - } - } - - fn visit_mac(&mut self, _: &ast::Mac) { - // Just stop, macros are poison to us. - } - - fn visit_pat(&mut self, p: &ast::Pat) { - self.process_pat(p); - if !self.collecting { - self.collected_paths.clear(); - } - } - - fn visit_arm(&mut self, arm: &ast::Arm) { - assert!(self.collected_paths.len() == 0 && !self.collecting); - self.collecting = true; - - for pattern in arm.pats.iter() { - // collect paths from the arm's patterns - self.visit_pat(&**pattern); - } - self.collecting = false; - // process collected paths - for &(id, ref p, ref immut, ref_kind) in self.collected_paths.iter() { - let value = if *immut { - self.span.snippet(p.span).into_string() - } else { - "".to_string() - }; - let sub_span = self.span.span_for_first_ident(p.span); - let def_map = self.analysis.ty_cx.def_map.borrow(); - if !def_map.contains_key(&id) { - self.sess.span_bug(p.span, - format!("def_map has no key for {} in visit_arm", - id).as_slice()); - } - let def = &(*def_map)[id]; - match *def { - def::DefLocal(id) => self.fmt.variable_str(p.span, - sub_span, - id, - path_to_string(p).as_slice(), - value.as_slice(), - ""), - def::DefVariant(_,id,_) => self.fmt.ref_str(ref_kind, - p.span, - sub_span, - id, - self.cur_scope), - // FIXME(nrc) what is this doing here? - def::DefStatic(_, _) => {} - def::DefConst(..) => {} - _ => error!("unexpected definition kind when processing collected paths: {}", - *def) - } - } - self.collected_paths.clear(); - visit::walk_expr_opt(self, &arm.guard); - self.visit_expr(&*arm.body); - } - - fn visit_stmt(&mut self, s: &ast::Stmt) { - if generated_code(s.span) { - return - } - - visit::walk_stmt(self, s) - } - - fn visit_local(&mut self, l: &ast::Local) { - if generated_code(l.span) { - return - } - - // The local could declare multiple new vars, we must walk the - // pattern and collect them all. - assert!(self.collected_paths.len() == 0 && !self.collecting); - self.collecting = true; - self.visit_pat(&*l.pat); - self.collecting = false; - - let value = self.span.snippet(l.span); - - for &(id, ref p, ref immut, _) in self.collected_paths.iter() { - let value = if *immut { value.to_string() } else { "".to_string() }; - let types = self.analysis.ty_cx.node_types.borrow(); - let typ = ppaux::ty_to_string(&self.analysis.ty_cx, (*types)[id]); - // Get the span only for the name of the variable (I hope the path - // is only ever a variable name, but who knows?). - let sub_span = self.span.span_for_last_ident(p.span); - // Rust uses the id of the pattern for var lookups, so we'll use it too. - self.fmt.variable_str(p.span, - sub_span, - id, - path_to_string(p).as_slice(), - value.as_slice(), - typ.as_slice()); - } - self.collected_paths.clear(); - - // Just walk the initialiser and type (don't want to walk the pattern again). - self.visit_ty(&*l.ty); - visit::walk_expr_opt(self, &l.init); - } -} - -pub fn process_crate(sess: &Session, - krate: &ast::Crate, - analysis: &CrateAnalysis, - odir: &Option) { - if generated_code(krate.span) { - return; - } - - let cratename = match attr::find_crate_name(krate.attrs.as_slice()) { - Some(name) => name.get().to_string(), - None => { - info!("Could not find crate name, using 'unknown_crate'"); - String::from_str("unknown_crate") - }, - }; - - info!("Dumping crate {}", cratename); - - // find a path to dump our data to - let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") { - Some(val) => Path::new(val), - None => match *odir { - Some(ref val) => val.join("dxr"), - None => Path::new("dxr-temp"), - }, - }; - - match fs::mkdir_recursive(&root_path, io::USER_RWX) { - Err(e) => sess.err(format!("Could not create directory {}: {}", - root_path.display(), e).as_slice()), - _ => (), - } - - { - let disp = root_path.display(); - info!("Writing output to {}", disp); - } - - // Create output file. - let mut out_name = cratename.clone(); - out_name.push_str(".csv"); - root_path.push(out_name); - let output_file = match File::create(&root_path) { - Ok(f) => box f, - Err(e) => { - let disp = root_path.display(); - sess.fatal(format!("Could not open {}: {}", disp, e).as_slice()); - } - }; - root_path.pop(); - - let mut visitor = DxrVisitor { - sess: sess, - analysis: analysis, - collected_paths: vec!(), - collecting: false, - fmt: FmtStrs::new(box Recorder { - out: output_file as Box, - dump_spans: false, - }, - SpanUtils { - sess: sess, - err_count: Cell::new(0) - }, - cratename.clone()), - span: SpanUtils { - sess: sess, - err_count: Cell::new(0) - }, - cur_scope: 0 - }; - - visitor.dump_crate_info(cratename.as_slice(), krate); - - visit::walk_crate(&mut visitor, krate); -} diff --git a/src/librustc/middle/save/recorder.rs b/src/librustc/middle/save/recorder.rs deleted file mode 100644 index b8d495bd495..00000000000 --- a/src/librustc/middle/save/recorder.rs +++ /dev/null @@ -1,592 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use self::Row::*; - -use middle::save::escape; -use middle::save::span_utils::SpanUtils; - -use std::vec::Vec; - -use syntax::ast; -use syntax::ast::{NodeId,DefId}; -use syntax::codemap::*; - -pub struct Recorder { - // output file - pub out: Box, - pub dump_spans: bool, -} - -impl Recorder { - pub fn record(&mut self, info: &str) { - match write!(self.out, "{}", info) { - Err(_) => error!("Error writing output '{}'", info), - _ => (), - } - } - - pub fn dump_span(&mut self, - su: SpanUtils, - kind: &str, - span: Span, - _sub_span: Option) { - assert!(self.dump_spans); - let result = format!("span,kind,{},{},text,\"{}\"\n", - kind, su.extent_str(span), escape(su.snippet(span))); - self.record(result.as_slice()); - } -} - -pub struct FmtStrs<'a> { - pub recorder: Box, - span: SpanUtils<'a>, - krate: String, -} - -macro_rules! s { ($e:expr) => { format!("{}", $e) }} -macro_rules! svec { - ($($e:expr),*) => ({ - // leading _ to allow empty construction without a warning. - let mut _temp = ::std::vec::Vec::new(); - $(_temp.push(s!($e));)* - _temp - }) -} - -pub enum Row { - Variable, - Enum, - Variant, - VariantStruct, - Function, - MethodDecl, - Struct, - Trait, - Impl, - Module, - UseAlias, - ExternCrate, - Inheritance, - MethodCall, - Typedef, - ExternalCrate, - Crate, - FnCall, - ModRef, - VarRef, - TypeRef, - StructRef, - FnRef, -} - -impl<'a> FmtStrs<'a> { - pub fn new(rec: Box, span: SpanUtils<'a>, krate: String) -> FmtStrs<'a> { - FmtStrs { - recorder: rec, - span: span, - krate: krate, - } - } - - // A map from kind of item to a tuple of - // a string representation of the name - // a vector of field names - // whether this kind requires a span - // whether dump_spans should dump for this kind - fn lookup_row(r: Row) -> (&'static str, Vec<&'static str>, bool, bool) { - match r { - Variable => ("variable", - vec!("id","name","qualname","value","type","scopeid"), - true, true), - Enum => ("enum", vec!("id","qualname","scopeid"), true, true), - Variant => ("variant", vec!("id","name","qualname","value","scopeid"), true, true), - VariantStruct => ("variant_struct", - vec!("id","ctor_id","qualname","value","scopeid"), true, true), - Function => ("function", vec!("id","qualname","declid","declidcrate","scopeid"), - true, true), - MethodDecl => ("method_decl", vec!("id","qualname","scopeid"), true, true), - Struct => ("struct", vec!("id","ctor_id","qualname","scopeid"), true, true), - Trait => ("trait", vec!("id","qualname","scopeid"), true, true), - Impl => ("impl", vec!("id","refid","refidcrate","scopeid"), true, true), - Module => ("module", vec!("id","qualname","scopeid","def_file"), true, false), - UseAlias => ("use_alias", - vec!("id","refid","refidcrate","name","scopeid"), - true, true), - ExternCrate => ("extern_crate", - vec!("id","name","location","crate","scopeid"), - true, true), - Inheritance => ("inheritance", - vec!("base","basecrate","derived","derivedcrate"), - true, false), - MethodCall => ("method_call", - vec!("refid","refidcrate","declid","declidcrate","scopeid"), - true, true), - Typedef => ("typedef", vec!("id","qualname","value"), true, true), - ExternalCrate => ("external_crate", vec!("name","crate","file_name"), false, false), - Crate => ("crate", vec!("name"), true, false), - FnCall => ("fn_call", vec!("refid","refidcrate","qualname","scopeid"), true, true), - ModRef => ("mod_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true), - VarRef => ("var_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true), - TypeRef => ("type_ref", - vec!("refid","refidcrate","qualname","scopeid"), - true, true), - StructRef => ("struct_ref", - vec!("refid","refidcrate","qualname","scopeid"), - true, true), - FnRef => ("fn_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true) - } - } - - pub fn make_values_str(&self, - kind: &'static str, - fields: &Vec<&'static str>, - values: Vec, - span: Span) -> Option { - if values.len() != fields.len() { - self.span.sess.span_bug(span, format!( - "Mismatch between length of fields for '{}', expected '{}', found '{}'", - kind, fields.len(), values.len()).as_slice()); - } - - let values = values.iter().map(|s| { - if s.len() > 1020 { - s.as_slice().slice_to(1020) - } else { - s.as_slice() - } - }); - - let pairs = fields.iter().zip(values); - let mut strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape( - if *f == "qualname" { - let mut n = self.krate.clone(); - n.push_str("::"); - n.push_str(v); - n - } else { - String::from_str(v) - } - ))); - Some(strs.fold(String::new(), |mut s, ss| { - s.push_str(ss.as_slice()); - s - })) - } - - pub fn record_without_span(&mut self, - kind: Row, - values: Vec, - span: Span) { - let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind); - - if needs_span { - self.span.sess.span_bug(span, format!( - "Called record_without_span for '{}' which does requires a span", - label).as_slice()); - } - assert!(!dump_spans); - - if self.recorder.dump_spans { - return; - } - - let values_str = match self.make_values_str(label, fields, values, span) { - Some(vs) => vs, - None => return, - }; - - let mut result = String::from_str(label); - result.push_str(values_str.as_slice()); - result.push_str("\n"); - self.recorder.record(result.as_slice()); - } - - pub fn record_with_span(&mut self, - kind: Row, - span: Span, - sub_span: Span, - values: Vec) { - let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind); - - if self.recorder.dump_spans { - if dump_spans { - self.recorder.dump_span(self.span, label, span, Some(sub_span)); - } - return; - } - - if !needs_span { - self.span.sess.span_bug(span, - format!("Called record_with_span for '{}' \ - which does not require a span", label).as_slice()); - } - - let values_str = match self.make_values_str(label, fields, values, span) { - Some(vs) => vs, - None => return, - }; - let result = format!("{},{}{}\n", label, self.span.extent_str(sub_span), values_str); - self.recorder.record(result.as_slice()); - } - - pub fn check_and_record(&mut self, - kind: Row, - span: Span, - sub_span: Option, - values: Vec) { - match sub_span { - Some(sub_span) => self.record_with_span(kind, span, sub_span, values), - None => { - let (label, _, _, _) = FmtStrs::lookup_row(kind); - self.span.report_span_err(label, span); - } - } - } - - pub fn variable_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - value: &str, - typ: &str) { - // Getting a fully qualified name for a variable is hard because in - // the local case they can be overridden in one block and there is no nice way - // to refer to such a scope in english, so we just hack it by appending the - // variable def's node id - let mut qualname = String::from_str(name); - qualname.push_str("$"); - qualname.push_str(id.to_string().as_slice()); - self.check_and_record(Variable, - span, - sub_span, - svec!(id, name, qualname, value, typ, 0u)); - } - - // formal parameters - pub fn formal_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - fn_name: &str, - name: &str, - typ: &str) { - let mut qualname = String::from_str(fn_name); - qualname.push_str("::"); - qualname.push_str(name); - self.check_and_record(Variable, - span, - sub_span, - svec!(id, name, qualname, "", typ, 0u)); - } - - // value is the initialising expression of the static if it is not mut, otherwise "". - pub fn static_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - qualname: &str, - value: &str, - typ: &str, - scope_id: NodeId) { - self.check_and_record(Variable, - span, - sub_span, - svec!(id, name, qualname, value, typ, scope_id)); - } - - pub fn field_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - qualname: &str, - typ: &str, - scope_id: NodeId) { - self.check_and_record(Variable, - span, - sub_span, - svec!(id, name, qualname, "", typ, scope_id)); - } - - pub fn enum_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - scope_id: NodeId) { - self.check_and_record(Enum, - span, - sub_span, - svec!(id, name, scope_id)); - } - - pub fn tuple_variant_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - qualname: &str, - val: &str, - scope_id: NodeId) { - self.check_and_record(Variant, - span, - sub_span, - svec!(id, name, qualname, val, scope_id)); - } - - pub fn struct_variant_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - ctor_id: NodeId, - name: &str, - val: &str, - scope_id: NodeId) { - self.check_and_record(VariantStruct, - span, - sub_span, - svec!(id, ctor_id, name, val, scope_id)); - } - - pub fn fn_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - scope_id: NodeId) { - self.check_and_record(Function, - span, - sub_span, - svec!(id, name, "", "", scope_id)); - } - - pub fn method_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - decl_id: Option, - scope_id: NodeId) { - let values = match decl_id { - Some(decl_id) => svec!(id, name, decl_id.node, decl_id.krate, scope_id), - None => svec!(id, name, "", "", scope_id) - }; - self.check_and_record(Function, - span, - sub_span, - values); - } - - pub fn method_decl_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - scope_id: NodeId) { - self.check_and_record(MethodDecl, - span, - sub_span, - svec!(id, name, scope_id)); - } - - pub fn struct_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - ctor_id: NodeId, - name: &str, - scope_id: NodeId) { - self.check_and_record(Struct, - span, - sub_span, - svec!(id, ctor_id, name, scope_id)); - } - - pub fn trait_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - scope_id: NodeId) { - self.check_and_record(Trait, - span, - sub_span, - svec!(id, name, scope_id)); - } - - pub fn impl_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - ref_id: DefId, - scope_id: NodeId) { - self.check_and_record(Impl, - span, - sub_span, - svec!(id, ref_id.node, ref_id.krate, scope_id)); - } - - pub fn mod_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - name: &str, - parent: NodeId, - filename: &str) { - self.check_and_record(Module, - span, - sub_span, - svec!(id, name, parent, filename)); - } - - pub fn use_alias_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - mod_id: Option, - name: &str, - parent: NodeId) { - let (mod_node, mod_crate) = match mod_id { - Some(mod_id) => (mod_id.node, mod_id.krate), - None => (0, 0) - }; - self.check_and_record(UseAlias, - span, - sub_span, - svec!(id, mod_node, mod_crate, name, parent)); - } - - pub fn extern_crate_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - cnum: ast::CrateNum, - name: &str, - loc: &str, - parent: NodeId) { - self.check_and_record(ExternCrate, - span, - sub_span, - svec!(id, name, loc, cnum, parent)); - } - - pub fn inherit_str(&mut self, - span: Span, - sub_span: Option, - base_id: DefId, - deriv_id: NodeId) { - self.check_and_record(Inheritance, - span, - sub_span, - svec!(base_id.node, - base_id.krate, - deriv_id, - 0u)); - } - - pub fn fn_call_str(&mut self, - span: Span, - sub_span: Option, - id: DefId, - scope_id:NodeId) { - self.check_and_record(FnCall, - span, - sub_span, - svec!(id.node, id.krate, "", scope_id)); - } - - pub fn meth_call_str(&mut self, - span: Span, - sub_span: Option, - defid: Option, - declid: Option, - scope_id: NodeId) { - let (dfn, dfk) = match defid { - Some(defid) => (defid.node, defid.krate), - None => (0, 0) - }; - let (dcn, dck) = match declid { - Some(declid) => (s!(declid.node), s!(declid.krate)), - None => ("".to_string(), "".to_string()) - }; - self.check_and_record(MethodCall, - span, - sub_span, - svec!(dfn, dfk, dcn, dck, scope_id)); - } - - pub fn sub_mod_ref_str(&mut self, - span: Span, - sub_span: Span, - qualname: &str, - parent:NodeId) { - self.record_with_span(ModRef, - span, - sub_span, - svec!(0u, 0u, qualname, parent)); - } - - pub fn typedef_str(&mut self, - span: Span, - sub_span: Option, - id: NodeId, - qualname: &str, - value: &str) { - self.check_and_record(Typedef, - span, - sub_span, - svec!(id, qualname, value)); - } - - pub fn crate_str(&mut self, - span: Span, - name: &str) { - self.record_with_span(Crate, - span, - span, - svec!(name)); - } - - pub fn external_crate_str(&mut self, - span: Span, - name: &str, - num: ast::CrateNum) { - let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo); - self.record_without_span(ExternalCrate, - svec!(name, num, lo_loc.file.name), - span); - } - - pub fn sub_type_ref_str(&mut self, - span: Span, - sub_span: Span, - qualname: &str) { - self.record_with_span(TypeRef, - span, - sub_span, - svec!(0u, 0u, qualname, 0u)); - } - - // A slightly generic function for a reference to an item of any kind. - pub fn ref_str(&mut self, - kind: Row, - span: Span, - sub_span: Option, - id: DefId, - scope_id: NodeId) { - self.check_and_record(kind, - span, - sub_span, - svec!(id.node, id.krate, "", scope_id)); - } -} diff --git a/src/librustc/middle/save/span_utils.rs b/src/librustc/middle/save/span_utils.rs deleted file mode 100644 index 14d8bf0cfed..00000000000 --- a/src/librustc/middle/save/span_utils.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use driver::session::Session; - -use middle::save::generated_code; - -use std::cell::Cell; - -use syntax::ast; -use syntax::codemap::*; -use syntax::parse::lexer; -use syntax::parse::lexer::{Reader,StringReader}; -use syntax::parse::token; -use syntax::parse::token::{keywords, Token}; - -pub struct SpanUtils<'a> { - pub sess: &'a Session, - pub err_count: Cell, -} - -impl<'a> SpanUtils<'a> { - // Standard string for extents/location. - pub fn extent_str(&self, span: Span) -> String { - let lo_loc = self.sess.codemap().lookup_char_pos(span.lo); - let hi_loc = self.sess.codemap().lookup_char_pos(span.hi); - let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo); - let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi); - let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos; - let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos; - - format!("file_name,{},file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\ - file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}", - lo_loc.file.name, - lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(), lo_pos_byte.to_uint(), - hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint(), hi_pos_byte.to_uint()) - } - - // sub_span starts at span.lo, so we need to adjust the positions etc. - // If sub_span is None, we don't need to adjust. - pub fn make_sub_span(&self, span: Span, sub_span: Option) -> Option { - let loc = self.sess.codemap().lookup_char_pos(span.lo); - assert!(!generated_code(span), - "generated code; we should not be processing this `{}` in {}, line {}", - self.snippet(span), loc.file.name, loc.line); - - match sub_span { - None => None, - Some(sub) => { - let FileMapAndBytePos {fm, pos} = - self.sess.codemap().lookup_byte_offset(span.lo); - let base = pos + fm.start_pos; - Some(Span { - lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos, - hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos, - expn_id: NO_EXPANSION, - }) - } - } - } - - pub fn snippet(&self, span: Span) -> String { - match self.sess.codemap().span_to_snippet(span) { - Some(s) => s, - None => String::new(), - } - } - - pub fn retokenise_span(&self, span: Span) -> StringReader<'a> { - // sadness - we don't have spans for sub-expressions nor access to the tokens - // so in order to get extents for the function name itself (which dxr expects) - // we need to re-tokenise the fn definition - - // Note: this is a bit awful - it adds the contents of span to the end of - // the codemap as a new filemap. This is mostly OK, but means we should - // not iterate over the codemap. Also, any spans over the new filemap - // are incompatible with spans over other filemaps. - let filemap = self.sess.codemap().new_filemap(String::from_str(""), - self.snippet(span)); - let s = self.sess; - lexer::StringReader::new(s.diagnostic(), filemap) - } - - // Re-parses a path and returns the span for the last identifier in the path - pub fn span_for_last_ident(&self, span: Span) -> Option { - let mut result = None; - - let mut toks = self.retokenise_span(span); - let mut bracket_count = 0u; - loop { - let ts = toks.real_token(); - if ts.tok == token::Eof { - return self.make_sub_span(span, result) - } - if bracket_count == 0 && - (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) { - result = Some(ts.sp); - } - - bracket_count += match ts.tok { - token::Lt => 1, - token::Gt => -1, - token::BinOp(token::Shr) => -2, - _ => 0 - } - } - } - - // Return the span for the first identifier in the path. - pub fn span_for_first_ident(&self, span: Span) -> Option { - let mut toks = self.retokenise_span(span); - let mut bracket_count = 0u; - loop { - let ts = toks.real_token(); - if ts.tok == token::Eof { - return None; - } - if bracket_count == 0 && - (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) { - return self.make_sub_span(span, Some(ts.sp)); - } - - bracket_count += match ts.tok { - token::Lt => 1, - token::Gt => -1, - token::BinOp(token::Shr) => -2, - _ => 0 - } - } - } - - // Return the span for the last ident before a `(` or `<` or '::<' and outside any - // any brackets, or the last span. - pub fn sub_span_for_meth_name(&self, span: Span) -> Option { - let mut toks = self.retokenise_span(span); - let mut prev = toks.real_token(); - let mut result = None; - let mut bracket_count = 0u; - let mut last_span = None; - while prev.tok != token::Eof { - last_span = None; - let mut next = toks.real_token(); - - if (next.tok == token::OpenDelim(token::Paren) || - next.tok == token::Lt) && - bracket_count == 0 && - prev.tok.is_ident() { - result = Some(prev.sp); - } - - if bracket_count == 0 && - next.tok == token::ModSep { - let old = prev; - prev = next; - next = toks.real_token(); - if next.tok == token::Lt && - old.tok.is_ident() { - result = Some(old.sp); - } - } - - bracket_count += match prev.tok { - token::OpenDelim(token::Paren) | token::Lt => 1, - token::CloseDelim(token::Paren) | token::Gt => -1, - token::BinOp(token::Shr) => -2, - _ => 0 - }; - - if prev.tok.is_ident() && bracket_count == 0 { - last_span = Some(prev.sp); - } - prev = next; - } - if result.is_none() && last_span.is_some() { - return self.make_sub_span(span, last_span); - } - return self.make_sub_span(span, result); - } - - // Return the span for the last ident before a `<` and outside any - // brackets, or the last span. - pub fn sub_span_for_type_name(&self, span: Span) -> Option { - let mut toks = self.retokenise_span(span); - let mut prev = toks.real_token(); - let mut result = None; - let mut bracket_count = 0u; - loop { - let next = toks.real_token(); - - if (next.tok == token::Lt || - next.tok == token::Colon) && - bracket_count == 0 && - prev.tok.is_ident() { - result = Some(prev.sp); - } - - bracket_count += match prev.tok { - token::Lt => 1, - token::Gt => -1, - token::BinOp(token::Shr) => -2, - _ => 0 - }; - - if next.tok == token::Eof { - break; - } - prev = next; - } - if bracket_count != 0 { - let loc = self.sess.codemap().lookup_char_pos(span.lo); - self.sess.span_bug(span, - format!("Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}", - self.snippet(span), loc.file.name, loc.line).as_slice()); - } - if result.is_none() && prev.tok.is_ident() && bracket_count == 0 { - return self.make_sub_span(span, Some(prev.sp)); - } - self.make_sub_span(span, result) - } - - // Reparse span and return an owned vector of sub spans of the first limit - // identifier tokens in the given nesting level. - // example with Foo, Bar> - // Nesting = 0: all idents outside of brackets: ~[Foo] - // Nesting = 1: idents within one level of brackets: ~[Bar, Bar] - pub fn spans_with_brackets(&self, span: Span, nesting: int, limit: int) -> Vec { - let mut result: Vec = vec!(); - - let mut toks = self.retokenise_span(span); - // We keep track of how many brackets we're nested in - let mut bracket_count = 0i; - loop { - let ts = toks.real_token(); - if ts.tok == token::Eof { - if bracket_count != 0 { - let loc = self.sess.codemap().lookup_char_pos(span.lo); - self.sess.span_bug(span, format!( - "Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}", - self.snippet(span), loc.file.name, loc.line).as_slice()); - } - return result - } - if (result.len() as int) == limit { - return result; - } - bracket_count += match ts.tok { - token::Lt => 1, - token::Gt => -1, - token::BinOp(token::Shl) => 2, - token::BinOp(token::Shr) => -2, - _ => 0 - }; - if ts.tok.is_ident() && - bracket_count == nesting { - result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); - } - } - } - - pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option { - let mut toks = self.retokenise_span(span); - let mut prev = toks.real_token(); - loop { - if prev.tok == token::Eof { - return None; - } - let next = toks.real_token(); - if next.tok == tok { - return self.make_sub_span(span, Some(prev.sp)); - } - prev = next; - } - } - - pub fn sub_span_after_keyword(&self, - span: Span, - keyword: keywords::Keyword) -> Option { - let mut toks = self.retokenise_span(span); - loop { - let ts = toks.real_token(); - if ts.tok == token::Eof { - return None; - } - if ts.tok.is_keyword(keyword) { - let ts = toks.real_token(); - if ts.tok == token::Eof { - return None - } else { - println!("found keyword: {} at {}", ts, ts.sp); - return self.make_sub_span(span, Some(ts.sp)); - } - } - } - } - - // Returns a list of the spans of idents in a patch. - // E.g., For foo::bar::baz, we return [foo, bar, baz] (well, their spans) - pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec { - if generated_code(path.span) { - return vec!(); - } - - self.spans_with_brackets(path.span, 0, -1) - } - - // Return an owned vector of the subspans of the param identifier - // tokens found in span. - pub fn spans_for_ty_params(&self, span: Span, number: int) -> Vec { - if generated_code(span) { - return vec!(); - } - // Type params are nested within one level of brackets: - // i.e. we want ~[A, B] from Foo> - self.spans_with_brackets(span, 1, number) - } - - pub fn report_span_err(&self, kind: &str, span: Span) { - let loc = self.sess.codemap().lookup_char_pos(span.lo); - info!("({}) Could not find sub_span in `{}` in {}, line {}", - kind, self.snippet(span), loc.file.name, loc.line); - self.err_count.set(self.err_count.get()+1); - if self.err_count.get() > 1000 { - self.sess.bug("span errors reached 1000, giving up"); - } - } -} diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs deleted file mode 100644 index d7a2e7429f9..00000000000 --- a/src/librustc/middle/trans/_match.rs +++ /dev/null @@ -1,1746 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * - * # Compilation of match statements - * - * I will endeavor to explain the code as best I can. I have only a loose - * understanding of some parts of it. - * - * ## Matching - * - * The basic state of the code is maintained in an array `m` of `Match` - * objects. Each `Match` describes some list of patterns, all of which must - * match against the current list of values. If those patterns match, then - * the arm listed in the match is the correct arm. A given arm may have - * multiple corresponding match entries, one for each alternative that - * remains. As we proceed these sets of matches are adjusted by the various - * `enter_XXX()` functions, each of which adjusts the set of options given - * some information about the value which has been matched. - * - * So, initially, there is one value and N matches, each of which have one - * constituent pattern. N here is usually the number of arms but may be - * greater, if some arms have multiple alternatives. For example, here: - * - * enum Foo { A, B(int), C(uint, uint) } - * match foo { - * A => ..., - * B(x) => ..., - * C(1u, 2) => ..., - * C(_) => ... - * } - * - * The value would be `foo`. There would be four matches, each of which - * contains one pattern (and, in one case, a guard). We could collect the - * various options and then compile the code for the case where `foo` is an - * `A`, a `B`, and a `C`. When we generate the code for `C`, we would (1) - * drop the two matches that do not match a `C` and (2) expand the other two - * into two patterns each. In the first case, the two patterns would be `1u` - * and `2`, and the in the second case the _ pattern would be expanded into - * `_` and `_`. The two values are of course the arguments to `C`. - * - * Here is a quick guide to the various functions: - * - * - `compile_submatch()`: The main workhouse. It takes a list of values and - * a list of matches and finds the various possibilities that could occur. - * - * - `enter_XXX()`: modifies the list of matches based on some information - * about the value that has been matched. For example, - * `enter_rec_or_struct()` adjusts the values given that a record or struct - * has been matched. This is an infallible pattern, so *all* of the matches - * must be either wildcards or record/struct patterns. `enter_opt()` - * handles the fallible cases, and it is correspondingly more complex. - * - * ## Bindings - * - * We store information about the bound variables for each arm as part of the - * per-arm `ArmData` struct. There is a mapping from identifiers to - * `BindingInfo` structs. These structs contain the mode/id/type of the - * binding, but they also contain an LLVM value which points at an alloca - * called `llmatch`. For by value bindings that are Copy, we also create - * an extra alloca that we copy the matched value to so that any changes - * we do to our copy is not reflected in the original and vice-versa. - * We don't do this if it's a move since the original value can't be used - * and thus allowing us to cheat in not creating an extra alloca. - * - * The `llmatch` binding always stores a pointer into the value being matched - * which points at the data for the binding. If the value being matched has - * type `T`, then, `llmatch` will point at an alloca of type `T*` (and hence - * `llmatch` has type `T**`). So, if you have a pattern like: - * - * let a: A = ...; - * let b: B = ...; - * match (a, b) { (ref c, d) => { ... } } - * - * For `c` and `d`, we would generate allocas of type `C*` and `D*` - * respectively. These are called the `llmatch`. As we match, when we come - * up against an identifier, we store the current pointer into the - * corresponding alloca. - * - * Once a pattern is completely matched, and assuming that there is no guard - * pattern, we will branch to a block that leads to the body itself. For any - * by-value bindings, this block will first load the ptr from `llmatch` (the - * one of type `D*`) and then load a second time to get the actual value (the - * one of type `D`). For by ref bindings, the value of the local variable is - * simply the first alloca. - * - * So, for the example above, we would generate a setup kind of like this: - * - * +-------+ - * | Entry | - * +-------+ - * | - * +--------------------------------------------+ - * | llmatch_c = (addr of first half of tuple) | - * | llmatch_d = (addr of second half of tuple) | - * +--------------------------------------------+ - * | - * +--------------------------------------+ - * | *llbinding_d = **llmatch_d | - * +--------------------------------------+ - * - * If there is a guard, the situation is slightly different, because we must - * execute the guard code. Moreover, we need to do so once for each of the - * alternatives that lead to the arm, because if the guard fails, they may - * have different points from which to continue the search. Therefore, in that - * case, we generate code that looks more like: - * - * +-------+ - * | Entry | - * +-------+ - * | - * +-------------------------------------------+ - * | llmatch_c = (addr of first half of tuple) | - * | llmatch_d = (addr of first half of tuple) | - * +-------------------------------------------+ - * | - * +-------------------------------------------------+ - * | *llbinding_d = **llmatch_d | - * | check condition | - * | if false { goto next case } | - * | if true { goto body } | - * +-------------------------------------------------+ - * - * The handling for the cleanups is a bit... sensitive. Basically, the body - * is the one that invokes `add_clean()` for each binding. During the guard - * evaluation, we add temporary cleanups and revoke them after the guard is - * evaluated (it could fail, after all). Note that guards and moves are - * just plain incompatible. - * - * Some relevant helper functions that manage bindings: - * - `create_bindings_map()` - * - `insert_lllocals()` - * - * - * ## Notes on vector pattern matching. - * - * Vector pattern matching is surprisingly tricky. The problem is that - * the structure of the vector isn't fully known, and slice matches - * can be done on subparts of it. - * - * The way that vector pattern matches are dealt with, then, is as - * follows. First, we make the actual condition associated with a - * vector pattern simply a vector length comparison. So the pattern - * [1, .. x] gets the condition "vec len >= 1", and the pattern - * [.. x] gets the condition "vec len >= 0". The problem here is that - * having the condition "vec len >= 1" hold clearly does not mean that - * only a pattern that has exactly that condition will match. This - * means that it may well be the case that a condition holds, but none - * of the patterns matching that condition match; to deal with this, - * when doing vector length matches, we have match failures proceed to - * the next condition to check. - * - * There are a couple more subtleties to deal with. While the "actual" - * condition associated with vector length tests is simply a test on - * the vector length, the actual vec_len Opt entry contains more - * information used to restrict which matches are associated with it. - * So that all matches in a submatch are matching against the same - * values from inside the vector, they are split up by how many - * elements they match at the front and at the back of the vector. In - * order to make sure that arms are properly checked in order, even - * with the overmatching conditions, each vec_len Opt entry is - * associated with a range of matches. - * Consider the following: - * - * match &[1, 2, 3] { - * [1, 1, .. _] => 0, - * [1, 2, 2, .. _] => 1, - * [1, 2, 3, .. _] => 2, - * [1, 2, .. _] => 3, - * _ => 4 - * } - * The proper arm to match is arm 2, but arms 0 and 3 both have the - * condition "len >= 2". If arm 3 was lumped in with arm 0, then the - * wrong branch would be taken. Instead, vec_len Opts are associated - * with a contiguous range of matches that have the same "shape". - * This is sort of ugly and requires a bunch of special handling of - * vec_len options. - * - */ - -pub use self::BranchKind::*; -pub use self::OptResult::*; -pub use self::TransBindingMode::*; -use self::Opt::*; -use self::FailureHandler::*; - -use back::abi; -use driver::config::FullDebugInfo; -use llvm::{ValueRef, BasicBlockRef}; -use middle::check_match::StaticInliner; -use middle::check_match; -use middle::const_eval; -use middle::def; -use middle::expr_use_visitor as euv; -use middle::lang_items::StrEqFnLangItem; -use middle::mem_categorization as mc; -use middle::pat_util::*; -use middle::resolve::DefMap; -use middle::trans::adt; -use middle::trans::base::*; -use middle::trans::build::{AddCase, And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load}; -use middle::trans::build::{Mul, Not, Store, Sub, add_comment}; -use middle::trans::build; -use middle::trans::callee; -use middle::trans::cleanup::{mod, CleanupMethods}; -use middle::trans::common::*; -use middle::trans::consts; -use middle::trans::datum::*; -use middle::trans::expr::{mod, Dest}; -use middle::trans::tvec; -use middle::trans::type_of; -use middle::trans::debuginfo; -use middle::ty; -use util::common::indenter; -use util::nodemap::FnvHashMap; -use util::ppaux::{Repr, vec_map_to_string}; - -use std; -use std::iter::AdditiveIterator; -use std::rc::Rc; -use syntax::ast; -use syntax::ast::{DUMMY_NODE_ID, Ident}; -use syntax::codemap::Span; -use syntax::fold::Folder; -use syntax::ptr::P; - -#[deriving(Show)] -struct ConstantExpr<'a>(&'a ast::Expr); - -impl<'a> ConstantExpr<'a> { - fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool { - let ConstantExpr(expr) = self; - let ConstantExpr(other_expr) = other; - match const_eval::compare_lit_exprs(tcx, expr, other_expr) { - Some(val1) => val1 == 0, - None => panic!("compare_list_exprs: type mismatch"), - } - } -} - -// An option identifying a branch (either a literal, an enum variant or a range) -#[deriving(Show)] -enum Opt<'a> { - ConstantValue(ConstantExpr<'a>), - ConstantRange(ConstantExpr<'a>, ConstantExpr<'a>), - Variant(ty::Disr, Rc, ast::DefId), - SliceLengthEqual(uint), - SliceLengthGreaterOrEqual(/* prefix length */ uint, /* suffix length */ uint), -} - -impl<'a> Opt<'a> { - fn eq(&self, other: &Opt<'a>, tcx: &ty::ctxt) -> bool { - match (self, other) { - (&ConstantValue(a), &ConstantValue(b)) => a.eq(b, tcx), - (&ConstantRange(a1, a2), &ConstantRange(b1, b2)) => { - a1.eq(b1, tcx) && a2.eq(b2, tcx) - } - (&Variant(a_disr, ref a_repr, a_def), &Variant(b_disr, ref b_repr, b_def)) => { - a_disr == b_disr && *a_repr == *b_repr && a_def == b_def - } - (&SliceLengthEqual(a), &SliceLengthEqual(b)) => a == b, - (&SliceLengthGreaterOrEqual(a1, a2), &SliceLengthGreaterOrEqual(b1, b2)) => { - a1 == b1 && a2 == b2 - } - _ => false - } - } - - fn trans<'blk, 'tcx>(&self, mut bcx: Block<'blk, 'tcx>) -> OptResult<'blk, 'tcx> { - let _icx = push_ctxt("match::trans_opt"); - let ccx = bcx.ccx(); - match *self { - ConstantValue(ConstantExpr(lit_expr)) => { - let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id); - let (llval, _) = consts::const_expr(ccx, &*lit_expr); - let lit_datum = immediate_rvalue(llval, lit_ty); - let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); - SingleResult(Result::new(bcx, lit_datum.val)) - } - ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2)) => { - let (l1, _) = consts::const_expr(ccx, &**l1); - let (l2, _) = consts::const_expr(ccx, &**l2); - RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) - } - Variant(disr_val, ref repr, _) => { - adt::trans_case(bcx, &**repr, disr_val) - } - SliceLengthEqual(length) => { - SingleResult(Result::new(bcx, C_uint(ccx, length))) - } - SliceLengthGreaterOrEqual(prefix, suffix) => { - LowerBound(Result::new(bcx, C_uint(ccx, prefix + suffix))) - } - } - } -} - -#[deriving(PartialEq)] -pub enum BranchKind { - NoBranch, - Single, - Switch, - Compare, - CompareSliceLength -} - -pub enum OptResult<'blk, 'tcx: 'blk> { - SingleResult(Result<'blk, 'tcx>), - RangeResult(Result<'blk, 'tcx>, Result<'blk, 'tcx>), - LowerBound(Result<'blk, 'tcx>) -} - -#[deriving(Clone)] -pub enum TransBindingMode { - TrByCopy(/* llbinding */ ValueRef), - TrByMove, - TrByRef, -} - -/** - * Information about a pattern binding: - * - `llmatch` is a pointer to a stack slot. The stack slot contains a - * pointer into the value being matched. Hence, llmatch has type `T**` - * where `T` is the value being matched. - * - `trmode` is the trans binding mode - * - `id` is the node id of the binding - * - `ty` is the Rust type of the binding */ - #[deriving(Clone)] -pub struct BindingInfo { - pub llmatch: ValueRef, - pub trmode: TransBindingMode, - pub id: ast::NodeId, - pub span: Span, - pub ty: ty::t, -} - -type BindingsMap = FnvHashMap; - -struct ArmData<'p, 'blk, 'tcx: 'blk> { - bodycx: Block<'blk, 'tcx>, - arm: &'p ast::Arm, - bindings_map: BindingsMap -} - -/** - * Info about Match. - * If all `pats` are matched then arm `data` will be executed. - * As we proceed `bound_ptrs` are filled with pointers to values to be bound, - * these pointers are stored in llmatch variables just before executing `data` arm. - */ -struct Match<'a, 'p: 'a, 'blk: 'a, 'tcx: 'blk> { - pats: Vec<&'p ast::Pat>, - data: &'a ArmData<'p, 'blk, 'tcx>, - bound_ptrs: Vec<(Ident, ValueRef)>, -} - -impl<'a, 'p, 'blk, 'tcx> Repr for Match<'a, 'p, 'blk, 'tcx> { - fn repr(&self, tcx: &ty::ctxt) -> String { - if tcx.sess.verbose() { - // for many programs, this just take too long to serialize - self.pats.repr(tcx) - } else { - format!("{} pats", self.pats.len()) - } - } -} - -fn has_nested_bindings(m: &[Match], col: uint) -> bool { - for br in m.iter() { - match br.pats[col].node { - ast::PatIdent(_, _, Some(_)) => return true, - _ => () - } - } - return false; -} - -fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - m: &[Match<'a, 'p, 'blk, 'tcx>], - col: uint, - val: ValueRef) - -> Vec> { - debug!("expand_nested_bindings(bcx={}, m={}, col={}, val={})", - bcx.to_str(), - m.repr(bcx.tcx()), - col, - bcx.val_to_string(val)); - let _indenter = indenter(); - - m.iter().map(|br| { - let mut bound_ptrs = br.bound_ptrs.clone(); - let mut pat = br.pats[col]; - loop { - pat = match pat.node { - ast::PatIdent(_, ref path, Some(ref inner)) => { - bound_ptrs.push((path.node, val)); - &**inner - }, - _ => break - } - } - - let mut pats = br.pats.clone(); - pats[col] = pat; - Match { - pats: pats, - data: &*br.data, - bound_ptrs: bound_ptrs - } - }).collect() -} - -type EnterPatterns<'a, 'p> = |&[&'p ast::Pat]|: 'a -> Option>; - -fn enter_match<'a, 'b, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - dm: &DefMap, - m: &[Match<'a, 'p, 'blk, 'tcx>], - col: uint, - val: ValueRef, - e: EnterPatterns<'b, 'p>) - -> Vec> { - debug!("enter_match(bcx={}, m={}, col={}, val={})", - bcx.to_str(), - m.repr(bcx.tcx()), - col, - bcx.val_to_string(val)); - let _indenter = indenter(); - - m.iter().filter_map(|br| { - e(br.pats.as_slice()).map(|pats| { - let this = br.pats[col]; - let mut bound_ptrs = br.bound_ptrs.clone(); - match this.node { - ast::PatIdent(_, ref path, None) => { - if pat_is_binding(dm, &*this) { - bound_ptrs.push((path.node, val)); - } - } - ast::PatVec(ref before, Some(ref slice), ref after) => { - match slice.node { - ast::PatIdent(_, ref path, None) => { - let subslice_val = bind_subslice_pat( - bcx, this.id, val, - before.len(), after.len()); - bound_ptrs.push((path.node, subslice_val)); - } - _ => {} - } - } - _ => {} - } - Match { - pats: pats, - data: br.data, - bound_ptrs: bound_ptrs - } - }) - }).collect() -} - -fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - dm: &DefMap, - m: &[Match<'a, 'p, 'blk, 'tcx>], - col: uint, - val: ValueRef) - -> Vec> { - debug!("enter_default(bcx={}, m={}, col={}, val={})", - bcx.to_str(), - m.repr(bcx.tcx()), - col, - bcx.val_to_string(val)); - let _indenter = indenter(); - - // Collect all of the matches that can match against anything. - enter_match(bcx, dm, m, col, val, |pats| { - if pat_is_binding_or_wild(dm, &*pats[col]) { - let mut r = pats[..col].to_vec(); - r.push_all(pats[col + 1..]); - Some(r) - } else { - None - } - }) -} - -// nmatsakis: what does enter_opt do? -// in trans/match -// trans/match.rs is like stumbling around in a dark cave -// pcwalton: the enter family of functions adjust the set of -// patterns as needed -// yeah, at some point I kind of achieved some level of -// understanding -// anyhow, they adjust the patterns given that something of that -// kind has been found -// pcwalton: ok, right, so enter_XXX() adjusts the patterns, as I -// said -// enter_match() kind of embodies the generic code -// it is provided with a function that tests each pattern to see -// if it might possibly apply and so forth -// so, if you have a pattern like {a: _, b: _, _} and one like _ -// then _ would be expanded to (_, _) -// one spot for each of the sub-patterns -// enter_opt() is one of the more complex; it covers the fallible -// cases -// enter_rec_or_struct() or enter_tuple() are simpler, since they -// are infallible patterns -// so all patterns must either be records (resp. tuples) or -// wildcards - -/// The above is now outdated in that enter_match() now takes a function that -/// takes the complete row of patterns rather than just the first one. -/// Also, most of the enter_() family functions have been unified with -/// the check_match specialization step. -fn enter_opt<'a, 'p, 'blk, 'tcx>( - bcx: Block<'blk, 'tcx>, - _: ast::NodeId, - dm: &DefMap, - m: &[Match<'a, 'p, 'blk, 'tcx>], - opt: &Opt, - col: uint, - variant_size: uint, - val: ValueRef) - -> Vec> { - debug!("enter_opt(bcx={}, m={}, opt={}, col={}, val={})", - bcx.to_str(), - m.repr(bcx.tcx()), - *opt, - col, - bcx.val_to_string(val)); - let _indenter = indenter(); - - let ctor = match opt { - &ConstantValue(ConstantExpr(expr)) => check_match::ConstantValue( - const_eval::eval_const_expr(bcx.tcx(), &*expr) - ), - &ConstantRange(ConstantExpr(lo), ConstantExpr(hi)) => check_match::ConstantRange( - const_eval::eval_const_expr(bcx.tcx(), &*lo), - const_eval::eval_const_expr(bcx.tcx(), &*hi) - ), - &SliceLengthEqual(n) => - check_match::Slice(n), - &SliceLengthGreaterOrEqual(before, after) => - check_match::SliceWithSubslice(before, after), - &Variant(_, _, def_id) => - check_match::Constructor::Variant(def_id) - }; - - let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; - enter_match(bcx, dm, m, col, val, |pats| - check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size) - ) -} - -// Returns the options in one column of matches. An option is something that -// needs to be conditionally matched at runtime; for example, the discriminant -// on a set of enum variants or a literal. -fn get_branches<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - m: &[Match<'a, 'p, 'blk, 'tcx>], col: uint) - -> Vec> { - let tcx = bcx.tcx(); - - let mut found: Vec = vec![]; - for br in m.iter() { - let cur = br.pats[col]; - let opt = match cur.node { - ast::PatLit(ref l) => ConstantValue(ConstantExpr(&**l)), - ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => { - // This is either an enum variant or a variable binding. - let opt_def = tcx.def_map.borrow().get(&cur.id).cloned(); - match opt_def { - Some(def::DefVariant(enum_id, var_id, _)) => { - let variant = ty::enum_variant_with_id(tcx, enum_id, var_id); - Variant(variant.disr_val, adt::represent_node(bcx, cur.id), var_id) - } - _ => continue - } - } - ast::PatRange(ref l1, ref l2) => { - ConstantRange(ConstantExpr(&**l1), ConstantExpr(&**l2)) - } - ast::PatVec(ref before, None, ref after) => { - SliceLengthEqual(before.len() + after.len()) - } - ast::PatVec(ref before, Some(_), ref after) => { - SliceLengthGreaterOrEqual(before.len(), after.len()) - } - _ => continue - }; - - if !found.iter().any(|x| x.eq(&opt, tcx)) { - found.push(opt); - } - } - found -} - -struct ExtractedBlock<'blk, 'tcx: 'blk> { - vals: Vec, - bcx: Block<'blk, 'tcx>, -} - -fn extract_variant_args<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - repr: &adt::Repr, - disr_val: ty::Disr, - val: ValueRef) - -> ExtractedBlock<'blk, 'tcx> { - let _icx = push_ctxt("match::extract_variant_args"); - let args = Vec::from_fn(adt::num_args(repr, disr_val), |i| { - adt::trans_field_ptr(bcx, repr, val, disr_val, i) - }); - - ExtractedBlock { vals: args, bcx: bcx } -} - -fn match_datum(val: ValueRef, left_ty: ty::t) -> Datum { - /*! - * Helper for converting from the ValueRef that we pass around in - * the match code, which is always an lvalue, into a Datum. Eventually - * we should just pass around a Datum and be done with it. - */ - Datum::new(val, left_ty, Lvalue) -} - -fn bind_subslice_pat(bcx: Block, - pat_id: ast::NodeId, - val: ValueRef, - offset_left: uint, - offset_right: uint) -> ValueRef { - let _icx = push_ctxt("match::bind_subslice_pat"); - let vec_ty = node_id_type(bcx, pat_id); - let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty))); - let vec_datum = match_datum(val, vec_ty); - let (base, len) = vec_datum.get_vec_base_and_len(bcx); - - let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), offset_left)); - let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset); - let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right); - let slice_len = Sub(bcx, len, slice_len_offset); - let slice_ty = ty::mk_slice(bcx.tcx(), - ty::ReStatic, - ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}); - let scratch = rvalue_scratch_datum(bcx, slice_ty, ""); - Store(bcx, slice_begin, - GEPi(bcx, scratch.val, &[0u, abi::slice_elt_base])); - Store(bcx, slice_len, GEPi(bcx, scratch.val, &[0u, abi::slice_elt_len])); - scratch.val -} - -fn extract_vec_elems<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - left_ty: ty::t, - before: uint, - after: uint, - val: ValueRef) - -> ExtractedBlock<'blk, 'tcx> { - let _icx = push_ctxt("match::extract_vec_elems"); - let vec_datum = match_datum(val, left_ty); - let (base, len) = vec_datum.get_vec_base_and_len(bcx); - let mut elems = vec![]; - elems.extend(range(0, before).map(|i| GEPi(bcx, base, &[i]))); - elems.extend(range(0, after).rev().map(|i| { - InBoundsGEP(bcx, base, &[ - Sub(bcx, len, C_uint(bcx.ccx(), i + 1)) - ]) - })); - ExtractedBlock { vals: elems, bcx: bcx } -} - -// Macro for deciding whether any of the remaining matches fit a given kind of -// pattern. Note that, because the macro is well-typed, either ALL of the -// matches should fit that sort of pattern or NONE (however, some of the -// matches may be wildcards like _ or identifiers). -macro_rules! any_pat ( - ($m:expr, $col:expr, $pattern:pat) => ( - ($m).iter().any(|br| { - match br.pats[$col].node { - $pattern => true, - _ => false - } - }) - ) -) - -fn any_uniq_pat(m: &[Match], col: uint) -> bool { - any_pat!(m, col, ast::PatBox(_)) -} - -fn any_region_pat(m: &[Match], col: uint) -> bool { - any_pat!(m, col, ast::PatRegion(_)) -} - -fn any_irrefutable_adt_pat(tcx: &ty::ctxt, m: &[Match], col: uint) -> bool { - m.iter().any(|br| { - let pat = br.pats[col]; - match pat.node { - ast::PatTup(_) => true, - ast::PatStruct(..) => { - match tcx.def_map.borrow().get(&pat.id) { - Some(&def::DefVariant(..)) => false, - _ => true, - } - } - ast::PatEnum(..) | ast::PatIdent(_, _, None) => { - match tcx.def_map.borrow().get(&pat.id) { - Some(&def::DefStruct(..)) => true, - _ => false - } - } - _ => false - } - }) -} - -/// What to do when the pattern match fails. -enum FailureHandler { - Infallible, - JumpToBasicBlock(BasicBlockRef), - Unreachable -} - -impl FailureHandler { - fn is_fallible(&self) -> bool { - match *self { - Infallible => false, - _ => true - } - } - - fn is_infallible(&self) -> bool { - !self.is_fallible() - } - - fn handle_fail(&self, bcx: Block) { - match *self { - Infallible => - panic!("attempted to panic in a non-panicking panic handler!"), - JumpToBasicBlock(basic_block) => - Br(bcx, basic_block), - Unreachable => - build::Unreachable(bcx) - } - } -} - -fn pick_column_to_specialize(def_map: &DefMap, m: &[Match]) -> Option { - fn pat_score(def_map: &DefMap, pat: &ast::Pat) -> uint { - match pat.node { - ast::PatIdent(_, _, Some(ref inner)) => pat_score(def_map, &**inner), - _ if pat_is_refutable(def_map, pat) => 1u, - _ => 0u - } - } - - let column_score: |&[Match], uint| -> uint = |m, col| { - let total_score = m.iter() - .map(|row| row.pats[col]) - .map(|pat| pat_score(def_map, pat)) - .sum(); - - // Irrefutable columns always go first, they'd only be duplicated in the branches. - if total_score == 0 { - std::uint::MAX - } else { - total_score - } - }; - - let column_contains_any_nonwild_patterns: |&uint| -> bool = |&col| { - m.iter().any(|row| match row.pats[col].node { - ast::PatWild(_) => false, - _ => true - }) - }; - - range(0, m[0].pats.len()) - .filter(column_contains_any_nonwild_patterns) - .map(|col| (col, column_score(m, col))) - .max_by(|&(_, score)| score) - .map(|(col, _)| col) -} - -// Compiles a comparison between two things. -fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - rhs_t: ty::t) - -> Result<'blk, 'tcx> { - fn compare_str<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - rhs_t: ty::t) - -> Result<'blk, 'tcx> { - let did = langcall(cx, - None, - format!("comparison of `{}`", - cx.ty_to_string(rhs_t)).as_slice(), - StrEqFnLangItem); - callee::trans_lang_call(cx, did, &[lhs, rhs], None) - } - - let _icx = push_ctxt("compare_values"); - if ty::type_is_scalar(rhs_t) { - let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, ast::BiEq); - return Result::new(rs.bcx, rs.val); - } - - match ty::get(rhs_t).sty { - ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { - ty::ty_str => compare_str(cx, lhs, rhs, rhs_t), - ty::ty_vec(ty, _) => match ty::get(ty).sty { - ty::ty_uint(ast::TyU8) => { - // NOTE: cast &[u8] to &str and abuse the str_eq lang item, - // which calls memcmp(). - let t = ty::mk_str_slice(cx.tcx(), ty::ReStatic, ast::MutImmutable); - let lhs = BitCast(cx, lhs, type_of::type_of(cx.ccx(), t).ptr_to()); - let rhs = BitCast(cx, rhs, type_of::type_of(cx.ccx(), t).ptr_to()); - compare_str(cx, lhs, rhs, rhs_t) - }, - _ => cx.sess().bug("only byte strings supported in compare_values"), - }, - _ => cx.sess().bug("only string and byte strings supported in compare_values"), - }, - _ => cx.sess().bug("only scalars, byte strings, and strings supported in compare_values"), - } -} - -fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - bindings_map: &BindingsMap, - cs: Option) - -> Block<'blk, 'tcx> { - /*! - * For each binding in `data.bindings_map`, adds an appropriate entry into - * the `fcx.lllocals` map - */ - - for (&ident, &binding_info) in bindings_map.iter() { - let llval = match binding_info.trmode { - // By value mut binding for a copy type: load from the ptr - // into the matched value and copy to our alloca - TrByCopy(llbinding) => { - let llval = Load(bcx, binding_info.llmatch); - let datum = Datum::new(llval, binding_info.ty, Lvalue); - call_lifetime_start(bcx, llbinding); - bcx = datum.store_to(bcx, llbinding); - match cs { - Some(cs) => bcx.fcx.schedule_lifetime_end(cs, llbinding), - _ => {} - } - - llbinding - }, - - // By value move bindings: load from the ptr into the matched value - TrByMove => Load(bcx, binding_info.llmatch), - - // By ref binding: use the ptr into the matched value - TrByRef => binding_info.llmatch - }; - - let datum = Datum::new(llval, binding_info.ty, Lvalue); - match cs { - Some(cs) => { - bcx.fcx.schedule_drop_and_zero_mem(cs, llval, binding_info.ty); - bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch); - } - _ => {} - } - - debug!("binding {} to {}", - binding_info.id, - bcx.val_to_string(llval)); - bcx.fcx.lllocals.borrow_mut().insert(binding_info.id, datum); - - if bcx.sess().opts.debuginfo == FullDebugInfo { - debuginfo::create_match_binding_metadata(bcx, - ident, - binding_info); - } - } - bcx -} - -fn compile_guard<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - guard_expr: &ast::Expr, - data: &ArmData, - m: &[Match<'a, 'p, 'blk, 'tcx>], - vals: &[ValueRef], - chk: &FailureHandler, - has_genuine_default: bool) - -> Block<'blk, 'tcx> { - debug!("compile_guard(bcx={}, guard_expr={}, m={}, vals={})", - bcx.to_str(), - bcx.expr_to_string(guard_expr), - m.repr(bcx.tcx()), - vec_map_to_string(vals, |v| bcx.val_to_string(*v))); - let _indenter = indenter(); - - let mut bcx = insert_lllocals(bcx, &data.bindings_map, None); - - let val = unpack_datum!(bcx, expr::trans(bcx, guard_expr)); - let val = val.to_llbool(bcx); - - for (_, &binding_info) in data.bindings_map.iter() { - match binding_info.trmode { - TrByCopy(llbinding) => call_lifetime_end(bcx, llbinding), - _ => {} - } - } - - with_cond(bcx, Not(bcx, val), |bcx| { - // Guard does not match: remove all bindings from the lllocals table - for (_, &binding_info) in data.bindings_map.iter() { - call_lifetime_end(bcx, binding_info.llmatch); - bcx.fcx.lllocals.borrow_mut().remove(&binding_info.id); - } - match chk { - // If the default arm is the only one left, move on to the next - // condition explicitly rather than (possibly) falling back to - // the default arm. - &JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => { - chk.handle_fail(bcx); - } - _ => { - compile_submatch(bcx, m, vals, chk, has_genuine_default); - } - }; - bcx - }) -} - -fn compile_submatch<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - m: &[Match<'a, 'p, 'blk, 'tcx>], - vals: &[ValueRef], - chk: &FailureHandler, - has_genuine_default: bool) { - debug!("compile_submatch(bcx={}, m={}, vals={})", - bcx.to_str(), - m.repr(bcx.tcx()), - vec_map_to_string(vals, |v| bcx.val_to_string(*v))); - let _indenter = indenter(); - let _icx = push_ctxt("match::compile_submatch"); - let mut bcx = bcx; - if m.len() == 0u { - if chk.is_fallible() { - chk.handle_fail(bcx); - } - return; - } - - let tcx = bcx.tcx(); - let def_map = &tcx.def_map; - match pick_column_to_specialize(def_map, m) { - Some(col) => { - let val = vals[col]; - if has_nested_bindings(m, col) { - let expanded = expand_nested_bindings(bcx, m, col, val); - compile_submatch_continue(bcx, - expanded.as_slice(), - vals, - chk, - col, - val, - has_genuine_default) - } else { - compile_submatch_continue(bcx, m, vals, chk, col, val, has_genuine_default) - } - } - None => { - let data = &m[0].data; - for &(ref ident, ref value_ptr) in m[0].bound_ptrs.iter() { - let llmatch = data.bindings_map[*ident].llmatch; - call_lifetime_start(bcx, llmatch); - Store(bcx, *value_ptr, llmatch); - } - match data.arm.guard { - Some(ref guard_expr) => { - bcx = compile_guard(bcx, - &**guard_expr, - m[0].data, - m[1..m.len()], - vals, - chk, - has_genuine_default); - } - _ => () - } - Br(bcx, data.bodycx.llbb); - } - } -} - -fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - m: &[Match<'a, 'p, 'blk, 'tcx>], - vals: &[ValueRef], - chk: &FailureHandler, - col: uint, - val: ValueRef, - has_genuine_default: bool) { - let fcx = bcx.fcx; - let tcx = bcx.tcx(); - let dm = &tcx.def_map; - - let mut vals_left = vals[0u..col].to_vec(); - vals_left.push_all(vals[col + 1u..]); - let ccx = bcx.fcx.ccx; - - // Find a real id (we're adding placeholder wildcard patterns, but - // each column is guaranteed to have at least one real pattern) - let pat_id = m.iter().map(|br| br.pats[col].id) - .find(|&id| id != DUMMY_NODE_ID) - .unwrap_or(DUMMY_NODE_ID); - - let left_ty = if pat_id == DUMMY_NODE_ID { - ty::mk_nil(tcx) - } else { - node_id_type(bcx, pat_id) - }; - - let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; - let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) { - let repr = adt::represent_type(bcx.ccx(), left_ty); - let arg_count = adt::num_args(&*repr, 0); - let field_vals: Vec = std::iter::range(0, arg_count).map(|ix| - adt::trans_field_ptr(bcx, &*repr, val, 0, ix) - ).collect(); - Some(field_vals) - } else if any_uniq_pat(m, col) || any_region_pat(m, col) { - Some(vec!(Load(bcx, val))) - } else { - match ty::get(left_ty).sty { - ty::ty_vec(_, Some(n)) => { - let args = extract_vec_elems(bcx, left_ty, n, 0, val); - Some(args.vals) - } - _ => None - } - }; - - match adt_vals { - Some(field_vals) => { - let pats = enter_match(bcx, dm, m, col, val, |pats| - check_match::specialize(&mcx, pats, - &check_match::Single, col, - field_vals.len()) - ); - let mut vals = field_vals; - vals.push_all(vals_left.as_slice()); - compile_submatch(bcx, pats.as_slice(), vals.as_slice(), chk, has_genuine_default); - return; - } - _ => () - } - - // Decide what kind of branch we need - let opts = get_branches(bcx, m, col); - debug!("options={}", opts); - let mut kind = NoBranch; - let mut test_val = val; - debug!("test_val={}", bcx.val_to_string(test_val)); - if opts.len() > 0u { - match opts[0] { - ConstantValue(_) | ConstantRange(_, _) => { - test_val = load_if_immediate(bcx, val, left_ty); - kind = if ty::type_is_integral(left_ty) { - Switch - } else { - Compare - }; - } - Variant(_, ref repr, _) => { - let (the_kind, val_opt) = adt::trans_switch(bcx, &**repr, val); - kind = the_kind; - for &tval in val_opt.iter() { test_val = tval; } - } - SliceLengthEqual(_) | SliceLengthGreaterOrEqual(_, _) => { - let (_, len) = tvec::get_base_and_len(bcx, val, left_ty); - test_val = len; - kind = Switch; - } - } - } - for o in opts.iter() { - match *o { - ConstantRange(_, _) => { kind = Compare; break }, - SliceLengthGreaterOrEqual(_, _) => { kind = CompareSliceLength; break }, - _ => () - } - } - let else_cx = match kind { - NoBranch | Single => bcx, - _ => bcx.fcx.new_temp_block("match_else") - }; - let sw = if kind == Switch { - build::Switch(bcx, test_val, else_cx.llbb, opts.len()) - } else { - C_int(ccx, 0i) // Placeholder for when not using a switch - }; - - let defaults = enter_default(else_cx, dm, m, col, val); - let exhaustive = chk.is_infallible() && defaults.len() == 0u; - let len = opts.len(); - - // Compile subtrees for each option - for (i, opt) in opts.iter().enumerate() { - // In some cases of range and vector pattern matching, we need to - // override the failure case so that instead of failing, it proceeds - // to try more matching. branch_chk, then, is the proper failure case - // for the current conditional branch. - let mut branch_chk = None; - let mut opt_cx = else_cx; - if !exhaustive || i + 1 < len { - opt_cx = bcx.fcx.new_temp_block("match_case"); - match kind { - Single => Br(bcx, opt_cx.llbb), - Switch => { - match opt.trans(bcx) { - SingleResult(r) => { - AddCase(sw, r.val, opt_cx.llbb); - bcx = r.bcx; - } - _ => { - bcx.sess().bug( - "in compile_submatch, expected \ - opt.trans() to return a SingleResult") - } - } - } - Compare | CompareSliceLength => { - let t = if kind == Compare { - left_ty - } else { - ty::mk_uint() // vector length - }; - let Result { bcx: after_cx, val: matches } = { - match opt.trans(bcx) { - SingleResult(Result { bcx, val }) => { - compare_values(bcx, test_val, val, t) - } - RangeResult(Result { val: vbegin, .. }, - Result { bcx, val: vend }) => { - let Result { bcx, val: llge } = - compare_scalar_types( - bcx, test_val, - vbegin, t, ast::BiGe); - let Result { bcx, val: llle } = - compare_scalar_types( - bcx, test_val, vend, - t, ast::BiLe); - Result::new(bcx, And(bcx, llge, llle)) - } - LowerBound(Result { bcx, val }) => { - compare_scalar_types(bcx, test_val, val, t, ast::BiGe) - } - } - }; - bcx = fcx.new_temp_block("compare_next"); - - // If none of the sub-cases match, and the current condition - // is guarded or has multiple patterns, move on to the next - // condition, if there is any, rather than falling back to - // the default. - let guarded = m[i].data.arm.guard.is_some(); - let multi_pats = m[i].pats.len() > 1; - if i + 1 < len && (guarded || multi_pats || kind == CompareSliceLength) { - branch_chk = Some(JumpToBasicBlock(bcx.llbb)); - } - CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); - } - _ => () - } - } else if kind == Compare || kind == CompareSliceLength { - Br(bcx, else_cx.llbb); - } - - let mut size = 0u; - let mut unpacked = Vec::new(); - match *opt { - Variant(disr_val, ref repr, _) => { - let ExtractedBlock {vals: argvals, bcx: new_bcx} = - extract_variant_args(opt_cx, &**repr, disr_val, val); - size = argvals.len(); - unpacked = argvals; - opt_cx = new_bcx; - } - SliceLengthEqual(len) => { - let args = extract_vec_elems(opt_cx, left_ty, len, 0, val); - size = args.vals.len(); - unpacked = args.vals.clone(); - opt_cx = args.bcx; - } - SliceLengthGreaterOrEqual(before, after) => { - let args = extract_vec_elems(opt_cx, left_ty, before, after, val); - size = args.vals.len(); - unpacked = args.vals.clone(); - opt_cx = args.bcx; - } - ConstantValue(_) | ConstantRange(_, _) => () - } - let opt_ms = enter_opt(opt_cx, pat_id, dm, m, opt, col, size, val); - let mut opt_vals = unpacked; - opt_vals.push_all(vals_left.as_slice()); - compile_submatch(opt_cx, - opt_ms.as_slice(), - opt_vals.as_slice(), - branch_chk.as_ref().unwrap_or(chk), - has_genuine_default); - } - - // Compile the fall-through case, if any - if !exhaustive && kind != Single { - if kind == Compare || kind == CompareSliceLength { - Br(bcx, else_cx.llbb); - } - match chk { - // If there is only one default arm left, move on to the next - // condition explicitly rather than (eventually) falling back to - // the last default arm. - &JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => { - chk.handle_fail(else_cx); - } - _ => { - compile_submatch(else_cx, - defaults.as_slice(), - vals_left.as_slice(), - chk, - has_genuine_default); - } - } - } -} - -pub fn trans_match<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - match_expr: &ast::Expr, - discr_expr: &ast::Expr, - arms: &[ast::Arm], - dest: Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("match::trans_match"); - trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest) -} - -/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body` -fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool { - match discr.node { - ast::ExprPath(..) => match bcx.def(discr.id) { - def::DefLocal(vid) | def::DefUpvar(vid, _, _) => { - let mut rc = ReassignmentChecker { - node: vid, - reassigned: false - }; - { - let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx); - visitor.walk_expr(body); - } - rc.reassigned - } - _ => false - }, - _ => false - } -} - -struct ReassignmentChecker { - node: ast::NodeId, - reassigned: bool -} - -impl euv::Delegate for ReassignmentChecker { - fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {} - fn consume_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::ConsumeMode) {} - fn borrow(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: ty::Region, - _: ty::BorrowKind, _: euv::LoanCause) {} - fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {} - - fn mutate(&mut self, _: ast::NodeId, _: Span, cmt: mc::cmt, _: euv::MutateMode) { - match cmt.cat { - mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: vid, .. }, .. }) | - mc::cat_local(vid) => self.reassigned = self.node == vid, - _ => {} - } - } -} - -fn create_bindings_map(bcx: Block, pat: &ast::Pat, - discr: &ast::Expr, body: &ast::Expr) -> BindingsMap { - // Create the bindings map, which is a mapping from each binding name - // to an alloca() that will be the value for that local variable. - // Note that we use the names because each binding will have many ids - // from the various alternatives. - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - let reassigned = is_discr_reassigned(bcx, discr, body); - let mut bindings_map = FnvHashMap::new(); - pat_bindings(&tcx.def_map, &*pat, |bm, p_id, span, path1| { - let ident = path1.node; - let variable_ty = node_id_type(bcx, p_id); - let llvariable_ty = type_of::type_of(ccx, variable_ty); - let tcx = bcx.tcx(); - - let llmatch; - let trmode; - match bm { - ast::BindByValue(_) - if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => { - llmatch = alloca_no_lifetime(bcx, - llvariable_ty.ptr_to(), - "__llmatch"); - trmode = TrByCopy(alloca_no_lifetime(bcx, - llvariable_ty, - bcx.ident(ident).as_slice())); - } - ast::BindByValue(_) => { - // in this case, the final type of the variable will be T, - // but during matching we need to store a *T as explained - // above - llmatch = alloca_no_lifetime(bcx, - llvariable_ty.ptr_to(), - bcx.ident(ident).as_slice()); - trmode = TrByMove; - } - ast::BindByRef(_) => { - llmatch = alloca_no_lifetime(bcx, - llvariable_ty, - bcx.ident(ident).as_slice()); - trmode = TrByRef; - } - }; - bindings_map.insert(ident, BindingInfo { - llmatch: llmatch, - trmode: trmode, - id: p_id, - span: span, - ty: variable_ty - }); - }); - return bindings_map; -} - -fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>, - match_id: ast::NodeId, - discr_expr: &ast::Expr, - arms: &[ast::Arm], - dest: Dest) -> Block<'blk, 'tcx> { - let _icx = push_ctxt("match::trans_match_inner"); - let fcx = scope_cx.fcx; - let mut bcx = scope_cx; - let tcx = bcx.tcx(); - - let discr_datum = unpack_datum!(bcx, expr::trans_to_lvalue(bcx, discr_expr, - "match")); - if bcx.unreachable.get() { - return bcx; - } - - let t = node_id_type(bcx, discr_expr.id); - let chk = if ty::type_is_empty(tcx, t) { - Unreachable - } else { - Infallible - }; - - let arm_datas: Vec = arms.iter().map(|arm| ArmData { - bodycx: fcx.new_id_block("case_body", arm.body.id), - arm: arm, - bindings_map: create_bindings_map(bcx, &*arm.pats[0], discr_expr, &*arm.body) - }).collect(); - - let mut static_inliner = StaticInliner::new(scope_cx.tcx()); - let arm_pats: Vec>> = arm_datas.iter().map(|arm_data| { - arm_data.arm.pats.iter().map(|p| static_inliner.fold_pat((*p).clone())).collect() - }).collect(); - let mut matches = Vec::new(); - for (arm_data, pats) in arm_datas.iter().zip(arm_pats.iter()) { - matches.extend(pats.iter().map(|p| Match { - pats: vec![&**p], - data: arm_data, - bound_ptrs: Vec::new(), - })); - } - - // `compile_submatch` works one column of arm patterns a time and - // then peels that column off. So as we progress, it may become - // impossible to tell whether we have a genuine default arm, i.e. - // `_ => foo` or not. Sometimes it is important to know that in order - // to decide whether moving on to the next condition or falling back - // to the default arm. - let has_default = arms.last().map_or(false, |arm| { - arm.pats.len() == 1 - && arm.pats.last().unwrap().node == ast::PatWild(ast::PatWildSingle) - }); - - compile_submatch(bcx, matches.as_slice(), &[discr_datum.val], &chk, has_default); - - let mut arm_cxs = Vec::new(); - for arm_data in arm_datas.iter() { - let mut bcx = arm_data.bodycx; - - // insert bindings into the lllocals map and add cleanups - let cs = fcx.push_custom_cleanup_scope(); - bcx = insert_lllocals(bcx, &arm_data.bindings_map, Some(cleanup::CustomScope(cs))); - bcx = expr::trans_into(bcx, &*arm_data.arm.body, dest); - bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, cs); - arm_cxs.push(bcx); - } - - bcx = scope_cx.fcx.join_blocks(match_id, arm_cxs.as_slice()); - return bcx; -} - -pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - local: &ast::Local) - -> Block<'blk, 'tcx> { - /*! - * Generates code for a local variable declaration like - * `let ;` or `let = `. - */ - let _icx = push_ctxt("match::store_local"); - let mut bcx = bcx; - let tcx = bcx.tcx(); - let pat = &*local.pat; - - fn create_dummy_locals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - pat: &ast::Pat) - -> Block<'blk, 'tcx> { - // create dummy memory for the variables if we have no - // value to store into them immediately - let tcx = bcx.tcx(); - pat_bindings(&tcx.def_map, pat, |_, p_id, _, path1| { - let scope = cleanup::var_scope(tcx, p_id); - bcx = mk_binding_alloca( - bcx, p_id, &path1.node, scope, (), - |(), bcx, llval, ty| { zero_mem(bcx, llval, ty); bcx }); - }); - bcx - } - - match local.init { - Some(ref init_expr) => { - // Optimize the "let x = expr" case. This just writes - // the result of evaluating `expr` directly into the alloca - // for `x`. Often the general path results in similar or the - // same code post-optimization, but not always. In particular, - // in unsafe code, you can have expressions like - // - // let x = intrinsics::uninit(); - // - // In such cases, the more general path is unsafe, because - // it assumes it is matching against a valid value. - match simple_identifier(&*pat) { - Some(ident) => { - let var_scope = cleanup::var_scope(tcx, local.id); - return mk_binding_alloca( - bcx, pat.id, ident, var_scope, (), - |(), bcx, v, _| expr::trans_into(bcx, &**init_expr, - expr::SaveIn(v))); - } - - None => {} - } - - // General path. - let init_datum = - unpack_datum!(bcx, expr::trans_to_lvalue(bcx, &**init_expr, "let")); - if bcx.sess().asm_comments() { - add_comment(bcx, "creating zeroable ref llval"); - } - let var_scope = cleanup::var_scope(tcx, local.id); - bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope) - } - None => { - create_dummy_locals(bcx, pat) - } - } -} - -pub fn store_arg<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - pat: &ast::Pat, - arg: Datum, - arg_scope: cleanup::ScopeId) - -> Block<'blk, 'tcx> { - /*! - * Generates code for argument patterns like `fn foo(: T)`. - * Creates entries in the `lllocals` map for each of the bindings - * in `pat`. - * - * # Arguments - * - * - `pat` is the argument pattern - * - `llval` is a pointer to the argument value (in other words, - * if the argument type is `T`, then `llval` is a `T*`). In some - * cases, this code may zero out the memory `llval` points at. - */ - - let _icx = push_ctxt("match::store_arg"); - - match simple_identifier(&*pat) { - Some(ident) => { - // Generate nicer LLVM for the common case of fn a pattern - // like `x: T` - let arg_ty = node_id_type(bcx, pat.id); - if type_of::arg_is_indirect(bcx.ccx(), arg_ty) - && bcx.sess().opts.debuginfo != FullDebugInfo { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up, unless - // we emit extra-debug-info, which requires local allocas :(. - let arg_val = arg.add_clean(bcx.fcx, arg_scope); - bcx.fcx.lllocals.borrow_mut() - .insert(pat.id, Datum::new(arg_val, arg_ty, Lvalue)); - bcx - } else { - mk_binding_alloca( - bcx, pat.id, ident, arg_scope, arg, - |arg, bcx, llval, _| arg.store_to(bcx, llval)) - } - } - - None => { - // General path. Copy out the values that are used in the - // pattern. - let arg = unpack_datum!( - bcx, arg.to_lvalue_datum_in_scope(bcx, "__arg", arg_scope)); - bind_irrefutable_pat(bcx, pat, arg.val, arg_scope) - } - } -} - -/// Generates code for the pattern binding in a `for` loop like -/// `for in { ... }`. -pub fn store_for_loop_binding<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - pat: &ast::Pat, - llvalue: ValueRef, - body_scope: cleanup::ScopeId) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("match::store_for_loop_binding"); - - if simple_identifier(&*pat).is_some() { - // Generate nicer LLVM for the common case of a `for` loop pattern - // like `for x in blahblah { ... }`. - let binding_type = node_id_type(bcx, pat.id); - bcx.fcx.lllocals.borrow_mut().insert(pat.id, - Datum::new(llvalue, - binding_type, - Lvalue)); - return bcx - } - - // General path. Copy out the values that are used in the pattern. - bind_irrefutable_pat(bcx, pat, llvalue, body_scope) -} - -fn mk_binding_alloca<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>, - p_id: ast::NodeId, - ident: &ast::Ident, - cleanup_scope: cleanup::ScopeId, - arg: A, - populate: |A, Block<'blk, 'tcx>, ValueRef, ty::t| - -> Block<'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let var_ty = node_id_type(bcx, p_id); - - // Allocate memory on stack for the binding. - let llval = alloc_ty(bcx, var_ty, bcx.ident(*ident).as_slice()); - - // Subtle: be sure that we *populate* the memory *before* - // we schedule the cleanup. - let bcx = populate(arg, bcx, llval, var_ty); - bcx.fcx.schedule_lifetime_end(cleanup_scope, llval); - bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty); - - // Now that memory is initialized and has cleanup scheduled, - // create the datum and insert into the local variable map. - let datum = Datum::new(llval, var_ty, Lvalue); - bcx.fcx.lllocals.borrow_mut().insert(p_id, datum); - bcx -} - -fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - pat: &ast::Pat, - val: ValueRef, - cleanup_scope: cleanup::ScopeId) - -> Block<'blk, 'tcx> { - /*! - * A simple version of the pattern matching code that only handles - * irrefutable patterns. This is used in let/argument patterns, - * not in match statements. Unifying this code with the code above - * sounds nice, but in practice it produces very inefficient code, - * since the match code is so much more general. In most cases, - * LLVM is able to optimize the code, but it causes longer compile - * times and makes the generated code nigh impossible to read. - * - * # Arguments - * - bcx: starting basic block context - * - pat: the irrefutable pattern being matched. - * - val: the value being matched -- must be an lvalue (by ref, with cleanup) - */ - - debug!("bind_irrefutable_pat(bcx={}, pat={})", - bcx.to_str(), - pat.repr(bcx.tcx())); - - if bcx.sess().asm_comments() { - add_comment(bcx, format!("bind_irrefutable_pat(pat={})", - pat.repr(bcx.tcx())).as_slice()); - } - - let _indenter = indenter(); - - let _icx = push_ctxt("match::bind_irrefutable_pat"); - let mut bcx = bcx; - let tcx = bcx.tcx(); - let ccx = bcx.ccx(); - match pat.node { - ast::PatIdent(pat_binding_mode, ref path1, ref inner) => { - if pat_is_binding(&tcx.def_map, &*pat) { - // Allocate the stack slot where the value of this - // binding will live and place it into the appropriate - // map. - bcx = mk_binding_alloca( - bcx, pat.id, &path1.node, cleanup_scope, (), - |(), bcx, llval, ty| { - match pat_binding_mode { - ast::BindByValue(_) => { - // By value binding: move the value that `val` - // points at into the binding's stack slot. - let d = Datum::new(val, ty, Lvalue); - d.store_to(bcx, llval) - } - - ast::BindByRef(_) => { - // By ref binding: the value of the variable - // is the pointer `val` itself. - Store(bcx, val, llval); - bcx - } - } - }); - } - - for inner_pat in inner.iter() { - bcx = bind_irrefutable_pat(bcx, &**inner_pat, val, cleanup_scope); - } - } - ast::PatEnum(_, ref sub_pats) => { - let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).cloned(); - match opt_def { - Some(def::DefVariant(enum_id, var_id, _)) => { - let repr = adt::represent_node(bcx, pat.id); - let vinfo = ty::enum_variant_with_id(ccx.tcx(), - enum_id, - var_id); - let args = extract_variant_args(bcx, - &*repr, - vinfo.disr_val, - val); - for sub_pat in sub_pats.iter() { - for (i, &argval) in args.vals.iter().enumerate() { - bcx = bind_irrefutable_pat(bcx, &*sub_pat[i], - argval, cleanup_scope); - } - } - } - Some(def::DefStruct(..)) => { - match *sub_pats { - None => { - // This is a unit-like struct. Nothing to do here. - } - Some(ref elems) => { - // This is the tuple struct case. - let repr = adt::represent_node(bcx, pat.id); - for (i, elem) in elems.iter().enumerate() { - let fldptr = adt::trans_field_ptr(bcx, &*repr, - val, 0, i); - bcx = bind_irrefutable_pat(bcx, &**elem, - fldptr, cleanup_scope); - } - } - } - } - _ => { - // Nothing to do here. - } - } - } - ast::PatStruct(_, ref fields, _) => { - let tcx = bcx.tcx(); - let pat_ty = node_id_type(bcx, pat.id); - let pat_repr = adt::represent_type(bcx.ccx(), pat_ty); - expr::with_field_tys(tcx, pat_ty, Some(pat.id), |discr, field_tys| { - for f in fields.iter() { - let ix = ty::field_idx_strict(tcx, f.node.ident.name, field_tys); - let fldptr = adt::trans_field_ptr(bcx, &*pat_repr, val, - discr, ix); - bcx = bind_irrefutable_pat(bcx, &*f.node.pat, fldptr, cleanup_scope); - } - }) - } - ast::PatTup(ref elems) => { - let repr = adt::represent_node(bcx, pat.id); - for (i, elem) in elems.iter().enumerate() { - let fldptr = adt::trans_field_ptr(bcx, &*repr, val, 0, i); - bcx = bind_irrefutable_pat(bcx, &**elem, fldptr, cleanup_scope); - } - } - ast::PatBox(ref inner) => { - let llbox = Load(bcx, val); - bcx = bind_irrefutable_pat(bcx, &**inner, llbox, cleanup_scope); - } - ast::PatRegion(ref inner) => { - let loaded_val = Load(bcx, val); - bcx = bind_irrefutable_pat(bcx, &**inner, loaded_val, cleanup_scope); - } - ast::PatVec(ref before, ref slice, ref after) => { - let pat_ty = node_id_type(bcx, pat.id); - let mut extracted = extract_vec_elems(bcx, pat_ty, before.len(), after.len(), val); - match slice { - &Some(_) => { - extracted.vals.insert( - before.len(), - bind_subslice_pat(bcx, pat.id, val, before.len(), after.len()) - ); - } - &None => () - } - bcx = before - .iter() - .chain(slice.iter()) - .chain(after.iter()) - .zip(extracted.vals.into_iter()) - .fold(bcx, |bcx, (inner, elem)| - bind_irrefutable_pat(bcx, &**inner, elem, cleanup_scope) - ); - } - ast::PatMac(..) => { - bcx.sess().span_bug(pat.span, "unexpanded macro"); - } - ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () - } - return bcx; -} diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs deleted file mode 100644 index d48868c2889..00000000000 --- a/src/librustc/middle/trans/adt.rs +++ /dev/null @@ -1,1158 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * # Representation of Algebraic Data Types - * - * This module determines how to represent enums, structs, and tuples - * based on their monomorphized types; it is responsible both for - * choosing a representation and translating basic operations on - * values of those types. (Note: exporting the representations for - * debuggers is handled in debuginfo.rs, not here.) - * - * Note that the interface treats everything as a general case of an - * enum, so structs/tuples/etc. have one pseudo-variant with - * discriminant 0; i.e., as if they were a univariant enum. - * - * Having everything in one place will enable improvements to data - * structure representation; possibilities include: - * - * - User-specified alignment (e.g., cacheline-aligning parts of - * concurrently accessed data structures); LLVM can't represent this - * directly, so we'd have to insert padding fields in any structure - * that might contain one and adjust GEP indices accordingly. See - * issue #4578. - * - * - Store nested enums' discriminants in the same word. Rather, if - * some variants start with enums, and those enums representations - * have unused alignment padding between discriminant and body, the - * outer enum's discriminant can be stored there and those variants - * can start at offset 0. Kind of fancy, and might need work to - * make copies of the inner enum type cooperate, but it could help - * with `Option` or `Result` wrapped around another enum. - * - * - Tagged pointers would be neat, but given that any type can be - * used unboxed and any field can have pointers (including mutable) - * taken to it, implementing them for Rust seems difficult. - */ - -#![allow(unsigned_negation)] - -pub use self::PointerField::*; -pub use self::Repr::*; - -use std::num::Int; -use std::rc::Rc; - -use llvm::{ValueRef, True, IntEQ, IntNE}; -use back::abi::slice_elt_base; -use middle::subst; -use middle::subst::Subst; -use middle::trans::_match; -use middle::trans::build::*; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::common::*; -use middle::trans::datum; -use middle::trans::machine; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::ty; -use middle::ty::Disr; -use syntax::ast; -use syntax::attr; -use syntax::attr::IntType; -use util::ppaux::ty_to_string; - -type Hint = attr::ReprAttr; - - -/// Representations. -#[deriving(Eq, PartialEq, Show)] -pub enum Repr { - /// C-like enums; basically an int. - CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType) - /** - * Single-case variants, and structs/tuples/records. - * - * Structs with destructors need a dynamic destroyedness flag to - * avoid running the destructor too many times; this is included - * in the `Struct` if present. - */ - Univariant(Struct, bool), - /** - * General-case enums: for each case there is a struct, and they - * all start with a field for the discriminant. - * - * Types with destructors need a dynamic destroyedness flag to - * avoid running the destructor too many times; the last argument - * indicates whether such a flag is present. - */ - General(IntType, Vec, bool), - /** - * Two cases distinguished by a nullable pointer: the case with discriminant - * `nndiscr` must have single field which is known to be nonnull due to its type. - * The other case is known to be zero sized. Hence we represent the enum - * as simply a nullable pointer: if not null it indicates the `nndiscr` variant, - * otherwise it indicates the other case. - */ - RawNullablePointer { - nndiscr: Disr, - nnty: ty::t, - nullfields: Vec - }, - /** - * Two cases distinguished by a nullable pointer: the case with discriminant - * `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th - * field is known to be nonnull due to its type; if that field is null, then - * it represents the other case, which is inhabited by at most one value - * (and all other fields are undefined/unused). - * - * For example, `std::option::Option` instantiated at a safe pointer type - * is represented such that `None` is a null pointer and `Some` is the - * identity function. - */ - StructWrappedNullablePointer { - nonnull: Struct, - nndiscr: Disr, - ptrfield: PointerField, - nullfields: Vec, - } -} - -/// For structs, and struct-like parts of anything fancier. -#[deriving(Eq, PartialEq, Show)] -pub struct Struct { - // If the struct is DST, then the size and alignment do not take into - // account the unsized fields of the struct. - pub size: u64, - pub align: u32, - pub sized: bool, - pub packed: bool, - pub fields: Vec -} - -/** - * Convenience for `represent_type`. There should probably be more or - * these, for places in trans where the `ty::t` isn't directly - * available. - */ -pub fn represent_node(bcx: Block, node: ast::NodeId) -> Rc { - represent_type(bcx.ccx(), node_id_type(bcx, node)) -} - -/// Decides how to represent a given type. -pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc { - debug!("Representing: {}", ty_to_string(cx.tcx(), t)); - match cx.adt_reprs().borrow().get(&t) { - Some(repr) => return repr.clone(), - None => {} - } - - let repr = Rc::new(represent_type_uncached(cx, t)); - debug!("Represented as: {}", repr) - cx.adt_reprs().borrow_mut().insert(t, repr.clone()); - repr -} - -fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr { - match ty::get(t).sty { - ty::ty_tup(ref elems) => { - return Univariant(mk_struct(cx, elems.as_slice(), false, t), false) - } - ty::ty_struct(def_id, ref substs) => { - let fields = ty::lookup_struct_fields(cx.tcx(), def_id); - let mut ftys = fields.iter().map(|field| { - ty::lookup_field_type(cx.tcx(), def_id, field.id, substs) - }).collect::>(); - let packed = ty::lookup_packed(cx.tcx(), def_id); - let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); - if dtor { ftys.push(ty::mk_bool()); } - - return Univariant(mk_struct(cx, ftys.as_slice(), packed, t), dtor) - } - ty::ty_unboxed_closure(def_id, _, ref substs) => { - let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs); - let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); - return Univariant(mk_struct(cx, upvar_types.as_slice(), false, t), - false) - } - ty::ty_enum(def_id, ref substs) => { - let cases = get_cases(cx.tcx(), def_id, substs); - let hint = *ty::lookup_repr_hints(cx.tcx(), def_id).as_slice().get(0) - .unwrap_or(&attr::ReprAny); - - let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); - - if cases.len() == 0 { - // Uninhabitable; represent as unit - // (Typechecking will reject discriminant-sizing attrs.) - assert_eq!(hint, attr::ReprAny); - let ftys = if dtor { vec!(ty::mk_bool()) } else { vec!() }; - return Univariant(mk_struct(cx, ftys.as_slice(), false, t), - dtor); - } - - if !dtor && cases.iter().all(|c| c.tys.len() == 0) { - // All bodies empty -> intlike - let discrs: Vec = cases.iter().map(|c| c.discr).collect(); - let bounds = IntBounds { - ulo: *discrs.iter().min().unwrap(), - uhi: *discrs.iter().max().unwrap(), - slo: discrs.iter().map(|n| *n as i64).min().unwrap(), - shi: discrs.iter().map(|n| *n as i64).max().unwrap() - }; - return mk_cenum(cx, hint, &bounds); - } - - // Since there's at least one - // non-empty body, explicit discriminants should have - // been rejected by a checker before this point. - if !cases.iter().enumerate().all(|(i,c)| c.discr == (i as Disr)) { - cx.sess().bug(format!("non-C-like enum {} with specified \ - discriminants", - ty::item_path_str(cx.tcx(), - def_id)).as_slice()); - } - - if cases.len() == 1 { - // Equivalent to a struct/tuple/newtype. - // (Typechecking will reject discriminant-sizing attrs.) - assert_eq!(hint, attr::ReprAny); - let mut ftys = cases[0].tys.clone(); - if dtor { ftys.push(ty::mk_bool()); } - return Univariant(mk_struct(cx, ftys.as_slice(), false, t), - dtor); - } - - if !dtor && cases.len() == 2 && hint == attr::ReprAny { - // Nullable pointer optimization - let mut discr = 0; - while discr < 2 { - if cases[1 - discr].is_zerolen(cx, t) { - let st = mk_struct(cx, cases[discr].tys.as_slice(), - false, t); - match cases[discr].find_ptr(cx) { - Some(ThinPointer(_)) if st.fields.len() == 1 => { - return RawNullablePointer { - nndiscr: discr as Disr, - nnty: st.fields[0], - nullfields: cases[1 - discr].tys.clone() - }; - } - Some(ptrfield) => { - return StructWrappedNullablePointer { - nndiscr: discr as Disr, - nonnull: st, - ptrfield: ptrfield, - nullfields: cases[1 - discr].tys.clone() - }; - } - None => { } - } - } - discr += 1; - } - } - - // The general case. - assert!((cases.len() - 1) as i64 >= 0); - let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64, - slo: 0, shi: (cases.len() - 1) as i64 }; - let ity = range_to_inttype(cx, hint, &bounds); - - let fields : Vec<_> = cases.iter().map(|c| { - let mut ftys = vec!(ty_of_inttype(ity)); - ftys.push_all(c.tys.as_slice()); - if dtor { ftys.push(ty::mk_bool()); } - mk_struct(cx, ftys.as_slice(), false, t) - }).collect(); - - ensure_enum_fits_in_address_space(cx, ity, fields.as_slice(), t); - - General(ity, fields, dtor) - } - _ => cx.sess().bug(format!("adt::represent_type called on non-ADT type: {}", - ty_to_string(cx.tcx(), t)).as_slice()) - } -} - -// this should probably all be in ty -struct Case { - discr: Disr, - tys: Vec -} - - -#[deriving(Eq, PartialEq, Show)] -pub enum PointerField { - ThinPointer(uint), - FatPointer(uint) -} - -impl Case { - fn is_zerolen(&self, cx: &CrateContext, scapegoat: ty::t) -> bool { - mk_struct(cx, self.tys.as_slice(), false, scapegoat).size == 0 - } - - fn find_ptr(&self, cx: &CrateContext) -> Option { - for (i, &ty) in self.tys.iter().enumerate() { - match ty::get(ty).sty { - // &T/&mut T/Box could either be a thin or fat pointer depending on T - ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty::get(ty).sty { - // &[T] and &str are a pointer and length pair - ty::ty_vec(_, None) | ty::ty_str => return Some(FatPointer(i)), - - // &Trait is a pair of pointers: the actual object and a vtable - ty::ty_trait(..) => return Some(FatPointer(i)), - - ty::ty_struct(..) if !ty::type_is_sized(cx.tcx(), ty) => { - return Some(FatPointer(i)) - } - - // Any other &T is just a pointer - _ => return Some(ThinPointer(i)) - }, - - // Functions are just pointers - ty::ty_bare_fn(..) => return Some(ThinPointer(i)), - - // Closures are a pair of pointers: the code and environment - ty::ty_closure(..) => return Some(FatPointer(i)), - - // Anything else is not a pointer - _ => continue - } - } - - None - } -} - -fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec { - ty::enum_variants(tcx, def_id).iter().map(|vi| { - let arg_tys = vi.args.iter().map(|&raw_ty| { - raw_ty.subst(tcx, substs) - }).collect(); - Case { discr: vi.disr_val, tys: arg_tys } - }).collect() -} - -fn mk_struct(cx: &CrateContext, tys: &[ty::t], packed: bool, scapegoat: ty::t) -> Struct { - let sized = tys.iter().all(|&ty| ty::type_is_sized(cx.tcx(), ty)); - let lltys : Vec = if sized { - tys.iter() - .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() - } else { - tys.iter().filter(|&ty| ty::type_is_sized(cx.tcx(), *ty)) - .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() - }; - - ensure_struct_fits_in_address_space(cx, lltys.as_slice(), packed, scapegoat); - - let llty_rec = Type::struct_(cx, lltys.as_slice(), packed); - Struct { - size: machine::llsize_of_alloc(cx, llty_rec), - align: machine::llalign_of_min(cx, llty_rec), - sized: sized, - packed: packed, - fields: tys.to_vec(), - } -} - -#[deriving(Show)] -struct IntBounds { - slo: i64, - shi: i64, - ulo: u64, - uhi: u64 -} - -fn mk_cenum(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> Repr { - let it = range_to_inttype(cx, hint, bounds); - match it { - attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr), - attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi) - } -} - -fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntType { - debug!("range_to_inttype: {} {}", hint, bounds); - // Lists of sizes to try. u64 is always allowed as a fallback. - #[allow(non_upper_case_globals)] - static choose_shortest: &'static[IntType] = &[ - attr::UnsignedInt(ast::TyU8), attr::SignedInt(ast::TyI8), - attr::UnsignedInt(ast::TyU16), attr::SignedInt(ast::TyI16), - attr::UnsignedInt(ast::TyU32), attr::SignedInt(ast::TyI32)]; - #[allow(non_upper_case_globals)] - static at_least_32: &'static[IntType] = &[ - attr::UnsignedInt(ast::TyU32), attr::SignedInt(ast::TyI32)]; - - let attempts; - match hint { - attr::ReprInt(span, ity) => { - if !bounds_usable(cx, ity, bounds) { - cx.sess().span_bug(span, "representation hint insufficient for discriminant range") - } - return ity; - } - attr::ReprExtern => { - attempts = match cx.sess().target.target.arch.as_slice() { - // WARNING: the ARM EABI has two variants; the one corresponding to `at_least_32` - // appears to be used on Linux and NetBSD, but some systems may use the variant - // corresponding to `choose_shortest`. However, we don't run on those yet...? - "arm" => at_least_32, - _ => at_least_32, - } - } - attr::ReprAny => { - attempts = choose_shortest; - }, - attr::ReprPacked => { - cx.tcx().sess.bug("range_to_inttype: found ReprPacked on an enum"); - } - } - for &ity in attempts.iter() { - if bounds_usable(cx, ity, bounds) { - return ity; - } - } - return attr::UnsignedInt(ast::TyU64); -} - -pub fn ll_inttype(cx: &CrateContext, ity: IntType) -> Type { - match ity { - attr::SignedInt(t) => Type::int_from_ty(cx, t), - attr::UnsignedInt(t) => Type::uint_from_ty(cx, t) - } -} - -fn bounds_usable(cx: &CrateContext, ity: IntType, bounds: &IntBounds) -> bool { - debug!("bounds_usable: {} {}", ity, bounds); - match ity { - attr::SignedInt(_) => { - let lllo = C_integral(ll_inttype(cx, ity), bounds.slo as u64, true); - let llhi = C_integral(ll_inttype(cx, ity), bounds.shi as u64, true); - bounds.slo == const_to_int(lllo) as i64 && bounds.shi == const_to_int(llhi) as i64 - } - attr::UnsignedInt(_) => { - let lllo = C_integral(ll_inttype(cx, ity), bounds.ulo, false); - let llhi = C_integral(ll_inttype(cx, ity), bounds.uhi, false); - bounds.ulo == const_to_uint(lllo) as u64 && bounds.uhi == const_to_uint(llhi) as u64 - } - } -} - -pub fn ty_of_inttype(ity: IntType) -> ty::t { - match ity { - attr::SignedInt(t) => ty::mk_mach_int(t), - attr::UnsignedInt(t) => ty::mk_mach_uint(t) - } -} - -// LLVM doesn't like types that don't fit in the address space -fn ensure_struct_fits_in_address_space(ccx: &CrateContext, - fields: &[Type], - packed: bool, - scapegoat: ty::t) { - let mut offset = 0; - for &llty in fields.iter() { - // Invariant: offset < ccx.max_obj_size() <= 1<<61 - if !packed { - let type_align = machine::llalign_of_min(ccx, llty); - offset = roundup(offset, type_align); - } - // type_align is a power-of-2, so still offset < ccx.max_obj_size() - // llsize_of_alloc(ccx, llty) is also less than ccx.max_obj_size() - // so the sum is less than 1<<62 (and therefore can't overflow). - offset += machine::llsize_of_alloc(ccx, llty); - - if offset >= ccx.max_obj_size() { - ccx.report_overbig_object(scapegoat); - } - } -} - -fn union_size_and_align(sts: &[Struct]) -> (machine::llsize, machine::llalign) { - let size = sts.iter().map(|st| st.size).max().unwrap(); - let most_aligned = sts.iter().max_by(|st| st.align).unwrap(); - (size, most_aligned.align) -} - -fn ensure_enum_fits_in_address_space(ccx: &CrateContext, - discr: IntType, - fields: &[Struct], - scapegoat: ty::t) { - let discr_size = machine::llsize_of_alloc(ccx, ll_inttype(ccx, discr)); - let (field_size, field_align) = union_size_and_align(fields); - - // field_align < 1<<32, discr_size <= 8, field_size < MAX_OBJ_SIZE <= 1<<61 - // so the sum is less than 1<<62 (and can't overflow). - let total_size = roundup(discr_size, field_align) + field_size; - - if total_size >= ccx.max_obj_size() { - ccx.report_overbig_object(scapegoat); - } -} - - -/** - * LLVM-level types are a little complicated. - * - * C-like enums need to be actual ints, not wrapped in a struct, - * because that changes the ABI on some platforms (see issue #10308). - * - * For nominal types, in some cases, we need to use LLVM named structs - * and fill in the actual contents in a second pass to prevent - * unbounded recursion; see also the comments in `trans::type_of`. - */ -pub fn type_of(cx: &CrateContext, r: &Repr) -> Type { - generic_type_of(cx, r, None, false, false) -} -// Pass dst=true if the type you are passing is a DST. Yes, we could figure -// this out, but if you call this on an unsized type without realising it, you -// are going to get the wrong type (it will not include the unsized parts of it). -pub fn sizing_type_of(cx: &CrateContext, r: &Repr, dst: bool) -> Type { - generic_type_of(cx, r, None, true, dst) -} -pub fn incomplete_type_of(cx: &CrateContext, r: &Repr, name: &str) -> Type { - generic_type_of(cx, r, Some(name), false, false) -} -pub fn finish_type_of(cx: &CrateContext, r: &Repr, llty: &mut Type) { - match *r { - CEnum(..) | General(..) | RawNullablePointer { .. } => { } - Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => - llty.set_struct_body(struct_llfields(cx, st, false, false).as_slice(), - st.packed) - } -} - -fn generic_type_of(cx: &CrateContext, - r: &Repr, - name: Option<&str>, - sizing: bool, - dst: bool) -> Type { - match *r { - CEnum(ity, _, _) => ll_inttype(cx, ity), - RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty), - Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => { - match name { - None => { - Type::struct_(cx, struct_llfields(cx, st, sizing, dst).as_slice(), - st.packed) - } - Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) } - } - } - General(ity, ref sts, _) => { - // We need a representation that has: - // * The alignment of the most-aligned field - // * The size of the largest variant (rounded up to that alignment) - // * No alignment padding anywhere any variant has actual data - // (currently matters only for enums small enough to be immediate) - // * The discriminant in an obvious place. - // - // So we start with the discriminant, pad it up to the alignment with - // more of its own type, then use alignment-sized ints to get the rest - // of the size. - // - // FIXME #10604: this breaks when vector types are present. - let (size, align) = union_size_and_align(sts.as_slice()); - let align_s = align as u64; - let discr_ty = ll_inttype(cx, ity); - let discr_size = machine::llsize_of_alloc(cx, discr_ty); - let align_units = (size + align_s - 1) / align_s - 1; - let pad_ty = match align_s { - 1 => Type::array(&Type::i8(cx), align_units), - 2 => Type::array(&Type::i16(cx), align_units), - 4 => Type::array(&Type::i32(cx), align_units), - 8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 => - Type::array(&Type::i64(cx), align_units), - a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4), - align_units), - _ => panic!("unsupported enum alignment: {}", align) - }; - assert_eq!(machine::llalign_of_min(cx, pad_ty), align); - assert_eq!(align_s % discr_size, 0); - let fields = vec!(discr_ty, - Type::array(&discr_ty, align_s / discr_size - 1), - pad_ty); - match name { - None => Type::struct_(cx, fields.as_slice(), false), - Some(name) => { - let mut llty = Type::named_struct(cx, name); - llty.set_struct_body(fields.as_slice(), false); - llty - } - } - } - } -} - -fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool, dst: bool) -> Vec { - if sizing { - st.fields.iter().filter(|&ty| !dst || ty::type_is_sized(cx.tcx(), *ty)) - .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() - } else { - st.fields.iter().map(|&ty| type_of::type_of(cx, ty)).collect() - } -} - -/** - * Obtain a representation of the discriminant sufficient to translate - * destructuring; this may or may not involve the actual discriminant. - * - * This should ideally be less tightly tied to `_match`. - */ -pub fn trans_switch(bcx: Block, r: &Repr, scrutinee: ValueRef) - -> (_match::BranchKind, Option) { - match *r { - CEnum(..) | General(..) | - RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { - (_match::Switch, Some(trans_get_discr(bcx, r, scrutinee, None))) - } - Univariant(..) => { - (_match::Single, None) - } - } -} - - - -/// Obtain the actual discriminant of a value. -pub fn trans_get_discr(bcx: Block, r: &Repr, scrutinee: ValueRef, cast_to: Option) - -> ValueRef { - let signed; - let val; - debug!("trans_get_discr r: {}", r); - match *r { - CEnum(ity, min, max) => { - val = load_discr(bcx, ity, scrutinee, min, max); - signed = ity.is_signed(); - } - General(ity, ref cases, _) => { - let ptr = GEPi(bcx, scrutinee, &[0, 0]); - val = load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr); - signed = ity.is_signed(); - } - Univariant(..) => { - val = C_u8(bcx.ccx(), 0); - signed = false; - } - RawNullablePointer { nndiscr, nnty, .. } => { - let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); - val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty)); - signed = false; - } - StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { - val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, ptrfield, scrutinee); - signed = false; - } - } - match cast_to { - None => val, - Some(llty) => if signed { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) } - } -} - -fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, ptrfield: PointerField, - scrutinee: ValueRef) -> ValueRef { - let llptrptr = match ptrfield { - ThinPointer(field) => GEPi(bcx, scrutinee, &[0, field]), - FatPointer(field) => GEPi(bcx, scrutinee, &[0, field, slice_elt_base]) - }; - let llptr = Load(bcx, llptrptr); - let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - ICmp(bcx, cmp, llptr, C_null(val_ty(llptr))) -} - -/// Helper for cases where the discriminant is simply loaded. -fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr) - -> ValueRef { - let llty = ll_inttype(bcx.ccx(), ity); - assert_eq!(val_ty(ptr), llty.ptr_to()); - let bits = machine::llbitsize_of_real(bcx.ccx(), llty); - assert!(bits <= 64); - let bits = bits as uint; - let mask = (-1u64 >> (64 - bits)) as Disr; - if (max + 1) & mask == min & mask { - // i.e., if the range is everything. The lo==hi case would be - // rejected by the LLVM verifier (it would mean either an - // empty set, which is impossible, or the entire range of the - // type, which is pointless). - Load(bcx, ptr) - } else { - // llvm::ConstantRange can deal with ranges that wrap around, - // so an overflow on (max + 1) is fine. - LoadRangeAssert(bcx, ptr, min, (max+1), /* signed: */ True) - } -} - -/** - * Yield information about how to dispatch a case of the - * discriminant-like value returned by `trans_switch`. - * - * This should ideally be less tightly tied to `_match`. - */ -pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) - -> _match::OptResult<'blk, 'tcx> { - match *r { - CEnum(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) - } - General(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) - } - Univariant(..) => { - bcx.ccx().sess().bug("no cases for univariants or structs") - } - RawNullablePointer { .. } | - StructWrappedNullablePointer { .. } => { - assert!(discr == 0 || discr == 1); - _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) - } - } -} - -/** - * Set the discriminant for a new value of the given case of the given - * representation. - */ -pub fn trans_set_discr(bcx: Block, r: &Repr, val: ValueRef, discr: Disr) { - match *r { - CEnum(ity, min, max) => { - assert_discr_in_range(ity, min, max, discr); - Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true), - val) - } - General(ity, ref cases, dtor) => { - if dtor { - let ptr = trans_field_ptr(bcx, r, val, discr, - cases[discr as uint].fields.len() - 2); - Store(bcx, C_u8(bcx.ccx(), 1), ptr); - } - Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true), - GEPi(bcx, val, &[0, 0])) - } - Univariant(ref st, dtor) => { - assert_eq!(discr, 0); - if dtor { - Store(bcx, C_u8(bcx.ccx(), 1), - GEPi(bcx, val, &[0, st.fields.len() - 1])); - } - } - RawNullablePointer { nndiscr, nnty, ..} => { - if discr != nndiscr { - let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); - Store(bcx, C_null(llptrty), val) - } - } - StructWrappedNullablePointer { ref nonnull, nndiscr, ptrfield, .. } => { - if discr != nndiscr { - let (llptrptr, llptrty) = match ptrfield { - ThinPointer(field) => - (GEPi(bcx, val, &[0, field]), - type_of::type_of(bcx.ccx(), nonnull.fields[field])), - FatPointer(field) => { - let v = GEPi(bcx, val, &[0, field, slice_elt_base]); - (v, val_ty(v).element_type()) - } - }; - Store(bcx, C_null(llptrty), llptrptr) - } - } - } -} - -fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) { - match ity { - attr::UnsignedInt(_) => assert!(min <= discr && discr <= max), - attr::SignedInt(_) => assert!(min as i64 <= discr as i64 && discr as i64 <= max as i64) - } -} - -/** - * The number of fields in a given case; for use when obtaining this - * information from the type or definition is less convenient. - */ -pub fn num_args(r: &Repr, discr: Disr) -> uint { - match *r { - CEnum(..) => 0, - Univariant(ref st, dtor) => { - assert_eq!(discr, 0); - st.fields.len() - (if dtor { 1 } else { 0 }) - } - General(_, ref cases, dtor) => { - cases[discr as uint].fields.len() - 1 - (if dtor { 1 } else { 0 }) - } - RawNullablePointer { nndiscr, ref nullfields, .. } => { - if discr == nndiscr { 1 } else { nullfields.len() } - } - StructWrappedNullablePointer { ref nonnull, nndiscr, - ref nullfields, .. } => { - if discr == nndiscr { nonnull.fields.len() } else { nullfields.len() } - } - } -} - -/// Access a field, at a point when the value's case is known. -pub fn trans_field_ptr(bcx: Block, r: &Repr, val: ValueRef, discr: Disr, - ix: uint) -> ValueRef { - // Note: if this ever needs to generate conditionals (e.g., if we - // decide to do some kind of cdr-coding-like non-unique repr - // someday), it will need to return a possibly-new bcx as well. - match *r { - CEnum(..) => { - bcx.ccx().sess().bug("element access in C-like enum") - } - Univariant(ref st, _dtor) => { - assert_eq!(discr, 0); - struct_field_ptr(bcx, st, val, ix, false) - } - General(_, ref cases, _) => { - struct_field_ptr(bcx, &cases[discr as uint], val, ix + 1, true) - } - RawNullablePointer { nndiscr, ref nullfields, .. } | - StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => { - // The unit-like case might have a nonzero number of unit-like fields. - // (e.d., Result of Either with (), as one side.) - let ty = type_of::type_of(bcx.ccx(), nullfields[ix]); - assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0); - // The contents of memory at this pointer can't matter, but use - // the value that's "reasonable" in case of pointer comparison. - PointerCast(bcx, val, ty.ptr_to()) - } - RawNullablePointer { nndiscr, nnty, .. } => { - assert_eq!(ix, 0); - assert_eq!(discr, nndiscr); - let ty = type_of::type_of(bcx.ccx(), nnty); - PointerCast(bcx, val, ty.ptr_to()) - } - StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { - assert_eq!(discr, nndiscr); - struct_field_ptr(bcx, nonnull, val, ix, false) - } - } -} - -pub fn struct_field_ptr(bcx: Block, st: &Struct, val: ValueRef, - ix: uint, needs_cast: bool) -> ValueRef { - let val = if needs_cast { - let ccx = bcx.ccx(); - let fields = st.fields.iter().map(|&ty| type_of::type_of(ccx, ty)).collect::>(); - let real_ty = Type::struct_(ccx, fields.as_slice(), st.packed); - PointerCast(bcx, val, real_ty.ptr_to()) - } else { - val - }; - - GEPi(bcx, val, &[0, ix]) -} - -pub fn fold_variants<'blk, 'tcx>( - bcx: Block<'blk, 'tcx>, r: &Repr, value: ValueRef, - f: |Block<'blk, 'tcx>, &Struct, ValueRef| -> Block<'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let fcx = bcx.fcx; - match *r { - Univariant(ref st, _) => { - f(bcx, st, value) - } - General(ity, ref cases, _) => { - let ccx = bcx.ccx(); - let unr_cx = fcx.new_temp_block("enum-variant-iter-unr"); - Unreachable(unr_cx); - - let discr_val = trans_get_discr(bcx, r, value, None); - let llswitch = Switch(bcx, discr_val, unr_cx.llbb, cases.len()); - let bcx_next = fcx.new_temp_block("enum-variant-iter-next"); - - for (discr, case) in cases.iter().enumerate() { - let mut variant_cx = fcx.new_temp_block( - format!("enum-variant-iter-{}", discr.to_string()).as_slice() - ); - let rhs_val = C_integral(ll_inttype(ccx, ity), discr as u64, true); - AddCase(llswitch, rhs_val, variant_cx.llbb); - - let fields = case.fields.iter().map(|&ty| - type_of::type_of(bcx.ccx(), ty)).collect::>(); - let real_ty = Type::struct_(ccx, fields.as_slice(), case.packed); - let variant_value = PointerCast(variant_cx, value, real_ty.ptr_to()); - - variant_cx = f(variant_cx, case, variant_value); - Br(variant_cx, bcx_next.llbb); - } - - bcx_next - } - _ => unreachable!() - } -} - -/// Access the struct drop flag, if present. -pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, r: &Repr, val: ValueRef) - -> datum::DatumBlock<'blk, 'tcx, datum::Expr> { - let ptr_ty = ty::mk_imm_ptr(bcx.tcx(), ty::mk_bool()); - match *r { - Univariant(ref st, true) => { - let flag_ptr = GEPi(bcx, val, &[0, st.fields.len() - 1]); - datum::immediate_rvalue_bcx(bcx, flag_ptr, ptr_ty).to_expr_datumblock() - } - General(_, _, true) => { - let fcx = bcx.fcx; - let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); - let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum( - bcx, ty::mk_bool(), "drop_flag", false, - cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx - )); - bcx = fold_variants(bcx, r, val, |variant_cx, st, value| { - let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false); - datum::Datum::new(ptr, ptr_ty, datum::Rvalue::new(datum::ByRef)) - .store_to(variant_cx, scratch.val) - }); - let expr_datum = scratch.to_expr_datum(); - fcx.pop_custom_cleanup_scope(custom_cleanup_scope); - datum::DatumBlock::new(bcx, expr_datum) - } - _ => bcx.ccx().sess().bug("tried to get drop flag of non-droppable type") - } -} - -/** - * Construct a constant value, suitable for initializing a - * GlobalVariable, given a case and constant values for its fields. - * Note that this may have a different LLVM type (and different - * alignment!) from the representation's `type_of`, so it needs a - * pointer cast before use. - * - * The LLVM type system does not directly support unions, and only - * pointers can be bitcast, so a constant (and, by extension, the - * GlobalVariable initialized by it) will have a type that can vary - * depending on which case of an enum it is. - * - * To understand the alignment situation, consider `enum E { V64(u64), - * V32(u32, u32) }` on Windows. The type has 8-byte alignment to - * accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, - * i32, i32}`, which is 4-byte aligned. - * - * Currently the returned value has the same size as the type, but - * this could be changed in the future to avoid allocating unnecessary - * space after values of shorter-than-maximum cases. - */ -pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr, - vals: &[ValueRef]) -> ValueRef { - match *r { - CEnum(ity, min, max) => { - assert_eq!(vals.len(), 0); - assert_discr_in_range(ity, min, max, discr); - C_integral(ll_inttype(ccx, ity), discr as u64, true) - } - General(ity, ref cases, _) => { - let case = &cases[discr as uint]; - let max_sz = cases.iter().map(|x| x.size).max().unwrap(); - let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true); - let mut f = vec![lldiscr]; - f.push_all(vals); - let mut contents = build_const_struct(ccx, case, f.as_slice()); - contents.push_all(&[padding(ccx, max_sz - case.size)]); - C_struct(ccx, contents.as_slice(), false) - } - Univariant(ref st, _dro) => { - assert!(discr == 0); - let contents = build_const_struct(ccx, st, vals); - C_struct(ccx, contents.as_slice(), st.packed) - } - RawNullablePointer { nndiscr, nnty, .. } => { - if discr == nndiscr { - assert_eq!(vals.len(), 1); - vals[0] - } else { - C_null(type_of::sizing_type_of(ccx, nnty)) - } - } - StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { - if discr == nndiscr { - C_struct(ccx, build_const_struct(ccx, - nonnull, - vals).as_slice(), - false) - } else { - let vals = nonnull.fields.iter().map(|&ty| { - // Always use null even if it's not the `ptrfield`th - // field; see #8506. - C_null(type_of::sizing_type_of(ccx, ty)) - }).collect::>(); - C_struct(ccx, build_const_struct(ccx, - nonnull, - vals.as_slice()).as_slice(), - false) - } - } - } -} - -/** - * Compute struct field offsets relative to struct begin. - */ -fn compute_struct_field_offsets(ccx: &CrateContext, st: &Struct) -> Vec { - let mut offsets = vec!(); - - let mut offset = 0; - for &ty in st.fields.iter() { - let llty = type_of::sizing_type_of(ccx, ty); - if !st.packed { - let type_align = type_of::align_of(ccx, ty); - offset = roundup(offset, type_align); - } - offsets.push(offset); - offset += machine::llsize_of_alloc(ccx, llty); - } - assert_eq!(st.fields.len(), offsets.len()); - offsets -} - -/** - * Building structs is a little complicated, because we might need to - * insert padding if a field's value is less aligned than its type. - * - * Continuing the example from `trans_const`, a value of type `(u32, - * E)` should have the `E` at offset 8, but if that field's - * initializer is 4-byte aligned then simply translating the tuple as - * a two-element struct will locate it at offset 4, and accesses to it - * will read the wrong memory. - */ -fn build_const_struct(ccx: &CrateContext, st: &Struct, vals: &[ValueRef]) - -> Vec { - assert_eq!(vals.len(), st.fields.len()); - - let target_offsets = compute_struct_field_offsets(ccx, st); - - // offset of current value - let mut offset = 0; - let mut cfields = Vec::new(); - for (&val, &target_offset) in vals.iter().zip(target_offsets.iter()) { - if !st.packed { - let val_align = machine::llalign_of_min(ccx, val_ty(val)); - offset = roundup(offset, val_align); - } - if offset != target_offset { - cfields.push(padding(ccx, target_offset - offset)); - offset = target_offset; - } - assert!(!is_undef(val)); - cfields.push(val); - offset += machine::llsize_of_alloc(ccx, val_ty(val)); - } - - assert!(st.sized && offset <= st.size); - if offset != st.size { - cfields.push(padding(ccx, st.size - offset)); - } - - cfields -} - -fn padding(ccx: &CrateContext, size: u64) -> ValueRef { - C_undef(Type::array(&Type::i8(ccx), size)) -} - -// FIXME this utility routine should be somewhere more general -#[inline] -fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a } - -/// Get the discriminant of a constant value. (Not currently used.) -pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) - -> Disr { - match *r { - CEnum(ity, _, _) => { - match ity { - attr::SignedInt(..) => const_to_int(val) as Disr, - attr::UnsignedInt(..) => const_to_uint(val) as Disr - } - } - General(ity, _, _) => { - match ity { - attr::SignedInt(..) => const_to_int(const_get_elt(ccx, val, &[0])) as Disr, - attr::UnsignedInt(..) => const_to_uint(const_get_elt(ccx, val, &[0])) as Disr - } - } - Univariant(..) => 0, - RawNullablePointer { nndiscr, .. } => { - if is_null(val) { - /* subtraction as uint is ok because nndiscr is either 0 or 1 */ - (1 - nndiscr) as Disr - } else { - nndiscr - } - } - StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { - let (idx, sub_idx) = match ptrfield { - ThinPointer(field) => (field, None), - FatPointer(field) => (field, Some(slice_elt_base)) - }; - if is_null(const_struct_field(ccx, val, idx, sub_idx)) { - /* subtraction as uint is ok because nndiscr is either 0 or 1 */ - (1 - nndiscr) as Disr - } else { - nndiscr - } - } - } -} - -/** - * Extract a field of a constant value, as appropriate for its - * representation. - * - * (Not to be confused with `common::const_get_elt`, which operates on - * raw LLVM-level structs and arrays.) - */ -pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef, - _discr: Disr, ix: uint) -> ValueRef { - match *r { - CEnum(..) => ccx.sess().bug("element access in C-like enum const"), - Univariant(..) => const_struct_field(ccx, val, ix, None), - General(..) => const_struct_field(ccx, val, ix + 1, None), - RawNullablePointer { .. } => { - assert_eq!(ix, 0); - val - } - StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix, None) - } -} - -/// Extract field of struct-like const, skipping our alignment padding. -fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint, sub_idx: Option) - -> ValueRef { - // Get the ix-th non-undef element of the struct. - let mut real_ix = 0; // actual position in the struct - let mut ix = ix; // logical index relative to real_ix - let mut field; - loop { - loop { - field = match sub_idx { - Some(si) => const_get_elt(ccx, val, &[real_ix, si as u32]), - None => const_get_elt(ccx, val, &[real_ix]) - }; - if !is_undef(field) { - break; - } - real_ix = real_ix + 1; - } - if ix == 0 { - return field; - } - ix = ix - 1; - real_ix = real_ix + 1; - } -} diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs deleted file mode 100644 index 04237a2d449..00000000000 --- a/src/librustc/middle/trans/asm.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! -# Translation of inline assembly. -*/ - -use llvm; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::common::*; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::expr; -use middle::trans::type_of; -use middle::trans::type_::Type; - -use std::c_str::ToCStr; -use std::string::String; -use syntax::ast; -use libc::{c_uint, c_char}; - -// Take an inline assembly expression and splat it out via LLVM -pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) - -> Block<'blk, 'tcx> { - let fcx = bcx.fcx; - let mut bcx = bcx; - let mut constraints = Vec::new(); - let mut output_types = Vec::new(); - - let temp_scope = fcx.push_custom_cleanup_scope(); - - let mut ext_inputs = Vec::new(); - let mut ext_constraints = Vec::new(); - - // Prepare the output operands - let outputs = ia.outputs.iter().enumerate().map(|(i, &(ref c, ref out, is_rw))| { - constraints.push((*c).clone()); - - let out_datum = unpack_datum!(bcx, expr::trans(bcx, &**out)); - output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty)); - let val = out_datum.val; - if is_rw { - ext_inputs.push(unpack_result!(bcx, { - callee::trans_arg_datum(bcx, - expr_ty(bcx, &**out), - out_datum, - cleanup::CustomScope(temp_scope), - callee::DontAutorefArg) - })); - ext_constraints.push(i.to_string()); - } - val - - }).collect::>(); - - // Now the input operands - let mut inputs = ia.inputs.iter().map(|&(ref c, ref input)| { - constraints.push((*c).clone()); - - let in_datum = unpack_datum!(bcx, expr::trans(bcx, &**input)); - unpack_result!(bcx, { - callee::trans_arg_datum(bcx, - expr_ty(bcx, &**input), - in_datum, - cleanup::CustomScope(temp_scope), - callee::DontAutorefArg) - }) - }).collect::>(); - inputs.push_all(ext_inputs.as_slice()); - - // no failure occurred preparing operands, no need to cleanup - fcx.pop_custom_cleanup_scope(temp_scope); - - let mut constraints = - String::from_str(constraints.iter() - .map(|s| s.get().to_string()) - .chain(ext_constraints.into_iter()) - .collect::>() - .connect(",") - .as_slice()); - - let mut clobbers = get_clobbers(); - if !ia.clobbers.get().is_empty() && !clobbers.is_empty() { - clobbers = format!("{},{}", ia.clobbers.get(), clobbers); - } else { - clobbers.push_str(ia.clobbers.get()); - } - - // Add the clobbers to our constraints list - if clobbers.len() != 0 && constraints.len() != 0 { - constraints.push(','); - constraints.push_str(clobbers.as_slice()); - } else { - constraints.push_str(clobbers.as_slice()); - } - - debug!("Asm Constraints: {}", constraints.as_slice()); - - let num_outputs = outputs.len(); - - // Depending on how many outputs we have, the return type is different - let output_type = if num_outputs == 0 { - Type::void(bcx.ccx()) - } else if num_outputs == 1 { - output_types[0] - } else { - Type::struct_(bcx.ccx(), output_types.as_slice(), false) - }; - - let dialect = match ia.dialect { - ast::AsmAtt => llvm::AD_ATT, - ast::AsmIntel => llvm::AD_Intel - }; - - let r = ia.asm.get().with_c_str(|a| { - constraints.as_slice().with_c_str(|c| { - InlineAsmCall(bcx, - a, - c, - inputs.as_slice(), - output_type, - ia.volatile, - ia.alignstack, - dialect) - }) - }); - - // Again, based on how many outputs we have - if num_outputs == 1 { - Store(bcx, r, outputs[0]); - } else { - for (i, o) in outputs.iter().enumerate() { - let v = ExtractValue(bcx, r, i); - Store(bcx, v, *o); - } - } - - // Store expn_id in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - unsafe { - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx().llcx(), - key.as_ptr() as *const c_char, key.len() as c_uint); - - let val: llvm::ValueRef = C_i32(bcx.ccx(), ia.expn_id.to_llvm_cookie()); - - llvm::LLVMSetMetadata(r, kind, - llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1)); - } - - return bcx; - -} - -// Default per-arch clobbers -// Basically what clang does - -#[cfg(any(target_arch = "arm", - target_arch = "mips", - target_arch = "mipsel"))] -fn get_clobbers() -> String { - "".to_string() -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn get_clobbers() -> String { - "~{dirflag},~{fpsr},~{flags}".to_string() -} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs deleted file mode 100644 index c05e61023ed..00000000000 --- a/src/librustc/middle/trans/base.rs +++ /dev/null @@ -1,3196 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// trans.rs: Translate the completed AST to the LLVM IR. -// -// Some functions here, such as trans_block and trans_expr, return a value -- -// the result of the translation to LLVM -- while others, such as trans_fn, -// trans_impl, and trans_item, are called only for the side effect of adding a -// particular definition to the LLVM IR output we're producing. -// -// Hopefully useful general knowledge about trans: -// -// * There's no way to find out the ty::t type of a ValueRef. Doing so -// would be "trying to get the eggs out of an omelette" (credit: -// pcwalton). You can, instead, find out its TypeRef by calling val_ty, -// but one TypeRef corresponds to many `ty::t`s; for instance, tup(int, int, -// int) and rec(x=int, y=int, z=int) will have the same TypeRef. - -#![allow(non_camel_case_types)] - -pub use self::IsUnboxedClosureFlag::*; -pub use self::ValueOrigin::*; -pub use self::scalar_type::*; - -use back::link::{mangle_exported_name}; -use back::{link, abi}; -use driver::config; -use driver::config::{NoDebugInfo, FullDebugInfo}; -use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation}; -use driver::session::Session; -use lint; -use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param}; -use llvm; -use metadata::{csearch, encoder, loader}; -use middle::astencode; -use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; -use middle::subst; -use middle::weak_lang_items; -use middle::subst::Subst; -use middle::trans::_match; -use middle::trans::adt; -use middle::trans::build::*; -use middle::trans::builder::{Builder, noname}; -use middle::trans::callee; -use middle::trans::cleanup::{CleanupMethods, ScopeId}; -use middle::trans::cleanup; -use middle::trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral}; -use middle::trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_uint, C_undef}; -use middle::trans::common::{CrateContext, ExternMap, FunctionContext}; -use middle::trans::common::{NodeInfo, Result, SubstP}; -use middle::trans::common::{node_id_type, param_substs, return_type_is_void}; -use middle::trans::common::{tydesc_info, type_is_immediate}; -use middle::trans::common::{type_is_zero_size, val_ty}; -use middle::trans::common; -use middle::trans::consts; -use middle::trans::context::SharedCrateContext; -use middle::trans::controlflow; -use middle::trans::datum; -use middle::trans::debuginfo; -use middle::trans::expr; -use middle::trans::foreign; -use middle::trans::glue; -use middle::trans::inline; -use middle::trans::intrinsic; -use middle::trans::machine; -use middle::trans::machine::{llsize_of, llsize_of_real, llalign_of_min}; -use middle::trans::meth; -use middle::trans::monomorphize; -use middle::trans::tvec; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::trans::type_of::*; -use middle::trans::value::Value; -use middle::ty; -use util::common::indenter; -use util::ppaux::{Repr, ty_to_string}; -use util::sha2::Sha256; -use util::nodemap::NodeMap; - -use arena::TypedArena; -use libc::{c_uint, uint64_t}; -use std::c_str::ToCStr; -use std::cell::{Cell, RefCell}; -use std::collections::HashSet; -use std::rc::Rc; -use std::{i8, i16, i32, i64}; -use syntax::abi::{Rust, RustCall, RustIntrinsic, Abi}; -use syntax::ast_util::local_def; -use syntax::attr::AttrMetaMethods; -use syntax::attr; -use syntax::codemap::Span; -use syntax::parse::token::InternedString; -use syntax::visit::Visitor; -use syntax::visit; -use syntax::{ast, ast_util, ast_map}; - -local_data_key!(task_local_insn_key: RefCell>) - -pub fn with_insn_ctxt(blk: |&[&'static str]|) { - match task_local_insn_key.get() { - Some(ctx) => blk(ctx.borrow().as_slice()), - None => () - } -} - -pub fn init_insn_ctxt() { - task_local_insn_key.replace(Some(RefCell::new(Vec::new()))); -} - -pub struct _InsnCtxt { - _cannot_construct_outside_of_this_module: () -} - -#[unsafe_destructor] -impl Drop for _InsnCtxt { - fn drop(&mut self) { - match task_local_insn_key.get() { - Some(ctx) => { ctx.borrow_mut().pop(); } - None => {} - } - } -} - -pub fn push_ctxt(s: &'static str) -> _InsnCtxt { - debug!("new InsnCtxt: {}", s); - match task_local_insn_key.get() { - Some(ctx) => ctx.borrow_mut().push(s), - None => {} - } - _InsnCtxt { _cannot_construct_outside_of_this_module: () } -} - -pub struct StatRecorder<'a, 'tcx: 'a> { - ccx: &'a CrateContext<'a, 'tcx>, - name: Option, - istart: uint, -} - -impl<'a, 'tcx> StatRecorder<'a, 'tcx> { - pub fn new(ccx: &'a CrateContext<'a, 'tcx>, name: String) - -> StatRecorder<'a, 'tcx> { - let istart = ccx.stats().n_llvm_insns.get(); - StatRecorder { - ccx: ccx, - name: Some(name), - istart: istart, - } - } -} - -#[unsafe_destructor] -impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { - fn drop(&mut self) { - if self.ccx.sess().trans_stats() { - let iend = self.ccx.stats().n_llvm_insns.get(); - self.ccx.stats().fn_stats.borrow_mut().push((self.name.take().unwrap(), - iend - self.istart)); - self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1); - // Reset LLVM insn count to avoid compound costs. - self.ccx.stats().n_llvm_insns.set(self.istart); - } - } -} - -// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions -pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, - ty: Type, output: ty::FnOutput) -> ValueRef { - - let llfn: ValueRef = name.with_c_str(|buf| { - unsafe { - llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf, ty.to_ref()) - } - }); - - // diverging functions may unwind, but can never return normally - if output == ty::FnDiverging { - llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute); - } - - if ccx.tcx().sess.opts.cg.no_redzone - .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) { - llvm::SetFunctionAttribute(llfn, llvm::NoRedZoneAttribute) - } - - llvm::SetFunctionCallConv(llfn, cc); - // Function addresses in Rust are never significant, allowing functions to be merged. - llvm::SetUnnamedAddr(llfn, true); - - if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check { - set_split_stack(llfn); - } - - llfn -} - -// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions -pub fn decl_cdecl_fn(ccx: &CrateContext, - name: &str, - ty: Type, - output: ty::t) -> ValueRef { - decl_fn(ccx, name, llvm::CCallConv, ty, ty::FnConverging(output)) -} - -// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions -pub fn get_extern_fn(ccx: &CrateContext, - externs: &mut ExternMap, - name: &str, - cc: llvm::CallConv, - ty: Type, - output: ty::t) - -> ValueRef { - match externs.get(name) { - Some(n) => return *n, - None => {} - } - let f = decl_fn(ccx, name, cc, ty, ty::FnConverging(output)); - externs.insert(name.to_string(), f); - f -} - -fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef { - match ccx.externs().borrow().get(name) { - Some(n) => return *n, - None => () - } - - let f = decl_rust_fn(ccx, fn_ty, name); - - csearch::get_item_attrs(&ccx.sess().cstore, did, |attrs| { - set_llvm_fn_attrs(ccx, attrs.as_slice(), f) - }); - - ccx.externs().borrow_mut().insert(name.to_string(), f); - f -} - -pub fn self_type_for_unboxed_closure(ccx: &CrateContext, - closure_id: ast::DefId, - fn_ty: ty::t) - -> ty::t { - let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); - let unboxed_closure = &(*unboxed_closures)[closure_id]; - match unboxed_closure.kind { - ty::FnUnboxedClosureKind => { - ty::mk_imm_rptr(ccx.tcx(), ty::ReStatic, fn_ty) - } - ty::FnMutUnboxedClosureKind => { - ty::mk_mut_rptr(ccx.tcx(), ty::ReStatic, fn_ty) - } - ty::FnOnceUnboxedClosureKind => fn_ty - } -} - -pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) - -> ty::UnboxedClosureKind { - let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); - (*unboxed_closures)[closure_id].kind -} - -pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { - let (inputs, output, abi, env) = match ty::get(fn_ty).sty { - ty::ty_bare_fn(ref f) => { - (f.sig.inputs.clone(), f.sig.output, f.abi, None) - } - ty::ty_closure(ref f) => { - (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx))) - } - ty::ty_unboxed_closure(closure_did, _, ref substs) => { - let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); - let unboxed_closure = &(*unboxed_closures)[closure_did]; - let function_type = unboxed_closure.closure_type.clone(); - let self_type = self_type_for_unboxed_closure(ccx, closure_did, fn_ty); - let llenvironment_type = type_of_explicit_arg(ccx, self_type); - (function_type.sig.inputs.iter().map(|t| t.subst(ccx.tcx(), substs)).collect(), - function_type.sig.output.subst(ccx.tcx(), substs), - RustCall, - Some(llenvironment_type)) - } - _ => panic!("expected closure or fn") - }; - - let llfty = type_of_rust_fn(ccx, env, inputs.as_slice(), output, abi); - debug!("decl_rust_fn(input count={},type={})", - inputs.len(), - ccx.tn().type_to_string(llfty)); - - let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output); - let attrs = get_fn_llvm_attributes(ccx, fn_ty); - attrs.apply_llfn(llfn); - - llfn -} - -pub fn decl_internal_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { - let llfn = decl_rust_fn(ccx, fn_ty, name); - llvm::SetLinkage(llfn, llvm::InternalLinkage); - llfn -} - -pub fn get_extern_const(ccx: &CrateContext, did: ast::DefId, - t: ty::t) -> ValueRef { - let name = csearch::get_symbol(&ccx.sess().cstore, did); - let ty = type_of(ccx, t); - match ccx.externs().borrow_mut().get(&name) { - Some(n) => return *n, - None => () - } - unsafe { - let c = name.with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), ty.to_ref(), buf) - }); - // Thread-local statics in some other crate need to *always* be linked - // against in a thread-local fashion, so we need to be sure to apply the - // thread-local attribute locally if it was present remotely. If we - // don't do this then linker errors can be generated where the linker - // complains that one object files has a thread local version of the - // symbol and another one doesn't. - ty::each_attr(ccx.tcx(), did, |attr| { - if attr.check_name("thread_local") { - llvm::set_thread_local(c, true); - } - true - }); - ccx.externs().borrow_mut().insert(name.to_string(), c); - return c; - } -} - -// Returns a pointer to the body for the box. The box may be an opaque -// box. The result will be casted to the type of body_t, if it is statically -// known. -pub fn at_box_body(bcx: Block, body_t: ty::t, boxptr: ValueRef) -> ValueRef { - let _icx = push_ctxt("at_box_body"); - let ccx = bcx.ccx(); - let ty = Type::at_box(ccx, type_of(ccx, body_t)); - let boxptr = PointerCast(bcx, boxptr, ty.ptr_to()); - GEPi(bcx, boxptr, &[0u, abi::box_field_body]) -} - -fn require_alloc_fn(bcx: Block, info_ty: ty::t, it: LangItem) -> ast::DefId { - match bcx.tcx().lang_items.require(it) { - Ok(id) => id, - Err(s) => { - bcx.sess().fatal(format!("allocation of `{}` {}", - bcx.ty_to_string(info_ty), - s).as_slice()); - } - } -} - -// The following malloc_raw_dyn* functions allocate a box to contain -// a given type, but with a potentially dynamic size. - -pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llty_ptr: Type, - info_ty: ty::t, - size: ValueRef, - align: ValueRef) - -> Result<'blk, 'tcx> { - let _icx = push_ctxt("malloc_raw_exchange"); - - // Allocate space: - let r = callee::trans_lang_call(bcx, - require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem), - &[size, align], - None); - - Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr)) -} - -pub fn malloc_raw_dyn_proc<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: ty::t) -> Result<'blk, 'tcx> { - let _icx = push_ctxt("malloc_raw_dyn_proc"); - let ccx = bcx.ccx(); - - // Grab the TypeRef type of ptr_ty. - let ptr_ty = ty::mk_uniq(bcx.tcx(), t); - let ptr_llty = type_of(ccx, ptr_ty); - - let llty = type_of(bcx.ccx(), t); - let size = llsize_of(bcx.ccx(), llty); - let llalign = C_uint(ccx, llalign_of_min(bcx.ccx(), llty)); - - // Allocate space and store the destructor pointer: - let Result {bcx, val: llbox} = malloc_raw_dyn(bcx, ptr_llty, t, size, llalign); - let dtor_ptr = GEPi(bcx, llbox, &[0u, abi::box_field_drop_glue]); - let drop_glue_field_ty = type_of(ccx, ty::mk_nil_ptr(bcx.tcx())); - let drop_glue = PointerCast(bcx, glue::get_drop_glue(ccx, ty::mk_uniq(bcx.tcx(), t)), - drop_glue_field_ty); - Store(bcx, drop_glue, dtor_ptr); - - Result::new(bcx, llbox) -} - -// Type descriptor and type glue stuff - -pub fn get_tydesc(ccx: &CrateContext, t: ty::t) -> Rc { - match ccx.tydescs().borrow().get(&t) { - Some(inf) => return inf.clone(), - _ => { } - } - - ccx.stats().n_static_tydescs.set(ccx.stats().n_static_tydescs.get() + 1u); - let inf = Rc::new(glue::declare_tydesc(ccx, t)); - - ccx.tydescs().borrow_mut().insert(t, inf.clone()); - inf -} - -#[allow(dead_code)] // useful -pub fn set_optimize_for_size(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::OptimizeForSizeAttribute) -} - -pub fn set_no_inline(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::NoInlineAttribute) -} - -#[allow(dead_code)] // useful -pub fn set_no_unwind(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::NoUnwindAttribute) -} - -// Tell LLVM to emit the information necessary to unwind the stack for the -// function f. -pub fn set_uwtable(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::UWTableAttribute) -} - -pub fn set_inline_hint(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::InlineHintAttribute) -} - -pub fn set_llvm_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) { - use syntax::attr::*; - // Set the inline hint if there is one - match find_inline_attr(attrs) { - InlineHint => set_inline_hint(llfn), - InlineAlways => set_always_inline(llfn), - InlineNever => set_no_inline(llfn), - InlineNone => { /* fallthrough */ } - } - - for attr in attrs.iter() { - let mut used = true; - match attr.name().get() { - "no_stack_check" => unset_split_stack(llfn), - "no_split_stack" => { - unset_split_stack(llfn); - ccx.sess().span_warn(attr.span, - "no_split_stack is a deprecated synonym for no_stack_check"); - } - "cold" => unsafe { - llvm::LLVMAddFunctionAttribute(llfn, - llvm::FunctionIndex as c_uint, - llvm::ColdAttribute as uint64_t) - }, - _ => used = false, - } - if used { - attr::mark_used(attr); - } - } -} - -pub fn set_always_inline(f: ValueRef) { - llvm::SetFunctionAttribute(f, llvm::AlwaysInlineAttribute) -} - -pub fn set_split_stack(f: ValueRef) { - "split-stack".with_c_str(|buf| { - unsafe { llvm::LLVMAddFunctionAttrString(f, llvm::FunctionIndex as c_uint, buf); } - }) -} - -pub fn unset_split_stack(f: ValueRef) { - "split-stack".with_c_str(|buf| { - unsafe { llvm::LLVMRemoveFunctionAttrString(f, llvm::FunctionIndex as c_uint, buf); } - }) -} - -// Double-check that we never ask LLVM to declare the same symbol twice. It -// silently mangles such symbols, breaking our linkage model. -pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) { - if ccx.all_llvm_symbols().borrow().contains(&sym) { - ccx.sess().bug(format!("duplicate LLVM symbol: {}", sym).as_slice()); - } - ccx.all_llvm_symbols().borrow_mut().insert(sym); -} - - -pub fn get_res_dtor(ccx: &CrateContext, - did: ast::DefId, - t: ty::t, - parent_id: ast::DefId, - substs: &subst::Substs) - -> ValueRef { - let _icx = push_ctxt("trans_res_dtor"); - let did = inline::maybe_instantiate_inline(ccx, did); - - if !substs.types.is_empty() { - assert_eq!(did.krate, ast::LOCAL_CRATE); - - // Since we're in trans we don't care for any region parameters - let ref substs = subst::Substs::erased(substs.types.clone()); - - let (val, _) = monomorphize::monomorphic_fn(ccx, did, substs, None); - - val - } else if did.krate == ast::LOCAL_CRATE { - get_item_val(ccx, did.node) - } else { - let tcx = ccx.tcx(); - let name = csearch::get_symbol(&ccx.sess().cstore, did); - let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs); - let llty = type_of_dtor(ccx, class_ty); - let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID, - &[glue::get_drop_glue_type(ccx, t)], ty::mk_nil(ccx.tcx())); - get_extern_fn(ccx, - &mut *ccx.externs().borrow_mut(), - name.as_slice(), - llvm::CCallConv, - llty, - dtor_ty) - } -} - -// Structural comparison: a rather involved form of glue. -pub fn maybe_name_value(cx: &CrateContext, v: ValueRef, s: &str) { - if cx.sess().opts.cg.save_temps { - s.with_c_str(|buf| { - unsafe { - llvm::LLVMSetValueName(v, buf) - } - }) - } -} - - -// Used only for creating scalar comparison glue. -pub enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } - -pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - t: ty::t, - op: ast::BinOp) - -> Result<'blk, 'tcx> { - let f = |a| Result::new(cx, compare_scalar_values(cx, lhs, rhs, a, op)); - - match ty::get(t).sty { - ty::ty_tup(ref tys) if tys.is_empty() => f(nil_type), - ty::ty_bool | ty::ty_uint(_) | ty::ty_char => f(unsigned_int), - ty::ty_ptr(mt) if ty::type_is_sized(cx.tcx(), mt.ty) => f(unsigned_int), - ty::ty_int(_) => f(signed_int), - ty::ty_float(_) => f(floating_point), - // Should never get here, because t is scalar. - _ => cx.sess().bug("non-scalar type passed to compare_scalar_types") - } -} - - -// A helper function to do the actual comparison of scalar values. -pub fn compare_scalar_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - nt: scalar_type, - op: ast::BinOp) - -> ValueRef { - let _icx = push_ctxt("compare_scalar_values"); - fn die(cx: Block) -> ! { - cx.sess().bug("compare_scalar_values: must be a comparison operator"); - } - match nt { - nil_type => { - // We don't need to do actual comparisons for nil. - // () == () holds but () < () does not. - match op { - ast::BiEq | ast::BiLe | ast::BiGe => return C_bool(cx.ccx(), true), - ast::BiNe | ast::BiLt | ast::BiGt => return C_bool(cx.ccx(), false), - // refinements would be nice - _ => die(cx) - } - } - floating_point => { - let cmp = match op { - ast::BiEq => llvm::RealOEQ, - ast::BiNe => llvm::RealUNE, - ast::BiLt => llvm::RealOLT, - ast::BiLe => llvm::RealOLE, - ast::BiGt => llvm::RealOGT, - ast::BiGe => llvm::RealOGE, - _ => die(cx) - }; - return FCmp(cx, cmp, lhs, rhs); - } - signed_int => { - let cmp = match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntSLT, - ast::BiLe => llvm::IntSLE, - ast::BiGt => llvm::IntSGT, - ast::BiGe => llvm::IntSGE, - _ => die(cx) - }; - return ICmp(cx, cmp, lhs, rhs); - } - unsigned_int => { - let cmp = match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntULT, - ast::BiLe => llvm::IntULE, - ast::BiGt => llvm::IntUGT, - ast::BiGe => llvm::IntUGE, - _ => die(cx) - }; - return ICmp(cx, cmp, lhs, rhs); - } - } -} - -pub fn compare_simd_types( - cx: Block, - lhs: ValueRef, - rhs: ValueRef, - t: ty::t, - size: uint, - op: ast::BinOp) - -> ValueRef { - match ty::get(t).sty { - ty::ty_float(_) => { - // The comparison operators for floating point vectors are challenging. - // LLVM outputs a `< size x i1 >`, but if we perform a sign extension - // then bitcast to a floating point vector, the result will be `-NaN` - // for each truth value. Because of this they are unsupported. - cx.sess().bug("compare_simd_types: comparison operators \ - not supported for floating point SIMD types") - }, - ty::ty_uint(_) | ty::ty_int(_) => { - let cmp = match op { - ast::BiEq => llvm::IntEQ, - ast::BiNe => llvm::IntNE, - ast::BiLt => llvm::IntSLT, - ast::BiLe => llvm::IntSLE, - ast::BiGt => llvm::IntSGT, - ast::BiGe => llvm::IntSGE, - _ => cx.sess().bug("compare_simd_types: must be a comparison operator"), - }; - let return_ty = Type::vector(&type_of(cx.ccx(), t), size as u64); - // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension - // to get the correctly sized type. This will compile to a single instruction - // once the IR is converted to assembly if the SIMD instruction is supported - // by the target architecture. - SExt(cx, ICmp(cx, cmp, lhs, rhs), return_ty) - }, - _ => cx.sess().bug("compare_simd_types: invalid SIMD type"), - } -} - -pub type val_and_ty_fn<'a, 'blk, 'tcx> = - |Block<'blk, 'tcx>, ValueRef, ty::t|: 'a -> Block<'blk, 'tcx>; - -// Iterates through the elements of a structural type. -pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, - av: ValueRef, - t: ty::t, - f: val_and_ty_fn<'a, 'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("iter_structural_ty"); - - fn iter_variant<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, - repr: &adt::Repr, - av: ValueRef, - variant: &ty::VariantInfo, - substs: &subst::Substs, - f: val_and_ty_fn<'a, 'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("iter_variant"); - let tcx = cx.tcx(); - let mut cx = cx; - - for (i, &arg) in variant.args.iter().enumerate() { - cx = f(cx, - adt::trans_field_ptr(cx, repr, av, variant.disr_val, i), - arg.subst(tcx, substs)); - } - return cx; - } - - let (data_ptr, info) = if ty::type_is_sized(cx.tcx(), t) { - (av, None) - } else { - let data = GEPi(cx, av, &[0, abi::slice_elt_base]); - let info = GEPi(cx, av, &[0, abi::slice_elt_len]); - (Load(cx, data), Some(Load(cx, info))) - }; - - let mut cx = cx; - match ty::get(t).sty { - ty::ty_struct(..) => { - let repr = adt::represent_type(cx.ccx(), t); - expr::with_field_tys(cx.tcx(), t, None, |discr, field_tys| { - for (i, field_ty) in field_tys.iter().enumerate() { - let field_ty = field_ty.mt.ty; - let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i); - - let val = if ty::type_is_sized(cx.tcx(), field_ty) { - llfld_a - } else { - let boxed_ty = ty::mk_open(cx.tcx(), field_ty); - let scratch = datum::rvalue_scratch_datum(cx, boxed_ty, "__fat_ptr_iter"); - Store(cx, llfld_a, GEPi(cx, scratch.val, &[0, abi::slice_elt_base])); - Store(cx, info.unwrap(), GEPi(cx, scratch.val, &[0, abi::slice_elt_len])); - scratch.val - }; - cx = f(cx, val, field_ty); - } - }) - } - ty::ty_unboxed_closure(def_id, _, ref substs) => { - let repr = adt::represent_type(cx.ccx(), t); - let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs); - for (i, upvar) in upvars.iter().enumerate() { - let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); - cx = f(cx, llupvar, upvar.ty); - } - } - ty::ty_vec(_, Some(n)) => { - let (base, len) = tvec::get_fixed_base_and_len(cx, data_ptr, n); - let unit_ty = ty::sequence_element_type(cx.tcx(), t); - cx = tvec::iter_vec_raw(cx, base, unit_ty, len, f); - } - ty::ty_tup(ref args) => { - let repr = adt::represent_type(cx.ccx(), t); - for (i, arg) in args.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); - cx = f(cx, llfld_a, *arg); - } - } - ty::ty_enum(tid, ref substs) => { - let fcx = cx.fcx; - let ccx = fcx.ccx; - - let repr = adt::represent_type(ccx, t); - let variants = ty::enum_variants(ccx.tcx(), tid); - let n_variants = (*variants).len(); - - // NB: we must hit the discriminant first so that structural - // comparison know not to proceed when the discriminants differ. - - match adt::trans_switch(cx, &*repr, av) { - (_match::Single, None) => { - cx = iter_variant(cx, &*repr, av, &*(*variants)[0], - substs, f); - } - (_match::Switch, Some(lldiscrim_a)) => { - cx = f(cx, lldiscrim_a, ty::mk_int()); - let unr_cx = fcx.new_temp_block("enum-iter-unr"); - Unreachable(unr_cx); - let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, - n_variants); - let next_cx = fcx.new_temp_block("enum-iter-next"); - - for variant in (*variants).iter() { - let variant_cx = - fcx.new_temp_block( - format!("enum-iter-variant-{}", - variant.disr_val.to_string().as_slice()) - .as_slice()); - match adt::trans_case(cx, &*repr, variant.disr_val) { - _match::SingleResult(r) => { - AddCase(llswitch, r.val, variant_cx.llbb) - } - _ => ccx.sess().unimpl("value from adt::trans_case \ - in iter_structural_ty") - } - let variant_cx = - iter_variant(variant_cx, - &*repr, - data_ptr, - &**variant, - substs, - |x,y,z| f(x,y,z)); - Br(variant_cx, next_cx.llbb); - } - cx = next_cx; - } - _ => ccx.sess().unimpl("value from adt::trans_switch \ - in iter_structural_ty") - } - } - _ => cx.sess().unimpl("type in iter_structural_ty") - } - return cx; -} - -pub fn cast_shift_expr_rhs(cx: Block, - op: ast::BinOp, - lhs: ValueRef, - rhs: ValueRef) - -> ValueRef { - cast_shift_rhs(op, lhs, rhs, - |a,b| Trunc(cx, a, b), - |a,b| ZExt(cx, a, b)) -} - -pub fn cast_shift_const_rhs(op: ast::BinOp, - lhs: ValueRef, rhs: ValueRef) -> ValueRef { - cast_shift_rhs(op, lhs, rhs, - |a, b| unsafe { llvm::LLVMConstTrunc(a, b.to_ref()) }, - |a, b| unsafe { llvm::LLVMConstZExt(a, b.to_ref()) }) -} - -pub fn cast_shift_rhs(op: ast::BinOp, - lhs: ValueRef, - rhs: ValueRef, - trunc: |ValueRef, Type| -> ValueRef, - zext: |ValueRef, Type| -> ValueRef) - -> ValueRef { - // Shifts may have any size int on the rhs - unsafe { - if ast_util::is_shift_binop(op) { - let mut rhs_llty = val_ty(rhs); - let mut lhs_llty = val_ty(lhs); - if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() } - if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() } - let rhs_sz = llvm::LLVMGetIntTypeWidth(rhs_llty.to_ref()); - let lhs_sz = llvm::LLVMGetIntTypeWidth(lhs_llty.to_ref()); - if lhs_sz < rhs_sz { - trunc(rhs, lhs_llty) - } else if lhs_sz > rhs_sz { - // FIXME (#1877: If shifting by negative - // values becomes not undefined then this is wrong. - zext(rhs, lhs_llty) - } else { - rhs - } - } else { - rhs - } - } -} - -pub fn fail_if_zero_or_overflows<'blk, 'tcx>( - cx: Block<'blk, 'tcx>, - span: Span, - divrem: ast::BinOp, - lhs: ValueRef, - rhs: ValueRef, - rhs_t: ty::t) - -> Block<'blk, 'tcx> { - let (zero_text, overflow_text) = if divrem == ast::BiDiv { - ("attempted to divide by zero", - "attempted to divide with overflow") - } else { - ("attempted remainder with a divisor of zero", - "attempted remainder with overflow") - }; - let (is_zero, is_signed) = match ty::get(rhs_t).sty { - ty::ty_int(t) => { - let zero = C_integral(Type::int_from_ty(cx.ccx(), t), 0u64, false); - (ICmp(cx, llvm::IntEQ, rhs, zero), true) - } - ty::ty_uint(t) => { - let zero = C_integral(Type::uint_from_ty(cx.ccx(), t), 0u64, false); - (ICmp(cx, llvm::IntEQ, rhs, zero), false) - } - _ => { - cx.sess().bug(format!("fail-if-zero on unexpected type: {}", - ty_to_string(cx.tcx(), rhs_t)).as_slice()); - } - }; - let bcx = with_cond(cx, is_zero, |bcx| { - controlflow::trans_fail(bcx, span, InternedString::new(zero_text)) - }); - - // To quote LLVM's documentation for the sdiv instruction: - // - // Division by zero leads to undefined behavior. Overflow also leads - // to undefined behavior; this is a rare case, but can occur, for - // example, by doing a 32-bit division of -2147483648 by -1. - // - // In order to avoid undefined behavior, we perform runtime checks for - // signed division/remainder which would trigger overflow. For unsigned - // integers, no action beyond checking for zero need be taken. - if is_signed { - let (llty, min) = match ty::get(rhs_t).sty { - ty::ty_int(t) => { - let llty = Type::int_from_ty(cx.ccx(), t); - let min = match t { - ast::TyI if llty == Type::i32(cx.ccx()) => i32::MIN as u64, - ast::TyI => i64::MIN as u64, - ast::TyI8 => i8::MIN as u64, - ast::TyI16 => i16::MIN as u64, - ast::TyI32 => i32::MIN as u64, - ast::TyI64 => i64::MIN as u64, - }; - (llty, min) - } - _ => unreachable!(), - }; - let minus_one = ICmp(bcx, llvm::IntEQ, rhs, - C_integral(llty, -1, false)); - with_cond(bcx, minus_one, |bcx| { - let is_min = ICmp(bcx, llvm::IntEQ, lhs, - C_integral(llty, min, true)); - with_cond(bcx, is_min, |bcx| { - controlflow::trans_fail(bcx, span, - InternedString::new(overflow_text)) - }) - }) - } else { - bcx - } -} - -pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> ValueRef { - let name = csearch::get_symbol(&ccx.sess().cstore, did); - match ty::get(t).sty { - ty::ty_bare_fn(ref fn_ty) => { - match ccx.sess().target.target.adjust_abi(fn_ty.abi) { - Rust | RustCall => { - get_extern_rust_fn(ccx, t, name.as_slice(), did) - } - RustIntrinsic => { - ccx.sess().bug("unexpected intrinsic in trans_external_path") - } - _ => { - foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, - name.as_slice()) - } - } - } - ty::ty_closure(_) => { - get_extern_rust_fn(ccx, t, name.as_slice(), did) - } - _ => { - get_extern_const(ccx, did, t) - } - } -} - -pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llfn: ValueRef, - llargs: Vec , - fn_ty: ty::t, - call_info: Option, - // FIXME(15064) is_lang_item is a horrible hack, please remove it - // at the soonest opportunity. - is_lang_item: bool) - -> (ValueRef, Block<'blk, 'tcx>) { - let _icx = push_ctxt("invoke_"); - if bcx.unreachable.get() { - return (C_null(Type::i8(bcx.ccx())), bcx); - } - - // FIXME(15064) Lang item methods may (in the reflect case) not have proper - // types, so doing an attribute lookup will fail. - let attributes = if is_lang_item { - llvm::AttrBuilder::new() - } else { - get_fn_llvm_attributes(bcx.ccx(), fn_ty) - }; - - match bcx.opt_node_id { - None => { - debug!("invoke at ???"); - } - Some(id) => { - debug!("invoke at {}", bcx.tcx().map.node_to_string(id)); - } - } - - if need_invoke(bcx) { - debug!("invoking {} at {}", llfn, bcx.llbb); - for &llarg in llargs.iter() { - debug!("arg: {}", llarg); - } - let normal_bcx = bcx.fcx.new_temp_block("normal-return"); - let landing_pad = bcx.fcx.get_landing_pad(); - - match call_info { - Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), - None => debuginfo::clear_source_location(bcx.fcx) - }; - - let llresult = Invoke(bcx, - llfn, - llargs.as_slice(), - normal_bcx.llbb, - landing_pad, - Some(attributes)); - return (llresult, normal_bcx); - } else { - debug!("calling {} at {}", llfn, bcx.llbb); - for &llarg in llargs.iter() { - debug!("arg: {}", llarg); - } - - match call_info { - Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), - None => debuginfo::clear_source_location(bcx.fcx) - }; - - let llresult = Call(bcx, llfn, llargs.as_slice(), Some(attributes)); - return (llresult, bcx); - } -} - -pub fn need_invoke(bcx: Block) -> bool { - if bcx.sess().no_landing_pads() { - return false; - } - - // Avoid using invoke if we are already inside a landing pad. - if bcx.is_lpad { - return false; - } - - bcx.fcx.needs_invoke() -} - -pub fn load_if_immediate(cx: Block, v: ValueRef, t: ty::t) -> ValueRef { - let _icx = push_ctxt("load_if_immediate"); - if type_is_immediate(cx.ccx(), t) { return load_ty(cx, v, t); } - return v; -} - -pub fn load_ty(cx: Block, ptr: ValueRef, t: ty::t) -> ValueRef { - /*! - * Helper for loading values from memory. Does the necessary conversion if - * the in-memory type differs from the type used for SSA values. Also - * handles various special cases where the type gives us better information - * about what we are loading. - */ - if type_is_zero_size(cx.ccx(), t) { - C_undef(type_of::type_of(cx.ccx(), t)) - } else if ty::type_is_bool(t) { - Trunc(cx, LoadRangeAssert(cx, ptr, 0, 2, llvm::False), Type::i1(cx.ccx())) - } else if ty::type_is_char(t) { - // a char is a Unicode codepoint, and so takes values from 0 - // to 0x10FFFF inclusive only. - LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False) - } else { - Load(cx, ptr) - } -} - -pub fn store_ty(cx: Block, v: ValueRef, dst: ValueRef, t: ty::t) { - /*! - * Helper for storing values in memory. Does the necessary conversion if - * the in-memory type differs from the type used for SSA values. - */ - if ty::type_is_bool(t) { - Store(cx, ZExt(cx, v, Type::i8(cx.ccx())), dst); - } else { - Store(cx, v, dst); - }; -} - -pub fn ignore_lhs(_bcx: Block, local: &ast::Local) -> bool { - match local.pat.node { - ast::PatWild(ast::PatWildSingle) => true, _ => false - } -} - -pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &ast::Local) - -> Block<'blk, 'tcx> { - debug!("init_local(bcx={}, local.id={})", bcx.to_str(), local.id); - let _indenter = indenter(); - let _icx = push_ctxt("init_local"); - _match::store_local(bcx, local) -} - -pub fn raw_block<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, - is_lpad: bool, - llbb: BasicBlockRef) - -> Block<'blk, 'tcx> { - common::BlockS::new(llbb, is_lpad, None, fcx) -} - -pub fn with_cond<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - val: ValueRef, - f: |Block<'blk, 'tcx>| -> Block<'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("with_cond"); - let fcx = bcx.fcx; - let next_cx = fcx.new_temp_block("next"); - let cond_cx = fcx.new_temp_block("cond"); - CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); - let after_cx = f(cond_cx); - if !after_cx.terminated.get() { - Br(after_cx, next_cx.llbb); - } - next_cx -} - -pub fn call_lifetime_start(cx: Block, ptr: ValueRef) { - if cx.sess().opts.optimize == config::No { - return; - } - - let _icx = push_ctxt("lifetime_start"); - let ccx = cx.ccx(); - - let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type())); - let ptr = PointerCast(cx, ptr, Type::i8p(ccx)); - let lifetime_start = ccx.get_intrinsic(&"llvm.lifetime.start"); - Call(cx, lifetime_start, &[llsize, ptr], None); -} - -pub fn call_lifetime_end(cx: Block, ptr: ValueRef) { - if cx.sess().opts.optimize == config::No { - return; - } - - let _icx = push_ctxt("lifetime_end"); - let ccx = cx.ccx(); - - let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type())); - let ptr = PointerCast(cx, ptr, Type::i8p(ccx)); - let lifetime_end = ccx.get_intrinsic(&"llvm.lifetime.end"); - Call(cx, lifetime_end, &[llsize, ptr], None); -} - -pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { - let _icx = push_ctxt("call_memcpy"); - let ccx = cx.ccx(); - let key = match ccx.sess().target.target.target_word_size.as_slice() { - "32" => "llvm.memcpy.p0i8.p0i8.i32", - "64" => "llvm.memcpy.p0i8.p0i8.i64", - tws => panic!("Unsupported target word size for memcpy: {}", tws), - }; - let memcpy = ccx.get_intrinsic(&key); - let src_ptr = PointerCast(cx, src, Type::i8p(ccx)); - let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx)); - let size = IntCast(cx, n_bytes, ccx.int_type()); - let align = C_i32(ccx, align as i32); - let volatile = C_bool(ccx, false); - Call(cx, memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); -} - -pub fn memcpy_ty(bcx: Block, dst: ValueRef, src: ValueRef, t: ty::t) { - let _icx = push_ctxt("memcpy_ty"); - let ccx = bcx.ccx(); - if ty::type_is_structural(t) { - let llty = type_of::type_of(ccx, t); - let llsz = llsize_of(ccx, llty); - let llalign = type_of::align_of(ccx, t); - call_memcpy(bcx, dst, src, llsz, llalign as u32); - } else { - store_ty(bcx, Load(bcx, src), dst, t); - } -} - -pub fn zero_mem(cx: Block, llptr: ValueRef, t: ty::t) { - if cx.unreachable.get() { return; } - let _icx = push_ctxt("zero_mem"); - let bcx = cx; - memzero(&B(bcx), llptr, t); -} - -// Always use this function instead of storing a zero constant to the memory -// in question. If you store a zero constant, LLVM will drown in vreg -// allocation for large data structures, and the generated code will be -// awful. (A telltale sign of this is large quantities of -// `mov [byte ptr foo],0` in the generated code.) -fn memzero(b: &Builder, llptr: ValueRef, ty: ty::t) { - let _icx = push_ctxt("memzero"); - let ccx = b.ccx; - - let llty = type_of::type_of(ccx, ty); - - let intrinsic_key = match ccx.sess().target.target.target_word_size.as_slice() { - "32" => "llvm.memset.p0i8.i32", - "64" => "llvm.memset.p0i8.i64", - tws => panic!("Unsupported target word size for memset: {}", tws), - }; - - let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); - let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to()); - let llzeroval = C_u8(ccx, 0); - let size = machine::llsize_of(ccx, llty); - let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); - let volatile = C_bool(ccx, false); - b.call(llintrinsicfn, &[llptr, llzeroval, size, align, volatile], None); -} - -pub fn alloc_ty(bcx: Block, t: ty::t, name: &str) -> ValueRef { - let _icx = push_ctxt("alloc_ty"); - let ccx = bcx.ccx(); - let ty = type_of::type_of(ccx, t); - assert!(!ty::type_has_params(t)); - let val = alloca(bcx, ty, name); - return val; -} - -pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef { - let p = alloca_no_lifetime(cx, ty, name); - call_lifetime_start(cx, p); - p -} - -pub fn alloca_no_lifetime(cx: Block, ty: Type, name: &str) -> ValueRef { - let _icx = push_ctxt("alloca"); - if cx.unreachable.get() { - unsafe { - return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); - } - } - debuginfo::clear_source_location(cx.fcx); - Alloca(cx, ty, name) -} - -pub fn alloca_zeroed(cx: Block, ty: ty::t, name: &str) -> ValueRef { - let llty = type_of::type_of(cx.ccx(), ty); - if cx.unreachable.get() { - unsafe { - return llvm::LLVMGetUndef(llty.ptr_to().to_ref()); - } - } - let p = alloca_no_lifetime(cx, llty, name); - let b = cx.fcx.ccx.builder(); - b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); - memzero(&b, p, ty); - p -} - -pub fn arrayalloca(cx: Block, ty: Type, v: ValueRef) -> ValueRef { - let _icx = push_ctxt("arrayalloca"); - if cx.unreachable.get() { - unsafe { - return llvm::LLVMGetUndef(ty.to_ref()); - } - } - debuginfo::clear_source_location(cx.fcx); - let p = ArrayAlloca(cx, ty, v); - call_lifetime_start(cx, p); - p -} - -// Creates the alloca slot which holds the pointer to the slot for the final return value -pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef { - let lloutputtype = type_of::type_of(fcx.ccx, output_type); - - // We create an alloca to hold a pointer of type `output_type` - // which will hold the pointer to the right alloca which has the - // final ret value - if fcx.needs_ret_allocas { - // Let's create the stack slot - let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr"); - - // and if we're using an out pointer, then store that in our newly made slot - if type_of::return_uses_outptr(fcx.ccx, output_type) { - let outptr = get_param(fcx.llfn, 0); - - let b = fcx.ccx.builder(); - b.position_before(fcx.alloca_insert_pt.get().unwrap()); - b.store(outptr, slot); - } - - slot - - // But if there are no nested returns, we skip the indirection and have a single - // retslot - } else { - if type_of::return_uses_outptr(fcx.ccx, output_type) { - get_param(fcx.llfn, 0) - } else { - AllocaFcx(fcx, lloutputtype, "sret_slot") - } - } -} - -struct CheckForNestedReturnsVisitor { - found: bool, - in_return: bool -} - -impl CheckForNestedReturnsVisitor { - fn explicit() -> CheckForNestedReturnsVisitor { - CheckForNestedReturnsVisitor { found: false, in_return: false } - } - fn implicit() -> CheckForNestedReturnsVisitor { - CheckForNestedReturnsVisitor { found: false, in_return: true } - } -} - -impl<'v> Visitor<'v> for CheckForNestedReturnsVisitor { - fn visit_expr(&mut self, e: &ast::Expr) { - match e.node { - ast::ExprRet(..) => { - if self.in_return { - self.found = true; - } else { - self.in_return = true; - visit::walk_expr(self, e); - self.in_return = false; - } - } - _ => visit::walk_expr(self, e) - } - } -} - -fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool { - match tcx.map.find(id) { - Some(ast_map::NodeItem(i)) => { - match i.node { - ast::ItemFn(_, _, _, _, ref blk) => { - let mut explicit = CheckForNestedReturnsVisitor::explicit(); - let mut implicit = CheckForNestedReturnsVisitor::implicit(); - visit::walk_item(&mut explicit, &*i); - visit::walk_expr_opt(&mut implicit, &blk.expr); - explicit.found || implicit.found - } - _ => tcx.sess.bug("unexpected item variant in has_nested_returns") - } - } - Some(ast_map::NodeTraitItem(trait_method)) => { - match *trait_method { - ast::ProvidedMethod(ref m) => { - match m.node { - ast::MethDecl(_, _, _, _, _, _, ref blk, _) => { - let mut explicit = CheckForNestedReturnsVisitor::explicit(); - let mut implicit = CheckForNestedReturnsVisitor::implicit(); - visit::walk_method_helper(&mut explicit, &**m); - visit::walk_expr_opt(&mut implicit, &blk.expr); - explicit.found || implicit.found - } - ast::MethMac(_) => tcx.sess.bug("unexpanded macro") - } - } - ast::RequiredMethod(_) => { - tcx.sess.bug("unexpected variant: required trait method \ - in has_nested_returns") - } - ast::TypeTraitItem(_) => { - tcx.sess.bug("unexpected variant: type trait item in \ - has_nested_returns") - } - } - } - Some(ast_map::NodeImplItem(ii)) => { - match *ii { - ast::MethodImplItem(ref m) => { - match m.node { - ast::MethDecl(_, _, _, _, _, _, ref blk, _) => { - let mut explicit = CheckForNestedReturnsVisitor::explicit(); - let mut implicit = CheckForNestedReturnsVisitor::implicit(); - visit::walk_method_helper(&mut explicit, &**m); - visit::walk_expr_opt(&mut implicit, &blk.expr); - explicit.found || implicit.found - } - ast::MethMac(_) => tcx.sess.bug("unexpanded macro") - } - } - ast::TypeImplItem(_) => { - tcx.sess.bug("unexpected variant: type impl item in \ - has_nested_returns") - } - } - } - Some(ast_map::NodeExpr(e)) => { - match e.node { - ast::ExprFnBlock(_, _, ref blk) | - ast::ExprProc(_, ref blk) | - ast::ExprUnboxedFn(_, _, _, ref blk) => { - let mut explicit = CheckForNestedReturnsVisitor::explicit(); - let mut implicit = CheckForNestedReturnsVisitor::implicit(); - visit::walk_expr(&mut explicit, e); - visit::walk_expr_opt(&mut implicit, &blk.expr); - explicit.found || implicit.found - } - _ => tcx.sess.bug("unexpected expr variant in has_nested_returns") - } - } - - Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false, - - // glue, shims, etc - None if id == ast::DUMMY_NODE_ID => false, - - _ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}", - tcx.map.path_to_string(id)).as_slice()) - } -} - -// NB: must keep 4 fns in sync: -// -// - type_of_fn -// - create_datums_for_fn_args. -// - new_fn_ctxt -// - trans_args -// -// Be warned! You must call `init_function` before doing anything with the -// returned function context. -pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, - llfndecl: ValueRef, - id: ast::NodeId, - has_env: bool, - output_type: ty::FnOutput, - param_substs: &'a param_substs, - sp: Option, - block_arena: &'a TypedArena>) - -> FunctionContext<'a, 'tcx> { - param_substs.validate(); - - debug!("new_fn_ctxt(path={}, id={}, param_substs={})", - if id == -1 { - "".to_string() - } else { - ccx.tcx().map.path_to_string(id).to_string() - }, - id, param_substs.repr(ccx.tcx())); - - let uses_outptr = match output_type { - ty::FnConverging(output_type) => { - let substd_output_type = output_type.substp(ccx.tcx(), param_substs); - type_of::return_uses_outptr(ccx, substd_output_type) - } - ty::FnDiverging => false - }; - let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl); - let nested_returns = has_nested_returns(ccx.tcx(), id); - - let mut fcx = FunctionContext { - llfn: llfndecl, - llenv: None, - llretslotptr: Cell::new(None), - alloca_insert_pt: Cell::new(None), - llreturn: Cell::new(None), - needs_ret_allocas: nested_returns, - personality: Cell::new(None), - caller_expects_out_pointer: uses_outptr, - lllocals: RefCell::new(NodeMap::new()), - llupvars: RefCell::new(NodeMap::new()), - id: id, - param_substs: param_substs, - span: sp, - block_arena: block_arena, - ccx: ccx, - debug_context: debug_context, - scopes: RefCell::new(Vec::new()) - }; - - if has_env { - fcx.llenv = Some(get_param(fcx.llfn, fcx.env_arg_pos() as c_uint)) - } - - fcx -} - -/// Performs setup on a newly created function, creating the entry scope block -/// and allocating space for the return pointer. -pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, - skip_retptr: bool, - output: ty::FnOutput) -> Block<'a, 'tcx> { - let entry_bcx = fcx.new_temp_block("entry-block"); - - // Use a dummy instruction as the insertion point for all allocas. - // This is later removed in FunctionContext::cleanup. - fcx.alloca_insert_pt.set(Some(unsafe { - Load(entry_bcx, C_null(Type::i8p(fcx.ccx))); - llvm::LLVMGetFirstInstruction(entry_bcx.llbb) - })); - - if let ty::FnConverging(output_type) = output { - // This shouldn't need to recompute the return type, - // as new_fn_ctxt did it already. - let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs); - if !return_type_is_void(fcx.ccx, substd_output_type) { - // If the function returns nil/bot, there is no real return - // value, so do not set `llretslotptr`. - if !skip_retptr || fcx.caller_expects_out_pointer { - // Otherwise, we normally allocate the llretslotptr, unless we - // have been instructed to skip it for immediate return - // values. - fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); - } - } - } - - entry_bcx -} - -// NB: must keep 4 fns in sync: -// -// - type_of_fn -// - create_datums_for_fn_args. -// - new_fn_ctxt -// - trans_args - -pub fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue { - use middle::trans::datum::{ByRef, ByValue}; - - datum::Rvalue { - mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue } - } -} - -// work around bizarre resolve errors -pub type RvalueDatum = datum::Datum; -pub type LvalueDatum = datum::Datum; - -// create_datums_for_fn_args: creates rvalue datums for each of the -// incoming function arguments. These will later be stored into -// appropriate lvalue datums. -pub fn create_datums_for_fn_args(fcx: &FunctionContext, - arg_tys: &[ty::t]) - -> Vec { - let _icx = push_ctxt("create_datums_for_fn_args"); - - // Return an array wrapping the ValueRefs that we get from `get_param` for - // each argument into datums. - arg_tys.iter().enumerate().map(|(i, &arg_ty)| { - let llarg = get_param(fcx.llfn, fcx.arg_pos(i) as c_uint); - datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty)) - }).collect() -} - -/// Creates rvalue datums for each of the incoming function arguments and -/// tuples the arguments. These will later be stored into appropriate lvalue -/// datums. -/// -/// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for. -fn create_datums_for_fn_args_under_call_abi( - mut bcx: Block, - arg_scope: cleanup::CustomScopeIndex, - arg_tys: &[ty::t]) - -> Vec { - let mut result = Vec::new(); - for (i, &arg_ty) in arg_tys.iter().enumerate() { - if i < arg_tys.len() - 1 { - // Regular argument. - let llarg = get_param(bcx.fcx.llfn, bcx.fcx.arg_pos(i) as c_uint); - result.push(datum::Datum::new(llarg, arg_ty, arg_kind(bcx.fcx, - arg_ty))); - continue - } - - // This is the last argument. Tuple it. - match ty::get(arg_ty).sty { - ty::ty_tup(ref tupled_arg_tys) => { - let tuple_args_scope_id = cleanup::CustomScope(arg_scope); - let tuple = - unpack_datum!(bcx, - datum::lvalue_scratch_datum(bcx, - arg_ty, - "tupled_args", - false, - tuple_args_scope_id, - (), - |(), - mut bcx, - llval| { - for (j, &tupled_arg_ty) in - tupled_arg_tys.iter().enumerate() { - let llarg = - get_param(bcx.fcx.llfn, - bcx.fcx.arg_pos(i + j) as c_uint); - let lldest = GEPi(bcx, llval, &[0, j]); - let datum = datum::Datum::new( - llarg, - tupled_arg_ty, - arg_kind(bcx.fcx, tupled_arg_ty)); - bcx = datum.store_to(bcx, lldest); - } - bcx - })); - let tuple = unpack_datum!(bcx, - tuple.to_expr_datum() - .to_rvalue_datum(bcx, - "argtuple")); - result.push(tuple); - } - _ => { - bcx.tcx().sess.bug("last argument of a function with \ - `rust-call` ABI isn't a tuple?!") - } - }; - - } - - result -} - -fn copy_args_to_allocas<'blk, 'tcx>(fcx: &FunctionContext<'blk, 'tcx>, - arg_scope: cleanup::CustomScopeIndex, - bcx: Block<'blk, 'tcx>, - args: &[ast::Arg], - arg_datums: Vec ) - -> Block<'blk, 'tcx> { - debug!("copy_args_to_allocas"); - - let _icx = push_ctxt("copy_args_to_allocas"); - let mut bcx = bcx; - - let arg_scope_id = cleanup::CustomScope(arg_scope); - - for (i, arg_datum) in arg_datums.into_iter().enumerate() { - // For certain mode/type combinations, the raw llarg values are passed - // by value. However, within the fn body itself, we want to always - // have all locals and arguments be by-ref so that we can cancel the - // cleanup and for better interaction with LLVM's debug info. So, if - // the argument would be passed by value, we store it into an alloca. - // This alloca should be optimized away by LLVM's mem-to-reg pass in - // the event it's not truly needed. - - bcx = _match::store_arg(bcx, &*args[i].pat, arg_datum, arg_scope_id); - - if fcx.ccx.sess().opts.debuginfo == FullDebugInfo { - debuginfo::create_argument_metadata(bcx, &args[i]); - } - } - - bcx -} - -fn copy_unboxed_closure_args_to_allocas<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - arg_scope: cleanup::CustomScopeIndex, - args: &[ast::Arg], - arg_datums: Vec, - monomorphized_arg_types: &[ty::t]) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("copy_unboxed_closure_args_to_allocas"); - let arg_scope_id = cleanup::CustomScope(arg_scope); - - assert_eq!(arg_datums.len(), 1); - - let arg_datum = arg_datums.into_iter().next().unwrap(); - - // Untuple the rest of the arguments. - let tuple_datum = - unpack_datum!(bcx, - arg_datum.to_lvalue_datum_in_scope(bcx, - "argtuple", - arg_scope_id)); - let untupled_arg_types = match ty::get(monomorphized_arg_types[0]).sty { - ty::ty_tup(ref types) => types.as_slice(), - _ => { - bcx.tcx().sess.span_bug(args[0].pat.span, - "first arg to `rust-call` ABI function \ - wasn't a tuple?!") - } - }; - for j in range(0, args.len()) { - let tuple_element_type = untupled_arg_types[j]; - let tuple_element_datum = - tuple_datum.get_element(bcx, - tuple_element_type, - |llval| GEPi(bcx, llval, &[0, j])); - let tuple_element_datum = tuple_element_datum.to_expr_datum(); - let tuple_element_datum = - unpack_datum!(bcx, - tuple_element_datum.to_rvalue_datum(bcx, - "arg")); - bcx = _match::store_arg(bcx, - &*args[j].pat, - tuple_element_datum, - arg_scope_id); - - if bcx.fcx.ccx.sess().opts.debuginfo == FullDebugInfo { - debuginfo::create_argument_metadata(bcx, &args[j]); - } - } - - bcx -} - -// Ties up the llstaticallocas -> llloadenv -> lltop edges, -// and builds the return block. -pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, - last_bcx: Block<'blk, 'tcx>, - retty: ty::FnOutput) { - let _icx = push_ctxt("finish_fn"); - - let ret_cx = match fcx.llreturn.get() { - Some(llreturn) => { - if !last_bcx.terminated.get() { - Br(last_bcx, llreturn); - } - raw_block(fcx, false, llreturn) - } - None => last_bcx - }; - - // This shouldn't need to recompute the return type, - // as new_fn_ctxt did it already. - let substd_retty = retty.substp(fcx.ccx.tcx(), fcx.param_substs); - build_return_block(fcx, ret_cx, substd_retty); - - debuginfo::clear_source_location(fcx); - fcx.cleanup(); -} - -// Builds the return block for a function. -pub fn build_return_block(fcx: &FunctionContext, ret_cx: Block, retty: ty::FnOutput) { - if fcx.llretslotptr.get().is_none() || - (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) { - return RetVoid(ret_cx); - } - - let retslot = if fcx.needs_ret_allocas { - Load(ret_cx, fcx.llretslotptr.get().unwrap()) - } else { - fcx.llretslotptr.get().unwrap() - }; - let retptr = Value(retslot); - match retptr.get_dominating_store(ret_cx) { - // If there's only a single store to the ret slot, we can directly return - // the value that was stored and omit the store and the alloca - Some(s) => { - let retval = s.get_operand(0).unwrap().get(); - s.erase_from_parent(); - - if retptr.has_no_uses() { - retptr.erase_from_parent(); - } - - let retval = if retty == ty::FnConverging(ty::mk_bool()) { - Trunc(ret_cx, retval, Type::i1(fcx.ccx)) - } else { - retval - }; - - if fcx.caller_expects_out_pointer { - if let ty::FnConverging(retty) = retty { - store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); - } - RetVoid(ret_cx) - } else { - Ret(ret_cx, retval) - } - } - // Otherwise, copy the return value to the ret slot - None => match retty { - ty::FnConverging(retty) => { - if fcx.caller_expects_out_pointer { - memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); - RetVoid(ret_cx) - } else { - Ret(ret_cx, load_ty(ret_cx, retslot, retty)) - } - } - ty::FnDiverging => { - if fcx.caller_expects_out_pointer { - RetVoid(ret_cx) - } else { - Ret(ret_cx, C_undef(Type::nil(fcx.ccx))) - } - } - } - } -} - -#[deriving(Clone, Eq, PartialEq)] -pub enum IsUnboxedClosureFlag { - NotUnboxedClosure, - IsUnboxedClosure, -} - -// trans_closure: Builds an LLVM function out of a source function. -// If the function closes over its environment a closure will be -// returned. -pub fn trans_closure(ccx: &CrateContext, - decl: &ast::FnDecl, - body: &ast::Block, - llfndecl: ValueRef, - param_substs: ¶m_substs, - fn_ast_id: ast::NodeId, - _attributes: &[ast::Attribute], - output_type: ty::FnOutput, - abi: Abi, - has_env: bool, - is_unboxed_closure: IsUnboxedClosureFlag, - maybe_load_env: for<'blk, 'tcx> |Block<'blk, 'tcx>, ScopeId| - -> Block<'blk, 'tcx>) { - ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); - - let _icx = push_ctxt("trans_closure"); - set_uwtable(llfndecl); - - debug!("trans_closure(..., param_substs={})", - param_substs.repr(ccx.tcx())); - - let arena = TypedArena::new(); - let fcx = new_fn_ctxt(ccx, - llfndecl, - fn_ast_id, - has_env, - output_type, - param_substs, - Some(body.span), - &arena); - let mut bcx = init_function(&fcx, false, output_type); - - // cleanup scope for the incoming arguments - let fn_cleanup_debug_loc = - debuginfo::get_cleanup_debug_loc_for_ast_node(fn_ast_id, body.span, true); - let arg_scope = fcx.push_custom_cleanup_scope_with_debug_loc(fn_cleanup_debug_loc); - - let block_ty = node_id_type(bcx, body.id); - - // Set up arguments to the function. - let monomorphized_arg_types = - decl.inputs.iter() - .map(|arg| node_id_type(bcx, arg.id)) - .collect::>(); - let monomorphized_arg_types = match is_unboxed_closure { - NotUnboxedClosure => monomorphized_arg_types, - - // Tuple up closure argument types for the "rust-call" ABI. - IsUnboxedClosure => vec![ty::mk_tup(ccx.tcx(), monomorphized_arg_types)] - }; - for monomorphized_arg_type in monomorphized_arg_types.iter() { - debug!("trans_closure: monomorphized_arg_type: {}", - ty_to_string(ccx.tcx(), *monomorphized_arg_type)); - } - debug!("trans_closure: function lltype: {}", - bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn)); - - let arg_datums = if abi != RustCall { - create_datums_for_fn_args(&fcx, - monomorphized_arg_types.as_slice()) - } else { - create_datums_for_fn_args_under_call_abi( - bcx, - arg_scope, - monomorphized_arg_types.as_slice()) - }; - - bcx = match is_unboxed_closure { - NotUnboxedClosure => { - copy_args_to_allocas(&fcx, - arg_scope, - bcx, - decl.inputs.as_slice(), - arg_datums) - } - IsUnboxedClosure => { - copy_unboxed_closure_args_to_allocas( - bcx, - arg_scope, - decl.inputs.as_slice(), - arg_datums, - monomorphized_arg_types.as_slice()) - } - }; - - bcx = maybe_load_env(bcx, cleanup::CustomScope(arg_scope)); - - // Up until here, IR instructions for this function have explicitly not been annotated with - // source code location, so we don't step into call setup code. From here on, source location - // emitting should be enabled. - debuginfo::start_emitting_source_locations(&fcx); - - let dest = match fcx.llretslotptr.get() { - Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(block_ty), "iret_slot")), - None => { - assert!(type_is_zero_size(bcx.ccx(), block_ty)); - expr::Ignore - } - }; - - // This call to trans_block is the place where we bridge between - // translation calls that don't have a return value (trans_crate, - // trans_mod, trans_item, et cetera) and those that do - // (trans_block, trans_expr, et cetera). - bcx = controlflow::trans_block(bcx, body, dest); - - match dest { - expr::SaveIn(slot) if fcx.needs_ret_allocas => { - Store(bcx, slot, fcx.llretslotptr.get().unwrap()); - } - _ => {} - } - - match fcx.llreturn.get() { - Some(_) => { - Br(bcx, fcx.return_exit_block()); - fcx.pop_custom_cleanup_scope(arg_scope); - } - None => { - // Microoptimization writ large: avoid creating a separate - // llreturn basic block - bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope); - } - }; - - // Put return block after all other blocks. - // This somewhat improves single-stepping experience in debugger. - unsafe { - let llreturn = fcx.llreturn.get(); - for &llreturn in llreturn.iter() { - llvm::LLVMMoveBasicBlockAfter(llreturn, bcx.llbb); - } - } - - // Insert the mandatory first few basic blocks before lltop. - finish_fn(&fcx, bcx, output_type); -} - -// trans_fn: creates an LLVM function corresponding to a source language -// function. -pub fn trans_fn(ccx: &CrateContext, - decl: &ast::FnDecl, - body: &ast::Block, - llfndecl: ValueRef, - param_substs: ¶m_substs, - id: ast::NodeId, - attrs: &[ast::Attribute]) { - let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string()); - debug!("trans_fn(param_substs={})", param_substs.repr(ccx.tcx())); - let _icx = push_ctxt("trans_fn"); - let fn_ty = ty::node_id_to_type(ccx.tcx(), id); - let output_type = ty::ty_fn_ret(fn_ty); - let abi = ty::ty_fn_abi(fn_ty); - trans_closure(ccx, - decl, - body, - llfndecl, - param_substs, - id, - attrs, - output_type, - abi, - false, - NotUnboxedClosure, - |bcx, _| bcx); -} - -pub fn trans_enum_variant(ccx: &CrateContext, - _enum_id: ast::NodeId, - variant: &ast::Variant, - _args: &[ast::VariantArg], - disr: ty::Disr, - param_substs: ¶m_substs, - llfndecl: ValueRef) { - let _icx = push_ctxt("trans_enum_variant"); - - trans_enum_variant_or_tuple_like_struct( - ccx, - variant.node.id, - disr, - param_substs, - llfndecl); -} - -pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - ctor_ty: ty::t, - disr: ty::Disr, - args: callee::CallArgs, - dest: expr::Dest, - call_info: Option) - -> Result<'blk, 'tcx> { - - let ccx = bcx.fcx.ccx; - let tcx = ccx.tcx(); - - let result_ty = match ty::get(ctor_ty).sty { - ty::ty_bare_fn(ref bft) => bft.sig.output.unwrap(), - _ => ccx.sess().bug( - format!("trans_enum_variant_constructor: \ - unexpected ctor return type {}", - ctor_ty.repr(tcx)).as_slice()) - }; - - // Get location to store the result. If the user does not care about - // the result, just make a stack slot - let llresult = match dest { - expr::SaveIn(d) => d, - expr::Ignore => { - if !type_is_zero_size(ccx, result_ty) { - alloc_ty(bcx, result_ty, "constructor_result") - } else { - C_undef(type_of::type_of(ccx, result_ty)) - } - } - }; - - if !type_is_zero_size(ccx, result_ty) { - match args { - callee::ArgExprs(exprs) => { - let fields = exprs.iter().map(|x| &**x).enumerate().collect::>(); - bcx = expr::trans_adt(bcx, - result_ty, - disr, - fields.as_slice(), - None, - expr::SaveIn(llresult), - call_info); - } - _ => ccx.sess().bug("expected expr as arguments for variant/struct tuple constructor") - } - } - - // If the caller doesn't care about the result - // drop the temporary we made - let bcx = match dest { - expr::SaveIn(_) => bcx, - expr::Ignore => { - glue::drop_ty(bcx, llresult, result_ty, call_info) - } - }; - - Result::new(bcx, llresult) -} - -pub fn trans_tuple_struct(ccx: &CrateContext, - _fields: &[ast::StructField], - ctor_id: ast::NodeId, - param_substs: ¶m_substs, - llfndecl: ValueRef) { - let _icx = push_ctxt("trans_tuple_struct"); - - trans_enum_variant_or_tuple_like_struct( - ccx, - ctor_id, - 0, - param_substs, - llfndecl); -} - -fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext, - ctor_id: ast::NodeId, - disr: ty::Disr, - param_substs: ¶m_substs, - llfndecl: ValueRef) { - let ctor_ty = ty::node_id_to_type(ccx.tcx(), ctor_id); - let ctor_ty = ctor_ty.substp(ccx.tcx(), param_substs); - - let result_ty = match ty::get(ctor_ty).sty { - ty::ty_bare_fn(ref bft) => bft.sig.output, - _ => ccx.sess().bug( - format!("trans_enum_variant_or_tuple_like_struct: \ - unexpected ctor return type {}", - ty_to_string(ccx.tcx(), ctor_ty)).as_slice()) - }; - - let arena = TypedArena::new(); - let fcx = new_fn_ctxt(ccx, llfndecl, ctor_id, false, result_ty, - param_substs, None, &arena); - let bcx = init_function(&fcx, false, result_ty); - - assert!(!fcx.needs_ret_allocas); - - let arg_tys = ty::ty_fn_args(ctor_ty); - - let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice()); - - if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) { - let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot"); - let repr = adt::represent_type(ccx, result_ty.unwrap()); - for (i, arg_datum) in arg_datums.into_iter().enumerate() { - let lldestptr = adt::trans_field_ptr(bcx, - &*repr, - dest, - disr, - i); - arg_datum.store_to(bcx, lldestptr); - } - adt::trans_set_discr(bcx, &*repr, dest, disr); - } - - finish_fn(&fcx, bcx, result_ty); -} - -fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) { - let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully - - let levels = ccx.tcx().node_lint_levels.borrow(); - let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCES); - let lvlsrc = match levels.get(&(id, lint_id)) { - None | Some(&(lint::Allow, _)) => return, - Some(&lvlsrc) => lvlsrc, - }; - - let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); - match *avar { - adt::General(_, ref variants, _) => { - for var in variants.iter() { - let mut size = 0; - for field in var.fields.iter().skip(1) { - // skip the discriminant - size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); - } - sizes.push(size); - } - }, - _ => { /* its size is either constant or unimportant */ } - } - - let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), - |(l, s, li), (idx, &size)| - if size > l { - (size, l, idx) - } else if size > s { - (l, size, li) - } else { - (l, s, li) - } - ); - - // we only warn if the largest variant is at least thrice as large as - // the second-largest. - if largest > slargest * 3 && slargest > 0 { - // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing - // pass for the latter already ran. - lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::VARIANT_SIZE_DIFFERENCES, - lvlsrc, Some(sp), - format!("enum variant is more than three times larger \ - ({} bytes) than the next largest (ignoring padding)", - largest).as_slice()); - - ccx.sess().span_note(enum_def.variants[largest_index].span, - "this variant is the largest"); - } -} - -pub struct TransItemVisitor<'a, 'tcx: 'a> { - pub ccx: &'a CrateContext<'a, 'tcx>, -} - -impl<'a, 'tcx, 'v> Visitor<'v> for TransItemVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &ast::Item) { - trans_item(self.ccx, i); - } -} - -pub fn llvm_linkage_by_name(name: &str) -> Option { - // Use the names from src/llvm/docs/LangRef.rst here. Most types are only - // applicable to variable declarations and may not really make sense for - // Rust code in the first place but whitelist them anyway and trust that - // the user knows what s/he's doing. Who knows, unanticipated use cases - // may pop up in the future. - // - // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported - // and don't have to be, LLVM treats them as no-ops. - match name { - "appending" => Some(llvm::AppendingLinkage), - "available_externally" => Some(llvm::AvailableExternallyLinkage), - "common" => Some(llvm::CommonLinkage), - "extern_weak" => Some(llvm::ExternalWeakLinkage), - "external" => Some(llvm::ExternalLinkage), - "internal" => Some(llvm::InternalLinkage), - "linkonce" => Some(llvm::LinkOnceAnyLinkage), - "linkonce_odr" => Some(llvm::LinkOnceODRLinkage), - "private" => Some(llvm::PrivateLinkage), - "weak" => Some(llvm::WeakAnyLinkage), - "weak_odr" => Some(llvm::WeakODRLinkage), - _ => None, - } -} - - -/// Enum describing the origin of an LLVM `Value`, for linkage purposes. -pub enum ValueOrigin { - /// The LLVM `Value` is in this context because the corresponding item was - /// assigned to the current compilation unit. - OriginalTranslation, - /// The `Value`'s corresponding item was assigned to some other compilation - /// unit, but the `Value` was translated in this context anyway because the - /// item is marked `#[inline]`. - InlinedCopy, -} - -/// Set the appropriate linkage for an LLVM `ValueRef` (function or global). -/// If the `llval` is the direct translation of a specific Rust item, `id` -/// should be set to the `NodeId` of that item. (This mapping should be -/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to -/// `None`.) `llval_origin` indicates whether `llval` is the translation of an -/// item assigned to `ccx`'s compilation unit or an inlined copy of an item -/// assigned to a different compilation unit. -pub fn update_linkage(ccx: &CrateContext, - llval: ValueRef, - id: Option, - llval_origin: ValueOrigin) { - match llval_origin { - InlinedCopy => { - // `llval` is a translation of an item defined in a separate - // compilation unit. This only makes sense if there are at least - // two compilation units. - assert!(ccx.sess().opts.cg.codegen_units > 1); - // `llval` is a copy of something defined elsewhere, so use - // `AvailableExternallyLinkage` to avoid duplicating code in the - // output. - llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage); - return; - }, - OriginalTranslation => {}, - } - - match id { - Some(id) => { - let item = ccx.tcx().map.get(id); - if let ast_map::NodeItem(i) = item { - if let Some(name) = attr::first_attr_value_str_by_name(i.attrs[], "linkage") { - if let Some(linkage) = llvm_linkage_by_name(name.get()) { - llvm::SetLinkage(llval, linkage); - } else { - ccx.sess().span_fatal(i.span, "invalid linkage specified"); - } - return; - } - } - } - _ => {} - } - - match id { - Some(id) if ccx.reachable().contains(&id) => { - llvm::SetLinkage(llval, llvm::ExternalLinkage); - }, - _ => { - // `id` does not refer to an item in `ccx.reachable`. - if ccx.sess().opts.cg.codegen_units > 1 { - llvm::SetLinkage(llval, llvm::ExternalLinkage); - } else { - llvm::SetLinkage(llval, llvm::InternalLinkage); - } - }, - } -} - -pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { - let _icx = push_ctxt("trans_item"); - - let from_external = ccx.external_srcs().borrow().contains_key(&item.id); - - match item.node { - ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => { - if !generics.is_type_parameterized() { - let trans_everywhere = attr::requests_inline(item.attrs.as_slice()); - // Ignore `trans_everywhere` for cross-crate inlined items - // (`from_external`). `trans_item` will be called once for each - // compilation unit that references the item, so it will still get - // translated everywhere it's needed. - for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) { - let llfn = get_item_val(ccx, item.id); - if abi != Rust { - foreign::trans_rust_fn_with_foreign_abi(ccx, - &**decl, - &**body, - item.attrs.as_slice(), - llfn, - ¶m_substs::empty(), - item.id, - None); - } else { - trans_fn(ccx, - &**decl, - &**body, - llfn, - ¶m_substs::empty(), - item.id, - item.attrs.as_slice()); - } - update_linkage(ccx, - llfn, - Some(item.id), - if is_origin { OriginalTranslation } else { InlinedCopy }); - } - } - - // Be sure to travel more than just one layer deep to catch nested - // items in blocks and such. - let mut v = TransItemVisitor{ ccx: ccx }; - v.visit_block(&**body); - } - ast::ItemImpl(ref generics, _, _, ref impl_items) => { - meth::trans_impl(ccx, - item.ident, - impl_items.as_slice(), - generics, - item.id); - } - ast::ItemMod(ref m) => { - trans_mod(&ccx.rotate(), m); - } - ast::ItemEnum(ref enum_definition, _) => { - enum_variant_size_lint(ccx, enum_definition, item.span, item.id); - } - ast::ItemConst(_, ref expr) => { - // Recurse on the expression to catch items in blocks - let mut v = TransItemVisitor{ ccx: ccx }; - v.visit_expr(&**expr); - } - ast::ItemStatic(_, m, ref expr) => { - // Recurse on the expression to catch items in blocks - let mut v = TransItemVisitor{ ccx: ccx }; - v.visit_expr(&**expr); - - consts::trans_static(ccx, m, item.id); - let g = get_item_val(ccx, item.id); - update_linkage(ccx, g, Some(item.id), OriginalTranslation); - - // Do static_assert checking. It can't really be done much earlier - // because we need to get the value of the bool out of LLVM - if attr::contains_name(item.attrs.as_slice(), "static_assert") { - if m == ast::MutMutable { - ccx.sess().span_fatal(expr.span, - "cannot have static_assert on a mutable \ - static"); - } - - let v = ccx.static_values().borrow()[item.id].clone(); - unsafe { - if !(llvm::LLVMConstIntGetZExtValue(v) != 0) { - ccx.sess().span_fatal(expr.span, "static assertion failed"); - } - } - } - }, - ast::ItemForeignMod(ref foreign_mod) => { - foreign::trans_foreign_mod(ccx, foreign_mod); - } - ast::ItemTrait(..) => { - // Inside of this trait definition, we won't be actually translating any - // functions, but the trait still needs to be walked. Otherwise default - // methods with items will not get translated and will cause ICE's when - // metadata time comes around. - let mut v = TransItemVisitor{ ccx: ccx }; - visit::walk_item(&mut v, item); - } - _ => {/* fall through */ } - } -} - -// Translate a module. Doing this amounts to translating the items in the -// module; there ends up being no artifact (aside from linkage names) of -// separate modules in the compiled program. That's because modules exist -// only as a convenience for humans working with the code, to organize names -// and control visibility. -pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) { - let _icx = push_ctxt("trans_mod"); - for item in m.items.iter() { - trans_item(ccx, &**item); - } -} - -fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId, - llfn: ValueRef) { - ccx.item_symbols().borrow_mut().insert(node_id, sym); - - // The stack exhaustion lang item shouldn't have a split stack because - // otherwise it would continue to be exhausted (bad), and both it and the - // eh_personality functions need to be externally linkable. - let def = ast_util::local_def(node_id); - if ccx.tcx().lang_items.stack_exhausted() == Some(def) { - unset_split_stack(llfn); - llvm::SetLinkage(llfn, llvm::ExternalLinkage); - } - if ccx.tcx().lang_items.eh_personality() == Some(def) { - llvm::SetLinkage(llfn, llvm::ExternalLinkage); - } - - - if is_entry_fn(ccx.sess(), node_id) { - create_entry_wrapper(ccx, sp, llfn); - } -} - -fn register_fn(ccx: &CrateContext, - sp: Span, - sym: String, - node_id: ast::NodeId, - node_type: ty::t) - -> ValueRef { - match ty::get(node_type).sty { - ty::ty_bare_fn(ref f) => { - assert!(f.abi == Rust || f.abi == RustCall); - } - _ => panic!("expected bare rust fn") - }; - - let llfn = decl_rust_fn(ccx, node_type, sym.as_slice()); - finish_register_fn(ccx, sp, sym, node_id, llfn); - llfn -} - -pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) - -> llvm::AttrBuilder { - use middle::ty::{BrAnon, ReLateBound}; - - let (fn_sig, abi, has_env) = match ty::get(fn_ty).sty { - ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true), - ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false), - ty::ty_unboxed_closure(closure_did, _, ref substs) => { - let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); - let ref function_type = (*unboxed_closures)[closure_did] - .closure_type; - - (function_type.sig.subst(ccx.tcx(), substs), RustCall, true) - } - _ => ccx.sess().bug("expected closure or function.") - }; - - - // Since index 0 is the return value of the llvm func, we start - // at either 1 or 2 depending on whether there's an env slot or not - let mut first_arg_offset = if has_env { 2 } else { 1 }; - let mut attrs = llvm::AttrBuilder::new(); - let ret_ty = fn_sig.output; - - // These have an odd calling convention, so we need to manually - // unpack the input ty's - let input_tys = match ty::get(fn_ty).sty { - ty::ty_unboxed_closure(_, _, _) => { - assert!(abi == RustCall); - - match ty::get(fn_sig.inputs[0]).sty { - ty::ty_tup(ref inputs) => inputs.clone(), - _ => ccx.sess().bug("expected tuple'd inputs") - } - }, - ty::ty_bare_fn(_) if abi == RustCall => { - let mut inputs = vec![fn_sig.inputs[0]]; - - match ty::get(fn_sig.inputs[1]).sty { - ty::ty_tup(ref t_in) => { - inputs.push_all(t_in.as_slice()); - inputs - } - _ => ccx.sess().bug("expected tuple'd inputs") - } - } - _ => fn_sig.inputs.clone() - }; - - if let ty::FnConverging(ret_ty) = ret_ty { - // A function pointer is called without the declaration - // available, so we have to apply any attributes with ABI - // implications directly to the call instruction. Right now, - // the only attribute we need to worry about is `sret`. - if type_of::return_uses_outptr(ccx, ret_ty) { - let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty)); - - // The outptr can be noalias and nocapture because it's entirely - // invisible to the program. We also know it's nonnull as well - // as how many bytes we can dereference - attrs.arg(1, llvm::StructRetAttribute) - .arg(1, llvm::NoAliasAttribute) - .arg(1, llvm::NoCaptureAttribute) - .arg(1, llvm::DereferenceableAttribute(llret_sz)); - - // Add one more since there's an outptr - first_arg_offset += 1; - } else { - // The `noalias` attribute on the return value is useful to a - // function ptr caller. - match ty::get(ret_ty).sty { - // `~` pointer return values never alias because ownership - // is transferred - ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} - ty::ty_uniq(_) => { - attrs.ret(llvm::NoAliasAttribute); - } - _ => {} - } - - // We can also mark the return value as `dereferenceable` in certain cases - match ty::get(ret_ty).sty { - // These are not really pointers but pairs, (pointer, len) - ty::ty_uniq(it) | - ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} - ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { - let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); - attrs.ret(llvm::DereferenceableAttribute(llret_sz)); - } - _ => {} - } - - match ty::get(ret_ty).sty { - ty::ty_bool => { - attrs.ret(llvm::ZExtAttribute); - } - _ => {} - } - } - } - - for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { - match ty::get(t).sty { - // this needs to be first to prevent fat pointers from falling through - _ if !type_is_immediate(ccx, t) => { - let llarg_sz = llsize_of_real(ccx, type_of::type_of(ccx, t)); - - // For non-immediate arguments the callee gets its own copy of - // the value on the stack, so there are no aliases. It's also - // program-invisible so can't possibly capture - attrs.arg(idx, llvm::NoAliasAttribute) - .arg(idx, llvm::NoCaptureAttribute) - .arg(idx, llvm::DereferenceableAttribute(llarg_sz)); - } - - ty::ty_bool => { - attrs.arg(idx, llvm::ZExtAttribute); - } - - // `~` pointer parameters never alias because ownership is transferred - ty::ty_uniq(inner) => { - let llsz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); - - attrs.arg(idx, llvm::NoAliasAttribute) - .arg(idx, llvm::DereferenceableAttribute(llsz)); - } - - // `&mut` pointer parameters never alias other parameters, or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, and can be marked as both - // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on - // memory dependencies rather than pointer equality - ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable || - !ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => { - - let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); - attrs.arg(idx, llvm::NoAliasAttribute) - .arg(idx, llvm::DereferenceableAttribute(llsz)); - - if mt.mutbl == ast::MutImmutable { - attrs.arg(idx, llvm::ReadOnlyAttribute); - } - - match b { - ReLateBound(_, BrAnon(_)) => { - attrs.arg(idx, llvm::NoCaptureAttribute); - } - _ => {} - } - } - - // When a reference in an argument has no named lifetime, it's impossible for that - // reference to escape this function (returned or stored beyond the call by a closure). - ty::ty_rptr(ReLateBound(_, BrAnon(_)), mt) => { - let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); - attrs.arg(idx, llvm::NoCaptureAttribute) - .arg(idx, llvm::DereferenceableAttribute(llsz)); - } - - // & pointer parameters are also never null and we know exactly how - // many bytes we can dereference - ty::ty_rptr(_, mt) => { - let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); - attrs.arg(idx, llvm::DereferenceableAttribute(llsz)); - } - _ => () - } - } - - attrs -} - -// only use this for foreign function ABIs and glue, use `register_fn` for Rust functions -pub fn register_fn_llvmty(ccx: &CrateContext, - sp: Span, - sym: String, - node_id: ast::NodeId, - cc: llvm::CallConv, - llfty: Type) -> ValueRef { - debug!("register_fn_llvmty id={} sym={}", node_id, sym); - - let llfn = decl_fn(ccx, sym.as_slice(), cc, llfty, ty::FnConverging(ty::mk_nil(ccx.tcx()))); - finish_register_fn(ccx, sp, sym, node_id, llfn); - llfn -} - -pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { - match *sess.entry_fn.borrow() { - Some((entry_id, _)) => node_id == entry_id, - None => false - } -} - -// Create a _rust_main(args: ~[str]) function which will be called from the -// runtime rust_start function -pub fn create_entry_wrapper(ccx: &CrateContext, - _sp: Span, - main_llfn: ValueRef) { - let et = ccx.sess().entry_type.get().unwrap(); - match et { - config::EntryMain => { - create_entry_fn(ccx, main_llfn, true); - } - config::EntryStart => create_entry_fn(ccx, main_llfn, false), - config::EntryNone => {} // Do nothing. - } - - fn create_entry_fn(ccx: &CrateContext, - rust_main: ValueRef, - use_start_lang_item: bool) { - let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], - &ccx.int_type()); - - let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil(ccx.tcx())); - - // FIXME: #16581: Marking a symbol in the executable with `dllexport` - // linkage forces MinGW's linker to output a `.reloc` section for ASLR - if ccx.sess().target.target.options.is_like_windows { - unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) } - } - - let llbb = "top".with_c_str(|buf| { - unsafe { - llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, buf) - } - }); - let bld = ccx.raw_builder(); - unsafe { - llvm::LLVMPositionBuilderAtEnd(bld, llbb); - - let (start_fn, args) = if use_start_lang_item { - let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) { - Ok(id) => id, - Err(s) => { ccx.sess().fatal(s.as_slice()); } - }; - let start_fn = if start_def_id.krate == ast::LOCAL_CRATE { - get_item_val(ccx, start_def_id.node) - } else { - let start_fn_type = csearch::get_type(ccx.tcx(), - start_def_id).ty; - trans_external_path(ccx, start_def_id, start_fn_type) - }; - - let args = { - let opaque_rust_main = "rust_main".with_c_str(|buf| { - llvm::LLVMBuildPointerCast(bld, rust_main, Type::i8p(ccx).to_ref(), buf) - }); - - vec!( - opaque_rust_main, - get_param(llfn, 0), - get_param(llfn, 1) - ) - }; - (start_fn, args) - } else { - debug!("using user-defined start fn"); - let args = vec!( - get_param(llfn, 0 as c_uint), - get_param(llfn, 1 as c_uint) - ); - - (rust_main, args) - }; - - let result = llvm::LLVMBuildCall(bld, - start_fn, - args.as_ptr(), - args.len() as c_uint, - noname()); - - llvm::LLVMBuildRet(bld, result); - } - } -} - -fn exported_name(ccx: &CrateContext, id: ast::NodeId, - ty: ty::t, attrs: &[ast::Attribute]) -> String { - match ccx.external_srcs().borrow().get(&id) { - Some(&did) => { - let sym = csearch::get_symbol(&ccx.sess().cstore, did); - debug!("found item {} in other crate...", sym); - return sym; - } - None => {} - } - - match attr::first_attr_value_str_by_name(attrs, "export_name") { - // Use provided name - Some(name) => name.get().to_string(), - - _ => ccx.tcx().map.with_path(id, |mut path| { - if attr::contains_name(attrs, "no_mangle") { - // Don't mangle - path.last().unwrap().to_string() - } else { - match weak_lang_items::link_name(attrs) { - Some(name) => name.get().to_string(), - None => { - // Usual name mangling - mangle_exported_name(ccx, path, ty, id) - } - } - } - }) - } -} - -fn contains_null(s: &str) -> bool { - s.bytes().any(|b| b == 0) -} - -pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { - debug!("get_item_val(id=`{}`)", id); - - match ccx.item_vals().borrow().get(&id).cloned() { - Some(v) => return v, - None => {} - } - - let item = ccx.tcx().map.get(id); - let val = match item { - ast_map::NodeItem(i) => { - let ty = ty::node_id_to_type(ccx.tcx(), i.id); - let sym = || exported_name(ccx, id, ty, i.attrs.as_slice()); - - let v = match i.node { - ast::ItemStatic(_, _, ref expr) => { - // If this static came from an external crate, then - // we need to get the symbol from csearch instead of - // using the current crate's name/version - // information in the hash of the symbol - let sym = sym(); - debug!("making {}", sym); - - // We need the translated value here, because for enums the - // LLVM type is not fully determined by the Rust type. - let (v, ty) = consts::const_expr(ccx, &**expr); - ccx.static_values().borrow_mut().insert(id, v); - unsafe { - // boolean SSA values are i1, but they have to be stored in i8 slots, - // otherwise some LLVM optimization passes don't work as expected - let llty = if ty::type_is_bool(ty) { - llvm::LLVMInt8TypeInContext(ccx.llcx()) - } else { - llvm::LLVMTypeOf(v) - }; - if contains_null(sym.as_slice()) { - ccx.sess().fatal( - format!("Illegal null byte in export_name \ - value: `{}`", sym).as_slice()); - } - let g = sym.as_slice().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), llty, buf) - }); - - if attr::contains_name(i.attrs.as_slice(), - "thread_local") { - llvm::set_thread_local(g, true); - } - ccx.item_symbols().borrow_mut().insert(i.id, sym); - g - } - } - - ast::ItemConst(_, ref expr) => { - let (v, _) = consts::const_expr(ccx, &**expr); - ccx.const_values().borrow_mut().insert(id, v); - v - } - - ast::ItemFn(_, _, abi, _, _) => { - let sym = sym(); - let llfn = if abi == Rust { - register_fn(ccx, i.span, sym, i.id, ty) - } else { - foreign::register_rust_fn_with_foreign_abi(ccx, - i.span, - sym, - i.id) - }; - set_llvm_fn_attrs(ccx, i.attrs.as_slice(), llfn); - llfn - } - - _ => panic!("get_item_val: weird result in table") - }; - - match attr::first_attr_value_str_by_name(i.attrs.as_slice(), - "link_section") { - Some(sect) => { - if contains_null(sect.get()) { - ccx.sess().fatal(format!("Illegal null byte in link_section value: `{}`", - sect.get()).as_slice()); - } - unsafe { - sect.get().with_c_str(|buf| { - llvm::LLVMSetSection(v, buf); - }) - } - }, - None => () - } - - v - } - - ast_map::NodeTraitItem(trait_method) => { - debug!("get_item_val(): processing a NodeTraitItem"); - match *trait_method { - ast::RequiredMethod(_) | ast::TypeTraitItem(_) => { - ccx.sess().bug("unexpected variant: required trait \ - method in get_item_val()"); - } - ast::ProvidedMethod(ref m) => { - register_method(ccx, id, &**m) - } - } - } - - ast_map::NodeImplItem(ii) => { - match *ii { - ast::MethodImplItem(ref m) => register_method(ccx, id, &**m), - ast::TypeImplItem(ref typedef) => { - ccx.sess().span_bug(typedef.span, - "unexpected variant: required impl \ - method in get_item_val()") - } - } - } - - ast_map::NodeForeignItem(ni) => { - match ni.node { - ast::ForeignItemFn(..) => { - let abi = ccx.tcx().map.get_foreign_abi(id); - let ty = ty::node_id_to_type(ccx.tcx(), ni.id); - let name = foreign::link_name(&*ni); - foreign::register_foreign_item_fn(ccx, abi, ty, name.get().as_slice()) - } - ast::ForeignItemStatic(..) => { - foreign::register_static(ccx, &*ni) - } - } - } - - ast_map::NodeVariant(ref v) => { - let llfn; - let args = match v.node.kind { - ast::TupleVariantKind(ref args) => args, - ast::StructVariantKind(_) => { - panic!("struct variant kind unexpected in get_item_val") - } - }; - assert!(args.len() != 0u); - let ty = ty::node_id_to_type(ccx.tcx(), id); - let parent = ccx.tcx().map.get_parent(id); - let enm = ccx.tcx().map.expect_item(parent); - let sym = exported_name(ccx, - id, - ty, - enm.attrs.as_slice()); - - llfn = match enm.node { - ast::ItemEnum(_, _) => { - register_fn(ccx, (*v).span, sym, id, ty) - } - _ => panic!("NodeVariant, shouldn't happen") - }; - set_inline_hint(llfn); - llfn - } - - ast_map::NodeStructCtor(struct_def) => { - // Only register the constructor if this is a tuple-like struct. - let ctor_id = match struct_def.ctor_id { - None => { - ccx.sess().bug("attempt to register a constructor of \ - a non-tuple-like struct") - } - Some(ctor_id) => ctor_id, - }; - let parent = ccx.tcx().map.get_parent(id); - let struct_item = ccx.tcx().map.expect_item(parent); - let ty = ty::node_id_to_type(ccx.tcx(), ctor_id); - let sym = exported_name(ccx, - id, - ty, - struct_item.attrs - .as_slice()); - let llfn = register_fn(ccx, struct_item.span, - sym, ctor_id, ty); - set_inline_hint(llfn); - llfn - } - - ref variant => { - ccx.sess().bug(format!("get_item_val(): unexpected variant: {}", - variant).as_slice()) - } - }; - - // All LLVM globals and functions are initially created as external-linkage - // declarations. If `trans_item`/`trans_fn` later turns the declaration - // into a definition, it adjusts the linkage then (using `update_linkage`). - // - // The exception is foreign items, which have their linkage set inside the - // call to `foreign::register_*` above. We don't touch the linkage after - // that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the - // other item translation functions do). - - ccx.item_vals().borrow_mut().insert(id, val); - val -} - -fn register_method(ccx: &CrateContext, id: ast::NodeId, - m: &ast::Method) -> ValueRef { - let mty = ty::node_id_to_type(ccx.tcx(), id); - - let sym = exported_name(ccx, id, mty, m.attrs.as_slice()); - - let llfn = register_fn(ccx, m.span, sym, id, mty); - set_llvm_fn_attrs(ccx, m.attrs.as_slice(), llfn); - llfn -} - -pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef { - unsafe { - return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref()); - } -} - -pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'tcx>, - ie: encoder::EncodeInlinedItem<'a>) - -> encoder::EncodeParams<'a, 'tcx> { - encoder::EncodeParams { - diag: cx.sess().diagnostic(), - tcx: cx.tcx(), - reexports2: cx.exp_map2(), - item_symbols: cx.item_symbols(), - link_meta: cx.link_meta(), - cstore: &cx.sess().cstore, - encode_inlined_item: ie, - reachable: cx.reachable(), - } -} - -pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { - use flate; - - let any_library = cx.sess().crate_types.borrow().iter().any(|ty| { - *ty != config::CrateTypeExecutable - }); - if !any_library { - return Vec::new() - } - - let encode_inlined_item: encoder::EncodeInlinedItem = - |ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii); - - let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item); - let metadata = encoder::encode_metadata(encode_parms, krate); - let mut compressed = encoder::metadata_encoding_version.to_vec(); - compressed.push_all(match flate::deflate_bytes(metadata.as_slice()) { - Some(compressed) => compressed, - None => cx.sess().fatal("failed to compress metadata"), - }.as_slice()); - let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice()); - let llconst = C_struct_in_context(cx.metadata_llcx(), &[llmeta], false); - let name = format!("rust_metadata_{}_{}", - cx.link_meta().crate_name, - cx.link_meta().crate_hash); - let llglobal = name.with_c_str(|buf| { - unsafe { - llvm::LLVMAddGlobal(cx.metadata_llmod(), val_ty(llconst).to_ref(), buf) - } - }); - unsafe { - llvm::LLVMSetInitializer(llglobal, llconst); - let name = loader::meta_section_name(cx.sess().target.target.options.is_like_osx); - name.with_c_str(|buf| { - llvm::LLVMSetSection(llglobal, buf) - }); - } - return metadata; -} - -/// Find any symbols that are defined in one compilation unit, but not declared -/// in any other compilation unit. Give these symbols internal linkage. -fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { - use std::c_str::CString; - - unsafe { - let mut declared = HashSet::new(); - - let iter_globals = |llmod| { - ValueIter { - cur: llvm::LLVMGetFirstGlobal(llmod), - step: llvm::LLVMGetNextGlobal, - } - }; - - let iter_functions = |llmod| { - ValueIter { - cur: llvm::LLVMGetFirstFunction(llmod), - step: llvm::LLVMGetNextFunction, - } - }; - - // Collect all external declarations in all compilation units. - for ccx in cx.iter() { - for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { - let linkage = llvm::LLVMGetLinkage(val); - // We only care about external declarations (not definitions) - // and available_externally definitions. - if !(linkage == llvm::ExternalLinkage as c_uint && - llvm::LLVMIsDeclaration(val) != 0) && - !(linkage == llvm::AvailableExternallyLinkage as c_uint) { - continue - } - - let name = CString::new(llvm::LLVMGetValueName(val), false); - declared.insert(name); - } - } - - // Examine each external definition. If the definition is not used in - // any other compilation unit, and is not reachable from other crates, - // then give it internal linkage. - for ccx in cx.iter() { - for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { - // We only care about external definitions. - if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint && - llvm::LLVMIsDeclaration(val) == 0) { - continue - } - - let name = CString::new(llvm::LLVMGetValueName(val), false); - if !declared.contains(&name) && - !reachable.contains(name.as_str().unwrap()) { - llvm::SetLinkage(val, llvm::InternalLinkage); - } - } - } - } - - - struct ValueIter { - cur: ValueRef, - step: unsafe extern "C" fn(ValueRef) -> ValueRef, - } - - impl Iterator for ValueIter { - fn next(&mut self) -> Option { - let old = self.cur; - if !old.is_null() { - self.cur = unsafe { (self.step)(old) }; - Some(old) - } else { - None - } - } - } -} - -pub fn trans_crate<'tcx>(analysis: CrateAnalysis<'tcx>) - -> (ty::ctxt<'tcx>, CrateTranslation) { - let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis; - let krate = tcx.map.krate(); - - // Before we touch LLVM, make sure that multithreading is enabled. - unsafe { - use std::sync::{Once, ONCE_INIT}; - static INIT: Once = ONCE_INIT; - static mut POISONED: bool = false; - INIT.doit(|| { - if llvm::LLVMStartMultithreaded() != 1 { - // use an extra bool to make sure that all future usage of LLVM - // cannot proceed despite the Once not running more than once. - POISONED = true; - } - }); - - if POISONED { - tcx.sess.bug("couldn't enable multi-threaded LLVM"); - } - } - - let link_meta = link::build_link_meta(&tcx.sess, krate, name); - - let codegen_units = tcx.sess.opts.cg.codegen_units; - let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(), - codegen_units, - tcx, - exp_map2, - Sha256::new(), - link_meta.clone(), - reachable); - - { - let ccx = shared_ccx.get_ccx(0); - - // First, verify intrinsics. - intrinsic::check_intrinsics(&ccx); - - // Next, translate the module. - { - let _icx = push_ctxt("text"); - trans_mod(&ccx, &krate.module); - } - } - - for ccx in shared_ccx.iter() { - glue::emit_tydescs(&ccx); - if ccx.sess().opts.debuginfo != NoDebugInfo { - debuginfo::finalize(&ccx); - } - } - - // Translate the metadata. - let metadata = write_metadata(&shared_ccx, krate); - - if shared_ccx.sess().trans_stats() { - let stats = shared_ccx.stats(); - println!("--- trans stats ---"); - println!("n_static_tydescs: {}", stats.n_static_tydescs.get()); - println!("n_glues_created: {}", stats.n_glues_created.get()); - println!("n_null_glues: {}", stats.n_null_glues.get()); - println!("n_real_glues: {}", stats.n_real_glues.get()); - - println!("n_fns: {}", stats.n_fns.get()); - println!("n_monos: {}", stats.n_monos.get()); - println!("n_inlines: {}", stats.n_inlines.get()); - println!("n_closures: {}", stats.n_closures.get()); - println!("fn stats:"); - stats.fn_stats.borrow_mut().sort_by(|&(_, insns_a), &(_, insns_b)| { - insns_b.cmp(&insns_a) - }); - for tuple in stats.fn_stats.borrow().iter() { - match *tuple { - (ref name, insns) => { - println!("{} insns, {}", insns, *name); - } - } - } - } - if shared_ccx.sess().count_llvm_insns() { - for (k, v) in shared_ccx.stats().llvm_insns.borrow().iter() { - println!("{:7u} {}", *v, *k); - } - } - - let modules = shared_ccx.iter() - .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) - .collect(); - - let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { - shared_ccx.item_symbols().borrow().get(id).map(|s| s.to_string()) - }).collect(); - - // For the purposes of LTO, we add to the reachable set all of the upstream - // reachable extern fns. These functions are all part of the public ABI of - // the final product, so LTO needs to preserve them. - shared_ccx.sess().cstore.iter_crate_data(|cnum, _| { - let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum); - reachable.extend(syms.into_iter().map(|did| { - csearch::get_symbol(&shared_ccx.sess().cstore, did) - })); - }); - - // Make sure that some other crucial symbols are not eliminated from the - // module. This includes the main function, the crate map (used for debug - // log settings and I/O), and finally the curious rust_stack_exhausted - // symbol. This symbol is required for use by the libmorestack library that - // we link in, so we must ensure that this symbol is not internalized (if - // defined in the crate). - reachable.push("main".to_string()); - reachable.push("rust_stack_exhausted".to_string()); - - // referenced from .eh_frame section on some platforms - reachable.push("rust_eh_personality".to_string()); - // referenced from rt/rust_try.ll - reachable.push("rust_eh_personality_catch".to_string()); - - if codegen_units > 1 { - internalize_symbols(&shared_ccx, &reachable.iter().map(|x| x.clone()).collect()); - } - - let metadata_module = ModuleTranslation { - llcx: shared_ccx.metadata_llcx(), - llmod: shared_ccx.metadata_llmod(), - }; - let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); - let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins"); - - let translation = CrateTranslation { - modules: modules, - metadata_module: metadata_module, - link: link_meta, - metadata: metadata, - reachable: reachable, - crate_formats: formats, - no_builtins: no_builtins, - }; - - (shared_ccx.take_tcx(), translation) -} diff --git a/src/librustc/middle/trans/basic_block.rs b/src/librustc/middle/trans/basic_block.rs deleted file mode 100644 index 01335071717..00000000000 --- a/src/librustc/middle/trans/basic_block.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use llvm::{BasicBlockRef}; -use middle::trans::value::{Users, Value}; -use std::iter::{Filter, Map}; - -pub struct BasicBlock(pub BasicBlockRef); - -pub type Preds<'a> = Map<'a, Value, BasicBlock, Filter<'a, Value, Users>>; - -/** - * Wrapper for LLVM BasicBlockRef - */ -impl BasicBlock { - pub fn get(&self) -> BasicBlockRef { - let BasicBlock(v) = *self; v - } - - pub fn as_value(self) -> Value { - unsafe { - Value(llvm::LLVMBasicBlockAsValue(self.get())) - } - } - - pub fn pred_iter(self) -> Preds<'static> { - self.as_value().user_iter() - .filter(|user| user.is_a_terminator_inst()) - .map(|user| user.get_parent().unwrap()) - } - - pub fn get_single_predecessor(self) -> Option { - let mut iter = self.pred_iter(); - match (iter.next(), iter.next()) { - (Some(first), None) => Some(first), - _ => None - } - } -} diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs deleted file mode 100644 index 3ae4fdf0838..00000000000 --- a/src/librustc/middle/trans/build.rs +++ /dev/null @@ -1,827 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(dead_code)] // FFI wrappers -#![allow(non_snake_case)] - -use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; -use llvm::{Opcode, IntPredicate, RealPredicate}; -use llvm::{ValueRef, BasicBlockRef}; -use middle::trans::common::*; -use syntax::codemap::Span; - -use middle::trans::builder::Builder; -use middle::trans::type_::Type; - -use libc::{c_uint, c_char}; - -pub fn terminate(cx: Block, _: &str) { - debug!("terminate({})", cx.to_str()); - cx.terminated.set(true); -} - -pub fn check_not_terminated(cx: Block) { - if cx.terminated.get() { - panic!("already terminated!"); - } -} - -pub fn B<'blk, 'tcx>(cx: Block<'blk, 'tcx>) -> Builder<'blk, 'tcx> { - let b = cx.fcx.ccx.builder(); - b.position_at_end(cx.llbb); - b -} - -// The difference between a block being unreachable and being terminated is -// somewhat obscure, and has to do with error checking. When a block is -// terminated, we're saying that trying to add any further statements in the -// block is an error. On the other hand, if something is unreachable, that -// means that the block was terminated in some way that we don't want to check -// for (panic/break/return statements, call to diverging functions, etc), and -// further instructions to the block should simply be ignored. - -pub fn RetVoid(cx: Block) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "RetVoid"); - B(cx).ret_void(); -} - -pub fn Ret(cx: Block, v: ValueRef) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "Ret"); - B(cx).ret(v); -} - -pub fn AggregateRet(cx: Block, ret_vals: &[ValueRef]) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "AggregateRet"); - B(cx).aggregate_ret(ret_vals); -} - -pub fn Br(cx: Block, dest: BasicBlockRef) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "Br"); - B(cx).br(dest); -} - -pub fn CondBr(cx: Block, - if_: ValueRef, - then: BasicBlockRef, - else_: BasicBlockRef) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "CondBr"); - B(cx).cond_br(if_, then, else_); -} - -pub fn Switch(cx: Block, v: ValueRef, else_: BasicBlockRef, num_cases: uint) - -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - check_not_terminated(cx); - terminate(cx, "Switch"); - B(cx).switch(v, else_, num_cases) -} - -pub fn AddCase(s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { - unsafe { - if llvm::LLVMIsUndef(s) == llvm::True { return; } - llvm::LLVMAddCase(s, on_val, dest); - } -} - -pub fn IndirectBr(cx: Block, addr: ValueRef, num_dests: uint) { - if cx.unreachable.get() { return; } - check_not_terminated(cx); - terminate(cx, "IndirectBr"); - B(cx).indirect_br(addr, num_dests); -} - -pub fn Invoke(cx: Block, - fn_: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef, - attributes: Option) - -> ValueRef { - if cx.unreachable.get() { - return C_null(Type::i8(cx.ccx())); - } - check_not_terminated(cx); - terminate(cx, "Invoke"); - debug!("Invoke({} with arguments ({}))", - cx.val_to_string(fn_), - args.iter().map(|a| cx.val_to_string(*a)).collect::>().connect(", ")); - B(cx).invoke(fn_, args, then, catch, attributes) -} - -pub fn Unreachable(cx: Block) { - if cx.unreachable.get() { - return - } - cx.unreachable.set(true); - if !cx.terminated.get() { - B(cx).unreachable(); - } -} - -pub fn _Undef(val: ValueRef) -> ValueRef { - unsafe { - return llvm::LLVMGetUndef(val_ty(val).to_ref()); - } -} - -/* Arithmetic */ -pub fn Add(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).add(lhs, rhs) -} - -pub fn NSWAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nswadd(lhs, rhs) -} - -pub fn NUWAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nuwadd(lhs, rhs) -} - -pub fn FAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).fadd(lhs, rhs) -} - -pub fn Sub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).sub(lhs, rhs) -} - -pub fn NSWSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nswsub(lhs, rhs) -} - -pub fn NUWSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nuwsub(lhs, rhs) -} - -pub fn FSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).fsub(lhs, rhs) -} - -pub fn Mul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).mul(lhs, rhs) -} - -pub fn NSWMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nswmul(lhs, rhs) -} - -pub fn NUWMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).nuwmul(lhs, rhs) -} - -pub fn FMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).fmul(lhs, rhs) -} - -pub fn UDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).udiv(lhs, rhs) -} - -pub fn SDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).sdiv(lhs, rhs) -} - -pub fn ExactSDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).exactsdiv(lhs, rhs) -} - -pub fn FDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).fdiv(lhs, rhs) -} - -pub fn URem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).urem(lhs, rhs) -} - -pub fn SRem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).srem(lhs, rhs) -} - -pub fn FRem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).frem(lhs, rhs) -} - -pub fn Shl(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).shl(lhs, rhs) -} - -pub fn LShr(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).lshr(lhs, rhs) -} - -pub fn AShr(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).ashr(lhs, rhs) -} - -pub fn And(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).and(lhs, rhs) -} - -pub fn Or(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).or(lhs, rhs) -} - -pub fn Xor(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).xor(lhs, rhs) -} - -pub fn BinOp(cx: Block, op: Opcode, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - if cx.unreachable.get() { return _Undef(lhs); } - B(cx).binop(op, lhs, rhs) -} - -pub fn Neg(cx: Block, v: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - B(cx).neg(v) -} - -pub fn NSWNeg(cx: Block, v: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - B(cx).nswneg(v) -} - -pub fn NUWNeg(cx: Block, v: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - B(cx).nuwneg(v) -} -pub fn FNeg(cx: Block, v: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - B(cx).fneg(v) -} - -pub fn Not(cx: Block, v: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - B(cx).not(v) -} - -/* Memory */ -pub fn Malloc(cx: Block, ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).malloc(ty) - } -} - -pub fn ArrayMalloc(cx: Block, ty: Type, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).array_malloc(ty, val) - } -} - -pub fn Alloca(cx: Block, ty: Type, name: &str) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } - AllocaFcx(cx.fcx, ty, name) - } -} - -pub fn AllocaFcx(fcx: &FunctionContext, ty: Type, name: &str) -> ValueRef { - let b = fcx.ccx.builder(); - b.position_before(fcx.alloca_insert_pt.get().unwrap()); - b.alloca(ty, name) -} - -pub fn ArrayAlloca(cx: Block, ty: Type, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } - let b = cx.fcx.ccx.builder(); - b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); - b.array_alloca(ty, val) - } -} - -pub fn Free(cx: Block, pointer_val: ValueRef) { - if cx.unreachable.get() { return; } - B(cx).free(pointer_val) -} - -pub fn Load(cx: Block, pointer_val: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { - let ty = val_ty(pointer_val); - let eltty = if ty.kind() == llvm::Array { - ty.element_type() - } else { - ccx.int_type() - }; - return llvm::LLVMGetUndef(eltty.to_ref()); - } - B(cx).load(pointer_val) - } -} - -pub fn VolatileLoad(cx: Block, pointer_val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).volatile_load(pointer_val) - } -} - -pub fn AtomicLoad(cx: Block, pointer_val: ValueRef, order: AtomicOrdering) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { - return llvm::LLVMGetUndef(ccx.int_type().to_ref()); - } - B(cx).atomic_load(pointer_val, order) - } -} - - -pub fn LoadRangeAssert(cx: Block, pointer_val: ValueRef, lo: u64, - hi: u64, signed: llvm::Bool) -> ValueRef { - if cx.unreachable.get() { - let ccx = cx.fcx.ccx; - let ty = val_ty(pointer_val); - let eltty = if ty.kind() == llvm::Array { - ty.element_type() - } else { - ccx.int_type() - }; - unsafe { - llvm::LLVMGetUndef(eltty.to_ref()) - } - } else { - B(cx).load_range_assert(pointer_val, lo, hi, signed) - } -} - -pub fn Store(cx: Block, val: ValueRef, ptr: ValueRef) { - if cx.unreachable.get() { return; } - B(cx).store(val, ptr) -} - -pub fn VolatileStore(cx: Block, val: ValueRef, ptr: ValueRef) { - if cx.unreachable.get() { return; } - B(cx).volatile_store(val, ptr) -} - -pub fn AtomicStore(cx: Block, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { - if cx.unreachable.get() { return; } - B(cx).atomic_store(val, ptr, order) -} - -pub fn GEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).gep(pointer, indices) - } -} - -// Simple wrapper around GEP that takes an array of ints and wraps them -// in C_i32() -#[inline] -pub fn GEPi(cx: Block, base: ValueRef, ixs: &[uint]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).gepi(base, ixs) - } -} - -pub fn InBoundsGEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).inbounds_gep(pointer, indices) - } -} - -pub fn StructGEP(cx: Block, pointer: ValueRef, idx: uint) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).struct_gep(pointer, idx) - } -} - -pub fn GlobalString(cx: Block, _str: *const c_char) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).global_string(_str) - } -} - -pub fn GlobalStringPtr(cx: Block, _str: *const c_char) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).global_string_ptr(_str) - } -} - -/* Casts */ -pub fn Trunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).trunc(val, dest_ty) - } -} - -pub fn ZExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).zext(val, dest_ty) - } -} - -pub fn SExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sext(val, dest_ty) - } -} - -pub fn FPToUI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptoui(val, dest_ty) - } -} - -pub fn FPToSI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptosi(val, dest_ty) - } -} - -pub fn UIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).uitofp(val, dest_ty) - } -} - -pub fn SIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sitofp(val, dest_ty) - } -} - -pub fn FPTrunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptrunc(val, dest_ty) - } -} - -pub fn FPExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fpext(val, dest_ty) - } -} - -pub fn PtrToInt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).ptrtoint(val, dest_ty) - } -} - -pub fn IntToPtr(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).inttoptr(val, dest_ty) - } -} - -pub fn BitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).bitcast(val, dest_ty) - } -} - -pub fn ZExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).zext_or_bitcast(val, dest_ty) - } -} - -pub fn SExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sext_or_bitcast(val, dest_ty) - } -} - -pub fn TruncOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).trunc_or_bitcast(val, dest_ty) - } -} - -pub fn Cast(cx: Block, op: Opcode, val: ValueRef, dest_ty: Type, - _: *const u8) - -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).cast(op, val, dest_ty) - } -} - -pub fn PointerCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).pointercast(val, dest_ty) - } -} - -pub fn IntCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).intcast(val, dest_ty) - } -} - -pub fn FPCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fpcast(val, dest_ty) - } -} - - -/* Comparisons */ -pub fn ICmp(cx: Block, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).icmp(op, lhs, rhs) - } -} - -pub fn FCmp(cx: Block, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).fcmp(op, lhs, rhs) - } -} - -/* Miscellaneous instructions */ -pub fn EmptyPhi(cx: Block, ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).empty_phi(ty) - } -} - -pub fn Phi(cx: Block, ty: Type, vals: &[ValueRef], - bbs: &[BasicBlockRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).phi(ty, vals, bbs) - } -} - -pub fn AddIncomingToPhi(phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { - unsafe { - if llvm::LLVMIsUndef(phi) == llvm::True { return; } - llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); - } -} - -pub fn _UndefReturn(cx: Block, fn_: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - let ty = val_ty(fn_); - let retty = if ty.kind() == llvm::Function { - ty.return_type() - } else { - ccx.int_type() - }; - B(cx).count_insn("ret_undef"); - llvm::LLVMGetUndef(retty.to_ref()) - } -} - -pub fn add_span_comment(cx: Block, sp: Span, text: &str) { - B(cx).add_span_comment(sp, text) -} - -pub fn add_comment(cx: Block, text: &str) { - B(cx).add_comment(text) -} - -pub fn InlineAsmCall(cx: Block, asm: *const c_char, cons: *const c_char, - inputs: &[ValueRef], output: Type, - volatile: bool, alignstack: bool, - dia: AsmDialect) -> ValueRef { - B(cx).inline_asm_call(asm, cons, inputs, output, volatile, alignstack, dia) -} - -pub fn Call(cx: Block, fn_: ValueRef, args: &[ValueRef], - attributes: Option) -> ValueRef { - if cx.unreachable.get() { return _UndefReturn(cx, fn_); } - B(cx).call(fn_, args, attributes) -} - -pub fn CallWithConv(cx: Block, fn_: ValueRef, args: &[ValueRef], conv: CallConv, - attributes: Option) -> ValueRef { - if cx.unreachable.get() { return _UndefReturn(cx, fn_); } - B(cx).call_with_conv(fn_, args, conv, attributes) -} - -pub fn AtomicFence(cx: Block, order: AtomicOrdering) { - if cx.unreachable.get() { return; } - B(cx).atomic_fence(order) -} - -pub fn Select(cx: Block, if_: ValueRef, then: ValueRef, else_: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(then); } - B(cx).select(if_, then, else_) -} - -pub fn VAArg(cx: Block, list: ValueRef, ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).va_arg(list, ty) - } -} - -pub fn ExtractElement(cx: Block, vec_val: ValueRef, index: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).extract_element(vec_val, index) - } -} - -pub fn InsertElement(cx: Block, vec_val: ValueRef, elt_val: ValueRef, - index: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).insert_element(vec_val, elt_val, index) - } -} - -pub fn ShuffleVector(cx: Block, v1: ValueRef, v2: ValueRef, - mask: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).shuffle_vector(v1, v2, mask) - } -} - -pub fn VectorSplat(cx: Block, num_elts: uint, elt_val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).vector_splat(num_elts, elt_val) - } -} - -pub fn ExtractValue(cx: Block, agg_val: ValueRef, index: uint) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).extract_value(agg_val, index) - } -} - -pub fn InsertValue(cx: Block, agg_val: ValueRef, elt_val: ValueRef, index: uint) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).insert_value(agg_val, elt_val, index) - } -} - -pub fn IsNull(cx: Block, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).is_null(val) - } -} - -pub fn IsNotNull(cx: Block, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).is_not_null(val) - } -} - -pub fn PtrDiff(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type().to_ref()); } - B(cx).ptrdiff(lhs, rhs) - } -} - -pub fn Trap(cx: Block) { - if cx.unreachable.get() { return; } - B(cx).trap(); -} - -pub fn LandingPad(cx: Block, ty: Type, pers_fn: ValueRef, - num_clauses: uint) -> ValueRef { - check_not_terminated(cx); - assert!(!cx.unreachable.get()); - B(cx).landing_pad(ty, pers_fn, num_clauses) -} - -pub fn SetCleanup(cx: Block, landing_pad: ValueRef) { - B(cx).set_cleanup(landing_pad) -} - -pub fn Resume(cx: Block, exn: ValueRef) -> ValueRef { - check_not_terminated(cx); - terminate(cx, "Resume"); - B(cx).resume(exn) -} - -// Atomic Operations -pub fn AtomicCmpXchg(cx: Block, dst: ValueRef, - cmp: ValueRef, src: ValueRef, - order: AtomicOrdering, - failure_order: AtomicOrdering) -> ValueRef { - B(cx).atomic_cmpxchg(dst, cmp, src, order, failure_order) -} -pub fn AtomicRMW(cx: Block, op: AtomicBinOp, - dst: ValueRef, src: ValueRef, - order: AtomicOrdering) -> ValueRef { - B(cx).atomic_rmw(op, dst, src, order) -} diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs deleted file mode 100644 index 36d156d70e5..00000000000 --- a/src/librustc/middle/trans/builder.rs +++ /dev/null @@ -1,985 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(dead_code)] // FFI wrappers - -use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; -use llvm::{Opcode, IntPredicate, RealPredicate, False}; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; -use middle::trans::base; -use middle::trans::common::*; -use middle::trans::machine::llalign_of_pref; -use middle::trans::type_::Type; -use util::nodemap::FnvHashMap; -use libc::{c_uint, c_char}; -use std::string::String; -use syntax::codemap::Span; - -pub struct Builder<'a, 'tcx: 'a> { - pub llbuilder: BuilderRef, - pub ccx: &'a CrateContext<'a, 'tcx>, -} - -// This is a really awful way to get a zero-length c-string, but better (and a -// lot more efficient) than doing str::as_c_str("", ...) every time. -pub fn noname() -> *const c_char { - static CNULL: c_char = 0; - &CNULL as *const c_char -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn new(ccx: &'a CrateContext<'a, 'tcx>) -> Builder<'a, 'tcx> { - Builder { - llbuilder: ccx.raw_builder(), - ccx: ccx, - } - } - - pub fn count_insn(&self, category: &str) { - if self.ccx.sess().trans_stats() { - self.ccx.stats().n_llvm_insns.set(self.ccx - .stats() - .n_llvm_insns - .get() + 1); - } - self.ccx.count_llvm_insn(); - if self.ccx.sess().count_llvm_insns() { - base::with_insn_ctxt(|v| { - let mut h = self.ccx.stats().llvm_insns.borrow_mut(); - - // Build version of path with cycles removed. - - // Pass 1: scan table mapping str -> rightmost pos. - let mut mm = FnvHashMap::new(); - let len = v.len(); - let mut i = 0u; - while i < len { - mm.insert(v[i], i); - i += 1u; - } - - // Pass 2: concat strings for each elt, skipping - // forwards over any cycles by advancing to rightmost - // occurrence of each element in path. - let mut s = String::from_str("."); - i = 0u; - while i < len { - i = mm[v[i]]; - s.push('/'); - s.push_str(v[i]); - i += 1u; - } - - s.push('/'); - s.push_str(category); - - let n = match h.get(&s) { - Some(&n) => n, - _ => 0u - }; - h.insert(s, n+1u); - }) - } - } - - pub fn position_before(&self, insn: ValueRef) { - unsafe { - llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); - } - } - - pub fn position_at_end(&self, llbb: BasicBlockRef) { - unsafe { - llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); - } - } - - pub fn ret_void(&self) { - self.count_insn("retvoid"); - unsafe { - llvm::LLVMBuildRetVoid(self.llbuilder); - } - } - - pub fn ret(&self, v: ValueRef) { - self.count_insn("ret"); - unsafe { - llvm::LLVMBuildRet(self.llbuilder, v); - } - } - - pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { - unsafe { - llvm::LLVMBuildAggregateRet(self.llbuilder, - ret_vals.as_ptr(), - ret_vals.len() as c_uint); - } - } - - pub fn br(&self, dest: BasicBlockRef) { - self.count_insn("br"); - unsafe { - llvm::LLVMBuildBr(self.llbuilder, dest); - } - } - - pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { - self.count_insn("condbr"); - unsafe { - llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); - } - } - - pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: uint) -> ValueRef { - unsafe { - llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) - } - } - - pub fn indirect_br(&self, addr: ValueRef, num_dests: uint) { - self.count_insn("indirectbr"); - unsafe { - llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); - } - } - - pub fn invoke(&self, - llfn: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef, - attributes: Option) - -> ValueRef { - self.count_insn("invoke"); - - debug!("Invoke {} with args ({})", - self.ccx.tn().val_to_string(llfn), - args.iter() - .map(|&v| self.ccx.tn().val_to_string(v)) - .collect::>() - .connect(", ")); - - unsafe { - let v = llvm::LLVMBuildInvoke(self.llbuilder, - llfn, - args.as_ptr(), - args.len() as c_uint, - then, - catch, - noname()); - match attributes { - Some(a) => a.apply_callsite(v), - None => {} - } - v - } - } - - pub fn unreachable(&self) { - self.count_insn("unreachable"); - unsafe { - llvm::LLVMBuildUnreachable(self.llbuilder); - } - } - - /* Arithmetic */ - pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("add"); - unsafe { - llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswadd"); - unsafe { - llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwadd"); - unsafe { - llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fadd"); - unsafe { - llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nwsub"); - unsafe { - llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwsub"); - unsafe { - llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("mul"); - unsafe { - llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswmul"); - unsafe { - llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwmul"); - unsafe { - llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fmul"); - unsafe { - llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("udiv"); - unsafe { - llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sdiv"); - unsafe { - llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("exactsdiv"); - unsafe { - llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fdiv"); - unsafe { - llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("urem"); - unsafe { - llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("srem"); - unsafe { - llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("frem"); - unsafe { - llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("shl"); - unsafe { - llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("lshr"); - unsafe { - llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ashr"); - unsafe { - llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("and"); - unsafe { - llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("or"); - unsafe { - llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("xor"); - unsafe { - llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - self.count_insn("binop"); - unsafe { - llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) - } - } - - pub fn neg(&self, v: ValueRef) -> ValueRef { - self.count_insn("neg"); - unsafe { - llvm::LLVMBuildNeg(self.llbuilder, v, noname()) - } - } - - pub fn nswneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nswneg"); - unsafe { - llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) - } - } - - pub fn nuwneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nuwneg"); - unsafe { - llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) - } - } - pub fn fneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("fneg"); - unsafe { - llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) - } - } - - pub fn not(&self, v: ValueRef) -> ValueRef { - self.count_insn("not"); - unsafe { - llvm::LLVMBuildNot(self.llbuilder, v, noname()) - } - } - - /* Memory */ - pub fn malloc(&self, ty: Type) -> ValueRef { - self.count_insn("malloc"); - unsafe { - llvm::LLVMBuildMalloc(self.llbuilder, ty.to_ref(), noname()) - } - } - - pub fn array_malloc(&self, ty: Type, val: ValueRef) -> ValueRef { - self.count_insn("arraymalloc"); - unsafe { - llvm::LLVMBuildArrayMalloc(self.llbuilder, ty.to_ref(), val, noname()) - } - } - - pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { - self.count_insn("alloca"); - unsafe { - if name.is_empty() { - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) - } else { - name.with_c_str(|c| { - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), c) - }) - } - } - } - - pub fn array_alloca(&self, ty: Type, val: ValueRef) -> ValueRef { - self.count_insn("arrayalloca"); - unsafe { - llvm::LLVMBuildArrayAlloca(self.llbuilder, ty.to_ref(), val, noname()) - } - } - - pub fn free(&self, ptr: ValueRef) { - self.count_insn("free"); - unsafe { - llvm::LLVMBuildFree(self.llbuilder, ptr); - } - } - - pub fn load(&self, ptr: ValueRef) -> ValueRef { - self.count_insn("load"); - unsafe { - llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()) - } - } - - pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { - self.count_insn("load.volatile"); - unsafe { - let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); - llvm::LLVMSetVolatile(insn, llvm::True); - insn - } - } - - pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering) -> ValueRef { - self.count_insn("load.atomic"); - unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); - let align = llalign_of_pref(self.ccx, ty.element_type()); - llvm::LLVMBuildAtomicLoad(self.llbuilder, ptr, noname(), order, - align as c_uint) - } - } - - - pub fn load_range_assert(&self, ptr: ValueRef, lo: u64, - hi: u64, signed: llvm::Bool) -> ValueRef { - let value = self.load(ptr); - - unsafe { - let t = llvm::LLVMGetElementType(llvm::LLVMTypeOf(ptr)); - let min = llvm::LLVMConstInt(t, lo, signed); - let max = llvm::LLVMConstInt(t, hi, signed); - - let v = [min, max]; - - llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.ccx.llcx(), - v.as_ptr(), - v.len() as c_uint)); - } - - value - } - - pub fn store(&self, val: ValueRef, ptr: ValueRef) { - debug!("Store {} -> {}", - self.ccx.tn().val_to_string(val), - self.ccx.tn().val_to_string(ptr)); - assert!(self.llbuilder.is_not_null()); - self.count_insn("store"); - unsafe { - llvm::LLVMBuildStore(self.llbuilder, val, ptr); - } - } - - pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) { - debug!("Store {} -> {}", - self.ccx.tn().val_to_string(val), - self.ccx.tn().val_to_string(ptr)); - assert!(self.llbuilder.is_not_null()); - self.count_insn("store.volatile"); - unsafe { - let insn = llvm::LLVMBuildStore(self.llbuilder, val, ptr); - llvm::LLVMSetVolatile(insn, llvm::True); - } - } - - pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { - debug!("Store {} -> {}", - self.ccx.tn().val_to_string(val), - self.ccx.tn().val_to_string(ptr)); - self.count_insn("store.atomic"); - unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); - let align = llalign_of_pref(self.ccx, ty.element_type()); - llvm::LLVMBuildAtomicStore(self.llbuilder, val, ptr, order, align as c_uint); - } - } - - pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("gep"); - unsafe { - llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), - indices.len() as c_uint, noname()) - } - } - - // Simple wrapper around GEP that takes an array of ints and wraps them - // in C_i32() - #[inline] - pub fn gepi(&self, base: ValueRef, ixs: &[uint]) -> ValueRef { - // Small vector optimization. This should catch 100% of the cases that - // we care about. - if ixs.len() < 16 { - let mut small_vec = [ C_i32(self.ccx, 0), ..16 ]; - for (small_vec_e, &ix) in small_vec.iter_mut().zip(ixs.iter()) { - *small_vec_e = C_i32(self.ccx, ix as i32); - } - self.inbounds_gep(base, small_vec[..ixs.len()]) - } else { - let v = ixs.iter().map(|i| C_i32(self.ccx, *i as i32)).collect::>(); - self.count_insn("gepi"); - self.inbounds_gep(base, v.as_slice()) - } - } - - pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("inboundsgep"); - unsafe { - llvm::LLVMBuildInBoundsGEP( - self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) - } - } - - pub fn struct_gep(&self, ptr: ValueRef, idx: uint) -> ValueRef { - self.count_insn("structgep"); - unsafe { - llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) - } - } - - pub fn global_string(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstring"); - unsafe { - llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) - } - } - - pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstringptr"); - unsafe { - llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) - } - } - - /* Casts */ - pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("trunc"); - unsafe { - llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zext"); - unsafe { - llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sext"); - unsafe { - llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptoui"); - unsafe { - llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptosi"); - unsafe { - llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) - } - } - - pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("uitofp"); - unsafe { - llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sitofp"); - unsafe { - llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptrunc"); - unsafe { - llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpext"); - unsafe { - llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("ptrtoint"); - unsafe { - llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("inttoptr"); - unsafe { - llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("bitcast"); - unsafe { - llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zextorbitcast"); - unsafe { - llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sextorbitcast"); - unsafe { - llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("truncorbitcast"); - unsafe { - llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("cast"); - unsafe { - llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) - } - } - - pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("pointercast"); - unsafe { - llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn intcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("intcast"); - unsafe { - llvm::LLVMBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpcast"); - unsafe { - llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - - /* Comparisons */ - pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("icmp"); - unsafe { - llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fcmp"); - unsafe { - llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - /* Miscellaneous instructions */ - pub fn empty_phi(&self, ty: Type) -> ValueRef { - self.count_insn("emptyphi"); - unsafe { - llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) - } - } - - pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { - assert_eq!(vals.len(), bbs.len()); - let phi = self.empty_phi(ty); - self.count_insn("addincoming"); - unsafe { - llvm::LLVMAddIncoming(phi, vals.as_ptr(), - bbs.as_ptr(), - vals.len() as c_uint); - phi - } - } - - pub fn add_span_comment(&self, sp: Span, text: &str) { - if self.ccx.sess().asm_comments() { - let s = format!("{} ({})", - text, - self.ccx.sess().codemap().span_to_string(sp)); - debug!("{}", s.as_slice()); - self.add_comment(s.as_slice()); - } - } - - pub fn add_comment(&self, text: &str) { - if self.ccx.sess().asm_comments() { - let sanitized = text.replace("$", ""); - let comment_text = format!("{} {}", "#", - sanitized.replace("\n", "\n\t# ")); - self.count_insn("inlineasm"); - let asm = comment_text.as_slice().with_c_str(|c| { - unsafe { - llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.ccx)).to_ref(), - c, noname(), False, False) - } - }); - self.call(asm, &[], None); - } - } - - pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, - inputs: &[ValueRef], output: Type, - volatile: bool, alignstack: bool, - dia: AsmDialect) -> ValueRef { - self.count_insn("inlineasm"); - - let volatile = if volatile { llvm::True } - else { llvm::False }; - let alignstack = if alignstack { llvm::True } - else { llvm::False }; - - let argtys = inputs.iter().map(|v| { - debug!("Asm Input Type: {}", self.ccx.tn().val_to_string(*v)); - val_ty(*v) - }).collect::>(); - - debug!("Asm Output Type: {}", self.ccx.tn().type_to_string(output)); - let fty = Type::func(argtys.as_slice(), &output); - unsafe { - let v = llvm::LLVMInlineAsm( - fty.to_ref(), asm, cons, volatile, alignstack, dia as c_uint); - self.call(v, inputs, None) - } - } - - pub fn call(&self, llfn: ValueRef, args: &[ValueRef], - attributes: Option) -> ValueRef { - self.count_insn("call"); - - debug!("Call {} with args ({})", - self.ccx.tn().val_to_string(llfn), - args.iter() - .map(|&v| self.ccx.tn().val_to_string(v)) - .collect::>() - .connect(", ")); - - unsafe { - let v = llvm::LLVMBuildCall(self.llbuilder, llfn, args.as_ptr(), - args.len() as c_uint, noname()); - match attributes { - Some(a) => a.apply_callsite(v), - None => {} - } - v - } - } - - pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef], - conv: CallConv, attributes: Option) -> ValueRef { - self.count_insn("callwithconv"); - let v = self.call(llfn, args, attributes); - llvm::SetInstructionCallConv(v, conv); - v - } - - pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { - self.count_insn("select"); - unsafe { - llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) - } - } - - pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { - self.count_insn("vaarg"); - unsafe { - llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) - } - } - - pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("extractelement"); - unsafe { - llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) - } - } - - pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("insertelement"); - unsafe { - llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) - } - } - - pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { - self.count_insn("shufflevector"); - unsafe { - llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) - } - } - - pub fn vector_splat(&self, num_elts: uint, elt: ValueRef) -> ValueRef { - unsafe { - let elt_ty = val_ty(elt); - let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); - let vec = self.insert_element(undef, elt, C_i32(self.ccx, 0)); - let vec_i32_ty = Type::vector(&Type::i32(self.ccx), num_elts as u64); - self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) - } - } - - pub fn extract_value(&self, agg_val: ValueRef, idx: uint) -> ValueRef { - self.count_insn("extractvalue"); - unsafe { - llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) - } - } - - pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, - idx: uint) -> ValueRef { - self.count_insn("insertvalue"); - unsafe { - llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, - noname()) - } - } - - pub fn is_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnull"); - unsafe { - llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) - } - } - - pub fn is_not_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnotnull"); - unsafe { - llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) - } - } - - pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ptrdiff"); - unsafe { - llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn trap(&self) { - unsafe { - let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); - let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); - let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); - let t: ValueRef = "llvm.trap".with_c_str(|buf| { - llvm::LLVMGetNamedFunction(m, buf) - }); - assert!((t as int != 0)); - let args: &[ValueRef] = &[]; - self.count_insn("trap"); - llvm::LLVMBuildCall( - self.llbuilder, t, args.as_ptr(), args.len() as c_uint, noname()); - } - } - - pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, num_clauses: uint) -> ValueRef { - self.count_insn("landingpad"); - unsafe { - llvm::LLVMBuildLandingPad( - self.llbuilder, ty.to_ref(), pers_fn, num_clauses as c_uint, noname()) - } - } - - pub fn set_cleanup(&self, landing_pad: ValueRef) { - self.count_insn("setcleanup"); - unsafe { - llvm::LLVMSetCleanup(landing_pad, llvm::True); - } - } - - pub fn resume(&self, exn: ValueRef) -> ValueRef { - self.count_insn("resume"); - unsafe { - llvm::LLVMBuildResume(self.llbuilder, exn) - } - } - - // Atomic Operations - pub fn atomic_cmpxchg(&self, dst: ValueRef, - cmp: ValueRef, src: ValueRef, - order: AtomicOrdering, - failure_order: AtomicOrdering) -> ValueRef { - unsafe { - llvm::LLVMBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, - order, failure_order) - } - } - pub fn atomic_rmw(&self, op: AtomicBinOp, - dst: ValueRef, src: ValueRef, - order: AtomicOrdering) -> ValueRef { - unsafe { - llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) - } - } - - pub fn atomic_fence(&self, order: AtomicOrdering) { - unsafe { - llvm::LLVMBuildAtomicFence(self.llbuilder, order); - } - } -} diff --git a/src/librustc/middle/trans/cabi.rs b/src/librustc/middle/trans/cabi.rs deleted file mode 100644 index d69238ae92e..00000000000 --- a/src/librustc/middle/trans/cabi.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use self::ArgKind::*; - -use llvm::Attribute; -use std::option; -use middle::trans::context::CrateContext; -use middle::trans::cabi_x86; -use middle::trans::cabi_x86_64; -use middle::trans::cabi_x86_win64; -use middle::trans::cabi_arm; -use middle::trans::cabi_mips; -use middle::trans::type_::Type; - -#[deriving(Clone, PartialEq)] -pub enum ArgKind { - /// Pass the argument directly using the normal converted - /// LLVM type or by coercing to another specified type - Direct, - /// Pass the argument indirectly via a hidden pointer - Indirect, - /// Ignore the argument (useful for empty struct) - Ignore, -} - -/// Information about how a specific C type -/// should be passed to or returned from a function -/// -/// This is borrowed from clang's ABIInfo.h -#[deriving(Clone)] -pub struct ArgType { - pub kind: ArgKind, - /// Original LLVM type - pub ty: Type, - /// Coerced LLVM Type - pub cast: option::Option, - /// Dummy argument, which is emitted before the real argument - pub pad: option::Option, - /// LLVM attribute of argument - pub attr: option::Option -} - -impl ArgType { - pub fn direct(ty: Type, cast: option::Option, - pad: option::Option, - attr: option::Option) -> ArgType { - ArgType { - kind: Direct, - ty: ty, - cast: cast, - pad: pad, - attr: attr - } - } - - pub fn indirect(ty: Type, attr: option::Option) -> ArgType { - ArgType { - kind: Indirect, - ty: ty, - cast: option::None, - pad: option::None, - attr: attr - } - } - - pub fn ignore(ty: Type) -> ArgType { - ArgType { - kind: Ignore, - ty: ty, - cast: None, - pad: None, - attr: None, - } - } - - pub fn is_indirect(&self) -> bool { - return self.kind == Indirect; - } - - pub fn is_ignore(&self) -> bool { - return self.kind == Ignore; - } -} - -/// Metadata describing how the arguments to a native function -/// should be passed in order to respect the native ABI. -/// -/// I will do my best to describe this structure, but these -/// comments are reverse-engineered and may be inaccurate. -NDM -pub struct FnType { - /// The LLVM types of each argument. - pub arg_tys: Vec , - - /// LLVM return type. - pub ret_ty: ArgType, -} - -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - match ccx.sess().target.target.arch.as_slice() { - "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), - "x86_64" => if ccx.sess().target.target.options.is_like_windows { - cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def) - } else { - cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def) - }, - "arm" => cabi_arm::compute_abi_info(ccx, atys, rty, ret_def), - "mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def), - a => ccx.sess().fatal((format!("unrecognized arch \"{}\" in target specification", a)) - .as_slice()), - } -} diff --git a/src/librustc/middle/trans/cabi_arm.rs b/src/librustc/middle/trans/cabi_arm.rs deleted file mode 100644 index 2782970b975..00000000000 --- a/src/librustc/middle/trans/cabi_arm.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; -use llvm::{StructRetAttribute, ZExtAttribute}; -use middle::trans::cabi::{FnType, ArgType}; -use middle::trans::context::CrateContext; -use middle::trans::type_::Type; - -use std::cmp; - -fn align_up_to(off: uint, a: uint) -> uint { - return (off + a - 1u) / a * a; -} - -fn align(off: uint, ty: Type) -> uint { - let a = ty_align(ty); - return align_up_to(off, a); -} - -fn ty_align(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 4, - Float => 4, - Double => 8, - Struct => { - if ty.is_packed() { - 1 - } else { - let str_tys = ty.field_types(); - str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) - } - } - Array => { - let elt = ty.element_type(); - ty_align(elt) - } - _ => panic!("ty_align: unhandled type") - } -} - -fn ty_size(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 4, - Float => 4, - Double => 8, - Struct => { - if ty.is_packed() { - let str_tys = ty.field_types(); - str_tys.iter().fold(0, |s, t| s + ty_size(*t)) - } else { - let str_tys = ty.field_types(); - let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); - align(size, ty) - } - } - Array => { - let len = ty.array_length(); - let elt = ty.element_type(); - let eltsz = ty_size(elt); - len * eltsz - } - _ => panic!("ty_size: unhandled type") - } -} - -fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { - if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - return ArgType::direct(ty, None, None, attr); - } - let size = ty_size(ty); - if size <= 4 { - let llty = if size <= 1 { - Type::i8(ccx) - } else if size <= 2 { - Type::i16(ccx) - } else { - Type::i32(ccx) - }; - return ArgType::direct(ty, Some(llty), None, None); - } - ArgType::indirect(ty, Some(StructRetAttribute)) -} - -fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType { - if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - return ArgType::direct(ty, None, None, attr); - } - let align = ty_align(ty); - let size = ty_size(ty); - let llty = if align <= 4 { - Type::array(&Type::i32(ccx), ((size + 3) / 4) as u64) - } else { - Type::array(&Type::i64(ccx), ((size + 7) / 8) as u64) - }; - ArgType::direct(ty, Some(llty), None, None) -} - -fn is_reg_ty(ty: Type) -> bool { - match ty.kind() { - Integer - | Pointer - | Float - | Double => true, - _ => false - } -} - -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - let mut arg_tys = Vec::new(); - for &aty in atys.iter() { - let ty = classify_arg_ty(ccx, aty); - arg_tys.push(ty); - } - - let ret_ty = if ret_def { - classify_ret_ty(ccx, rty) - } else { - ArgType::direct(Type::void(ccx), None, None, None) - }; - - return FnType { - arg_tys: arg_tys, - ret_ty: ret_ty, - }; -} diff --git a/src/librustc/middle/trans/cabi_mips.rs b/src/librustc/middle/trans/cabi_mips.rs deleted file mode 100644 index 5fe10d2d8e7..00000000000 --- a/src/librustc/middle/trans/cabi_mips.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use libc::c_uint; -use std::cmp; -use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; -use llvm::{StructRetAttribute, ZExtAttribute}; -use middle::trans::cabi::{ArgType, FnType}; -use middle::trans::context::CrateContext; -use middle::trans::type_::Type; - -fn align_up_to(off: uint, a: uint) -> uint { - return (off + a - 1u) / a * a; -} - -fn align(off: uint, ty: Type) -> uint { - let a = ty_align(ty); - return align_up_to(off, a); -} - -fn ty_align(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 4, - Float => 4, - Double => 8, - Struct => { - if ty.is_packed() { - 1 - } else { - let str_tys = ty.field_types(); - str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) - } - } - Array => { - let elt = ty.element_type(); - ty_align(elt) - } - _ => panic!("ty_size: unhandled type") - } -} - -fn ty_size(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 4, - Float => 4, - Double => 8, - Struct => { - if ty.is_packed() { - let str_tys = ty.field_types(); - str_tys.iter().fold(0, |s, t| s + ty_size(*t)) - } else { - let str_tys = ty.field_types(); - let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); - align(size, ty) - } - } - Array => { - let len = ty.array_length(); - let elt = ty.element_type(); - let eltsz = ty_size(elt); - len * eltsz - } - _ => panic!("ty_size: unhandled type") - } -} - -fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { - if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ArgType::direct(ty, None, None, attr) - } else { - ArgType::indirect(ty, Some(StructRetAttribute)) - } -} - -fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut uint) -> ArgType { - let orig_offset = *offset; - let size = ty_size(ty) * 8; - let mut align = ty_align(ty); - - align = cmp::min(cmp::max(align, 4), 8); - *offset = align_up_to(*offset, align); - *offset += align_up_to(size, align * 8) / 8; - - if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ArgType::direct(ty, None, None, attr) - } else { - ArgType::direct( - ty, - Some(struct_ty(ccx, ty)), - padding_ty(ccx, align, orig_offset), - None - ) - } -} - -fn is_reg_ty(ty: Type) -> bool { - return match ty.kind() { - Integer - | Pointer - | Float - | Double => true, - _ => false - }; -} - -fn padding_ty(ccx: &CrateContext, align: uint, offset: uint) -> Option { - if ((align - 1 ) & offset) > 0 { - Some(Type::i32(ccx)) - } else { - None - } -} - -fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec { - let int_ty = Type::i32(ccx); - let mut args = Vec::new(); - - let mut n = size / 32; - while n > 0 { - args.push(int_ty); - n -= 1; - } - - let r = size % 32; - if r > 0 { - unsafe { - args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint))); - } - } - - args -} - -fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { - let size = ty_size(ty) * 8; - Type::struct_(ccx, coerce_to_int(ccx, size).as_slice(), false) -} - -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - let ret_ty = if ret_def { - classify_ret_ty(ccx, rty) - } else { - ArgType::direct(Type::void(ccx), None, None, None) - }; - - let sret = ret_ty.is_indirect(); - let mut arg_tys = Vec::new(); - let mut offset = if sret { 4 } else { 0 }; - - for aty in atys.iter() { - let ty = classify_arg_ty(ccx, *aty, &mut offset); - arg_tys.push(ty); - }; - - return FnType { - arg_tys: arg_tys, - ret_ty: ret_ty, - }; -} diff --git a/src/librustc/middle/trans/cabi_x86.rs b/src/librustc/middle/trans/cabi_x86.rs deleted file mode 100644 index f779ef3d311..00000000000 --- a/src/librustc/middle/trans/cabi_x86.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::Strategy::*; -use llvm::*; -use middle::trans::cabi::{ArgType, FnType}; -use middle::trans::type_::Type; -use super::common::*; -use super::machine::*; - -enum Strategy { RetValue(Type), RetPointer } -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - let mut arg_tys = Vec::new(); - - let ret_ty; - if !ret_def { - ret_ty = ArgType::direct(Type::void(ccx), None, None, None); - } else if rty.kind() == Struct { - // Returning a structure. Most often, this will use - // a hidden first argument. On some platforms, though, - // small structs are returned as integers. - // - // Some links: - // http://www.angelcode.com/dev/callconv/callconv.html - // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp - - let t = &ccx.sess().target.target; - let strategy = if t.options.is_like_osx || t.options.is_like_windows { - match llsize_of_alloc(ccx, rty) { - 1 => RetValue(Type::i8(ccx)), - 2 => RetValue(Type::i16(ccx)), - 4 => RetValue(Type::i32(ccx)), - 8 => RetValue(Type::i64(ccx)), - _ => RetPointer - } - } else { - RetPointer - }; - - match strategy { - RetValue(t) => { - ret_ty = ArgType::direct(rty, Some(t), None, None); - } - RetPointer => { - ret_ty = ArgType::indirect(rty, Some(StructRetAttribute)); - } - } - } else { - let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ret_ty = ArgType::direct(rty, None, None, attr); - } - - for &t in atys.iter() { - let ty = match t.kind() { - Struct => { - let size = llsize_of_alloc(ccx, t); - if size == 0 { - ArgType::ignore(t) - } else { - ArgType::indirect(t, Some(ByValAttribute)) - } - } - _ => { - let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ArgType::direct(t, None, None, attr) - } - }; - arg_tys.push(ty); - } - - return FnType { - arg_tys: arg_tys, - ret_ty: ret_ty, - }; -} diff --git a/src/librustc/middle/trans/cabi_x86_64.rs b/src/librustc/middle/trans/cabi_x86_64.rs deleted file mode 100644 index cb235c015ff..00000000000 --- a/src/librustc/middle/trans/cabi_x86_64.rs +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// The classification code for the x86_64 ABI is taken from the clay language -// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp - -#![allow(non_upper_case_globals)] -use self::RegClass::*; - -use llvm; -use llvm::{Integer, Pointer, Float, Double}; -use llvm::{Struct, Array, Attribute}; -use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; -use middle::trans::cabi::{ArgType, FnType}; -use middle::trans::context::CrateContext; -use middle::trans::type_::Type; - -use std::cmp; - -#[deriving(Clone, PartialEq)] -enum RegClass { - NoClass, - Int, - SSEFs, - SSEFv, - SSEDs, - SSEDv, - SSEInt, - SSEUp, - X87, - X87Up, - ComplexX87, - Memory -} - -trait TypeMethods { - fn is_reg_ty(&self) -> bool; -} - -impl TypeMethods for Type { - fn is_reg_ty(&self) -> bool { - match self.kind() { - Integer | Pointer | Float | Double => true, - _ => false - } - } -} - -impl RegClass { - fn is_sse(&self) -> bool { - match *self { - SSEFs | SSEFv | SSEDs | SSEDv => true, - _ => false - } - } -} - -trait ClassList for Sized? { - fn is_pass_byval(&self) -> bool; - fn is_ret_bysret(&self) -> bool; -} - -impl ClassList for [RegClass] { - fn is_pass_byval(&self) -> bool { - if self.len() == 0 { return false; } - - let class = self[0]; - class == Memory - || class == X87 - || class == ComplexX87 - } - - fn is_ret_bysret(&self) -> bool { - if self.len() == 0 { return false; } - - self[0] == Memory - } -} - -fn classify_ty(ty: Type) -> Vec { - fn align(off: uint, ty: Type) -> uint { - let a = ty_align(ty); - return (off + a - 1u) / a * a; - } - - fn ty_align(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 8, - Float => 4, - Double => 8, - Struct => { - if ty.is_packed() { - 1 - } else { - let str_tys = ty.field_types(); - str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) - } - } - Array => { - let elt = ty.element_type(); - ty_align(elt) - } - _ => panic!("ty_size: unhandled type") - } - } - - fn ty_size(ty: Type) -> uint { - match ty.kind() { - Integer => { - unsafe { - ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 - } - } - Pointer => 8, - Float => 4, - Double => 8, - Struct => { - let str_tys = ty.field_types(); - if ty.is_packed() { - str_tys.iter().fold(0, |s, t| s + ty_size(*t)) - } else { - let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); - align(size, ty) - } - } - Array => { - let len = ty.array_length(); - let elt = ty.element_type(); - let eltsz = ty_size(elt); - len * eltsz - } - _ => panic!("ty_size: unhandled type") - } - } - - fn all_mem(cls: &mut [RegClass]) { - for elt in cls.iter_mut() { - *elt = Memory; - } - } - - fn unify(cls: &mut [RegClass], - i: uint, - newv: RegClass) { - if cls[i] == newv { - return; - } else if cls[i] == NoClass { - cls[i] = newv; - } else if newv == NoClass { - return; - } else if cls[i] == Memory || newv == Memory { - cls[i] = Memory; - } else if cls[i] == Int || newv == Int { - cls[i] = Int; - } else if cls[i] == X87 || - cls[i] == X87Up || - cls[i] == ComplexX87 || - newv == X87 || - newv == X87Up || - newv == ComplexX87 { - cls[i] = Memory; - } else { - cls[i] = newv; - } - } - - fn classify_struct(tys: &[Type], - cls: &mut [RegClass], - i: uint, - off: uint, - packed: bool) { - let mut field_off = off; - for ty in tys.iter() { - if !packed { - field_off = align(field_off, *ty); - } - classify(*ty, cls, i, field_off); - field_off += ty_size(*ty); - } - } - - fn classify(ty: Type, - cls: &mut [RegClass], ix: uint, - off: uint) { - let t_align = ty_align(ty); - let t_size = ty_size(ty); - - let misalign = off % t_align; - if misalign != 0u { - let mut i = off / 8u; - let e = (off + t_size + 7u) / 8u; - while i < e { - unify(cls, ix + i, Memory); - i += 1u; - } - return; - } - - match ty.kind() { - Integer | - Pointer => { - unify(cls, ix + off / 8u, Int); - } - Float => { - if off % 8u == 4u { - unify(cls, ix + off / 8u, SSEFv); - } else { - unify(cls, ix + off / 8u, SSEFs); - } - } - Double => { - unify(cls, ix + off / 8u, SSEDs); - } - Struct => { - classify_struct(ty.field_types().as_slice(), cls, ix, off, ty.is_packed()); - } - Array => { - let len = ty.array_length(); - let elt = ty.element_type(); - let eltsz = ty_size(elt); - let mut i = 0u; - while i < len { - classify(elt, cls, ix, off + i * eltsz); - i += 1u; - } - } - _ => panic!("classify: unhandled type") - } - } - - fn fixup(ty: Type, cls: &mut [RegClass]) { - let mut i = 0u; - let ty_kind = ty.kind(); - let e = cls.len(); - if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array) { - if cls[i].is_sse() { - i += 1u; - while i < e { - if cls[i] != SSEUp { - all_mem(cls); - return; - } - i += 1u; - } - } else { - all_mem(cls); - return - } - } else { - while i < e { - if cls[i] == Memory { - all_mem(cls); - return; - } - if cls[i] == X87Up { - // for darwin - // cls[i] = SSEDs; - all_mem(cls); - return; - } - if cls[i] == SSEUp { - cls[i] = SSEDv; - } else if cls[i].is_sse() { - i += 1; - while i != e && cls[i] == SSEUp { i += 1u; } - } else if cls[i] == X87 { - i += 1; - while i != e && cls[i] == X87Up { i += 1u; } - } else { - i += 1; - } - } - } - } - - let words = (ty_size(ty) + 7) / 8; - let mut cls = Vec::from_elem(words, NoClass); - if words > 4 { - all_mem(cls.as_mut_slice()); - return cls; - } - classify(ty, cls.as_mut_slice(), 0, 0); - fixup(ty, cls.as_mut_slice()); - return cls; -} - -fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { - fn llvec_len(cls: &[RegClass]) -> uint { - let mut len = 1u; - for c in cls.iter() { - if *c != SSEUp { - break; - } - len += 1u; - } - return len; - } - - let mut tys = Vec::new(); - let mut i = 0u; - let e = cls.len(); - while i < e { - match cls[i] { - Int => { - tys.push(Type::i64(ccx)); - } - SSEFv => { - let vec_len = llvec_len(cls[i + 1u..]); - let vec_ty = Type::vector(&Type::f32(ccx), (vec_len * 2u) as u64); - tys.push(vec_ty); - i += vec_len; - continue; - } - SSEFs => { - tys.push(Type::f32(ccx)); - } - SSEDs => { - tys.push(Type::f64(ccx)); - } - _ => panic!("llregtype: unhandled class") - } - i += 1u; - } - return Type::struct_(ccx, tys.as_slice(), false); -} - -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - fn x86_64_ty(ccx: &CrateContext, - ty: Type, - is_mem_cls: |cls: &[RegClass]| -> bool, - ind_attr: Attribute) - -> ArgType { - if !ty.is_reg_ty() { - let cls = classify_ty(ty); - if is_mem_cls(cls.as_slice()) { - ArgType::indirect(ty, Some(ind_attr)) - } else { - ArgType::direct(ty, - Some(llreg_ty(ccx, cls.as_slice())), - None, - None) - } - } else { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ArgType::direct(ty, None, None, attr) - } - } - - let mut arg_tys = Vec::new(); - for t in atys.iter() { - let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), ByValAttribute); - arg_tys.push(ty); - } - - let ret_ty = if ret_def { - x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), StructRetAttribute) - } else { - ArgType::direct(Type::void(ccx), None, None, None) - }; - - return FnType { - arg_tys: arg_tys, - ret_ty: ret_ty, - }; -} diff --git a/src/librustc/middle/trans/cabi_x86_win64.rs b/src/librustc/middle/trans/cabi_x86_win64.rs deleted file mode 100644 index 6bcd9aefcc6..00000000000 --- a/src/librustc/middle/trans/cabi_x86_win64.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::*; -use super::common::*; -use super::machine::*; -use middle::trans::cabi::{ArgType, FnType}; -use middle::trans::type_::Type; - -// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx - -pub fn compute_abi_info(ccx: &CrateContext, - atys: &[Type], - rty: Type, - ret_def: bool) -> FnType { - let mut arg_tys = Vec::new(); - - let ret_ty; - if !ret_def { - ret_ty = ArgType::direct(Type::void(ccx), None, None, None); - } else if rty.kind() == Struct { - ret_ty = match llsize_of_alloc(ccx, rty) { - 1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None), - 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), - 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), - 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), - _ => ArgType::indirect(rty, Some(StructRetAttribute)) - }; - } else { - let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ret_ty = ArgType::direct(rty, None, None, attr); - } - - for &t in atys.iter() { - let ty = match t.kind() { - Struct => { - match llsize_of_alloc(ccx, t) { - 1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None), - 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), - 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), - 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), - _ => ArgType::indirect(t, Some(ByValAttribute)) - } - } - _ => { - let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; - ArgType::direct(t, None, None, attr) - } - }; - arg_tys.push(ty); - } - - return FnType { - arg_tys: arg_tys, - ret_ty: ret_ty, - }; -} diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs deleted file mode 100644 index 956942068d9..00000000000 --- a/src/librustc/middle/trans/callee.rs +++ /dev/null @@ -1,1167 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * Handles translation of callees as well as other call-related - * things. Callees are a superset of normal rust values and sometimes - * have different representations. In particular, top-level fn items - * and methods are represented as just a fn ptr and not a full - * closure. - */ - -pub use self::AutorefArg::*; -pub use self::CalleeData::*; -pub use self::CallArgs::*; - -use arena::TypedArena; -use back::abi; -use back::link; -use driver::session; -use llvm::{ValueRef, get_param}; -use llvm; -use metadata::csearch; -use middle::def; -use middle::subst; -use middle::subst::{Subst}; -use middle::trans::adt; -use middle::trans::base; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::closure; -use middle::trans::common; -use middle::trans::common::*; -use middle::trans::datum::*; -use middle::trans::expr; -use middle::trans::glue; -use middle::trans::inline; -use middle::trans::foreign; -use middle::trans::intrinsic; -use middle::trans::meth; -use middle::trans::monomorphize; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::ty; -use middle::typeck::coherence::make_substs_for_receiver_types; -use middle::typeck::MethodCall; -use util::ppaux::Repr; -use util::ppaux::ty_to_string; - -use syntax::abi as synabi; -use syntax::ast; -use syntax::ast_map; -use syntax::ptr::P; - -pub struct MethodData { - pub llfn: ValueRef, - pub llself: ValueRef, -} - -pub enum CalleeData { - Closure(Datum), - - // Constructor for enum variant/tuple-like-struct - // i.e. Some, Ok - NamedTupleConstructor(subst::Substs, ty::Disr), - - // Represents a (possibly monomorphized) top-level fn item or method - // item. Note that this is just the fn-ptr and is not a Rust closure - // value (which is a pair). - Fn(/* llfn */ ValueRef), - - Intrinsic(ast::NodeId, subst::Substs), - - TraitItem(MethodData) -} - -pub struct Callee<'blk, 'tcx: 'blk> { - pub bcx: Block<'blk, 'tcx>, - pub data: CalleeData, -} - -fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) - -> Callee<'blk, 'tcx> { - let _icx = push_ctxt("trans_callee"); - debug!("callee::trans(expr={})", expr.repr(bcx.tcx())); - - // pick out special kinds of expressions that can be called: - match expr.node { - ast::ExprPath(_) => { - return trans_def(bcx, bcx.def(expr.id), expr); - } - _ => {} - } - - // any other expressions are closures: - return datum_callee(bcx, expr); - - fn datum_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) - -> Callee<'blk, 'tcx> { - let DatumBlock {mut bcx, datum} = expr::trans(bcx, expr); - match ty::get(datum.ty).sty { - ty::ty_bare_fn(..) => { - let llval = datum.to_llscalarish(bcx); - return Callee { - bcx: bcx, - data: Fn(llval), - }; - } - ty::ty_closure(..) => { - let datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "callee", expr.id)); - return Callee { - bcx: bcx, - data: Closure(datum), - }; - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("type of callee is neither bare-fn nor closure: \ - {}", - bcx.ty_to_string(datum.ty)).as_slice()); - } - } - } - - fn fn_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, llfn: ValueRef) - -> Callee<'blk, 'tcx> { - return Callee { - bcx: bcx, - data: Fn(llfn), - }; - } - - fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - def: def::Def, - ref_expr: &ast::Expr) - -> Callee<'blk, 'tcx> { - debug!("trans_def(def={}, ref_expr={})", def.repr(bcx.tcx()), ref_expr.repr(bcx.tcx())); - let expr_ty = node_id_type(bcx, ref_expr.id); - match def { - def::DefFn(did, _) if { - let maybe_def_id = inline::get_local_instance(bcx.ccx(), did); - let maybe_ast_node = maybe_def_id.and_then(|def_id| bcx.tcx().map - .find(def_id.node)); - match maybe_ast_node { - Some(ast_map::NodeStructCtor(_)) => true, - _ => false - } - } => { - let substs = node_id_substs(bcx, ExprId(ref_expr.id)); - Callee { - bcx: bcx, - data: NamedTupleConstructor(substs, 0) - } - } - def::DefFn(did, _) if match ty::get(expr_ty).sty { - ty::ty_bare_fn(ref f) => f.abi == synabi::RustIntrinsic, - _ => false - } => { - let substs = node_id_substs(bcx, ExprId(ref_expr.id)); - let def_id = inline::maybe_instantiate_inline(bcx.ccx(), did); - Callee { bcx: bcx, data: Intrinsic(def_id.node, substs) } - } - def::DefFn(did, _) | def::DefMethod(did, _, def::FromImpl(_)) | - def::DefStaticMethod(did, def::FromImpl(_)) => { - fn_callee(bcx, trans_fn_ref(bcx, did, ExprId(ref_expr.id))) - } - def::DefStaticMethod(meth_did, def::FromTrait(trait_did)) | - def::DefMethod(meth_did, _, def::FromTrait(trait_did)) => { - fn_callee(bcx, meth::trans_static_method_callee(bcx, meth_did, - trait_did, - ref_expr.id)) - } - def::DefVariant(tid, vid, _) => { - let vinfo = ty::enum_variant_with_id(bcx.tcx(), tid, vid); - let substs = node_id_substs(bcx, ExprId(ref_expr.id)); - - // Nullary variants are not callable - assert!(vinfo.args.len() > 0u); - - Callee { - bcx: bcx, - data: NamedTupleConstructor(substs, vinfo.disr_val) - } - } - def::DefStruct(_) => { - let substs = node_id_substs(bcx, ExprId(ref_expr.id)); - Callee { - bcx: bcx, - data: NamedTupleConstructor(substs, 0) - } - } - def::DefStatic(..) | - def::DefConst(..) | - def::DefLocal(..) | - def::DefUpvar(..) => { - datum_callee(bcx, ref_expr) - } - def::DefMod(..) | def::DefForeignMod(..) | def::DefTrait(..) | - def::DefTy(..) | def::DefPrimTy(..) | def::DefAssociatedTy(..) | - def::DefUse(..) | def::DefTyParamBinder(..) | - def::DefRegion(..) | def::DefLabel(..) | def::DefTyParam(..) | - def::DefSelfTy(..) => { - bcx.tcx().sess.span_bug( - ref_expr.span, - format!("cannot translate def {} \ - to a callable thing!", def).as_slice()); - } - } - } -} - -pub fn trans_fn_ref(bcx: Block, def_id: ast::DefId, node: ExprOrMethodCall) -> ValueRef { - /*! - * Translates a reference (with id `ref_id`) to the fn/method - * with id `def_id` into a function pointer. This may require - * monomorphization or inlining. - */ - - let _icx = push_ctxt("trans_fn_ref"); - - let substs = node_id_substs(bcx, node); - debug!("trans_fn_ref(def_id={}, node={}, substs={})", - def_id.repr(bcx.tcx()), - node, - substs.repr(bcx.tcx())); - trans_fn_ref_with_substs(bcx, def_id, node, substs) -} - -fn trans_fn_ref_with_substs_to_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - def_id: ast::DefId, - ref_id: ast::NodeId, - substs: subst::Substs) - -> Callee<'blk, 'tcx> { - Callee { - bcx: bcx, - data: Fn(trans_fn_ref_with_substs(bcx, - def_id, - ExprId(ref_id), - substs)), - } -} - -/// Translates the adapter that deconstructs a `Box` object into -/// `Trait` so that a by-value self method can be called. -pub fn trans_unboxing_shim(bcx: Block, - llshimmedfn: ValueRef, - fty: &ty::BareFnTy, - method_id: ast::DefId, - substs: &subst::Substs) - -> ValueRef { - let _icx = push_ctxt("trans_unboxing_shim"); - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - - let fty = fty.subst(tcx, substs); - - // Transform the self type to `Box`. - let self_type = fty.sig.inputs[0]; - let boxed_self_type = ty::mk_uniq(tcx, self_type); - let boxed_function_type = ty::FnSig { - binder_id: fty.sig.binder_id, - inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| { - if i == 0 { - boxed_self_type - } else { - *typ - } - }).collect(), - output: fty.sig.output, - variadic: false, - }; - let boxed_function_type = ty::BareFnTy { - fn_style: fty.fn_style, - abi: fty.abi, - sig: boxed_function_type, - }; - let boxed_function_type = ty::mk_bare_fn(tcx, boxed_function_type); - let function_type = match fty.abi { - synabi::RustCall => { - // We're passing through to a RustCall ABI function, but - // because the shim will already perform untupling, we - // need to pretend the shimmed function does not use - // RustCall so the untupled arguments can be passed - // through verbatim. This is kind of ugly. - let fake_ty = ty::FnSig { - binder_id: fty.sig.binder_id, - inputs: type_of::untuple_arguments_if_necessary(ccx, - fty.sig.inputs.as_slice(), - fty.abi), - output: fty.sig.output, - variadic: false, - }; - let fake_ty = ty::BareFnTy { - fn_style: fty.fn_style, - abi: synabi::Rust, - sig: fake_ty, - }; - ty::mk_bare_fn(tcx, fake_ty) - } - _ => { - ty::mk_bare_fn(tcx, fty) - } - }; - - let function_name = ty::with_path(tcx, method_id, |path| { - link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim") - }); - let llfn = decl_internal_rust_fn(ccx, - boxed_function_type, - function_name.as_slice()); - - let block_arena = TypedArena::new(); - let empty_param_substs = param_substs::empty(); - let return_type = ty::ty_fn_ret(boxed_function_type); - let fcx = new_fn_ctxt(ccx, - llfn, - ast::DUMMY_NODE_ID, - false, - return_type, - &empty_param_substs, - None, - &block_arena); - let mut bcx = init_function(&fcx, false, return_type); - - // Create the substituted versions of the self type. - let arg_scope = fcx.push_custom_cleanup_scope(); - let arg_scope_id = cleanup::CustomScope(arg_scope); - let boxed_arg_types = ty::ty_fn_args(boxed_function_type); - let boxed_self_type = boxed_arg_types[0]; - let arg_types = ty::ty_fn_args(function_type); - let self_type = arg_types[0]; - let boxed_self_kind = arg_kind(&fcx, boxed_self_type); - - // Create a datum for self. - let llboxedself = get_param(fcx.llfn, fcx.arg_pos(0) as u32); - let llboxedself = Datum::new(llboxedself, - boxed_self_type, - boxed_self_kind); - let boxed_self = - unpack_datum!(bcx, - llboxedself.to_lvalue_datum_in_scope(bcx, - "boxedself", - arg_scope_id)); - - // This `Load` is needed because lvalue data are always by-ref. - let llboxedself = Load(bcx, boxed_self.val); - - let llself = if type_is_immediate(ccx, self_type) { - let llboxedself = Load(bcx, llboxedself); - immediate_rvalue(llboxedself, self_type) - } else { - let llself = rvalue_scratch_datum(bcx, self_type, "self"); - memcpy_ty(bcx, llself.val, llboxedself, self_type); - llself - }; - - // Make sure we don't free the box twice! - boxed_self.kind.post_store(bcx, boxed_self.val, boxed_self_type); - - // Schedule a cleanup to free the box. - fcx.schedule_free_value(arg_scope_id, - llboxedself, - cleanup::HeapExchange, - self_type); - - // Now call the function. - let mut llshimmedargs = vec!(llself.val); - for i in range(1, arg_types.len()) { - llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32)); - } - assert!(!fcx.needs_ret_allocas); - let dest = fcx.llretslotptr.get().map(|_| - expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot")) - ); - bcx = trans_call_inner(bcx, - None, - function_type, - |bcx, _| { - Callee { - bcx: bcx, - data: Fn(llshimmedfn), - } - }, - ArgVals(llshimmedargs.as_slice()), - dest).bcx; - - bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope); - finish_fn(&fcx, bcx, return_type); - - llfn -} - -pub fn trans_fn_ref_with_substs( - bcx: Block, // - def_id: ast::DefId, // def id of fn - node: ExprOrMethodCall, // node id of use of fn; may be zero if N/A - substs: subst::Substs) // vtables for the call - -> ValueRef -{ - /*! - * Translates a reference to a fn/method item, monomorphizing and - * inlining as it goes. - * - * # Parameters - * - * - `bcx`: the current block where the reference to the fn occurs - * - `def_id`: def id of the fn or method item being referenced - * - `node`: node id of the reference to the fn/method, if applicable. - * This parameter may be zero; but, if so, the resulting value may not - * have the right type, so it must be cast before being used. - * - `substs`: values for each of the fn/method's parameters - */ - - let _icx = push_ctxt("trans_fn_ref_with_substs"); - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - - debug!("trans_fn_ref_with_substs(bcx={}, def_id={}, node={}, \ - substs={})", - bcx.to_str(), - def_id.repr(tcx), - node, - substs.repr(tcx)); - - assert!(substs.types.all(|t| !ty::type_needs_infer(*t))); - - // Load the info for the appropriate trait if necessary. - match ty::trait_of_item(tcx, def_id) { - None => {} - Some(trait_id) => { - ty::populate_implementations_for_trait_if_necessary(tcx, trait_id) - } - } - - // We need to do a bunch of special handling for default methods. - // We need to modify the def_id and our substs in order to monomorphize - // the function. - let (is_default, def_id, substs) = match ty::provided_source(tcx, def_id) { - None => (false, def_id, substs), - Some(source_id) => { - // There are two relevant substitutions when compiling - // default methods. First, there is the substitution for - // the type parameters of the impl we are using and the - // method we are calling. This substitution is the substs - // argument we already have. - // In order to compile a default method, though, we need - // to consider another substitution: the substitution for - // the type parameters on trait; the impl we are using - // implements the trait at some particular type - // parameters, and we need to substitute for those first. - // So, what we need to do is find this substitution and - // compose it with the one we already have. - - let impl_id = ty::impl_or_trait_item(tcx, def_id).container() - .id(); - let impl_or_trait_item = ty::impl_or_trait_item(tcx, source_id); - match impl_or_trait_item { - ty::MethodTraitItem(method) => { - let trait_ref = ty::impl_trait_ref(tcx, impl_id) - .expect("could not find trait_ref for impl with \ - default methods"); - - // Compute the first substitution - let first_subst = make_substs_for_receiver_types( - tcx, &*trait_ref, &*method); - - // And compose them - let new_substs = first_subst.subst(tcx, &substs); - - debug!("trans_fn_with_vtables - default method: \ - substs = {}, trait_subst = {}, \ - first_subst = {}, new_subst = {}", - substs.repr(tcx), trait_ref.substs.repr(tcx), - first_subst.repr(tcx), new_substs.repr(tcx)); - - (true, source_id, new_substs) - } - ty::TypeTraitItem(_) => { - bcx.tcx().sess.bug("trans_fn_ref_with_vtables() tried \ - to translate an associated type?!") - } - } - } - }; - - // If this is an unboxed closure, redirect to it. - match closure::get_or_create_declaration_if_unboxed_closure(bcx, - def_id, - &substs) { - None => {} - Some(llfn) => return llfn, - } - - // Check whether this fn has an inlined copy and, if so, redirect - // def_id to the local id of the inlined copy. - let def_id = inline::maybe_instantiate_inline(ccx, def_id); - - // We must monomorphise if the fn has type parameters, is a default method, - // or is a named tuple constructor. - let must_monomorphise = if !substs.types.is_empty() || is_default { - true - } else if def_id.krate == ast::LOCAL_CRATE { - let map_node = session::expect( - ccx.sess(), - tcx.map.find(def_id.node), - || "local item should be in ast map".to_string()); - - match map_node { - ast_map::NodeVariant(v) => match v.node.kind { - ast::TupleVariantKind(ref args) => args.len() > 0, - _ => false - }, - ast_map::NodeStructCtor(_) => true, - _ => false - } - } else { - false - }; - - // Create a monomorphic version of generic functions - if must_monomorphise { - // Should be either intra-crate or inlined. - assert_eq!(def_id.krate, ast::LOCAL_CRATE); - - let opt_ref_id = match node { - ExprId(id) => if id != 0 { Some(id) } else { None }, - MethodCall(_) => None, - }; - - let (val, must_cast) = - monomorphize::monomorphic_fn(ccx, def_id, &substs, opt_ref_id); - let mut val = val; - if must_cast && node != ExprId(0) { - // Monotype of the REFERENCE to the function (type params - // are subst'd) - let ref_ty = match node { - ExprId(id) => node_id_type(bcx, id), - MethodCall(method_call) => { - let t = (*bcx.tcx().method_map.borrow())[method_call].ty; - monomorphize_type(bcx, t) - } - }; - - val = PointerCast( - bcx, val, type_of::type_of_fn_from_ty(ccx, ref_ty).ptr_to()); - } - return val; - } - - // Polytype of the function item (may have type params) - let fn_tpt = ty::lookup_item_type(tcx, def_id); - - // Find the actual function pointer. - let mut val = { - if def_id.krate == ast::LOCAL_CRATE { - // Internal reference. - get_item_val(ccx, def_id.node) - } else { - // External reference. - trans_external_path(ccx, def_id, fn_tpt.ty) - } - }; - - // This is subtle and surprising, but sometimes we have to bitcast - // the resulting fn pointer. The reason has to do with external - // functions. If you have two crates that both bind the same C - // library, they may not use precisely the same types: for - // example, they will probably each declare their own structs, - // which are distinct types from LLVM's point of view (nominal - // types). - // - // Now, if those two crates are linked into an application, and - // they contain inlined code, you can wind up with a situation - // where both of those functions wind up being loaded into this - // application simultaneously. In that case, the same function - // (from LLVM's point of view) requires two types. But of course - // LLVM won't allow one function to have two types. - // - // What we currently do, therefore, is declare the function with - // one of the two types (whichever happens to come first) and then - // bitcast as needed when the function is referenced to make sure - // it has the type we expect. - // - // This can occur on either a crate-local or crate-external - // reference. It also occurs when testing libcore and in some - // other weird situations. Annoying. - let llty = type_of::type_of_fn_from_ty(ccx, fn_tpt.ty); - let llptrty = llty.ptr_to(); - if val_ty(val) != llptrty { - debug!("trans_fn_ref_with_vtables(): casting pointer!"); - val = BitCast(bcx, val, llptrty); - } else { - debug!("trans_fn_ref_with_vtables(): not casting pointer!"); - } - - val -} - -// ______________________________________________________________________ -// Translating calls - -pub fn trans_call<'blk, 'tcx>(in_cx: Block<'blk, 'tcx>, - call_ex: &ast::Expr, - f: &ast::Expr, - args: CallArgs, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_call"); - trans_call_inner(in_cx, - Some(common::expr_info(call_ex)), - expr_ty(in_cx, f), - |cx, _| trans(cx, f), - args, - Some(dest)).bcx -} - -pub fn trans_method_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - call_ex: &ast::Expr, - rcvr: &ast::Expr, - args: CallArgs, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_method_call"); - debug!("trans_method_call(call_ex={})", call_ex.repr(bcx.tcx())); - let method_call = MethodCall::expr(call_ex.id); - let method_ty = (*bcx.tcx().method_map.borrow())[method_call].ty; - trans_call_inner( - bcx, - Some(common::expr_info(call_ex)), - monomorphize_type(bcx, method_ty), - |cx, arg_cleanup_scope| { - meth::trans_method_callee(cx, method_call, Some(rcvr), arg_cleanup_scope) - }, - args, - Some(dest)).bcx -} - -pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - did: ast::DefId, - args: &[ValueRef], - dest: Option) - -> Result<'blk, 'tcx> { - let fty = if did.krate == ast::LOCAL_CRATE { - ty::node_id_to_type(bcx.tcx(), did.node) - } else { - csearch::get_type(bcx.tcx(), did).ty - }; - callee::trans_call_inner(bcx, - None, - fty, - |bcx, _| { - trans_fn_ref_with_substs_to_callee(bcx, - did, - 0, - subst::Substs::empty()) - }, - ArgVals(args), - dest) -} - -pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - call_info: Option, - callee_ty: ty::t, - get_callee: |bcx: Block<'blk, 'tcx>, - arg_cleanup_scope: cleanup::ScopeId| - -> Callee<'blk, 'tcx>, - args: CallArgs, - dest: Option) - -> Result<'blk, 'tcx> { - /*! - * This behemoth of a function translates function calls. - * Unfortunately, in order to generate more efficient LLVM - * output at -O0, it has quite a complex signature (refactoring - * this into two functions seems like a good idea). - * - * In particular, for lang items, it is invoked with a dest of - * None, and in that case the return value contains the result of - * the fn. The lang item must not return a structural type or else - * all heck breaks loose. - * - * For non-lang items, `dest` is always Some, and hence the result - * is written into memory somewhere. Nonetheless we return the - * actual return value of the function. - */ - - // Introduce a temporary cleanup scope that will contain cleanups - // for the arguments while they are being evaluated. The purpose - // this cleanup is to ensure that, should a panic occur while - // evaluating argument N, the values for arguments 0...N-1 are all - // cleaned up. If no panic occurs, the values are handed off to - // the callee, and hence none of the cleanups in this temporary - // scope will ever execute. - let fcx = bcx.fcx; - let ccx = fcx.ccx; - let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); - - let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope)); - let mut bcx = callee.bcx; - - let (abi, ret_ty) = match ty::get(callee_ty).sty { - ty::ty_bare_fn(ref f) => (f.abi, f.sig.output), - ty::ty_closure(ref f) => (f.abi, f.sig.output), - _ => panic!("expected bare rust fn or closure in trans_call_inner") - }; - - let (llfn, llenv, llself) = match callee.data { - Fn(llfn) => { - (llfn, None, None) - } - TraitItem(d) => { - (d.llfn, None, Some(d.llself)) - } - Closure(d) => { - // Closures are represented as (llfn, llclosure) pair: - // load the requisite values out. - let pair = d.to_llref(); - let llfn = GEPi(bcx, pair, &[0u, abi::fn_field_code]); - let llfn = Load(bcx, llfn); - let llenv = GEPi(bcx, pair, &[0u, abi::fn_field_box]); - let llenv = Load(bcx, llenv); - (llfn, Some(llenv), None) - } - Intrinsic(node, substs) => { - assert!(abi == synabi::RustIntrinsic); - assert!(dest.is_some()); - - let call_info = call_info.expect("no call info for intrinsic call?"); - return intrinsic::trans_intrinsic_call(bcx, node, callee_ty, - arg_cleanup_scope, args, - dest.unwrap(), substs, - call_info); - } - NamedTupleConstructor(substs, disr) => { - assert!(dest.is_some()); - fcx.pop_custom_cleanup_scope(arg_cleanup_scope); - - let ctor_ty = callee_ty.subst(bcx.tcx(), &substs); - return base::trans_named_tuple_constructor(bcx, - ctor_ty, - disr, - args, - dest.unwrap(), - call_info); - } - }; - - // Intrinsics should not become actual functions. - // We trans them in place in `trans_intrinsic_call` - assert!(abi != synabi::RustIntrinsic); - - let is_rust_fn = abi == synabi::Rust || abi == synabi::RustCall; - - // Generate a location to store the result. If the user does - // not care about the result, just make a stack slot. - let opt_llretslot = dest.and_then(|dest| match dest { - expr::SaveIn(dst) => Some(dst), - expr::Ignore => { - let ret_ty = match ret_ty { - ty::FnConverging(ret_ty) => ret_ty, - ty::FnDiverging => ty::mk_nil(ccx.tcx()) - }; - if !is_rust_fn || - type_of::return_uses_outptr(ccx, ret_ty) || - ty::type_needs_drop(bcx.tcx(), ret_ty) { - // Push the out-pointer if we use an out-pointer for this - // return type, otherwise push "undef". - if type_is_zero_size(ccx, ret_ty) { - let llty = type_of::type_of(ccx, ret_ty); - Some(C_undef(llty.ptr_to())) - } else { - Some(alloc_ty(bcx, ret_ty, "__llret")) - } - } else { - None - } - } - }); - - let mut llresult = unsafe { - llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref()) - }; - - // The code below invokes the function, using either the Rust - // conventions (if it is a rust fn) or the native conventions - // (otherwise). The important part is that, when all is said - // and done, either the return value of the function will have been - // written in opt_llretslot (if it is Some) or `llresult` will be - // set appropriately (otherwise). - if is_rust_fn { - let mut llargs = Vec::new(); - - if let (ty::FnConverging(ret_ty), Some(llretslot)) = (ret_ty, opt_llretslot) { - if type_of::return_uses_outptr(ccx, ret_ty) { - llargs.push(llretslot); - } - } - - // Push the environment (or a trait object's self). - match (llenv, llself) { - (Some(llenv), None) => llargs.push(llenv), - (None, Some(llself)) => llargs.push(llself), - _ => {} - } - - // Push the arguments. - bcx = trans_args(bcx, - args, - callee_ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - llself.is_some(), - abi); - - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - - // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, - llfn, - llargs, - callee_ty, - call_info, - dest.is_none()); - bcx = b; - llresult = llret; - - // If the Rust convention for this type is return via - // the return value, copy it into llretslot. - match (opt_llretslot, ret_ty) { - (Some(llretslot), ty::FnConverging(ret_ty)) => { - if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !type_is_zero_size(bcx.ccx(), ret_ty) - { - store_ty(bcx, llret, llretslot, ret_ty) - } - } - (_, _) => {} - } - } else { - // Lang items are the only case where dest is None, and - // they are always Rust fns. - assert!(dest.is_some()); - - let mut llargs = Vec::new(); - let arg_tys = match args { - ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, &**x)).collect(), - _ => panic!("expected arg exprs.") - }; - bcx = trans_args(bcx, - args, - callee_ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - false, - abi); - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - - bcx = foreign::trans_native_call(bcx, callee_ty, - llfn, opt_llretslot.unwrap(), - llargs.as_slice(), arg_tys); - } - - fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_cleanup_scope); - - // If the caller doesn't care about the result of this fn call, - // drop the temporary slot we made. - match (dest, opt_llretslot, ret_ty) { - (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => { - // drop the value if it is not being saved. - bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info); - call_lifetime_end(bcx, llretslot); - } - _ => {} - } - - if ret_ty == ty::FnDiverging { - Unreachable(bcx); - } - - Result::new(bcx, llresult) -} - -pub enum CallArgs<'a> { - // Supply value of arguments as a list of expressions that must be - // translated. This is used in the common case of `foo(bar, qux)`. - ArgExprs(&'a [P]), - - // Supply value of arguments as a list of LLVM value refs; frequently - // used with lang items and so forth, when the argument is an internal - // value. - ArgVals(&'a [ValueRef]), - - // For overloaded operators: `(lhs, Vec(rhs, rhs_id))`. `lhs` - // is the left-hand-side and `rhs/rhs_id` is the datum/expr-id of - // the right-hand-side arguments (if any). - ArgOverloadedOp(Datum, Vec<(Datum, ast::NodeId)>), - - // Supply value of arguments as a list of expressions that must be - // translated, for overloaded call operators. - ArgOverloadedCall(Vec<&'a ast::Expr>), -} - -fn trans_args_under_call_abi<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - arg_exprs: &[P], - fn_ty: ty::t, - llargs: &mut Vec, - arg_cleanup_scope: cleanup::ScopeId, - ignore_self: bool) - -> Block<'blk, 'tcx> { - // Translate the `self` argument first. - let arg_tys = ty::ty_fn_args(fn_ty); - if !ignore_self { - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &*arg_exprs[0])); - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, - arg_tys[0], - arg_datum, - arg_cleanup_scope, - DontAutorefArg) - })) - } - - // Now untuple the rest of the arguments. - let tuple_expr = &arg_exprs[1]; - let tuple_type = node_id_type(bcx, tuple_expr.id); - - match ty::get(tuple_type).sty { - ty::ty_tup(ref field_types) => { - let tuple_datum = unpack_datum!(bcx, - expr::trans(bcx, &**tuple_expr)); - let tuple_lvalue_datum = - unpack_datum!(bcx, - tuple_datum.to_lvalue_datum(bcx, - "args", - tuple_expr.id)); - let repr = adt::represent_type(bcx.ccx(), tuple_type); - let repr_ptr = &*repr; - for i in range(0, field_types.len()) { - let arg_datum = tuple_lvalue_datum.get_element( - bcx, - field_types[i], - |srcval| { - adt::trans_field_ptr(bcx, repr_ptr, srcval, 0, i) - }); - let arg_datum = arg_datum.to_expr_datum(); - let arg_datum = - unpack_datum!(bcx, arg_datum.to_rvalue_datum(bcx, "arg")); - let arg_datum = - unpack_datum!(bcx, arg_datum.to_appropriate_datum(bcx)); - llargs.push(arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)); - } - } - _ => { - bcx.sess().span_bug(tuple_expr.span, - "argument to `.call()` wasn't a tuple?!") - } - }; - - bcx -} - -fn trans_overloaded_call_args<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - arg_exprs: Vec<&ast::Expr>, - fn_ty: ty::t, - llargs: &mut Vec, - arg_cleanup_scope: cleanup::ScopeId, - ignore_self: bool) - -> Block<'blk, 'tcx> { - // Translate the `self` argument first. - let arg_tys = ty::ty_fn_args(fn_ty); - if !ignore_self { - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0])); - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, - arg_tys[0], - arg_datum, - arg_cleanup_scope, - DontAutorefArg) - })) - } - - // Now untuple the rest of the arguments. - let tuple_type = arg_tys[1]; - match ty::get(tuple_type).sty { - ty::ty_tup(ref field_types) => { - for (i, &field_type) in field_types.iter().enumerate() { - let arg_datum = - unpack_datum!(bcx, expr::trans(bcx, arg_exprs[i + 1])); - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, - field_type, - arg_datum, - arg_cleanup_scope, - DontAutorefArg) - })) - } - } - _ => { - bcx.sess().span_bug(arg_exprs[0].span, - "argument to `.call()` wasn't a tuple?!") - } - }; - - bcx -} - -pub fn trans_args<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - args: CallArgs, - fn_ty: ty::t, - llargs: &mut Vec , - arg_cleanup_scope: cleanup::ScopeId, - ignore_self: bool, - abi: synabi::Abi) - -> Block<'blk, 'tcx> { - debug!("trans_args(abi={})", abi); - - let _icx = push_ctxt("trans_args"); - let arg_tys = ty::ty_fn_args(fn_ty); - let variadic = ty::fn_is_variadic(fn_ty); - - let mut bcx = cx; - - // First we figure out the caller's view of the types of the arguments. - // This will be needed if this is a generic call, because the callee has - // to cast her view of the arguments to the caller's view. - match args { - ArgExprs(arg_exprs) => { - if abi == synabi::RustCall { - // This is only used for direct calls to the `call`, - // `call_mut` or `call_once` functions. - return trans_args_under_call_abi(cx, - arg_exprs, - fn_ty, - llargs, - arg_cleanup_scope, - ignore_self) - } - - let num_formal_args = arg_tys.len(); - for (i, arg_expr) in arg_exprs.iter().enumerate() { - if i == 0 && ignore_self { - continue; - } - let arg_ty = if i >= num_formal_args { - assert!(variadic); - expr_ty_adjusted(cx, &**arg_expr) - } else { - arg_tys[i] - }; - - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &**arg_expr)); - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, arg_ty, arg_datum, - arg_cleanup_scope, - DontAutorefArg) - })); - } - } - ArgOverloadedCall(arg_exprs) => { - return trans_overloaded_call_args(cx, - arg_exprs, - fn_ty, - llargs, - arg_cleanup_scope, - ignore_self) - } - ArgOverloadedOp(lhs, rhs) => { - assert!(!variadic); - - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, arg_tys[0], lhs, - arg_cleanup_scope, - DontAutorefArg) - })); - - assert_eq!(arg_tys.len(), 1 + rhs.len()); - for (rhs, rhs_id) in rhs.into_iter() { - llargs.push(unpack_result!(bcx, { - trans_arg_datum(bcx, arg_tys[1], rhs, - arg_cleanup_scope, - DoAutorefArg(rhs_id)) - })); - } - } - ArgVals(vs) => { - llargs.push_all(vs); - } - } - - bcx -} - -pub enum AutorefArg { - DontAutorefArg, - DoAutorefArg(ast::NodeId) -} - -pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - formal_arg_ty: ty::t, - arg_datum: Datum, - arg_cleanup_scope: cleanup::ScopeId, - autoref_arg: AutorefArg) - -> Result<'blk, 'tcx> { - let _icx = push_ctxt("trans_arg_datum"); - let mut bcx = bcx; - let ccx = bcx.ccx(); - - debug!("trans_arg_datum({})", - formal_arg_ty.repr(bcx.tcx())); - - let arg_datum_ty = arg_datum.ty; - - debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx())); - - let mut val; - // FIXME(#3548) use the adjustments table - match autoref_arg { - DoAutorefArg(arg_id) => { - // We will pass argument by reference - // We want an lvalue, so that we can pass by reference and - let arg_datum = unpack_datum!( - bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); - val = arg_datum.val; - } - DontAutorefArg => { - // Make this an rvalue, since we are going to be - // passing ownership. - let arg_datum = unpack_datum!( - bcx, arg_datum.to_rvalue_datum(bcx, "arg")); - - // Now that arg_datum is owned, get it into the appropriate - // mode (ref vs value). - let arg_datum = unpack_datum!( - bcx, arg_datum.to_appropriate_datum(bcx)); - - // Technically, ownership of val passes to the callee. - // However, we must cleanup should we panic before the - // callee is actually invoked. - val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope); - } - } - - if formal_arg_ty != arg_datum_ty { - // this could happen due to e.g. subtyping - let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); - debug!("casting actual type ({}) to match formal ({})", - bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); - debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty), - ty_to_string(bcx.tcx(), formal_arg_ty)); - val = PointerCast(bcx, val, llformal_arg_ty); - } - - debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); - Result::new(bcx, val) -} diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs deleted file mode 100644 index 1207c995998..00000000000 --- a/src/librustc/middle/trans/cleanup.rs +++ /dev/null @@ -1,1218 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * Code pertaining to cleanup of temporaries as well as execution of - * drop glue. See discussion in `doc.rs` for a high-level summary. - */ - -pub use self::ScopeId::*; -pub use self::CleanupScopeKind::*; -pub use self::EarlyExitLabel::*; -pub use self::Heap::*; - -use llvm::{BasicBlockRef, ValueRef}; -use middle::trans::base; -use middle::trans::build; -use middle::trans::callee; -use middle::trans::common; -use middle::trans::common::{Block, FunctionContext, ExprId, NodeInfo}; -use middle::trans::debuginfo; -use middle::trans::glue; -use middle::trans::type_::Type; -use middle::ty; -use std::fmt; -use syntax::ast; -use util::ppaux::Repr; - -pub struct CleanupScope<'blk, 'tcx: 'blk> { - // The id of this cleanup scope. If the id is None, - // this is a *temporary scope* that is pushed during trans to - // cleanup miscellaneous garbage that trans may generate whose - // lifetime is a subset of some expression. See module doc for - // more details. - kind: CleanupScopeKind<'blk, 'tcx>, - - // Cleanups to run upon scope exit. - cleanups: Vec, - - // The debug location any drop calls generated for this scope will be - // associated with. - debug_loc: Option, - - cached_early_exits: Vec, - cached_landing_pad: Option, -} - -#[deriving(Show)] -pub struct CustomScopeIndex { - index: uint -} - -pub const EXIT_BREAK: uint = 0; -pub const EXIT_LOOP: uint = 1; -pub const EXIT_MAX: uint = 2; - -pub enum CleanupScopeKind<'blk, 'tcx: 'blk> { - CustomScopeKind, - AstScopeKind(ast::NodeId), - LoopScopeKind(ast::NodeId, [Block<'blk, 'tcx>, ..EXIT_MAX]) -} - -impl<'blk, 'tcx: 'blk> fmt::Show for CleanupScopeKind<'blk, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::FormatError> { - match *self { - CustomScopeKind => write!(f, "CustomScopeKind"), - AstScopeKind(nid) => write!(f, "AstScopeKind({})", nid), - LoopScopeKind(nid, ref blks) => { - try!(write!(f, "LoopScopeKind({}, [", nid)); - for blk in blks.iter() { - try!(write!(f, "{:p}, ", blk)); - } - write!(f, "])") - } - } - } -} - -#[deriving(PartialEq, Show)] -pub enum EarlyExitLabel { - UnwindExit, - ReturnExit, - LoopExit(ast::NodeId, uint) -} - -pub struct CachedEarlyExit { - label: EarlyExitLabel, - cleanup_block: BasicBlockRef, -} - -pub trait Cleanup { - fn must_unwind(&self) -> bool; - fn clean_on_unwind(&self) -> bool; - fn is_lifetime_end(&self) -> bool; - fn trans<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: Option) - -> Block<'blk, 'tcx>; -} - -pub type CleanupObj = Box; - -#[deriving(Show)] -pub enum ScopeId { - AstScope(ast::NodeId), - CustomScope(CustomScopeIndex) -} - -impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { - fn push_ast_cleanup_scope(&self, debug_loc: NodeInfo) { - /*! - * Invoked when we start to trans the code contained - * within a new cleanup scope. - */ - - debug!("push_ast_cleanup_scope({})", - self.ccx.tcx().map.node_to_string(debug_loc.id)); - - // FIXME(#2202) -- currently closure bodies have a parent - // region, which messes up the assertion below, since there - // are no cleanup scopes on the stack at the start of - // trans'ing a closure body. I think though that this should - // eventually be fixed by closure bodies not having a parent - // region, though that's a touch unclear, and it might also be - // better just to narrow this assertion more (i.e., by - // excluding id's that correspond to closure bodies only). For - // now we just say that if there is already an AST scope on the stack, - // this new AST scope had better be its immediate child. - let top_scope = self.top_ast_scope(); - if top_scope.is_some() { - assert_eq!(self.ccx - .tcx() - .region_maps - .opt_encl_scope(debug_loc.id), - top_scope); - } - - self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id), - Some(debug_loc))); - } - - fn push_loop_cleanup_scope(&self, - id: ast::NodeId, - exits: [Block<'blk, 'tcx>, ..EXIT_MAX]) { - debug!("push_loop_cleanup_scope({})", - self.ccx.tcx().map.node_to_string(id)); - assert_eq!(Some(id), self.top_ast_scope()); - - // Just copy the debuginfo source location from the enclosing scope - let debug_loc = self.scopes - .borrow() - .last() - .unwrap() - .debug_loc; - - self.push_scope(CleanupScope::new(LoopScopeKind(id, exits), debug_loc)); - } - - fn push_custom_cleanup_scope(&self) -> CustomScopeIndex { - let index = self.scopes_len(); - debug!("push_custom_cleanup_scope(): {}", index); - - // Just copy the debuginfo source location from the enclosing scope - let debug_loc = self.scopes - .borrow() - .last() - .map(|opt_scope| opt_scope.debug_loc) - .unwrap_or(None); - - self.push_scope(CleanupScope::new(CustomScopeKind, debug_loc)); - CustomScopeIndex { index: index } - } - - fn push_custom_cleanup_scope_with_debug_loc(&self, - debug_loc: NodeInfo) - -> CustomScopeIndex { - let index = self.scopes_len(); - debug!("push_custom_cleanup_scope(): {}", index); - - self.push_scope(CleanupScope::new(CustomScopeKind, Some(debug_loc))); - CustomScopeIndex { index: index } - } - - fn pop_and_trans_ast_cleanup_scope(&self, - bcx: Block<'blk, 'tcx>, - cleanup_scope: ast::NodeId) - -> Block<'blk, 'tcx> { - /*! - * Removes the cleanup scope for id `cleanup_scope`, which - * must be at the top of the cleanup stack, and generates the - * code to do its cleanups for normal exit. - */ - - debug!("pop_and_trans_ast_cleanup_scope({})", - self.ccx.tcx().map.node_to_string(cleanup_scope)); - - assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope))); - - let scope = self.pop_scope(); - self.trans_scope_cleanups(bcx, &scope) - } - - fn pop_loop_cleanup_scope(&self, - cleanup_scope: ast::NodeId) { - /*! - * Removes the loop cleanup scope for id `cleanup_scope`, which - * must be at the top of the cleanup stack. Does not generate - * any cleanup code, since loop scopes should exit by - * branching to a block generated by `normal_exit_block`. - */ - - debug!("pop_loop_cleanup_scope({})", - self.ccx.tcx().map.node_to_string(cleanup_scope)); - - assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope))); - - let _ = self.pop_scope(); - } - - fn pop_custom_cleanup_scope(&self, - custom_scope: CustomScopeIndex) { - /*! - * Removes the top cleanup scope from the stack without - * executing its cleanups. The top cleanup scope must - * be the temporary scope `custom_scope`. - */ - - debug!("pop_custom_cleanup_scope({})", custom_scope.index); - assert!(self.is_valid_to_pop_custom_scope(custom_scope)); - let _ = self.pop_scope(); - } - - fn pop_and_trans_custom_cleanup_scope(&self, - bcx: Block<'blk, 'tcx>, - custom_scope: CustomScopeIndex) - -> Block<'blk, 'tcx> { - /*! - * Removes the top cleanup scope from the stack, which must be - * a temporary scope, and generates the code to do its - * cleanups for normal exit. - */ - - debug!("pop_and_trans_custom_cleanup_scope({})", custom_scope); - assert!(self.is_valid_to_pop_custom_scope(custom_scope)); - - let scope = self.pop_scope(); - self.trans_scope_cleanups(bcx, &scope) - } - - fn top_loop_scope(&self) -> ast::NodeId { - /*! - * Returns the id of the top-most loop scope - */ - - for scope in self.scopes.borrow().iter().rev() { - match scope.kind { - LoopScopeKind(id, _) => { - return id; - } - _ => {} - } - } - self.ccx.sess().bug("no loop scope found"); - } - - fn normal_exit_block(&'blk self, - cleanup_scope: ast::NodeId, - exit: uint) -> BasicBlockRef { - /*! - * Returns a block to branch to which will perform all pending - * cleanups and then break/continue (depending on `exit`) out - * of the loop with id `cleanup_scope` - */ - - self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit)) - } - - fn return_exit_block(&'blk self) -> BasicBlockRef { - /*! - * Returns a block to branch to which will perform all pending - * cleanups and then return from this function - */ - - self.trans_cleanups_to_exit_scope(ReturnExit) - } - - fn schedule_lifetime_end(&self, - cleanup_scope: ScopeId, - val: ValueRef) { - let drop = box LifetimeEnd { - ptr: val, - }; - - debug!("schedule_lifetime_end({}, val={})", - cleanup_scope, - self.ccx.tn().val_to_string(val)); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_drop_mem(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t) { - /*! - * Schedules a (deep) drop of `val`, which is a pointer to an - * instance of `ty` - */ - - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } - let drop = box DropValue { - is_immediate: false, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), - val: val, - ty: ty, - zero: false - }; - - debug!("schedule_drop_mem({}, val={}, ty={})", - cleanup_scope, - self.ccx.tn().val_to_string(val), - ty.repr(self.ccx.tcx())); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_drop_and_zero_mem(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t) { - /*! - * Schedules a (deep) drop and zero-ing of `val`, which is a pointer - * to an instance of `ty` - */ - - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } - let drop = box DropValue { - is_immediate: false, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), - val: val, - ty: ty, - zero: true - }; - - debug!("schedule_drop_and_zero_mem({}, val={}, ty={}, zero={})", - cleanup_scope, - self.ccx.tn().val_to_string(val), - ty.repr(self.ccx.tcx()), - true); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_drop_immediate(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t) { - /*! - * Schedules a (deep) drop of `val`, which is an instance of `ty` - */ - - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } - let drop = box DropValue { - is_immediate: true, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), - val: val, - ty: ty, - zero: false - }; - - debug!("schedule_drop_immediate({}, val={}, ty={})", - cleanup_scope, - self.ccx.tn().val_to_string(val), - ty.repr(self.ccx.tcx())); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_free_value(&self, - cleanup_scope: ScopeId, - val: ValueRef, - heap: Heap, - content_ty: ty::t) { - /*! - * Schedules a call to `free(val)`. Note that this is a shallow - * operation. - */ - - let drop = box FreeValue { ptr: val, heap: heap, content_ty: content_ty }; - - debug!("schedule_free_value({}, val={}, heap={})", - cleanup_scope, - self.ccx.tn().val_to_string(val), - heap); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_free_slice(&self, - cleanup_scope: ScopeId, - val: ValueRef, - size: ValueRef, - align: ValueRef, - heap: Heap) { - /*! - * Schedules a call to `free(val)`. Note that this is a shallow - * operation. - */ - - let drop = box FreeSlice { ptr: val, size: size, align: align, heap: heap }; - - debug!("schedule_free_slice({}, val={}, heap={})", - cleanup_scope, - self.ccx.tn().val_to_string(val), - heap); - - self.schedule_clean(cleanup_scope, drop as CleanupObj); - } - - fn schedule_clean(&self, - cleanup_scope: ScopeId, - cleanup: CleanupObj) { - match cleanup_scope { - AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup), - CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup), - } - } - - fn schedule_clean_in_ast_scope(&self, - cleanup_scope: ast::NodeId, - cleanup: CleanupObj) { - /*! - * Schedules a cleanup to occur upon exit from `cleanup_scope`. - * If `cleanup_scope` is not provided, then the cleanup is scheduled - * in the topmost scope, which must be a temporary scope. - */ - - debug!("schedule_clean_in_ast_scope(cleanup_scope={})", - cleanup_scope); - - for scope in self.scopes.borrow_mut().iter_mut().rev() { - if scope.kind.is_ast_with_id(cleanup_scope) { - scope.cleanups.push(cleanup); - scope.clear_cached_exits(); - return; - } else { - // will be adding a cleanup to some enclosing scope - scope.clear_cached_exits(); - } - } - - self.ccx.sess().bug( - format!("no cleanup scope {} found", - self.ccx.tcx().map.node_to_string(cleanup_scope)).as_slice()); - } - - fn schedule_clean_in_custom_scope(&self, - custom_scope: CustomScopeIndex, - cleanup: CleanupObj) { - /*! - * Schedules a cleanup to occur in the top-most scope, - * which must be a temporary scope. - */ - - debug!("schedule_clean_in_custom_scope(custom_scope={})", - custom_scope.index); - - assert!(self.is_valid_custom_scope(custom_scope)); - - let mut scopes = self.scopes.borrow_mut(); - let scope = &mut (*scopes)[custom_scope.index]; - scope.cleanups.push(cleanup); - scope.clear_cached_exits(); - } - - fn needs_invoke(&self) -> bool { - /*! - * Returns true if there are pending cleanups that should - * execute on panic. - */ - - self.scopes.borrow().iter().rev().any(|s| s.needs_invoke()) - } - - fn get_landing_pad(&'blk self) -> BasicBlockRef { - /*! - * Returns a basic block to branch to in the event of a panic. - * This block will run the panic cleanups and eventually - * invoke the LLVM `Resume` instruction. - */ - - let _icx = base::push_ctxt("get_landing_pad"); - - debug!("get_landing_pad"); - - let orig_scopes_len = self.scopes_len(); - assert!(orig_scopes_len > 0); - - // Remove any scopes that do not have cleanups on panic: - let mut popped_scopes = vec!(); - while !self.top_scope(|s| s.needs_invoke()) { - debug!("top scope does not need invoke"); - popped_scopes.push(self.pop_scope()); - } - - // Check for an existing landing pad in the new topmost scope: - let llbb = self.get_or_create_landing_pad(); - - // Push the scopes we removed back on: - loop { - match popped_scopes.pop() { - Some(scope) => self.push_scope(scope), - None => break - } - } - - assert_eq!(self.scopes_len(), orig_scopes_len); - - return llbb; - } -} - -impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { - fn top_ast_scope(&self) -> Option { - /*! - * Returns the id of the current top-most AST scope, if any. - */ - for scope in self.scopes.borrow().iter().rev() { - match scope.kind { - CustomScopeKind | LoopScopeKind(..) => {} - AstScopeKind(i) => { - return Some(i); - } - } - } - None - } - - fn top_nonempty_cleanup_scope(&self) -> Option { - self.scopes.borrow().iter().rev().position(|s| !s.cleanups.is_empty()) - } - - fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { - self.is_valid_custom_scope(custom_scope) && - custom_scope.index == self.scopes.borrow().len() - 1 - } - - fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { - let scopes = self.scopes.borrow(); - custom_scope.index < scopes.len() && - (*scopes)[custom_scope.index].kind.is_temp() - } - - fn trans_scope_cleanups(&self, // cannot borrow self, will recurse - bcx: Block<'blk, 'tcx>, - scope: &CleanupScope) -> Block<'blk, 'tcx> { - /*! Generates the cleanups for `scope` into `bcx` */ - - let mut bcx = bcx; - if !bcx.unreachable.get() { - for cleanup in scope.cleanups.iter().rev() { - bcx = cleanup.trans(bcx, scope.debug_loc); - } - } - bcx - } - - fn scopes_len(&self) -> uint { - self.scopes.borrow().len() - } - - fn push_scope(&self, scope: CleanupScope<'blk, 'tcx>) { - self.scopes.borrow_mut().push(scope) - } - - fn pop_scope(&self) -> CleanupScope<'blk, 'tcx> { - debug!("popping cleanup scope {}, {} scopes remaining", - self.top_scope(|s| s.block_name("")), - self.scopes_len() - 1); - - self.scopes.borrow_mut().pop().unwrap() - } - - fn top_scope(&self, f: |&CleanupScope<'blk, 'tcx>| -> R) -> R { - f(self.scopes.borrow().last().unwrap()) - } - - fn trans_cleanups_to_exit_scope(&'blk self, - label: EarlyExitLabel) - -> BasicBlockRef { - /*! - * Used when the caller wishes to jump to an early exit, such - * as a return, break, continue, or unwind. This function will - * generate all cleanups between the top of the stack and the - * exit `label` and return a basic block that the caller can - * branch to. - * - * For example, if the current stack of cleanups were as follows: - * - * AST 22 - * Custom 1 - * AST 23 - * Loop 23 - * Custom 2 - * AST 24 - * - * and the `label` specifies a break from `Loop 23`, then this - * function would generate a series of basic blocks as follows: - * - * Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk - * - * where `break_blk` is the block specified in `Loop 23` as - * the target for breaks. The return value would be the first - * basic block in that sequence (`Cleanup(AST 24)`). The - * caller could then branch to `Cleanup(AST 24)` and it will - * perform all cleanups and finally branch to the `break_blk`. - */ - - debug!("trans_cleanups_to_exit_scope label={} scopes={}", - label, self.scopes_len()); - - let orig_scopes_len = self.scopes_len(); - let mut prev_llbb; - let mut popped_scopes = vec!(); - - // First we pop off all the cleanup stacks that are - // traversed until the exit is reached, pushing them - // onto the side vector `popped_scopes`. No code is - // generated at this time. - // - // So, continuing the example from above, we would wind up - // with a `popped_scopes` vector of `[AST 24, Custom 2]`. - // (Presuming that there are no cached exits) - loop { - if self.scopes_len() == 0 { - match label { - UnwindExit => { - // Generate a block that will `Resume`. - let prev_bcx = self.new_block(true, "resume", None); - let personality = self.personality.get().expect( - "create_landing_pad() should have set this"); - build::Resume(prev_bcx, - build::Load(prev_bcx, personality)); - prev_llbb = prev_bcx.llbb; - break; - } - - ReturnExit => { - prev_llbb = self.get_llreturn(); - break; - } - - LoopExit(id, _) => { - self.ccx.sess().bug(format!( - "cannot exit from scope {}, \ - not in scope", id).as_slice()); - } - } - } - - // Check if we have already cached the unwinding of this - // scope for this label. If so, we can stop popping scopes - // and branch to the cached label, since it contains the - // cleanups for any subsequent scopes. - match self.top_scope(|s| s.cached_early_exit(label)) { - Some(cleanup_block) => { - prev_llbb = cleanup_block; - break; - } - None => { } - } - - // Pop off the scope, since we will be generating - // unwinding code for it. If we are searching for a loop exit, - // and this scope is that loop, then stop popping and set - // `prev_llbb` to the appropriate exit block from the loop. - popped_scopes.push(self.pop_scope()); - let scope = popped_scopes.last().unwrap(); - match label { - UnwindExit | ReturnExit => { } - LoopExit(id, exit) => { - match scope.kind.early_exit_block(id, exit) { - Some(exitllbb) => { - prev_llbb = exitllbb; - break; - } - - None => { } - } - } - } - } - - debug!("trans_cleanups_to_exit_scope: popped {} scopes", - popped_scopes.len()); - - // Now push the popped scopes back on. As we go, - // we track in `prev_llbb` the exit to which this scope - // should branch when it's done. - // - // So, continuing with our example, we will start out with - // `prev_llbb` being set to `break_blk` (or possibly a cached - // early exit). We will then pop the scopes from `popped_scopes` - // and generate a basic block for each one, prepending it in the - // series and updating `prev_llbb`. So we begin by popping `Custom 2` - // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)` - // branch to `prev_llbb == break_blk`, giving us a sequence like: - // - // Cleanup(Custom 2) -> prev_llbb - // - // We then pop `AST 24` and repeat the process, giving us the sequence: - // - // Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb - // - // At this point, `popped_scopes` is empty, and so the final block - // that we return to the user is `Cleanup(AST 24)`. - while !popped_scopes.is_empty() { - let mut scope = popped_scopes.pop().unwrap(); - - if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(&**c, label)) - { - let name = scope.block_name("clean"); - debug!("generating cleanups for {}", name); - let bcx_in = self.new_block(label.is_unwind(), - name.as_slice(), - None); - let mut bcx_out = bcx_in; - for cleanup in scope.cleanups.iter().rev() { - if cleanup_is_suitable_for(&**cleanup, label) { - bcx_out = cleanup.trans(bcx_out, - scope.debug_loc); - } - } - build::Br(bcx_out, prev_llbb); - prev_llbb = bcx_in.llbb; - } else { - debug!("no suitable cleanups in {}", - scope.block_name("clean")); - } - - scope.add_cached_early_exit(label, prev_llbb); - self.push_scope(scope); - } - - debug!("trans_cleanups_to_exit_scope: prev_llbb={}", prev_llbb); - - assert_eq!(self.scopes_len(), orig_scopes_len); - prev_llbb - } - - fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef { - /*! - * Creates a landing pad for the top scope, if one does not - * exist. The landing pad will perform all cleanups necessary - * for an unwind and then `resume` to continue error - * propagation: - * - * landing_pad -> ... cleanups ... -> [resume] - * - * (The cleanups and resume instruction are created by - * `trans_cleanups_to_exit_scope()`, not in this function - * itself.) - */ - - let pad_bcx; - - debug!("get_or_create_landing_pad"); - - // Check if a landing pad block exists; if not, create one. - { - let mut scopes = self.scopes.borrow_mut(); - let last_scope = scopes.last_mut().unwrap(); - match last_scope.cached_landing_pad { - Some(llbb) => { return llbb; } - None => { - let name = last_scope.block_name("unwind"); - pad_bcx = self.new_block(true, name.as_slice(), None); - last_scope.cached_landing_pad = Some(pad_bcx.llbb); - } - } - } - - // The landing pad return type (the type being propagated). Not sure what - // this represents but it's determined by the personality function and - // this is what the EH proposal example uses. - let llretty = Type::struct_(self.ccx, - &[Type::i8p(self.ccx), Type::i32(self.ccx)], - false); - - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { - Some(def_id) => callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)), - None => { - let mut personality = self.ccx.eh_personality().borrow_mut(); - match *personality { - Some(llpersonality) => llpersonality, - None => { - let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); - let f = base::decl_cdecl_fn(self.ccx, - "rust_eh_personality", - fty, - ty::mk_i32()); - *personality = Some(f); - f - } - } - } - }; - - // The only landing pad clause will be 'cleanup' - let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u); - - // The landing pad block is a cleanup - build::SetCleanup(pad_bcx, llretval); - - // We store the retval in a function-central alloca, so that calls to - // Resume can find it. - match self.personality.get() { - Some(addr) => { - build::Store(pad_bcx, llretval, addr); - } - None => { - let addr = base::alloca(pad_bcx, common::val_ty(llretval), ""); - self.personality.set(Some(addr)); - build::Store(pad_bcx, llretval, addr); - } - } - - // Generate the cleanup block and branch to it. - let cleanup_llbb = self.trans_cleanups_to_exit_scope(UnwindExit); - build::Br(pad_bcx, cleanup_llbb); - - return pad_bcx.llbb; - } -} - -impl<'blk, 'tcx> CleanupScope<'blk, 'tcx> { - fn new(kind: CleanupScopeKind<'blk, 'tcx>, - debug_loc: Option) - -> CleanupScope<'blk, 'tcx> { - CleanupScope { - kind: kind, - debug_loc: debug_loc, - cleanups: vec!(), - cached_early_exits: vec!(), - cached_landing_pad: None, - } - } - - fn clear_cached_exits(&mut self) { - self.cached_early_exits = vec!(); - self.cached_landing_pad = None; - } - - fn cached_early_exit(&self, - label: EarlyExitLabel) - -> Option { - self.cached_early_exits.iter(). - find(|e| e.label == label). - map(|e| e.cleanup_block) - } - - fn add_cached_early_exit(&mut self, - label: EarlyExitLabel, - blk: BasicBlockRef) { - self.cached_early_exits.push( - CachedEarlyExit { label: label, - cleanup_block: blk }); - } - - fn needs_invoke(&self) -> bool { - /*! True if this scope has cleanups that need unwinding */ - - self.cached_landing_pad.is_some() || - self.cleanups.iter().any(|c| c.must_unwind()) - } - - fn block_name(&self, prefix: &str) -> String { - /*! - * Returns a suitable name to use for the basic block that - * handles this cleanup scope - */ - - match self.kind { - CustomScopeKind => format!("{}_custom_", prefix), - AstScopeKind(id) => format!("{}_ast_{}_", prefix, id), - LoopScopeKind(id, _) => format!("{}_loop_{}_", prefix, id), - } - } - - pub fn drop_non_lifetime_clean(&mut self) { - self.cleanups.retain(|c| c.is_lifetime_end()); - } -} - -impl<'blk, 'tcx> CleanupScopeKind<'blk, 'tcx> { - fn is_temp(&self) -> bool { - match *self { - CustomScopeKind => true, - LoopScopeKind(..) | AstScopeKind(..) => false, - } - } - - fn is_ast_with_id(&self, id: ast::NodeId) -> bool { - match *self { - CustomScopeKind | LoopScopeKind(..) => false, - AstScopeKind(i) => i == id - } - } - - fn is_loop_with_id(&self, id: ast::NodeId) -> bool { - match *self { - CustomScopeKind | AstScopeKind(..) => false, - LoopScopeKind(i, _) => i == id - } - } - - fn early_exit_block(&self, - id: ast::NodeId, - exit: uint) -> Option { - /*! - * If this is a loop scope with id `id`, return the early - * exit block `exit`, else `None` - */ - - match *self { - LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb), - _ => None, - } - } -} - -impl EarlyExitLabel { - fn is_unwind(&self) -> bool { - match *self { - UnwindExit => true, - _ => false - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Cleanup types - -pub struct DropValue { - is_immediate: bool, - must_unwind: bool, - val: ValueRef, - ty: ty::t, - zero: bool -} - -impl Cleanup for DropValue { - fn must_unwind(&self) -> bool { - self.must_unwind - } - - fn clean_on_unwind(&self) -> bool { - self.must_unwind - } - - fn is_lifetime_end(&self) -> bool { - false - } - - fn trans<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: Option) - -> Block<'blk, 'tcx> { - let bcx = if self.is_immediate { - glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc) - } else { - glue::drop_ty(bcx, self.val, self.ty, debug_loc) - }; - if self.zero { - base::zero_mem(bcx, self.val, self.ty); - } - bcx - } -} - -#[deriving(Show)] -pub enum Heap { - HeapExchange -} - -pub struct FreeValue { - ptr: ValueRef, - heap: Heap, - content_ty: ty::t -} - -impl Cleanup for FreeValue { - fn must_unwind(&self) -> bool { - true - } - - fn clean_on_unwind(&self) -> bool { - true - } - - fn is_lifetime_end(&self) -> bool { - false - } - - fn trans<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: Option) - -> Block<'blk, 'tcx> { - apply_debug_loc(bcx.fcx, debug_loc); - - match self.heap { - HeapExchange => { - glue::trans_exchange_free_ty(bcx, self.ptr, self.content_ty) - } - } - } -} - -pub struct FreeSlice { - ptr: ValueRef, - size: ValueRef, - align: ValueRef, - heap: Heap, -} - -impl Cleanup for FreeSlice { - fn must_unwind(&self) -> bool { - true - } - - fn clean_on_unwind(&self) -> bool { - true - } - - fn is_lifetime_end(&self) -> bool { - false - } - - fn trans<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: Option) - -> Block<'blk, 'tcx> { - apply_debug_loc(bcx.fcx, debug_loc); - - match self.heap { - HeapExchange => { - glue::trans_exchange_free_dyn(bcx, self.ptr, self.size, self.align) - } - } - } -} - -pub struct LifetimeEnd { - ptr: ValueRef, -} - -impl Cleanup for LifetimeEnd { - fn must_unwind(&self) -> bool { - false - } - - fn clean_on_unwind(&self) -> bool { - true - } - - fn is_lifetime_end(&self) -> bool { - true - } - - fn trans<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: Option) - -> Block<'blk, 'tcx> { - apply_debug_loc(bcx.fcx, debug_loc); - base::call_lifetime_end(bcx, self.ptr); - bcx - } -} - -pub fn temporary_scope(tcx: &ty::ctxt, - id: ast::NodeId) - -> ScopeId { - match tcx.region_maps.temporary_scope(id) { - Some(scope) => { - let r = AstScope(scope); - debug!("temporary_scope({}) = {}", id, r); - r - } - None => { - tcx.sess.bug(format!("no temporary scope available for expr {}", - id).as_slice()) - } - } -} - -pub fn var_scope(tcx: &ty::ctxt, - id: ast::NodeId) - -> ScopeId { - let r = AstScope(tcx.region_maps.var_scope(id)); - debug!("var_scope({}) = {}", id, r); - r -} - -fn cleanup_is_suitable_for(c: &Cleanup, - label: EarlyExitLabel) -> bool { - !label.is_unwind() || c.clean_on_unwind() -} - -fn apply_debug_loc(fcx: &FunctionContext, debug_loc: Option) { - match debug_loc { - Some(ref src_loc) => { - debuginfo::set_source_location(fcx, src_loc.id, src_loc.span); - } - None => { - debuginfo::clear_source_location(fcx); - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// These traits just exist to put the methods into this file. - -pub trait CleanupMethods<'blk, 'tcx> { - fn push_ast_cleanup_scope(&self, id: NodeInfo); - fn push_loop_cleanup_scope(&self, - id: ast::NodeId, - exits: [Block<'blk, 'tcx>, ..EXIT_MAX]); - fn push_custom_cleanup_scope(&self) -> CustomScopeIndex; - fn push_custom_cleanup_scope_with_debug_loc(&self, - debug_loc: NodeInfo) - -> CustomScopeIndex; - fn pop_and_trans_ast_cleanup_scope(&self, - bcx: Block<'blk, 'tcx>, - cleanup_scope: ast::NodeId) - -> Block<'blk, 'tcx>; - fn pop_loop_cleanup_scope(&self, - cleanup_scope: ast::NodeId); - fn pop_custom_cleanup_scope(&self, - custom_scope: CustomScopeIndex); - fn pop_and_trans_custom_cleanup_scope(&self, - bcx: Block<'blk, 'tcx>, - custom_scope: CustomScopeIndex) - -> Block<'blk, 'tcx>; - fn top_loop_scope(&self) -> ast::NodeId; - fn normal_exit_block(&'blk self, - cleanup_scope: ast::NodeId, - exit: uint) -> BasicBlockRef; - fn return_exit_block(&'blk self) -> BasicBlockRef; - fn schedule_lifetime_end(&self, - cleanup_scope: ScopeId, - val: ValueRef); - fn schedule_drop_mem(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t); - fn schedule_drop_and_zero_mem(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t); - fn schedule_drop_immediate(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: ty::t); - fn schedule_free_value(&self, - cleanup_scope: ScopeId, - val: ValueRef, - heap: Heap, - content_ty: ty::t); - fn schedule_free_slice(&self, - cleanup_scope: ScopeId, - val: ValueRef, - size: ValueRef, - align: ValueRef, - heap: Heap); - fn schedule_clean(&self, - cleanup_scope: ScopeId, - cleanup: CleanupObj); - fn schedule_clean_in_ast_scope(&self, - cleanup_scope: ast::NodeId, - cleanup: CleanupObj); - fn schedule_clean_in_custom_scope(&self, - custom_scope: CustomScopeIndex, - cleanup: CleanupObj); - fn needs_invoke(&self) -> bool; - fn get_landing_pad(&'blk self) -> BasicBlockRef; -} - -trait CleanupHelperMethods<'blk, 'tcx> { - fn top_ast_scope(&self) -> Option; - fn top_nonempty_cleanup_scope(&self) -> Option; - fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; - fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; - fn trans_scope_cleanups(&self, - bcx: Block<'blk, 'tcx>, - scope: &CleanupScope<'blk, 'tcx>) -> Block<'blk, 'tcx>; - fn trans_cleanups_to_exit_scope(&'blk self, - label: EarlyExitLabel) - -> BasicBlockRef; - fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef; - fn scopes_len(&self) -> uint; - fn push_scope(&self, scope: CleanupScope<'blk, 'tcx>); - fn pop_scope(&self) -> CleanupScope<'blk, 'tcx>; - fn top_scope(&self, f: |&CleanupScope<'blk, 'tcx>| -> R) -> R; -} diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs deleted file mode 100644 index ab7b1aa33e7..00000000000 --- a/src/librustc/middle/trans/closure.rs +++ /dev/null @@ -1,659 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use back::abi; -use back::link::mangle_internal_name_by_path_and_seq; -use driver::config::FullDebugInfo; -use llvm::ValueRef; -use middle::def; -use middle::mem_categorization::Typer; -use middle::trans::adt; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::cleanup::{CleanupMethods, ScopeId}; -use middle::trans::common::*; -use middle::trans::datum::{Datum, DatumBlock, Expr, Lvalue, rvalue_scratch_datum}; -use middle::trans::debuginfo; -use middle::trans::expr; -use middle::trans::monomorphize::MonoId; -use middle::trans::type_of::*; -use middle::trans::type_::Type; -use middle::ty; -use middle::subst::{Subst, Substs}; -use util::ppaux::Repr; -use util::ppaux::ty_to_string; - -use arena::TypedArena; -use syntax::ast; -use syntax::ast_util; - -// ___Good to know (tm)__________________________________________________ -// -// The layout of a closure environment in memory is -// roughly as follows: -// -// struct rust_opaque_box { // see rust_internal.h -// unsigned ref_count; // obsolete (part of @T's header) -// fn(void*) *drop_glue; // destructor (for proc) -// rust_opaque_box *prev; // obsolete (part of @T's header) -// rust_opaque_box *next; // obsolete (part of @T's header) -// struct closure_data { -// upvar1_t upvar1; -// ... -// upvarN_t upvarN; -// } -// }; -// -// Note that the closure is itself a rust_opaque_box. This is true -// even for ~fn and ||, because we wish to keep binary compatibility -// between all kinds of closures. The allocation strategy for this -// closure depends on the closure type. For a sendfn, the closure -// (and the referenced type descriptors) will be allocated in the -// exchange heap. For a fn, the closure is allocated in the task heap -// and is reference counted. For a block, the closure is allocated on -// the stack. -// -// ## Opaque closures and the embedded type descriptor ## -// -// One interesting part of closures is that they encapsulate the data -// that they close over. So when I have a ptr to a closure, I do not -// know how many type descriptors it contains nor what upvars are -// captured within. That means I do not know precisely how big it is -// nor where its fields are located. This is called an "opaque -// closure". -// -// Typically an opaque closure suffices because we only manipulate it -// by ptr. The routine Type::at_box().ptr_to() returns an appropriate -// type for such an opaque closure; it allows access to the box fields, -// but not the closure_data itself. -// -// But sometimes, such as when cloning or freeing a closure, we need -// to know the full information. That is where the type descriptor -// that defines the closure comes in handy. We can use its take and -// drop glue functions to allocate/free data as needed. -// -// ## Subtleties concerning alignment ## -// -// It is important that we be able to locate the closure data *without -// knowing the kind of data that is being bound*. This can be tricky -// because the alignment requirements of the bound data affects the -// alignment requires of the closure_data struct as a whole. However, -// right now this is a non-issue in any case, because the size of the -// rust_opaque_box header is always a multiple of 16-bytes, which is -// the maximum alignment requirement we ever have to worry about. -// -// The only reason alignment matters is that, in order to learn what data -// is bound, we would normally first load the type descriptors: but their -// location is ultimately depend on their content! There is, however, a -// workaround. We can load the tydesc from the rust_opaque_box, which -// describes the closure_data struct and has self-contained derived type -// descriptors, and read the alignment from there. It's just annoying to -// do. Hopefully should this ever become an issue we'll have monomorphized -// and type descriptors will all be a bad dream. -// -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -pub struct EnvValue { - action: ast::CaptureClause, - datum: Datum -} - -impl EnvValue { - pub fn to_string(&self, ccx: &CrateContext) -> String { - format!("{}({})", self.action, self.datum.to_string(ccx)) - } -} - -// Given a closure ty, emits a corresponding tuple ty -pub fn mk_closure_tys(tcx: &ty::ctxt, - bound_values: &[EnvValue]) - -> ty::t { - // determine the types of the values in the env. Note that this - // is the actual types that will be stored in the map, not the - // logical types as the user sees them, so by-ref upvars must be - // converted to ptrs. - let bound_tys = bound_values.iter().map(|bv| { - match bv.action { - ast::CaptureByValue => bv.datum.ty, - ast::CaptureByRef => ty::mk_mut_ptr(tcx, bv.datum.ty) - } - }).collect(); - let cdata_ty = ty::mk_tup(tcx, bound_tys); - debug!("cdata_ty={}", ty_to_string(tcx, cdata_ty)); - return cdata_ty; -} - -fn tuplify_box_ty(tcx: &ty::ctxt, t: ty::t) -> ty::t { - let ptr = ty::mk_imm_ptr(tcx, ty::mk_i8()); - ty::mk_tup(tcx, vec!(ty::mk_uint(), ty::mk_nil_ptr(tcx), ptr, ptr, t)) -} - -fn allocate_cbox<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - store: ty::TraitStore, - cdata_ty: ty::t) - -> Result<'blk, 'tcx> { - let _icx = push_ctxt("closure::allocate_cbox"); - let tcx = bcx.tcx(); - - // Allocate and initialize the box: - let cbox_ty = tuplify_box_ty(tcx, cdata_ty); - match store { - ty::UniqTraitStore => { - malloc_raw_dyn_proc(bcx, cbox_ty) - } - ty::RegionTraitStore(..) => { - let llbox = alloc_ty(bcx, cbox_ty, "__closure"); - Result::new(bcx, llbox) - } - } -} - -pub struct ClosureResult<'blk, 'tcx: 'blk> { - llbox: ValueRef, // llvalue of ptr to closure - cdata_ty: ty::t, // type of the closure data - bcx: Block<'blk, 'tcx> // final bcx -} - -// Given a block context and a list of tydescs and values to bind -// construct a closure out of them. If copying is true, it is a -// heap allocated closure that copies the upvars into environment. -// Otherwise, it is stack allocated and copies pointers to the upvars. -pub fn store_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - bound_values: Vec , - store: ty::TraitStore) - -> ClosureResult<'blk, 'tcx> { - let _icx = push_ctxt("closure::store_environment"); - let ccx = bcx.ccx(); - let tcx = ccx.tcx(); - - // compute the type of the closure - let cdata_ty = mk_closure_tys(tcx, bound_values.as_slice()); - - // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a - // tuple. This could be a ptr in uniq or a box or on stack, - // whatever. - let cbox_ty = tuplify_box_ty(tcx, cdata_ty); - let cboxptr_ty = ty::mk_ptr(tcx, ty::mt {ty:cbox_ty, mutbl:ast::MutImmutable}); - let llboxptr_ty = type_of(ccx, cboxptr_ty); - - // If there are no bound values, no point in allocating anything. - if bound_values.is_empty() { - return ClosureResult {llbox: C_null(llboxptr_ty), - cdata_ty: cdata_ty, - bcx: bcx}; - } - - // allocate closure in the heap - let Result {bcx, val: llbox} = allocate_cbox(bcx, store, cdata_ty); - - let llbox = PointerCast(bcx, llbox, llboxptr_ty); - debug!("tuplify_box_ty = {}", ty_to_string(tcx, cbox_ty)); - - // Copy expr values into boxed bindings. - let mut bcx = bcx; - for (i, bv) in bound_values.into_iter().enumerate() { - debug!("Copy {} into closure", bv.to_string(ccx)); - - if ccx.sess().asm_comments() { - add_comment(bcx, format!("Copy {} into closure", - bv.to_string(ccx)).as_slice()); - } - - let bound_data = GEPi(bcx, llbox, &[0u, abi::box_field_body, i]); - - match bv.action { - ast::CaptureByValue => { - bcx = bv.datum.store_to(bcx, bound_data); - } - ast::CaptureByRef => { - Store(bcx, bv.datum.to_llref(), bound_data); - } - } - } - - ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx } -} - -// Given a context and a list of upvars, build a closure. This just -// collects the upvars and packages them up for store_environment. -fn build_closure<'blk, 'tcx>(bcx0: Block<'blk, 'tcx>, - freevar_mode: ast::CaptureClause, - freevars: &Vec, - store: ty::TraitStore) - -> ClosureResult<'blk, 'tcx> { - let _icx = push_ctxt("closure::build_closure"); - - // If we need to, package up the iterator body to call - let bcx = bcx0; - - // Package up the captured upvars - let mut env_vals = Vec::new(); - for freevar in freevars.iter() { - let datum = expr::trans_local_var(bcx, freevar.def); - env_vals.push(EnvValue {action: freevar_mode, datum: datum}); - } - - store_environment(bcx, env_vals, store) -} - -// Given an enclosing block context, a new function context, a closure type, -// and a list of upvars, generate code to load and populate the environment -// with the upvars and type descriptors. -fn load_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - cdata_ty: ty::t, - freevars: &Vec, - store: ty::TraitStore) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("closure::load_environment"); - - // Don't bother to create the block if there's nothing to load - if freevars.len() == 0 { - return bcx; - } - - // Load a pointer to the closure data, skipping over the box header: - let llcdata = at_box_body(bcx, cdata_ty, bcx.fcx.llenv.unwrap()); - - // Store the pointer to closure data in an alloca for debug info because that's what the - // llvm.dbg.declare intrinsic expects - let env_pointer_alloca = if bcx.sess().opts.debuginfo == FullDebugInfo { - let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr"); - Store(bcx, llcdata, alloc); - Some(alloc) - } else { - None - }; - - // Populate the upvars from the environment - let mut i = 0u; - for freevar in freevars.iter() { - let mut upvarptr = GEPi(bcx, llcdata, &[0u, i]); - match store { - ty::RegionTraitStore(..) => { upvarptr = Load(bcx, upvarptr); } - ty::UniqTraitStore => {} - } - let def_id = freevar.def.def_id(); - - bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvarptr); - for &env_pointer_alloca in env_pointer_alloca.iter() { - debuginfo::create_captured_var_metadata( - bcx, - def_id.node, - cdata_ty, - env_pointer_alloca, - i, - store, - freevar.span); - } - - i += 1u; - } - - bcx -} - -fn load_unboxed_closure_environment<'blk, 'tcx>( - bcx: Block<'blk, 'tcx>, - arg_scope_id: ScopeId, - freevar_mode: ast::CaptureClause, - freevars: &Vec, - closure_id: ast::DefId) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("closure::load_environment"); - - if freevars.len() == 0 { - return bcx - } - - // Special case for small by-value selfs. - let self_type = self_type_for_unboxed_closure(bcx.ccx(), closure_id, - node_id_type(bcx, closure_id.node)); - let kind = kind_for_unboxed_closure(bcx.ccx(), closure_id); - let llenv = if kind == ty::FnOnceUnboxedClosureKind && - !arg_is_indirect(bcx.ccx(), self_type) { - let datum = rvalue_scratch_datum(bcx, - self_type, - "unboxed_closure_env"); - store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type); - datum.val - } else { - bcx.fcx.llenv.unwrap() - }; - - for (i, freevar) in freevars.iter().enumerate() { - let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]); - if freevar_mode == ast::CaptureByRef { - upvar_ptr = Load(bcx, upvar_ptr); - } - let def_id = freevar.def.def_id(); - bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr); - - if kind == ty::FnOnceUnboxedClosureKind && freevar_mode == ast::CaptureByValue { - bcx.fcx.schedule_drop_mem(arg_scope_id, - upvar_ptr, - node_id_type(bcx, def_id.node)) - } - } - - bcx -} - -fn fill_fn_pair(bcx: Block, pair: ValueRef, llfn: ValueRef, llenvptr: ValueRef) { - Store(bcx, llfn, GEPi(bcx, pair, &[0u, abi::fn_field_code])); - let llenvptr = PointerCast(bcx, llenvptr, Type::i8p(bcx.ccx())); - Store(bcx, llenvptr, GEPi(bcx, pair, &[0u, abi::fn_field_box])); -} - -pub fn trans_expr_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - store: ty::TraitStore, - decl: &ast::FnDecl, - body: &ast::Block, - id: ast::NodeId, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - /*! - * - * Translates the body of a closure expression. - * - * - `store` - * - `decl` - * - `body` - * - `id`: The id of the closure expression. - * - `cap_clause`: information about captured variables, if any. - * - `dest`: where to write the closure value, which must be a - (fn ptr, env) pair - */ - - let _icx = push_ctxt("closure::trans_expr_fn"); - - let dest_addr = match dest { - expr::SaveIn(p) => p, - expr::Ignore => { - return bcx; // closure construction is non-side-effecting - } - }; - - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - let fty = node_id_type(bcx, id); - let s = tcx.map.with_path(id, |path| { - mangle_internal_name_by_path_and_seq(path, "closure") - }); - let llfn = decl_internal_rust_fn(ccx, fty, s.as_slice()); - - // set an inline hint for all closures - set_inline_hint(llfn); - - let freevar_mode = tcx.capture_mode(id); - let freevars: Vec = - ty::with_freevars(tcx, id, |fv| fv.iter().map(|&fv| fv).collect()); - - let ClosureResult { - llbox, - cdata_ty, - bcx - } = build_closure(bcx, freevar_mode, &freevars, store); - trans_closure(ccx, - decl, - body, - llfn, - bcx.fcx.param_substs, - id, - &[], - ty::ty_fn_ret(fty), - ty::ty_fn_abi(fty), - true, - NotUnboxedClosure, - |bcx, _| load_environment(bcx, cdata_ty, &freevars, store)); - fill_fn_pair(bcx, dest_addr, llfn, llbox); - bcx -} - -/// Returns the LLVM function declaration for an unboxed closure, creating it -/// if necessary. If the ID does not correspond to a closure ID, returns None. -pub fn get_or_create_declaration_if_unboxed_closure<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - closure_id: ast::DefId, - substs: &Substs) - -> Option { - let ccx = bcx.ccx(); - if !ccx.tcx().unboxed_closures.borrow().contains_key(&closure_id) { - // Not an unboxed closure. - return None - } - - let function_type = ty::node_id_to_type(bcx.tcx(), closure_id.node); - let function_type = function_type.subst(bcx.tcx(), substs); - - // Normalize type so differences in regions and typedefs don't cause - // duplicate declarations - let function_type = ty::normalize_ty(bcx.tcx(), function_type); - let params = match ty::get(function_type).sty { - ty::ty_unboxed_closure(_, _, ref substs) => substs.types.clone(), - _ => unreachable!() - }; - let mono_id = MonoId { - def: closure_id, - params: params - }; - - match ccx.unboxed_closure_vals().borrow().get(&mono_id) { - Some(llfn) => { - debug!("get_or_create_declaration_if_unboxed_closure(): found \ - closure"); - return Some(*llfn) - } - None => {} - } - - let symbol = ccx.tcx().map.with_path(closure_id.node, |path| { - mangle_internal_name_by_path_and_seq(path, "unboxed_closure") - }); - - let llfn = decl_internal_rust_fn(ccx, function_type, symbol.as_slice()); - - // set an inline hint for all closures - set_inline_hint(llfn); - - debug!("get_or_create_declaration_if_unboxed_closure(): inserting new \ - closure {} (type {})", - mono_id, - ccx.tn().type_to_string(val_ty(llfn))); - ccx.unboxed_closure_vals().borrow_mut().insert(mono_id, llfn); - - Some(llfn) -} - -pub fn trans_unboxed_closure<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - decl: &ast::FnDecl, - body: &ast::Block, - id: ast::NodeId, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("closure::trans_unboxed_closure"); - - debug!("trans_unboxed_closure()"); - - let closure_id = ast_util::local_def(id); - let llfn = get_or_create_declaration_if_unboxed_closure( - bcx, - closure_id, - &bcx.fcx.param_substs.substs).unwrap(); - - let function_type = (*bcx.tcx().unboxed_closures.borrow())[closure_id] - .closure_type - .clone(); - let function_type = ty::mk_closure(bcx.tcx(), function_type); - - let freevars: Vec = - ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect()); - let freevars_ptr = &freevars; - let freevar_mode = bcx.tcx().capture_mode(id); - - trans_closure(bcx.ccx(), - decl, - body, - llfn, - bcx.fcx.param_substs, - id, - &[], - ty::ty_fn_ret(function_type), - ty::ty_fn_abi(function_type), - true, - IsUnboxedClosure, - |bcx, arg_scope| { - load_unboxed_closure_environment(bcx, - arg_scope, - freevar_mode, - freevars_ptr, - closure_id) - }); - - // Don't hoist this to the top of the function. It's perfectly legitimate - // to have a zero-size unboxed closure (in which case dest will be - // `Ignore`) and we must still generate the closure body. - let dest_addr = match dest { - expr::SaveIn(p) => p, - expr::Ignore => { - debug!("trans_unboxed_closure() ignoring result"); - return bcx - } - }; - - let repr = adt::represent_type(bcx.ccx(), node_id_type(bcx, id)); - - // Create the closure. - for (i, freevar) in freevars_ptr.iter().enumerate() { - let datum = expr::trans_local_var(bcx, freevar.def); - let upvar_slot_dest = adt::trans_field_ptr(bcx, - &*repr, - dest_addr, - 0, - i); - match freevar_mode { - ast::CaptureByValue => { - bcx = datum.store_to(bcx, upvar_slot_dest); - } - ast::CaptureByRef => { - Store(bcx, datum.to_llref(), upvar_slot_dest); - } - } - } - adt::trans_set_discr(bcx, &*repr, dest_addr, 0); - - bcx -} - -pub fn get_wrapper_for_bare_fn(ccx: &CrateContext, - closure_ty: ty::t, - def: def::Def, - fn_ptr: ValueRef, - is_local: bool) -> ValueRef { - - let def_id = match def { - def::DefFn(did, _) | def::DefStaticMethod(did, _) | - def::DefVariant(_, did, _) | def::DefStruct(did) => did, - _ => { - ccx.sess().bug(format!("get_wrapper_for_bare_fn: \ - expected a statically resolved fn, got \ - {}", - def).as_slice()); - } - }; - - match ccx.closure_bare_wrapper_cache().borrow().get(&fn_ptr) { - Some(&llval) => return llval, - None => {} - } - - let tcx = ccx.tcx(); - - debug!("get_wrapper_for_bare_fn(closure_ty={})", closure_ty.repr(tcx)); - - let f = match ty::get(closure_ty).sty { - ty::ty_closure(ref f) => f, - _ => { - ccx.sess().bug(format!("get_wrapper_for_bare_fn: \ - expected a closure ty, got {}", - closure_ty.repr(tcx)).as_slice()); - } - }; - - let name = ty::with_path(tcx, def_id, |path| { - mangle_internal_name_by_path_and_seq(path, "as_closure") - }); - let llfn = if is_local { - decl_internal_rust_fn(ccx, closure_ty, name.as_slice()) - } else { - decl_rust_fn(ccx, closure_ty, name.as_slice()) - }; - - ccx.closure_bare_wrapper_cache().borrow_mut().insert(fn_ptr, llfn); - - // This is only used by statics inlined from a different crate. - if !is_local { - // Don't regenerate the wrapper, just reuse the original one. - return llfn; - } - - let _icx = push_ctxt("closure::get_wrapper_for_bare_fn"); - - let arena = TypedArena::new(); - let empty_param_substs = param_substs::empty(); - let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output, - &empty_param_substs, None, &arena); - let bcx = init_function(&fcx, true, f.sig.output); - - let args = create_datums_for_fn_args(&fcx, - ty::ty_fn_args(closure_ty) - .as_slice()); - let mut llargs = Vec::new(); - match fcx.llretslotptr.get() { - Some(llretptr) => { - assert!(!fcx.needs_ret_allocas); - llargs.push(llretptr); - } - None => {} - } - llargs.extend(args.iter().map(|arg| arg.val)); - - let retval = Call(bcx, fn_ptr, llargs.as_slice(), None); - match f.sig.output { - ty::FnConverging(output_type) => { - if return_type_is_void(ccx, output_type) || fcx.llretslotptr.get().is_some() { - RetVoid(bcx); - } else { - Ret(bcx, retval); - } - } - ty::FnDiverging => { - RetVoid(bcx); - } - } - - // HACK(eddyb) finish_fn cannot be used here, we returned directly. - debuginfo::clear_source_location(&fcx); - fcx.cleanup(); - - llfn -} - -pub fn make_closure_from_bare_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - closure_ty: ty::t, - def: def::Def, - fn_ptr: ValueRef) - -> DatumBlock<'blk, 'tcx, Expr> { - let scratch = rvalue_scratch_datum(bcx, closure_ty, "__adjust"); - let wrapper = get_wrapper_for_bare_fn(bcx.ccx(), closure_ty, def, fn_ptr, true); - fill_fn_pair(bcx, scratch.val, wrapper, C_null(Type::i8p(bcx.ccx()))); - - DatumBlock::new(bcx, scratch.to_expr_datum()) -} diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs deleted file mode 100644 index e4bfdf74d85..00000000000 --- a/src/librustc/middle/trans/common.rs +++ /dev/null @@ -1,955 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_camel_case_types, non_snake_case)] - -//! Code that is useful in various trans modules. - -pub use self::ExprOrMethodCall::*; - -use driver::session::Session; -use llvm; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; -use llvm::{True, False, Bool}; -use middle::def; -use middle::lang_items::LangItem; -use middle::mem_categorization as mc; -use middle::subst; -use middle::subst::Subst; -use middle::trans::base; -use middle::trans::build; -use middle::trans::cleanup; -use middle::trans::datum; -use middle::trans::debuginfo; -use middle::trans::machine; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::traits; -use middle::ty; -use middle::ty_fold; -use middle::ty_fold::TypeFoldable; -use middle::typeck; -use middle::typeck::infer; -use util::ppaux::Repr; -use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; - -use arena::TypedArena; -use libc::{c_uint, c_char}; -use std::c_str::ToCStr; -use std::cell::{Cell, RefCell}; -use std::rc::Rc; -use std::vec::Vec; -use syntax::ast::Ident; -use syntax::ast; -use syntax::ast_map::{PathElem, PathName}; -use syntax::codemap::Span; -use syntax::parse::token::InternedString; -use syntax::parse::token; - -pub use middle::trans::context::CrateContext; - -fn type_is_newtype_immediate(ccx: &CrateContext, ty: ty::t) -> bool { - match ty::get(ty).sty { - ty::ty_struct(def_id, ref substs) => { - let fields = ty::struct_fields(ccx.tcx(), def_id, substs); - fields.len() == 1 && - fields[0].name == - token::special_idents::unnamed_field.name && - type_is_immediate(ccx, fields[0].mt.ty) - } - _ => false - } -} - -pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { - use middle::trans::machine::llsize_of_alloc; - use middle::trans::type_of::sizing_type_of; - - let tcx = ccx.tcx(); - let simple = ty::type_is_scalar(ty) || - ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || - type_is_newtype_immediate(ccx, ty) || - ty::type_is_simd(tcx, ty); - if simple && !ty::type_is_fat_ptr(tcx, ty) { - return true; - } - if !ty::type_is_sized(tcx, ty) { - return false; - } - match ty::get(ty).sty { - ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | - ty::ty_unboxed_closure(..) => { - let llty = sizing_type_of(ccx, ty); - llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type()) - } - _ => type_is_zero_size(ccx, ty) - } -} - -pub fn type_is_zero_size(ccx: &CrateContext, ty: ty::t) -> bool { - /*! - * Identify types which have size zero at runtime. - */ - - use middle::trans::machine::llsize_of_alloc; - use middle::trans::type_of::sizing_type_of; - let llty = sizing_type_of(ccx, ty); - llsize_of_alloc(ccx, llty) == 0 -} - -pub fn return_type_is_void(ccx: &CrateContext, ty: ty::t) -> bool { - /*! - * Identifies types which we declare to be equivalent to `void` - * in C for the purpose of function return types. These are - * `()`, bot, and uninhabited enums. Note that all such types - * are also zero-size, but not all zero-size types use a `void` - * return type (in order to aid with C ABI compatibility). - */ - - ty::type_is_nil(ty) || ty::type_is_empty(ccx.tcx(), ty) -} - -/// Generates a unique symbol based off the name given. This is used to create -/// unique symbols for things like closures. -pub fn gensym_name(name: &str) -> PathElem { - let num = token::gensym(name).uint(); - // use one colon which will get translated to a period by the mangler, and - // we're guaranteed that `num` is globally unique for this crate. - PathName(token::gensym(format!("{}:{}", name, num).as_slice())) -} - -pub struct tydesc_info { - pub ty: ty::t, - pub tydesc: ValueRef, - pub size: ValueRef, - pub align: ValueRef, - pub name: ValueRef, -} - -/* - * A note on nomenclature of linking: "extern", "foreign", and "upcall". - * - * An "extern" is an LLVM symbol we wind up emitting an undefined external - * reference to. This means "we don't have the thing in this compilation unit, - * please make sure you link it in at runtime". This could be a reference to - * C code found in a C library, or rust code found in a rust crate. - * - * Most "externs" are implicitly declared (automatically) as a result of a - * user declaring an extern _module_ dependency; this causes the rust driver - * to locate an extern crate, scan its compilation metadata, and emit extern - * declarations for any symbols used by the declaring crate. - * - * A "foreign" is an extern that references C (or other non-rust ABI) code. - * There is no metadata to scan for extern references so in these cases either - * a header-digester like bindgen, or manual function prototypes, have to - * serve as declarators. So these are usually given explicitly as prototype - * declarations, in rust code, with ABI attributes on them noting which ABI to - * link via. - * - * An "upcall" is a foreign call generated by the compiler (not corresponding - * to any user-written call in the code) into the runtime library, to perform - * some helper task such as bringing a task to life, allocating memory, etc. - * - */ - -pub struct NodeInfo { - pub id: ast::NodeId, - pub span: Span, -} - -pub fn expr_info(expr: &ast::Expr) -> NodeInfo { - NodeInfo { id: expr.id, span: expr.span } -} - -pub struct BuilderRef_res { - pub b: BuilderRef, -} - -impl Drop for BuilderRef_res { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(self.b); - } - } -} - -pub fn BuilderRef_res(b: BuilderRef) -> BuilderRef_res { - BuilderRef_res { - b: b - } -} - -pub type ExternMap = FnvHashMap; - -// Here `self_ty` is the real type of the self parameter to this method. It -// will only be set in the case of default methods. -pub struct param_substs { - pub substs: subst::Substs, -} - -impl param_substs { - pub fn empty() -> param_substs { - param_substs { - substs: subst::Substs::trans_empty(), - } - } - - pub fn validate(&self) { - assert!(self.substs.types.all(|t| !ty::type_needs_infer(*t))); - } -} - -impl Repr for param_substs { - fn repr(&self, tcx: &ty::ctxt) -> String { - self.substs.repr(tcx) - } -} - -pub trait SubstP { - fn substp(&self, tcx: &ty::ctxt, param_substs: ¶m_substs) - -> Self; -} - -impl SubstP for T { - fn substp(&self, tcx: &ty::ctxt, substs: ¶m_substs) -> T { - self.subst(tcx, &substs.substs) - } -} - -// work around bizarre resolve errors -pub type RvalueDatum = datum::Datum; -pub type LvalueDatum = datum::Datum; - -// Function context. Every LLVM function we create will have one of -// these. -pub struct FunctionContext<'a, 'tcx: 'a> { - // The ValueRef returned from a call to llvm::LLVMAddFunction; the - // address of the first instruction in the sequence of - // instructions for this function that will go in the .text - // section of the executable we're generating. - pub llfn: ValueRef, - - // The environment argument in a closure. - pub llenv: Option, - - // A pointer to where to store the return value. If the return type is - // immediate, this points to an alloca in the function. Otherwise, it's a - // pointer to the hidden first parameter of the function. After function - // construction, this should always be Some. - pub llretslotptr: Cell>, - - // These pub elements: "hoisted basic blocks" containing - // administrative activities that have to happen in only one place in - // the function, due to LLVM's quirks. - // A marker for the place where we want to insert the function's static - // allocas, so that LLVM will coalesce them into a single alloca call. - pub alloca_insert_pt: Cell>, - pub llreturn: Cell>, - - // If the function has any nested return's, including something like: - // fn foo() -> Option { Some(Foo { x: return None }) }, then - // we use a separate alloca for each return - pub needs_ret_allocas: bool, - - // The a value alloca'd for calls to upcalls.rust_personality. Used when - // outputting the resume instruction. - pub personality: Cell>, - - // True if the caller expects this fn to use the out pointer to - // return. Either way, your code should write into the slot llretslotptr - // points to, but if this value is false, that slot will be a local alloca. - pub caller_expects_out_pointer: bool, - - // Maps the DefId's for local variables to the allocas created for - // them in llallocas. - pub lllocals: RefCell>, - - // Same as above, but for closure upvars - pub llupvars: RefCell>, - - // The NodeId of the function, or -1 if it doesn't correspond to - // a user-defined function. - pub id: ast::NodeId, - - // If this function is being monomorphized, this contains the type - // substitutions used. - pub param_substs: &'a param_substs, - - // The source span and nesting context where this function comes from, for - // error reporting and symbol generation. - pub span: Option, - - // The arena that blocks are allocated from. - pub block_arena: &'a TypedArena>, - - // This function's enclosing crate context. - pub ccx: &'a CrateContext<'a, 'tcx>, - - // Used and maintained by the debuginfo module. - pub debug_context: debuginfo::FunctionDebugContext, - - // Cleanup scopes. - pub scopes: RefCell>>, -} - -impl<'a, 'tcx> FunctionContext<'a, 'tcx> { - pub fn arg_pos(&self, arg: uint) -> uint { - let arg = self.env_arg_pos() + arg; - if self.llenv.is_some() { - arg + 1 - } else { - arg - } - } - - pub fn out_arg_pos(&self) -> uint { - assert!(self.caller_expects_out_pointer); - 0u - } - - pub fn env_arg_pos(&self) -> uint { - if self.caller_expects_out_pointer { - 1u - } else { - 0u - } - } - - pub fn cleanup(&self) { - unsafe { - llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt - .get() - .unwrap()); - } - } - - pub fn get_llreturn(&self) -> BasicBlockRef { - if self.llreturn.get().is_none() { - - self.llreturn.set(Some(unsafe { - "return".with_c_str(|buf| { - llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf) - }) - })) - } - - self.llreturn.get().unwrap() - } - - pub fn get_ret_slot(&self, bcx: Block, output: ty::FnOutput, name: &str) -> ValueRef { - if self.needs_ret_allocas { - base::alloca_no_lifetime(bcx, match output { - ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type), - ty::FnDiverging => Type::void(bcx.ccx()) - }, name) - } else { - self.llretslotptr.get().unwrap() - } - } - - pub fn new_block(&'a self, - is_lpad: bool, - name: &str, - opt_node_id: Option) - -> Block<'a, 'tcx> { - unsafe { - let llbb = name.with_c_str(|buf| { - llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), - self.llfn, - buf) - }); - BlockS::new(llbb, is_lpad, opt_node_id, self) - } - } - - pub fn new_id_block(&'a self, - name: &str, - node_id: ast::NodeId) - -> Block<'a, 'tcx> { - self.new_block(false, name, Some(node_id)) - } - - pub fn new_temp_block(&'a self, - name: &str) - -> Block<'a, 'tcx> { - self.new_block(false, name, None) - } - - pub fn join_blocks(&'a self, - id: ast::NodeId, - in_cxs: &[Block<'a, 'tcx>]) - -> Block<'a, 'tcx> { - let out = self.new_id_block("join", id); - let mut reachable = false; - for bcx in in_cxs.iter() { - if !bcx.unreachable.get() { - build::Br(*bcx, out.llbb); - reachable = true; - } - } - if !reachable { - build::Unreachable(out); - } - return out; - } -} - -// Basic block context. We create a block context for each basic block -// (single-entry, single-exit sequence of instructions) we generate from Rust -// code. Each basic block we generate is attached to a function, typically -// with many basic blocks per function. All the basic blocks attached to a -// function are organized as a directed graph. -pub struct BlockS<'blk, 'tcx: 'blk> { - // The BasicBlockRef returned from a call to - // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic - // block to the function pointed to by llfn. We insert - // instructions into that block by way of this block context. - // The block pointing to this one in the function's digraph. - pub llbb: BasicBlockRef, - pub terminated: Cell, - pub unreachable: Cell, - - // Is this block part of a landing pad? - pub is_lpad: bool, - - // AST node-id associated with this block, if any. Used for - // debugging purposes only. - pub opt_node_id: Option, - - // The function context for the function to which this block is - // attached. - pub fcx: &'blk FunctionContext<'blk, 'tcx>, -} - -pub type Block<'blk, 'tcx> = &'blk BlockS<'blk, 'tcx>; - -impl<'blk, 'tcx> BlockS<'blk, 'tcx> { - pub fn new(llbb: BasicBlockRef, - is_lpad: bool, - opt_node_id: Option, - fcx: &'blk FunctionContext<'blk, 'tcx>) - -> Block<'blk, 'tcx> { - fcx.block_arena.alloc(BlockS { - llbb: llbb, - terminated: Cell::new(false), - unreachable: Cell::new(false), - is_lpad: is_lpad, - opt_node_id: opt_node_id, - fcx: fcx - }) - } - - pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> { - self.fcx.ccx - } - pub fn tcx(&self) -> &'blk ty::ctxt<'tcx> { - self.fcx.ccx.tcx() - } - pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() } - - pub fn ident(&self, ident: Ident) -> String { - token::get_ident(ident).get().to_string() - } - - pub fn node_id_to_string(&self, id: ast::NodeId) -> String { - self.tcx().map.node_to_string(id).to_string() - } - - pub fn expr_to_string(&self, e: &ast::Expr) -> String { - e.repr(self.tcx()) - } - - pub fn def(&self, nid: ast::NodeId) -> def::Def { - match self.tcx().def_map.borrow().get(&nid) { - Some(v) => v.clone(), - None => { - self.tcx().sess.bug(format!( - "no def associated with node id {}", nid).as_slice()); - } - } - } - - pub fn val_to_string(&self, val: ValueRef) -> String { - self.ccx().tn().val_to_string(val) - } - - pub fn llty_str(&self, ty: Type) -> String { - self.ccx().tn().type_to_string(ty) - } - - pub fn ty_to_string(&self, t: ty::t) -> String { - t.repr(self.tcx()) - } - - pub fn to_str(&self) -> String { - format!("[block {:p}]", self) - } -} - -impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { - fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { - self.tcx() - } - - fn node_ty(&self, id: ast::NodeId) -> mc::McResult { - Ok(node_id_type(self, id)) - } - - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { - self.tcx() - .method_map - .borrow() - .get(&method_call) - .map(|method| monomorphize_type(self, method.ty)) - } - - fn adjustments<'a>(&'a self) -> &'a RefCell> { - &self.tcx().adjustments - } - - fn is_method_call(&self, id: ast::NodeId) -> bool { - self.tcx().method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) - } - - fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { - self.tcx().region_maps.temporary_scope(rvalue_id) - } - - fn unboxed_closures<'a>(&'a self) - -> &'a RefCell> { - &self.tcx().unboxed_closures - } - - fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { - self.tcx().upvar_borrow_map.borrow()[upvar_id].clone() - } - - fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause { - self.tcx().capture_modes.borrow()[closure_expr_id].clone() - } -} - -pub struct Result<'blk, 'tcx: 'blk> { - pub bcx: Block<'blk, 'tcx>, - pub val: ValueRef -} - -impl<'b, 'tcx> Result<'b, 'tcx> { - pub fn new(bcx: Block<'b, 'tcx>, val: ValueRef) -> Result<'b, 'tcx> { - Result { - bcx: bcx, - val: val, - } - } -} - -pub fn val_ty(v: ValueRef) -> Type { - unsafe { - Type::from_ref(llvm::LLVMTypeOf(v)) - } -} - -// LLVM constant constructors. -pub fn C_null(t: Type) -> ValueRef { - unsafe { - llvm::LLVMConstNull(t.to_ref()) - } -} - -pub fn C_undef(t: Type) -> ValueRef { - unsafe { - llvm::LLVMGetUndef(t.to_ref()) - } -} - -pub fn C_integral(t: Type, u: u64, sign_extend: bool) -> ValueRef { - unsafe { - llvm::LLVMConstInt(t.to_ref(), u, sign_extend as Bool) - } -} - -pub fn C_floating(s: &str, t: Type) -> ValueRef { - unsafe { - s.with_c_str(|buf| llvm::LLVMConstRealOfString(t.to_ref(), buf)) - } -} - -pub fn C_nil(ccx: &CrateContext) -> ValueRef { - C_struct(ccx, &[], false) -} - -pub fn C_bool(ccx: &CrateContext, val: bool) -> ValueRef { - C_integral(Type::i1(ccx), val as u64, false) -} - -pub fn C_i32(ccx: &CrateContext, i: i32) -> ValueRef { - C_integral(Type::i32(ccx), i as u64, true) -} - -pub fn C_i64(ccx: &CrateContext, i: i64) -> ValueRef { - C_integral(Type::i64(ccx), i as u64, true) -} - -pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef { - C_integral(Type::i64(ccx), i, false) -} - -pub fn C_int(ccx: &CrateContext, i: I) -> ValueRef { - let v = i.as_i64(); - - match machine::llbitsize_of_real(ccx, ccx.int_type()) { - 32 => assert!(v < (1<<31) && v >= -(1<<31)), - 64 => {}, - n => panic!("unsupported target size: {}", n) - } - - C_integral(ccx.int_type(), v as u64, true) -} - -pub fn C_uint(ccx: &CrateContext, i: I) -> ValueRef { - let v = i.as_u64(); - - match machine::llbitsize_of_real(ccx, ccx.int_type()) { - 32 => assert!(v < (1<<32)), - 64 => {}, - n => panic!("unsupported target size: {}", n) - } - - C_integral(ccx.int_type(), v, false) -} - -pub trait AsI64 { fn as_i64(self) -> i64; } -pub trait AsU64 { fn as_u64(self) -> u64; } - -// FIXME: remove the intptr conversions, because they -// are host-architecture-dependent -impl AsI64 for i64 { fn as_i64(self) -> i64 { self as i64 }} -impl AsI64 for i32 { fn as_i64(self) -> i64 { self as i64 }} -impl AsI64 for int { fn as_i64(self) -> i64 { self as i64 }} - -impl AsU64 for u64 { fn as_u64(self) -> u64 { self as u64 }} -impl AsU64 for u32 { fn as_u64(self) -> u64 { self as u64 }} -impl AsU64 for uint { fn as_u64(self) -> u64 { self as u64 }} - -pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef { - C_integral(Type::i8(ccx), i as u64, false) -} - - -// This is a 'c-like' raw string, which differs from -// our boxed-and-length-annotated strings. -pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> ValueRef { - unsafe { - match cx.const_cstr_cache().borrow().get(&s) { - Some(&llval) => return llval, - None => () - } - - let sc = llvm::LLVMConstStringInContext(cx.llcx(), - s.get().as_ptr() as *const c_char, - s.get().len() as c_uint, - !null_terminated as Bool); - - let gsym = token::gensym("str"); - let g = format!("str{}", gsym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf) - }); - llvm::LLVMSetInitializer(g, sc); - llvm::LLVMSetGlobalConstant(g, True); - llvm::SetLinkage(g, llvm::InternalLinkage); - - cx.const_cstr_cache().borrow_mut().insert(s, g); - g - } -} - -// NB: Do not use `do_spill_noroot` to make this into a constant string, or -// you will be kicked off fast isel. See issue #4352 for an example of this. -pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef { - unsafe { - let len = s.get().len(); - let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s, false), - Type::i8p(cx).to_ref()); - C_named_struct(cx.tn().find_type("str_slice").unwrap(), &[cs, C_uint(cx, len)]) - } -} - -pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { - unsafe { - let len = data.len(); - let lldata = C_bytes(cx, data); - - let gsym = token::gensym("binary"); - let g = format!("binary{}", gsym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), buf) - }); - llvm::LLVMSetInitializer(g, lldata); - llvm::LLVMSetGlobalConstant(g, True); - llvm::SetLinkage(g, llvm::InternalLinkage); - - let cs = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); - C_struct(cx, &[cs, C_uint(cx, len)], false) - } -} - -pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { - C_struct_in_context(cx.llcx(), elts, packed) -} - -pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { - unsafe { - llvm::LLVMConstStructInContext(llcx, - elts.as_ptr(), elts.len() as c_uint, - packed as Bool) - } -} - -pub fn C_named_struct(t: Type, elts: &[ValueRef]) -> ValueRef { - unsafe { - llvm::LLVMConstNamedStruct(t.to_ref(), elts.as_ptr(), elts.len() as c_uint) - } -} - -pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { - unsafe { - return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); - } -} - -pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef { - C_bytes_in_context(cx.llcx(), bytes) -} - -pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { - unsafe { - let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); - } -} - -pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint]) - -> ValueRef { - unsafe { - let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); - - debug!("const_get_elt(v={}, us={}, r={})", - cx.tn().val_to_string(v), us, cx.tn().val_to_string(r)); - - return r; - } -} - -pub fn is_const(v: ValueRef) -> bool { - unsafe { - llvm::LLVMIsConstant(v) == True - } -} - -pub fn const_to_int(v: ValueRef) -> i64 { - unsafe { - llvm::LLVMConstIntGetSExtValue(v) - } -} - -pub fn const_to_uint(v: ValueRef) -> u64 { - unsafe { - llvm::LLVMConstIntGetZExtValue(v) - } -} - -pub fn is_undef(val: ValueRef) -> bool { - unsafe { - llvm::LLVMIsUndef(val) != False - } -} - -pub fn is_null(val: ValueRef) -> bool { - unsafe { - llvm::LLVMIsNull(val) != False - } -} - -pub fn monomorphize_type(bcx: &BlockS, t: ty::t) -> ty::t { - t.subst(bcx.tcx(), &bcx.fcx.param_substs.substs) -} - -pub fn node_id_type(bcx: &BlockS, id: ast::NodeId) -> ty::t { - let tcx = bcx.tcx(); - let t = ty::node_id_to_type(tcx, id); - monomorphize_type(bcx, t) -} - -pub fn expr_ty(bcx: Block, ex: &ast::Expr) -> ty::t { - node_id_type(bcx, ex.id) -} - -pub fn expr_ty_adjusted(bcx: Block, ex: &ast::Expr) -> ty::t { - monomorphize_type(bcx, ty::expr_ty_adjusted(bcx.tcx(), ex)) -} - -pub fn fulfill_obligation(ccx: &CrateContext, - span: Span, - trait_ref: Rc) - -> traits::Vtable<()> -{ - /*! - * Attempts to resolve an obligation. The result is a shallow - * vtable resolution -- meaning that we do not (necessarily) resolve - * all nested obligations on the impl. Note that type check should - * guarantee to us that all nested obligations *could be* resolved - * if we wanted to. - */ - - let tcx = ccx.tcx(); - - // Remove any references to regions; this helps improve caching. - let trait_ref = ty_fold::erase_regions(tcx, trait_ref); - - // First check the cache. - match ccx.trait_cache().borrow().get(&trait_ref) { - Some(vtable) => { - info!("Cache hit: {}", trait_ref.repr(ccx.tcx())); - return (*vtable).clone(); - } - None => { } - } - - ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id); - let infcx = infer::new_infer_ctxt(tcx); - - // Parameter environment is used to give details about type parameters, - // but since we are in trans, everything is fully monomorphized. - let param_env = ty::empty_parameter_environment(); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env, tcx); - let obligation = traits::Obligation::misc(span, trait_ref.clone()); - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - debug!("Encountered ambiguity selecting `{}` during trans, \ - presuming due to overflow", - trait_ref.repr(tcx)); - ccx.sess().span_fatal( - span, - "reached the recursion limit during monomorphization"); - } - Err(e) => { - tcx.sess.span_bug( - span, - format!("Encountered error `{}` selecting `{}` during trans", - e.repr(tcx), - trait_ref.repr(tcx)).as_slice()) - } - }; - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. However, in principle, - // we only need to do this until the impl's type parameters are - // fully bound. It could be a slight optimization to stop - // iterating early. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map_move_nested(|obligation| { - fulfill_cx.register_obligation(tcx, obligation); - }); - match fulfill_cx.select_all_or_error(&infcx, ¶m_env, tcx) { - Ok(()) => { } - Err(errors) => { - if errors.iter().all(|e| e.is_overflow()) { - // See Ok(None) case above. - ccx.sess().span_fatal( - span, - "reached the recursion limit during monomorphization"); - } else { - tcx.sess.span_bug( - span, - format!("Encountered errors `{}` fulfilling `{}` during trans", - errors.repr(tcx), - trait_ref.repr(tcx)).as_slice()); - } - } - } - - // Use skolemize to simultaneously replace all type variables with - // their bindings and replace all regions with 'static. This is - // sort of overkill because we do not expect there to be any - // unbound type variables, hence no skolemized types should ever - // be inserted. - let vtable = vtable.fold_with(&mut infcx.skolemizer()); - - info!("Cache miss: {}", trait_ref.repr(ccx.tcx())); - ccx.trait_cache().borrow_mut().insert(trait_ref, - vtable.clone()); - - vtable -} - -// Key used to lookup values supplied for type parameters in an expr. -#[deriving(PartialEq, Show)] -pub enum ExprOrMethodCall { - // Type parameters for a path like `None::` - ExprId(ast::NodeId), - - // Type parameters for a method call like `a.foo::()` - MethodCall(typeck::MethodCall) -} - -pub fn node_id_substs(bcx: Block, - node: ExprOrMethodCall) - -> subst::Substs -{ - let tcx = bcx.tcx(); - - let substs = match node { - ExprId(id) => { - ty::node_id_item_substs(tcx, id).substs - } - MethodCall(method_call) => { - (*tcx.method_map.borrow())[method_call].substs.clone() - } - }; - - if substs.types.any(|t| ty::type_needs_infer(*t)) { - bcx.sess().bug( - format!("type parameters for node {} include inference types: \ - {}", - node, - substs.repr(bcx.tcx())).as_slice()); - } - - let substs = substs.erase_regions(); - substs.substp(tcx, bcx.fcx.param_substs) -} - -pub fn langcall(bcx: Block, - span: Option, - msg: &str, - li: LangItem) - -> ast::DefId { - match bcx.tcx().lang_items.require(li) { - Ok(id) => id, - Err(s) => { - let msg = format!("{} {}", msg, s); - match span { - Some(span) => bcx.tcx().sess.span_fatal(span, msg.as_slice()), - None => bcx.tcx().sess.fatal(msg.as_slice()), - } - } - } -} diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs deleted file mode 100644 index 18c9ef98244..00000000000 --- a/src/librustc/middle/trans/consts.rs +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use back::abi; -use llvm; -use llvm::{ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True, False}; -use llvm::{IntEQ, IntNE, IntUGT, IntUGE, IntULT, IntULE, IntSGT, IntSGE, IntSLT, IntSLE, - RealOEQ, RealOGT, RealOGE, RealOLT, RealOLE, RealONE}; -use metadata::csearch; -use middle::const_eval; -use middle::def; -use middle::trans::adt; -use middle::trans::base; -use middle::trans::base::push_ctxt; -use middle::trans::closure; -use middle::trans::common::*; -use middle::trans::consts; -use middle::trans::expr; -use middle::trans::inline; -use middle::trans::machine; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::trans::debuginfo; -use middle::ty; -use util::ppaux::{Repr, ty_to_string}; - -use std::c_str::ToCStr; -use libc::c_uint; -use syntax::{ast, ast_util}; -use syntax::ptr::P; - -pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit) - -> ValueRef { - let _icx = push_ctxt("trans_lit"); - debug!("const_lit: {}", lit); - match lit.node { - ast::LitByte(b) => C_integral(Type::uint_from_ty(cx, ast::TyU8), b as u64, false), - ast::LitChar(i) => C_integral(Type::char(cx), i as u64, false), - ast::LitInt(i, ast::SignedIntLit(t, _)) => { - C_integral(Type::int_from_ty(cx, t), i, true) - } - ast::LitInt(u, ast::UnsignedIntLit(t)) => { - C_integral(Type::uint_from_ty(cx, t), u, false) - } - ast::LitInt(i, ast::UnsuffixedIntLit(_)) => { - let lit_int_ty = ty::node_id_to_type(cx.tcx(), e.id); - match ty::get(lit_int_ty).sty { - ty::ty_int(t) => { - C_integral(Type::int_from_ty(cx, t), i as u64, true) - } - ty::ty_uint(t) => { - C_integral(Type::uint_from_ty(cx, t), i as u64, false) - } - _ => cx.sess().span_bug(lit.span, - format!("integer literal has type {} (expected int \ - or uint)", - ty_to_string(cx.tcx(), lit_int_ty)).as_slice()) - } - } - ast::LitFloat(ref fs, t) => { - C_floating(fs.get(), Type::float_from_ty(cx, t)) - } - ast::LitFloatUnsuffixed(ref fs) => { - let lit_float_ty = ty::node_id_to_type(cx.tcx(), e.id); - match ty::get(lit_float_ty).sty { - ty::ty_float(t) => { - C_floating(fs.get(), Type::float_from_ty(cx, t)) - } - _ => { - cx.sess().span_bug(lit.span, - "floating point literal doesn't have the right type"); - } - } - } - ast::LitBool(b) => C_bool(cx, b), - ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()), - ast::LitBinary(ref data) => C_binary_slice(cx, data.as_slice()), - } -} - -pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef { - unsafe { - let b = llvm::LLVMConstPointerCast(a, t.ptr_to().to_ref()); - assert!(cx.const_globals().borrow_mut().insert(b as int, a).is_none()); - b - } -} - -fn const_vec(cx: &CrateContext, e: &ast::Expr, - es: &[P]) -> (ValueRef, Type) { - let vec_ty = ty::expr_ty(cx.tcx(), e); - let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); - let llunitty = type_of::type_of(cx, unit_ty); - let vs = es.iter().map(|e| const_expr(cx, &**e).val0()) - .collect::>(); - // If the vector contains enums, an LLVM array won't work. - let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) { - C_struct(cx, vs.as_slice(), false) - } else { - C_array(llunitty, vs.as_slice()) - }; - (v, llunitty) -} - -pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef { - unsafe { - let gv = "const".with_c_str(|name| { - llvm::LLVMAddGlobal(cx.llmod(), val_ty(cv).to_ref(), name) - }); - llvm::LLVMSetInitializer(gv, cv); - llvm::LLVMSetGlobalConstant(gv, - if mutbl == ast::MutImmutable {True} else {False}); - SetLinkage(gv, PrivateLinkage); - gv - } -} - -fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { - let v = match cx.const_globals().borrow().get(&(v as int)) { - Some(&v) => v, - None => v - }; - unsafe { - llvm::LLVMGetInitializer(v) - } -} - -fn const_deref_newtype(cx: &CrateContext, v: ValueRef, t: ty::t) - -> ValueRef { - let repr = adt::represent_type(cx, t); - adt::const_get_field(cx, &*repr, v, 0, 0) -} - -fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool) - -> (ValueRef, ty::t) { - match ty::deref(t, explicit) { - Some(ref mt) => { - match ty::get(t).sty { - ty::ty_ptr(mt) | ty::ty_rptr(_, mt) => { - if ty::type_is_sized(cx.tcx(), mt.ty) { - (const_deref_ptr(cx, v), mt.ty) - } else { - // Derefing a fat pointer does not change the representation, - // just the type to ty_open. - (v, ty::mk_open(cx.tcx(), mt.ty)) - } - } - ty::ty_enum(..) | ty::ty_struct(..) => { - assert!(mt.mutbl != ast::MutMutable); - (const_deref_newtype(cx, v, t), mt.ty) - } - _ => { - cx.sess().bug(format!("unexpected dereferenceable type {}", - ty_to_string(cx.tcx(), t)).as_slice()) - } - } - } - None => { - cx.sess().bug(format!("cannot dereference const of type {}", - ty_to_string(cx.tcx(), t)).as_slice()) - } - } -} - -pub fn get_const_val(cx: &CrateContext, - mut def_id: ast::DefId) -> ValueRef { - let contains_key = cx.const_values().borrow().contains_key(&def_id.node); - if !ast_util::is_local(def_id) || !contains_key { - if !ast_util::is_local(def_id) { - def_id = inline::maybe_instantiate_inline(cx, def_id); - } - - match cx.tcx().map.expect_item(def_id.node).node { - ast::ItemConst(..) => { base::get_item_val(cx, def_id.node); } - _ => {} - } - } - - cx.const_values().borrow()[def_id.node].clone() -} - -pub fn const_expr(cx: &CrateContext, e: &ast::Expr) -> (ValueRef, ty::t) { - let llconst = const_expr_unadjusted(cx, e); - let mut llconst = llconst; - let ety = ty::expr_ty(cx.tcx(), e); - let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); - let opt_adj = cx.tcx().adjustments.borrow().get(&e.id).cloned(); - match opt_adj { - None => { } - Some(adj) => { - match adj { - ty::AdjustAddEnv(ty::RegionTraitStore(ty::ReStatic, _)) => { - let def = ty::resolve_expr(cx.tcx(), e); - let wrapper = closure::get_wrapper_for_bare_fn(cx, - ety_adjusted, - def, - llconst, - true); - llconst = C_struct(cx, &[wrapper, C_null(Type::i8p(cx))], false) - } - ty::AdjustAddEnv(store) => { - cx.sess() - .span_bug(e.span, - format!("unexpected static function: {}", - store).as_slice()) - } - ty::AdjustDerefRef(ref adj) => { - let mut ty = ety; - // Save the last autoderef in case we can avoid it. - if adj.autoderefs > 0 { - for _ in range(0, adj.autoderefs-1) { - let (dv, dt) = const_deref(cx, llconst, ty, false); - llconst = dv; - ty = dt; - } - } - - match adj.autoref { - None => { - let (dv, dt) = const_deref(cx, llconst, ty, false); - llconst = dv; - - // If we derefed a fat pointer then we will have an - // open type here. So we need to update the type with - // the one returned from const_deref. - ety_adjusted = dt; - } - Some(ref autoref) => { - match *autoref { - ty::AutoUnsafe(_, None) | - ty::AutoPtr(ty::ReStatic, _, None) => { - // Don't copy data to do a deref+ref - // (i.e., skip the last auto-deref). - if adj.autoderefs == 0 { - llconst = const_addr_of(cx, llconst, ast::MutImmutable); - } - } - ty::AutoPtr(ty::ReStatic, _, Some(box ty::AutoUnsize(..))) => { - if adj.autoderefs > 0 { - // Seeing as we are deref'ing here and take a reference - // again to make the pointer part of the far pointer below, - // we just skip the whole thing. We still need the type - // though. This works even if we don't need to deref - // because of byref semantics. Note that this is not just - // an optimisation, it is necessary for mutable vectors to - // work properly. - let (_, dt) = const_deref(cx, llconst, ty, false); - ty = dt; - } else { - llconst = const_addr_of(cx, llconst, ast::MutImmutable) - } - - match ty::get(ty).sty { - ty::ty_vec(unit_ty, Some(len)) => { - let llunitty = type_of::type_of(cx, unit_ty); - let llptr = const_ptrcast(cx, llconst, llunitty); - assert_eq!(abi::slice_elt_base, 0); - assert_eq!(abi::slice_elt_len, 1); - llconst = C_struct(cx, &[ - llptr, - C_uint(cx, len) - ], false); - } - _ => cx.sess().span_bug(e.span, - format!("unimplemented type in const unsize: {}", - ty_to_string(cx.tcx(), ty)).as_slice()) - } - } - _ => { - cx.sess() - .span_bug(e.span, - format!("unimplemented const \ - autoref {}", - autoref).as_slice()) - } - } - } - } - } - } - } - } - - let llty = type_of::sizing_type_of(cx, ety_adjusted); - let csize = machine::llsize_of_alloc(cx, val_ty(llconst)); - let tsize = machine::llsize_of_alloc(cx, llty); - if csize != tsize { - unsafe { - // FIXME these values could use some context - llvm::LLVMDumpValue(llconst); - llvm::LLVMDumpValue(C_undef(llty)); - } - cx.sess().bug(format!("const {} of type {} has size {} instead of {}", - e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety), - csize, tsize).as_slice()); - } - (llconst, ety_adjusted) -} - -// the bool returned is whether this expression can be inlined into other crates -// if it's assigned to a static. -fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { - let map_list = |exprs: &[P]| { - exprs.iter().map(|e| const_expr(cx, &**e).val0()) - .fold(Vec::new(), |mut l, val| { l.push(val); l }) - }; - unsafe { - let _icx = push_ctxt("const_expr"); - return match e.node { - ast::ExprLit(ref lit) => { - consts::const_lit(cx, e, &**lit) - } - ast::ExprBinary(b, ref e1, ref e2) => { - let (te1, _) = const_expr(cx, &**e1); - let (te2, _) = const_expr(cx, &**e2); - - let te2 = base::cast_shift_const_rhs(b, te1, te2); - - /* Neither type is bottom, and we expect them to be unified - * already, so the following is safe. */ - let ty = ty::expr_ty(cx.tcx(), &**e1); - let is_float = ty::type_is_fp(ty); - let signed = ty::type_is_signed(ty); - return match b { - ast::BiAdd => { - if is_float { llvm::LLVMConstFAdd(te1, te2) } - else { llvm::LLVMConstAdd(te1, te2) } - } - ast::BiSub => { - if is_float { llvm::LLVMConstFSub(te1, te2) } - else { llvm::LLVMConstSub(te1, te2) } - } - ast::BiMul => { - if is_float { llvm::LLVMConstFMul(te1, te2) } - else { llvm::LLVMConstMul(te1, te2) } - } - ast::BiDiv => { - if is_float { llvm::LLVMConstFDiv(te1, te2) } - else if signed { llvm::LLVMConstSDiv(te1, te2) } - else { llvm::LLVMConstUDiv(te1, te2) } - } - ast::BiRem => { - if is_float { llvm::LLVMConstFRem(te1, te2) } - else if signed { llvm::LLVMConstSRem(te1, te2) } - else { llvm::LLVMConstURem(te1, te2) } - } - ast::BiAnd => llvm::LLVMConstAnd(te1, te2), - ast::BiOr => llvm::LLVMConstOr(te1, te2), - ast::BiBitXor => llvm::LLVMConstXor(te1, te2), - ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2), - ast::BiBitOr => llvm::LLVMConstOr(te1, te2), - ast::BiShl => llvm::LLVMConstShl(te1, te2), - ast::BiShr => { - if signed { llvm::LLVMConstAShr(te1, te2) } - else { llvm::LLVMConstLShr(te1, te2) } - } - ast::BiEq => { - if is_float { ConstFCmp(RealOEQ, te1, te2) } - else { ConstICmp(IntEQ, te1, te2) } - }, - ast::BiLt => { - if is_float { ConstFCmp(RealOLT, te1, te2) } - else { - if signed { ConstICmp(IntSLT, te1, te2) } - else { ConstICmp(IntULT, te1, te2) } - } - }, - ast::BiLe => { - if is_float { ConstFCmp(RealOLE, te1, te2) } - else { - if signed { ConstICmp(IntSLE, te1, te2) } - else { ConstICmp(IntULE, te1, te2) } - } - }, - ast::BiNe => { - if is_float { ConstFCmp(RealONE, te1, te2) } - else { ConstICmp(IntNE, te1, te2) } - }, - ast::BiGe => { - if is_float { ConstFCmp(RealOGE, te1, te2) } - else { - if signed { ConstICmp(IntSGE, te1, te2) } - else { ConstICmp(IntUGE, te1, te2) } - } - }, - ast::BiGt => { - if is_float { ConstFCmp(RealOGT, te1, te2) } - else { - if signed { ConstICmp(IntSGT, te1, te2) } - else { ConstICmp(IntUGT, te1, te2) } - } - }, - } - }, - ast::ExprUnary(u, ref e) => { - let (te, _) = const_expr(cx, &**e); - let ty = ty::expr_ty(cx.tcx(), &**e); - let is_float = ty::type_is_fp(ty); - return match u { - ast::UnUniq | ast::UnDeref => { - let (dv, _dt) = const_deref(cx, te, ty, true); - dv - } - ast::UnNot => llvm::LLVMConstNot(te), - ast::UnNeg => { - if is_float { llvm::LLVMConstFNeg(te) } - else { llvm::LLVMConstNeg(te) } - } - } - } - ast::ExprField(ref base, field, _) => { - let (bv, bt) = const_expr(cx, &**base); - let brepr = adt::represent_type(cx, bt); - expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { - let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys); - adt::const_get_field(cx, &*brepr, bv, discr, ix) - }) - } - ast::ExprTupField(ref base, idx, _) => { - let (bv, bt) = const_expr(cx, &**base); - let brepr = adt::represent_type(cx, bt); - expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { - adt::const_get_field(cx, &*brepr, bv, discr, idx.node) - }) - } - - ast::ExprIndex(ref base, ref index) => { - let (bv, bt) = const_expr(cx, &**base); - let iv = match const_eval::eval_const_expr(cx.tcx(), &**index) { - const_eval::const_int(i) => i as u64, - const_eval::const_uint(u) => u, - _ => cx.sess().span_bug(index.span, - "index is not an integer-constant expression") - }; - let (arr, len) = match ty::get(bt).sty { - ty::ty_vec(_, Some(u)) => (bv, C_uint(cx, u)), - ty::ty_open(ty) => match ty::get(ty).sty { - ty::ty_vec(_, None) | ty::ty_str => { - let e1 = const_get_elt(cx, bv, &[0]); - (const_deref_ptr(cx, e1), const_get_elt(cx, bv, &[1])) - }, - _ => cx.sess().span_bug(base.span, - format!("index-expr base must be a vector \ - or string type, found {}", - ty_to_string(cx.tcx(), bt)).as_slice()) - }, - ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { - ty::ty_vec(_, Some(u)) => { - (const_deref_ptr(cx, bv), C_uint(cx, u)) - }, - _ => cx.sess().span_bug(base.span, - format!("index-expr base must be a vector \ - or string type, found {}", - ty_to_string(cx.tcx(), bt)).as_slice()) - }, - _ => cx.sess().span_bug(base.span, - format!("index-expr base must be a vector \ - or string type, found {}", - ty_to_string(cx.tcx(), bt)).as_slice()) - }; - - let len = llvm::LLVMConstIntGetZExtValue(len) as u64; - let len = match ty::get(bt).sty { - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { - ty::ty_str => { - assert!(len > 0); - len - 1 - } - _ => len - }, - _ => len - }; - if iv >= len { - // FIXME #3170: report this earlier on in the const-eval - // pass. Reporting here is a bit late. - cx.sess().span_err(e.span, - "const index-expr is out of bounds"); - } - const_get_elt(cx, arr, &[iv as c_uint]) - } - ast::ExprCast(ref base, _) => { - let ety = ty::expr_ty(cx.tcx(), e); - let llty = type_of::type_of(cx, ety); - let (v, basety) = const_expr(cx, &**base); - return match (expr::cast_type_kind(cx.tcx(), basety), - expr::cast_type_kind(cx.tcx(), ety)) { - - (expr::cast_integral, expr::cast_integral) => { - let s = ty::type_is_signed(basety) as Bool; - llvm::LLVMConstIntCast(v, llty.to_ref(), s) - } - (expr::cast_integral, expr::cast_float) => { - if ty::type_is_signed(basety) { - llvm::LLVMConstSIToFP(v, llty.to_ref()) - } else { - llvm::LLVMConstUIToFP(v, llty.to_ref()) - } - } - (expr::cast_float, expr::cast_float) => { - llvm::LLVMConstFPCast(v, llty.to_ref()) - } - (expr::cast_float, expr::cast_integral) => { - if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) } - else { llvm::LLVMConstFPToUI(v, llty.to_ref()) } - } - (expr::cast_enum, expr::cast_integral) => { - let repr = adt::represent_type(cx, basety); - let discr = adt::const_get_discrim(cx, &*repr, v); - let iv = C_integral(cx.int_type(), discr, false); - let ety_cast = expr::cast_type_kind(cx.tcx(), ety); - match ety_cast { - expr::cast_integral => { - let s = ty::type_is_signed(ety) as Bool; - llvm::LLVMConstIntCast(iv, llty.to_ref(), s) - } - _ => cx.sess().bug("enum cast destination is not \ - integral") - } - } - (expr::cast_pointer, expr::cast_pointer) => { - llvm::LLVMConstPointerCast(v, llty.to_ref()) - } - (expr::cast_integral, expr::cast_pointer) => { - llvm::LLVMConstIntToPtr(v, llty.to_ref()) - } - (expr::cast_pointer, expr::cast_integral) => { - llvm::LLVMConstPtrToInt(v, llty.to_ref()) - } - _ => { - cx.sess().impossible_case(e.span, - "bad combination of types for cast") - } - } - } - ast::ExprAddrOf(mutbl, ref sub) => { - // If this is the address of some static, then we need to return - // the actual address of the static itself (short circuit the rest - // of const eval). - let mut cur = sub; - loop { - match cur.node { - ast::ExprParen(ref sub) => cur = sub, - _ => break, - } - } - let opt_def = cx.tcx().def_map.borrow().get(&cur.id).cloned(); - match opt_def { - Some(def::DefStatic(def_id, _)) => { - let ty = ty::expr_ty(cx.tcx(), e); - return get_static_val(cx, def_id, ty); - } - _ => {} - } - - // If this isn't the address of a static, then keep going through - // normal constant evaluation. - let (e, _) = const_expr(cx, &**sub); - const_addr_of(cx, e, mutbl) - } - ast::ExprTup(ref es) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let vals = map_list(es.as_slice()); - adt::trans_const(cx, &*repr, 0, vals.as_slice()) - } - ast::ExprStruct(_, ref fs, ref base_opt) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let tcx = cx.tcx(); - - let base_val = match *base_opt { - Some(ref base) => Some(const_expr(cx, &**base)), - None => None - }; - - expr::with_field_tys(tcx, ety, Some(e.id), |discr, field_tys| { - let cs = field_tys.iter().enumerate() - .map(|(ix, &field_ty)| { - match fs.iter().find(|f| field_ty.name == f.ident.node.name) { - Some(ref f) => const_expr(cx, &*f.expr).val0(), - None => { - match base_val { - Some((bv, _)) => { - adt::const_get_field(cx, &*repr, bv, - discr, ix) - } - None => { - cx.sess().span_bug(e.span, - "missing struct field") - } - } - } - } - }).collect::>(); - adt::trans_const(cx, &*repr, discr, cs.as_slice()) - }) - } - ast::ExprVec(ref es) => { - const_vec(cx, e, es.as_slice()).val0() - } - ast::ExprRepeat(ref elem, ref count) => { - let vec_ty = ty::expr_ty(cx.tcx(), e); - let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); - let llunitty = type_of::type_of(cx, unit_ty); - let n = match const_eval::eval_const_expr(cx.tcx(), &**count) { - const_eval::const_int(i) => i as uint, - const_eval::const_uint(i) => i as uint, - _ => cx.sess().span_bug(count.span, "count must be integral const expression.") - }; - let vs = Vec::from_elem(n, const_expr(cx, &**elem).val0()); - if vs.iter().any(|vi| val_ty(*vi) != llunitty) { - C_struct(cx, vs.as_slice(), false) - } else { - C_array(llunitty, vs.as_slice()) - } - } - ast::ExprPath(ref pth) => { - // Assert that there are no type parameters in this path. - assert!(pth.segments.iter().all(|seg| !seg.parameters.has_types())); - - let opt_def = cx.tcx().def_map.borrow().get(&e.id).cloned(); - match opt_def { - Some(def::DefFn(def_id, _)) => { - if !ast_util::is_local(def_id) { - let ty = csearch::get_type(cx.tcx(), def_id).ty; - base::trans_external_path(cx, def_id, ty) - } else { - assert!(ast_util::is_local(def_id)); - base::get_item_val(cx, def_id.node) - } - } - Some(def::DefConst(def_id)) => { - get_const_val(cx, def_id) - } - Some(def::DefVariant(enum_did, variant_did, _)) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let vinfo = ty::enum_variant_with_id(cx.tcx(), - enum_did, - variant_did); - adt::trans_const(cx, &*repr, vinfo.disr_val, &[]) - } - Some(def::DefStruct(_)) => { - let ety = ty::expr_ty(cx.tcx(), e); - let llty = type_of::type_of(cx, ety); - C_null(llty) - } - _ => { - cx.sess().span_bug(e.span, "expected a const, fn, struct, \ - or variant def") - } - } - } - ast::ExprCall(ref callee, ref args) => { - let opt_def = cx.tcx().def_map.borrow().get(&callee.id).cloned(); - match opt_def { - Some(def::DefStruct(_)) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let arg_vals = map_list(args.as_slice()); - adt::trans_const(cx, &*repr, 0, arg_vals.as_slice()) - } - Some(def::DefVariant(enum_did, variant_did, _)) => { - let ety = ty::expr_ty(cx.tcx(), e); - let repr = adt::represent_type(cx, ety); - let vinfo = ty::enum_variant_with_id(cx.tcx(), - enum_did, - variant_did); - let arg_vals = map_list(args.as_slice()); - adt::trans_const(cx, - &*repr, - vinfo.disr_val, - arg_vals.as_slice()) - } - _ => cx.sess().span_bug(e.span, "expected a struct or variant def") - } - } - ast::ExprParen(ref e) => const_expr(cx, &**e).val0(), - ast::ExprBlock(ref block) => { - match block.expr { - Some(ref expr) => const_expr(cx, &**expr).val0(), - None => C_nil(cx) - } - } - _ => cx.sess().span_bug(e.span, - "bad constant expression type in consts::const_expr") - }; - } -} - -pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) { - unsafe { - let _icx = push_ctxt("trans_static"); - let g = base::get_item_val(ccx, id); - // At this point, get_item_val has already translated the - // constant's initializer to determine its LLVM type. - let v = ccx.static_values().borrow()[id].clone(); - // boolean SSA values are i1, but they have to be stored in i8 slots, - // otherwise some LLVM optimization passes don't work as expected - let v = if llvm::LLVMTypeOf(v) == Type::i1(ccx).to_ref() { - llvm::LLVMConstZExt(v, Type::i8(ccx).to_ref()) - } else { - v - }; - llvm::LLVMSetInitializer(g, v); - - // As an optimization, all shared statics which do not have interior - // mutability are placed into read-only memory. - if m != ast::MutMutable { - let node_ty = ty::node_id_to_type(ccx.tcx(), id); - let tcontents = ty::type_contents(ccx.tcx(), node_ty); - if !tcontents.interior_unsafe() { - llvm::LLVMSetGlobalConstant(g, True); - } - } - debuginfo::create_global_var_metadata(ccx, id, g); - } -} - -fn get_static_val(ccx: &CrateContext, did: ast::DefId, ty: ty::t) -> ValueRef { - if ast_util::is_local(did) { return base::get_item_val(ccx, did.node) } - base::trans_external_path(ccx, did, ty) -} diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs deleted file mode 100644 index ded41e56a16..00000000000 --- a/src/librustc/middle/trans/context.rs +++ /dev/null @@ -1,886 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use driver::config::NoDebugInfo; -use driver::session::Session; -use llvm; -use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; -use llvm::{TargetData}; -use llvm::mk_target_data; -use metadata::common::LinkMeta; -use middle::resolve; -use middle::traits; -use middle::trans::adt; -use middle::trans::base; -use middle::trans::builder::Builder; -use middle::trans::common::{ExternMap,tydesc_info,BuilderRef_res}; -use middle::trans::debuginfo; -use middle::trans::monomorphize::MonoId; -use middle::trans::type_::{Type, TypeNames}; -use middle::ty; -use util::ppaux::Repr; -use util::sha2::Sha256; -use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet}; - -use std::cell::{Cell, RefCell}; -use std::c_str::ToCStr; -use std::ptr; -use std::rc::Rc; -use syntax::ast; -use syntax::parse::token::InternedString; - -pub struct Stats { - pub n_static_tydescs: Cell, - pub n_glues_created: Cell, - pub n_null_glues: Cell, - pub n_real_glues: Cell, - pub n_fns: Cell, - pub n_monos: Cell, - pub n_inlines: Cell, - pub n_closures: Cell, - pub n_llvm_insns: Cell, - pub llvm_insns: RefCell>, - // (ident, llvm-instructions) - pub fn_stats: RefCell >, -} - -/// The shared portion of a `CrateContext`. There is one `SharedCrateContext` -/// per crate. The data here is shared between all compilation units of the -/// crate, so it must not contain references to any LLVM data structures -/// (aside from metadata-related ones). -pub struct SharedCrateContext<'tcx> { - local_ccxs: Vec, - - metadata_llmod: ModuleRef, - metadata_llcx: ContextRef, - - exp_map2: resolve::ExportMap2, - reachable: NodeSet, - item_symbols: RefCell>, - link_meta: LinkMeta, - symbol_hasher: RefCell, - tcx: ty::ctxt<'tcx>, - stats: Stats, - - available_monomorphizations: RefCell>, - available_drop_glues: RefCell>, -} - -/// The local portion of a `CrateContext`. There is one `LocalCrateContext` -/// per compilation unit. Each one has its own LLVM `ContextRef` so that -/// several compilation units may be optimized in parallel. All other LLVM -/// data structures in the `LocalCrateContext` are tied to that `ContextRef`. -pub struct LocalCrateContext { - llmod: ModuleRef, - llcx: ContextRef, - td: TargetData, - tn: TypeNames, - externs: RefCell, - item_vals: RefCell>, - drop_glues: RefCell>, - tydescs: RefCell>>, - /// Set when running emit_tydescs to enforce that no more tydescs are - /// created. - finished_tydescs: Cell, - /// Track mapping of external ids to local items imported for inlining - external: RefCell>>, - /// Backwards version of the `external` map (inlined items to where they - /// came from) - external_srcs: RefCell>, - /// Cache instances of monomorphized functions - monomorphized: RefCell>, - monomorphizing: RefCell>, - /// Cache generated vtables - vtables: RefCell), ValueRef>>, - /// Cache of constant strings, - const_cstr_cache: RefCell>, - - /// Reverse-direction for const ptrs cast from globals. - /// Key is an int, cast from a ValueRef holding a *T, - /// Val is a ValueRef holding a *[T]. - /// - /// Needed because LLVM loses pointer->pointee association - /// when we ptrcast, and we have to ptrcast during translation - /// of a [T] const because we form a slice, a [*T,int] pair, not - /// a pointer to an LLVM array type. - const_globals: RefCell>, - - /// Cache of emitted const values - const_values: RefCell>, - - /// Cache of emitted static values - static_values: RefCell>, - - /// Cache of external const values - extern_const_values: RefCell>, - - impl_method_cache: RefCell>, - - /// Cache of closure wrappers for bare fn's. - closure_bare_wrapper_cache: RefCell>, - - lltypes: RefCell>, - llsizingtypes: RefCell>, - adt_reprs: RefCell>>, - type_hashcodes: RefCell>, - all_llvm_symbols: RefCell>, - int_type: Type, - opaque_vec_type: Type, - builder: BuilderRef_res, - - /// Holds the LLVM values for closure IDs. - unboxed_closure_vals: RefCell>, - - dbg_cx: Option, - - eh_personality: RefCell>, - - intrinsics: RefCell>, - - /// Number of LLVM instructions translated into this `LocalCrateContext`. - /// This is used to perform some basic load-balancing to keep all LLVM - /// contexts around the same size. - n_llvm_insns: Cell, - - trait_cache: RefCell, - traits::Vtable<()>>>, -} - -pub struct CrateContext<'a, 'tcx: 'a> { - shared: &'a SharedCrateContext<'tcx>, - local: &'a LocalCrateContext, - /// The index of `local` in `shared.local_ccxs`. This is used in - /// `maybe_iter(true)` to identify the original `LocalCrateContext`. - index: uint, -} - -pub struct CrateContextIterator<'a, 'tcx: 'a> { - shared: &'a SharedCrateContext<'tcx>, - index: uint, -} - -impl<'a, 'tcx> Iterator> for CrateContextIterator<'a,'tcx> { - fn next(&mut self) -> Option> { - if self.index >= self.shared.local_ccxs.len() { - return None; - } - - let index = self.index; - self.index += 1; - - Some(CrateContext { - shared: self.shared, - local: &self.shared.local_ccxs[index], - index: index, - }) - } -} - -/// The iterator produced by `CrateContext::maybe_iter`. -pub struct CrateContextMaybeIterator<'a, 'tcx: 'a> { - shared: &'a SharedCrateContext<'tcx>, - index: uint, - single: bool, - origin: uint, -} - -impl<'a, 'tcx> Iterator<(CrateContext<'a, 'tcx>, bool)> for CrateContextMaybeIterator<'a, 'tcx> { - fn next(&mut self) -> Option<(CrateContext<'a, 'tcx>, bool)> { - if self.index >= self.shared.local_ccxs.len() { - return None; - } - - let index = self.index; - self.index += 1; - if self.single { - self.index = self.shared.local_ccxs.len(); - } - - let ccx = CrateContext { - shared: self.shared, - local: &self.shared.local_ccxs[index], - index: index, - }; - Some((ccx, index == self.origin)) - } -} - - -unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { - let llcx = llvm::LLVMContextCreate(); - let llmod = mod_name.with_c_str(|buf| { - llvm::LLVMModuleCreateWithNameInContext(buf, llcx) - }); - sess.target - .target - .data_layout - .as_slice() - .with_c_str(|buf| { - llvm::LLVMSetDataLayout(llmod, buf); - }); - sess.target - .target - .llvm_target - .as_slice() - .with_c_str(|buf| { - llvm::LLVMRustSetNormalizedTarget(llmod, buf); - }); - (llcx, llmod) -} - -impl<'tcx> SharedCrateContext<'tcx> { - pub fn new(crate_name: &str, - local_count: uint, - tcx: ty::ctxt<'tcx>, - emap2: resolve::ExportMap2, - symbol_hasher: Sha256, - link_meta: LinkMeta, - reachable: NodeSet) - -> SharedCrateContext<'tcx> { - let (metadata_llcx, metadata_llmod) = unsafe { - create_context_and_module(&tcx.sess, "metadata") - }; - - let mut shared_ccx = SharedCrateContext { - local_ccxs: Vec::with_capacity(local_count), - metadata_llmod: metadata_llmod, - metadata_llcx: metadata_llcx, - exp_map2: emap2, - reachable: reachable, - item_symbols: RefCell::new(NodeMap::new()), - link_meta: link_meta, - symbol_hasher: RefCell::new(symbol_hasher), - tcx: tcx, - stats: Stats { - n_static_tydescs: Cell::new(0u), - n_glues_created: Cell::new(0u), - n_null_glues: Cell::new(0u), - n_real_glues: Cell::new(0u), - n_fns: Cell::new(0u), - n_monos: Cell::new(0u), - n_inlines: Cell::new(0u), - n_closures: Cell::new(0u), - n_llvm_insns: Cell::new(0u), - llvm_insns: RefCell::new(FnvHashMap::new()), - fn_stats: RefCell::new(Vec::new()), - }, - available_monomorphizations: RefCell::new(FnvHashSet::new()), - available_drop_glues: RefCell::new(FnvHashMap::new()), - }; - - for i in range(0, local_count) { - // Append ".rs" to crate name as LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}.{}.rs", crate_name, i); - let local_ccx = LocalCrateContext::new(&shared_ccx, llmod_id.as_slice()); - shared_ccx.local_ccxs.push(local_ccx); - } - - shared_ccx - } - - pub fn iter<'a>(&'a self) -> CrateContextIterator<'a, 'tcx> { - CrateContextIterator { - shared: self, - index: 0, - } - } - - pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a, 'tcx> { - CrateContext { - shared: self, - local: &self.local_ccxs[index], - index: index, - } - } - - fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a, 'tcx> { - let (local_ccx, index) = - self.local_ccxs - .iter() - .zip(range(0, self.local_ccxs.len())) - .min_by(|&(local_ccx, _idx)| local_ccx.n_llvm_insns.get()) - .unwrap(); - CrateContext { - shared: self, - local: local_ccx, - index: index, - } - } - - - pub fn metadata_llmod(&self) -> ModuleRef { - self.metadata_llmod - } - - pub fn metadata_llcx(&self) -> ContextRef { - self.metadata_llcx - } - - pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { - &self.exp_map2 - } - - pub fn reachable<'a>(&'a self) -> &'a NodeSet { - &self.reachable - } - - pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { - &self.item_symbols - } - - pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { - &self.link_meta - } - - pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { - &self.symbol_hasher - } - - pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { - &self.tcx - } - - pub fn take_tcx(self) -> ty::ctxt<'tcx> { - self.tcx - } - - pub fn sess<'a>(&'a self) -> &'a Session { - &self.tcx.sess - } - - pub fn stats<'a>(&'a self) -> &'a Stats { - &self.stats - } -} - -impl LocalCrateContext { - fn new(shared: &SharedCrateContext, - name: &str) - -> LocalCrateContext { - unsafe { - let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, name); - - let td = mk_target_data(shared.tcx - .sess - .target - .target - .data_layout - .as_slice()); - - let dbg_cx = if shared.tcx.sess.opts.debuginfo != NoDebugInfo { - Some(debuginfo::CrateDebugContext::new(llmod)) - } else { - None - }; - - let mut local_ccx = LocalCrateContext { - llmod: llmod, - llcx: llcx, - td: td, - tn: TypeNames::new(), - externs: RefCell::new(FnvHashMap::new()), - item_vals: RefCell::new(NodeMap::new()), - drop_glues: RefCell::new(FnvHashMap::new()), - tydescs: RefCell::new(FnvHashMap::new()), - finished_tydescs: Cell::new(false), - external: RefCell::new(DefIdMap::new()), - external_srcs: RefCell::new(NodeMap::new()), - monomorphized: RefCell::new(FnvHashMap::new()), - monomorphizing: RefCell::new(DefIdMap::new()), - vtables: RefCell::new(FnvHashMap::new()), - const_cstr_cache: RefCell::new(FnvHashMap::new()), - const_globals: RefCell::new(FnvHashMap::new()), - const_values: RefCell::new(NodeMap::new()), - static_values: RefCell::new(NodeMap::new()), - extern_const_values: RefCell::new(DefIdMap::new()), - impl_method_cache: RefCell::new(FnvHashMap::new()), - closure_bare_wrapper_cache: RefCell::new(FnvHashMap::new()), - lltypes: RefCell::new(FnvHashMap::new()), - llsizingtypes: RefCell::new(FnvHashMap::new()), - adt_reprs: RefCell::new(FnvHashMap::new()), - type_hashcodes: RefCell::new(FnvHashMap::new()), - all_llvm_symbols: RefCell::new(FnvHashSet::new()), - int_type: Type::from_ref(ptr::null_mut()), - opaque_vec_type: Type::from_ref(ptr::null_mut()), - builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)), - unboxed_closure_vals: RefCell::new(FnvHashMap::new()), - dbg_cx: dbg_cx, - eh_personality: RefCell::new(None), - intrinsics: RefCell::new(FnvHashMap::new()), - n_llvm_insns: Cell::new(0u), - trait_cache: RefCell::new(FnvHashMap::new()), - }; - - local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared)); - local_ccx.opaque_vec_type = Type::opaque_vec(&local_ccx.dummy_ccx(shared)); - - // Done mutating local_ccx directly. (The rest of the - // initialization goes through RefCell.) - { - let ccx = local_ccx.dummy_ccx(shared); - - let mut str_slice_ty = Type::named_struct(&ccx, "str_slice"); - str_slice_ty.set_struct_body(&[Type::i8p(&ccx), ccx.int_type()], false); - ccx.tn().associate_type("str_slice", &str_slice_ty); - - ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); - - if ccx.sess().count_llvm_insns() { - base::init_insn_ctxt() - } - } - - local_ccx - } - } - - /// Create a dummy `CrateContext` from `self` and the provided - /// `SharedCrateContext`. This is somewhat dangerous because `self` may - /// not actually be an element of `shared.local_ccxs`, which can cause some - /// operations to panic unexpectedly. - /// - /// This is used in the `LocalCrateContext` constructor to allow calling - /// functions that expect a complete `CrateContext`, even before the local - /// portion is fully initialized and attached to the `SharedCrateContext`. - fn dummy_ccx<'a, 'tcx>(&'a self, shared: &'a SharedCrateContext<'tcx>) - -> CrateContext<'a, 'tcx> { - CrateContext { - shared: shared, - local: self, - index: -1 as uint, - } - } -} - -impl<'b, 'tcx> CrateContext<'b, 'tcx> { - pub fn shared(&self) -> &'b SharedCrateContext<'tcx> { - self.shared - } - - pub fn local(&self) -> &'b LocalCrateContext { - self.local - } - - - /// Get a (possibly) different `CrateContext` from the same - /// `SharedCrateContext`. - pub fn rotate(&self) -> CrateContext<'b, 'tcx> { - self.shared.get_smallest_ccx() - } - - /// Either iterate over only `self`, or iterate over all `CrateContext`s in - /// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)` - /// pairs, where `is_origin` is `true` if `ccx` is `self` and `false` - /// otherwise. This method is useful for avoiding code duplication in - /// cases where it may or may not be necessary to translate code into every - /// context. - pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b, 'tcx> { - CrateContextMaybeIterator { - shared: self.shared, - index: if iter_all { 0 } else { self.index }, - single: !iter_all, - origin: self.index, - } - } - - - pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { - &self.shared.tcx - } - - pub fn sess<'a>(&'a self) -> &'a Session { - &self.shared.tcx.sess - } - - pub fn builder<'a>(&'a self) -> Builder<'a, 'tcx> { - Builder::new(self) - } - - pub fn raw_builder<'a>(&'a self) -> BuilderRef { - self.local.builder.b - } - - pub fn tydesc_type(&self) -> Type { - self.local.tn.find_type("tydesc").unwrap() - } - - pub fn get_intrinsic(&self, key: & &'static str) -> ValueRef { - match self.intrinsics().borrow().get(key).cloned() { - Some(v) => return v, - _ => {} - } - match declare_intrinsic(self, key) { - Some(v) => return v, - None => panic!() - } - } - - pub fn is_split_stack_supported(&self) -> bool { - self.sess().target.target.options.morestack - } - - - pub fn llmod(&self) -> ModuleRef { - self.local.llmod - } - - pub fn llcx(&self) -> ContextRef { - self.local.llcx - } - - pub fn td<'a>(&'a self) -> &'a TargetData { - &self.local.td - } - - pub fn tn<'a>(&'a self) -> &'a TypeNames { - &self.local.tn - } - - pub fn externs<'a>(&'a self) -> &'a RefCell { - &self.local.externs - } - - pub fn item_vals<'a>(&'a self) -> &'a RefCell> { - &self.local.item_vals - } - - pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { - &self.shared.exp_map2 - } - - pub fn reachable<'a>(&'a self) -> &'a NodeSet { - &self.shared.reachable - } - - pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { - &self.shared.item_symbols - } - - pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { - &self.shared.link_meta - } - - pub fn drop_glues<'a>(&'a self) -> &'a RefCell> { - &self.local.drop_glues - } - - pub fn tydescs<'a>(&'a self) -> &'a RefCell>> { - &self.local.tydescs - } - - pub fn finished_tydescs<'a>(&'a self) -> &'a Cell { - &self.local.finished_tydescs - } - - pub fn external<'a>(&'a self) -> &'a RefCell>> { - &self.local.external - } - - pub fn external_srcs<'a>(&'a self) -> &'a RefCell> { - &self.local.external_srcs - } - - pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { - &self.local.monomorphized - } - - pub fn monomorphizing<'a>(&'a self) -> &'a RefCell> { - &self.local.monomorphizing - } - - pub fn vtables<'a>(&'a self) -> &'a RefCell), ValueRef>> { - &self.local.vtables - } - - pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell> { - &self.local.const_cstr_cache - } - - pub fn const_globals<'a>(&'a self) -> &'a RefCell> { - &self.local.const_globals - } - - pub fn const_values<'a>(&'a self) -> &'a RefCell> { - &self.local.const_values - } - - pub fn static_values<'a>(&'a self) -> &'a RefCell> { - &self.local.static_values - } - - pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { - &self.local.extern_const_values - } - - pub fn impl_method_cache<'a>(&'a self) - -> &'a RefCell> { - &self.local.impl_method_cache - } - - pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell> { - &self.local.closure_bare_wrapper_cache - } - - pub fn lltypes<'a>(&'a self) -> &'a RefCell> { - &self.local.lltypes - } - - pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell> { - &self.local.llsizingtypes - } - - pub fn adt_reprs<'a>(&'a self) -> &'a RefCell>> { - &self.local.adt_reprs - } - - pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { - &self.shared.symbol_hasher - } - - pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell> { - &self.local.type_hashcodes - } - - pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell> { - &self.local.all_llvm_symbols - } - - pub fn stats<'a>(&'a self) -> &'a Stats { - &self.shared.stats - } - - pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell> { - &self.shared.available_monomorphizations - } - - pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell> { - &self.shared.available_drop_glues - } - - pub fn int_type(&self) -> Type { - self.local.int_type - } - - pub fn opaque_vec_type(&self) -> Type { - self.local.opaque_vec_type - } - - pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell> { - &self.local.unboxed_closure_vals - } - - pub fn dbg_cx<'a>(&'a self) -> &'a Option { - &self.local.dbg_cx - } - - pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { - &self.local.eh_personality - } - - fn intrinsics<'a>(&'a self) -> &'a RefCell> { - &self.local.intrinsics - } - - pub fn count_llvm_insn(&self) { - self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1); - } - - pub fn trait_cache(&self) -> &RefCell, traits::Vtable<()>>> { - &self.local.trait_cache - } - - pub fn max_obj_size(&self) -> u64 { - 1<<31 /* FIXME #18069: select based on architecture */ - } - - pub fn report_overbig_object(&self, obj: ty::t) -> ! { - self.sess().fatal( - format!("the type `{}` is too big for the current architecture", - obj.repr(self.tcx())).as_slice()) - } -} - -fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { - macro_rules! ifn ( - ($name:expr fn() -> $ret:expr) => ( - if *key == $name { - let f = base::decl_cdecl_fn( - ccx, $name, Type::func(&[], &$ret), - ty::mk_nil(ccx.tcx())); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr fn($($arg:expr),*) -> $ret:expr) => ( - if *key == $name { - let f = base::decl_cdecl_fn(ccx, $name, - Type::func(&[$($arg),*], &$ret), ty::mk_nil(ccx.tcx())); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); - return Some(f); - } - ) - ) - macro_rules! mk_struct ( - ($($field_ty:expr),*) => (Type::struct_(ccx, &[$($field_ty),*], false)) - ) - - let i8p = Type::i8p(ccx); - let void = Type::void(ccx); - let i1 = Type::i1(ccx); - let t_i8 = Type::i8(ccx); - let t_i16 = Type::i16(ccx); - let t_i32 = Type::i32(ccx); - let t_i64 = Type::i64(ccx); - let t_f32 = Type::f32(ccx); - let t_f64 = Type::f64(ccx); - - ifn!("llvm.memcpy.p0i8.p0i8.i32" fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i32" fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i32" fn(i8p, t_i8, t_i32, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i64" fn(i8p, t_i8, t_i64, t_i32, i1) -> void); - - ifn!("llvm.trap" fn() -> void); - ifn!("llvm.debugtrap" fn() -> void); - ifn!("llvm.frameaddress" fn(t_i32) -> i8p); - - ifn!("llvm.powi.f32" fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64" fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32" fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64" fn(t_f64, t_f64) -> t_f64); - - ifn!("llvm.sqrt.f32" fn(t_f32) -> t_f32); - ifn!("llvm.sqrt.f64" fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32" fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64" fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32" fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64" fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32" fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64" fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32" fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64" fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32" fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64" fn(t_f64) -> t_f64); - - ifn!("llvm.fma.f32" fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64" fn(t_f64, t_f64, t_f64) -> t_f64); - - ifn!("llvm.fabs.f32" fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64" fn(t_f64) -> t_f64); - - ifn!("llvm.floor.f32" fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64" fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32" fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64" fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32" fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64" fn(t_f64) -> t_f64); - - ifn!("llvm.rint.f32" fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64" fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32" fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64" fn(t_f64) -> t_f64); - - ifn!("llvm.ctpop.i8" fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16" fn(t_i16) -> t_i16); - ifn!("llvm.ctpop.i32" fn(t_i32) -> t_i32); - ifn!("llvm.ctpop.i64" fn(t_i64) -> t_i64); - - ifn!("llvm.ctlz.i8" fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16" fn(t_i16, i1) -> t_i16); - ifn!("llvm.ctlz.i32" fn(t_i32, i1) -> t_i32); - ifn!("llvm.ctlz.i64" fn(t_i64, i1) -> t_i64); - - ifn!("llvm.cttz.i8" fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16" fn(t_i16, i1) -> t_i16); - ifn!("llvm.cttz.i32" fn(t_i32, i1) -> t_i32); - ifn!("llvm.cttz.i64" fn(t_i64, i1) -> t_i64); - - ifn!("llvm.bswap.i16" fn(t_i16) -> t_i16); - ifn!("llvm.bswap.i32" fn(t_i32) -> t_i32); - ifn!("llvm.bswap.i64" fn(t_i64) -> t_i64); - - ifn!("llvm.sadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.sadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.sadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.sadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.uadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.uadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.uadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.uadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.ssub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.ssub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.ssub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.ssub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.usub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.usub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.usub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.usub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.smul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.smul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.smul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.smul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.umul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.umul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.umul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.umul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - - ifn!("llvm.lifetime.start" fn(t_i64,i8p) -> void); - ifn!("llvm.lifetime.end" fn(t_i64, i8p) -> void); - - ifn!("llvm.expect.i1" fn(i1, i1) -> i1); - ifn!("llvm.assume" fn(i1) -> void); - - // Some intrinsics were introduced in later versions of LLVM, but they have - // fallbacks in libc or libm and such. Currently, all of these intrinsics - // were introduced in LLVM 3.4, so we case on that. - macro_rules! compatible_ifn ( - ($name:expr, $cname:ident ($($arg:expr),*) -> $ret:expr) => ( - if unsafe { llvm::LLVMVersionMinor() >= 4 } { - // The `if key == $name` is already in ifn! - ifn!($name fn($($arg),*) -> $ret); - } else if *key == $name { - let f = base::decl_cdecl_fn(ccx, stringify!($cname), - Type::func(&[$($arg),*], &$ret), - ty::mk_nil(ccx.tcx())); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); - return Some(f); - } - ) - ) - - compatible_ifn!("llvm.copysign.f32", copysignf(t_f32, t_f32) -> t_f32); - compatible_ifn!("llvm.copysign.f64", copysign(t_f64, t_f64) -> t_f64); - compatible_ifn!("llvm.round.f32", roundf(t_f32) -> t_f32); - compatible_ifn!("llvm.round.f64", round(t_f64) -> t_f64); - - - if ccx.sess().opts.debuginfo != NoDebugInfo { - ifn!("llvm.dbg.declare" fn(Type::metadata(ccx), Type::metadata(ccx)) -> void); - ifn!("llvm.dbg.value" fn(Type::metadata(ccx), t_i64, Type::metadata(ccx)) -> void); - } - return None; -} diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs deleted file mode 100644 index d1908eb62f8..00000000000 --- a/src/librustc/middle/trans/controlflow.rs +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::*; -use driver::config::FullDebugInfo; -use middle::def; -use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem}; -use middle::trans::_match; -use middle::trans::adt; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::cleanup; -use middle::trans::common::*; -use middle::trans::consts; -use middle::trans::datum; -use middle::trans::debuginfo; -use middle::trans::expr; -use middle::trans::meth; -use middle::trans::type_::Type; -use middle::trans; -use middle::ty; -use middle::typeck::MethodCall; -use util::ppaux::Repr; -use util::ppaux; - -use syntax::ast; -use syntax::ast::Ident; -use syntax::ast_util; -use syntax::codemap::Span; -use syntax::parse::token::InternedString; -use syntax::parse::token; -use syntax::visit::Visitor; - -pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - s: &ast::Stmt) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_stmt"); - let fcx = cx.fcx; - debug!("trans_stmt({})", s.repr(cx.tcx())); - - if cx.sess().asm_comments() { - add_span_comment(cx, s.span, s.repr(cx.tcx()).as_slice()); - } - - let mut bcx = cx; - - let id = ast_util::stmt_id(s); - let cleanup_debug_loc = - debuginfo::get_cleanup_debug_loc_for_ast_node(id, s.span, false); - fcx.push_ast_cleanup_scope(cleanup_debug_loc); - - match s.node { - ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => { - bcx = trans_stmt_semi(bcx, &**e); - } - ast::StmtDecl(ref d, _) => { - match d.node { - ast::DeclLocal(ref local) => { - bcx = init_local(bcx, &**local); - if cx.sess().opts.debuginfo == FullDebugInfo { - trans::debuginfo::create_local_var_metadata(bcx, - &**local); - } - } - // Inner items are visited by `trans_item`/`trans_meth`. - ast::DeclItem(_) => {}, - } - } - ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro") - } - - bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s)); - - return bcx; -} - -pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_stmt_semi"); - let ty = expr_ty(cx, e); - if ty::type_needs_drop(cx.tcx(), ty) { - expr::trans_to_lvalue(cx, e, "stmt").bcx - } else { - expr::trans_into(cx, e, expr::Ignore) - } -} - -pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - b: &ast::Block, - mut dest: expr::Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_block"); - let fcx = bcx.fcx; - let mut bcx = bcx; - - let cleanup_debug_loc = - debuginfo::get_cleanup_debug_loc_for_ast_node(b.id, b.span, true); - fcx.push_ast_cleanup_scope(cleanup_debug_loc); - - for s in b.stmts.iter() { - bcx = trans_stmt(bcx, &**s); - } - - if dest != expr::Ignore { - let block_ty = node_id_type(bcx, b.id); - if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) { - dest = expr::Ignore; - } - } - - match b.expr { - Some(ref e) => { - bcx = expr::trans_into(bcx, &**e, dest); - } - None => { - assert!(dest == expr::Ignore || bcx.unreachable.get()); - } - } - - bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id); - - return bcx; -} - -pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - if_id: ast::NodeId, - cond: &ast::Expr, - thn: &ast::Block, - els: Option<&ast::Expr>, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})", - bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id, - dest.to_string(bcx.ccx())); - let _icx = push_ctxt("trans_if"); - let mut bcx = bcx; - - let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool()); - - // Drop branches that are known to be impossible - if is_const(cond_val) && !is_undef(cond_val) { - if const_to_uint(cond_val) == 1 { - match els { - Some(elexpr) => { - let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx }; - trans.visit_expr(&*elexpr); - } - None => {} - } - // if true { .. } [else { .. }] - bcx = trans_block(bcx, &*thn, dest); - trans::debuginfo::clear_source_location(bcx.fcx); - } else { - let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ; - trans.visit_block(&*thn); - - match els { - // if false { .. } else { .. } - Some(elexpr) => { - bcx = expr::trans_into(bcx, &*elexpr, dest); - trans::debuginfo::clear_source_location(bcx.fcx); - } - - // if false { .. } - None => { } - } - } - - return bcx; - } - - let name = format!("then-block-{}-", thn.id); - let then_bcx_in = bcx.fcx.new_id_block(name.as_slice(), thn.id); - let then_bcx_out = trans_block(then_bcx_in, &*thn, dest); - trans::debuginfo::clear_source_location(bcx.fcx); - - let next_bcx; - match els { - Some(elexpr) => { - let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id); - let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest); - next_bcx = bcx.fcx.join_blocks(if_id, - &[then_bcx_out, else_bcx_out]); - CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); - } - - None => { - next_bcx = bcx.fcx.new_id_block("next-block", if_id); - Br(then_bcx_out, next_bcx.llbb); - CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb); - } - } - - // Clear the source location because it is still set to whatever has been translated - // right before. - trans::debuginfo::clear_source_location(next_bcx.fcx); - - next_bcx -} - -pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - loop_id: ast::NodeId, - cond: &ast::Expr, - body: &ast::Block) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_while"); - let fcx = bcx.fcx; - - // bcx - // | - // cond_bcx_in <--------+ - // | | - // cond_bcx_out | - // | | | - // | body_bcx_in | - // cleanup_blk | | - // | body_bcx_out --+ - // next_bcx_in - - let next_bcx_in = fcx.new_id_block("while_exit", loop_id); - let cond_bcx_in = fcx.new_id_block("while_cond", cond.id); - let body_bcx_in = fcx.new_id_block("while_body", body.id); - - fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]); - - Br(bcx, cond_bcx_in.llbb); - - // compile the block where we will handle loop cleanups - let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK); - - // compile the condition - let Result {bcx: cond_bcx_out, val: cond_val} = - expr::trans(cond_bcx_in, cond).to_llbool(); - CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb); - - // loop body: - let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); - Br(body_bcx_out, cond_bcx_in.llbb); - - fcx.pop_loop_cleanup_scope(loop_id); - return next_bcx_in; -} - -/// Translates a `for` loop. -pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - loop_info: NodeInfo, - pat: &ast::Pat, - head: &ast::Expr, - body: &ast::Block) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_for"); - - // bcx - // | - // loopback_bcx_in <-------+ - // | | - // loopback_bcx_out | - // | | | - // | body_bcx_in | - // cleanup_blk | | - // | body_bcx_out --+ - // next_bcx_in - - // Codegen the head to create the iterator value. - let iterator_datum = - unpack_datum!(bcx, expr::trans_to_lvalue(bcx, head, "for_head")); - let iterator_type = node_id_type(bcx, head.id); - debug!("iterator type is {}, datum type is {}", - ppaux::ty_to_string(bcx.tcx(), iterator_type), - ppaux::ty_to_string(bcx.tcx(), iterator_datum.ty)); - let lliterator = load_ty(bcx, iterator_datum.val, iterator_datum.ty); - - // Create our basic blocks and set up our loop cleanups. - let next_bcx_in = bcx.fcx.new_id_block("for_exit", loop_info.id); - let loopback_bcx_in = bcx.fcx.new_id_block("for_loopback", head.id); - let body_bcx_in = bcx.fcx.new_id_block("for_body", body.id); - bcx.fcx.push_loop_cleanup_scope(loop_info.id, - [next_bcx_in, loopback_bcx_in]); - Br(bcx, loopback_bcx_in.llbb); - let cleanup_llbb = bcx.fcx.normal_exit_block(loop_info.id, - cleanup::EXIT_BREAK); - - // Set up the method call (to `.next()`). - let method_call = MethodCall::expr(loop_info.id); - let method_type = (*loopback_bcx_in.tcx() - .method_map - .borrow())[method_call] - .ty; - let method_type = monomorphize_type(loopback_bcx_in, method_type); - let method_result_type = ty::ty_fn_ret(method_type).unwrap(); - let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); - let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope); - - // Compile the method call (to `.next()`). - let mut loopback_bcx_out = loopback_bcx_in; - let option_datum = - unpack_datum!(loopback_bcx_out, - datum::lvalue_scratch_datum(loopback_bcx_out, - method_result_type, - "loop_option", - false, - option_cleanup_scope_id, - (), - |(), bcx, lloption| { - let Result { - bcx, - val: _ - } = callee::trans_call_inner(bcx, - Some(loop_info), - method_type, - |bcx, arg_cleanup_scope| { - meth::trans_method_callee( - bcx, - method_call, - None, - arg_cleanup_scope) - }, - callee::ArgVals(&[lliterator]), - Some(expr::SaveIn(lloption))); - bcx - })); - - // Check the discriminant; if the `None` case, exit the loop. - let option_representation = adt::represent_type(loopback_bcx_out.ccx(), - method_result_type); - let lldiscriminant = adt::trans_get_discr(loopback_bcx_out, - &*option_representation, - option_datum.val, - None); - let i1_type = Type::i1(loopback_bcx_out.ccx()); - let llcondition = Trunc(loopback_bcx_out, lldiscriminant, i1_type); - CondBr(loopback_bcx_out, llcondition, body_bcx_in.llbb, cleanup_llbb); - - // Now we're in the body. Unpack the `Option` value into the programmer- - // supplied pattern. - let llpayload = adt::trans_field_ptr(body_bcx_in, - &*option_representation, - option_datum.val, - 1, - 0); - let binding_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); - let binding_cleanup_scope_id = - cleanup::CustomScope(binding_cleanup_scope); - let mut body_bcx_out = - _match::store_for_loop_binding(body_bcx_in, - pat, - llpayload, - binding_cleanup_scope_id); - - // Codegen the body. - body_bcx_out = trans_block(body_bcx_out, body, expr::Ignore); - body_bcx_out = - body_bcx_out.fcx - .pop_and_trans_custom_cleanup_scope(body_bcx_out, - binding_cleanup_scope); - body_bcx_out = - body_bcx_out.fcx - .pop_and_trans_custom_cleanup_scope(body_bcx_out, - option_cleanup_scope); - Br(body_bcx_out, loopback_bcx_in.llbb); - - // Codegen cleanups and leave. - next_bcx_in.fcx.pop_loop_cleanup_scope(loop_info.id); - next_bcx_in -} - -pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - loop_id: ast::NodeId, - body: &ast::Block) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_loop"); - let fcx = bcx.fcx; - - // bcx - // | - // body_bcx_in - // | - // body_bcx_out - // - // next_bcx - // - // Links between body_bcx_in and next_bcx are created by - // break statements. - - let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id); - let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id); - - fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]); - - Br(bcx, body_bcx_in.llbb); - let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); - Br(body_bcx_out, body_bcx_in.llbb); - - fcx.pop_loop_cleanup_scope(loop_id); - - return next_bcx_in; -} - -pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr_id: ast::NodeId, - opt_label: Option, - exit: uint) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_break_cont"); - let fcx = bcx.fcx; - - if bcx.unreachable.get() { - return bcx; - } - - // Locate loop that we will break to - let loop_id = match opt_label { - None => fcx.top_loop_scope(), - Some(_) => { - match bcx.tcx().def_map.borrow().get(&expr_id) { - Some(&def::DefLabel(loop_id)) => loop_id, - ref r => { - bcx.tcx().sess.bug(format!("{} in def-map for label", - r).as_slice()) - } - } - } - }; - - // Generate appropriate cleanup code and branch - let cleanup_llbb = fcx.normal_exit_block(loop_id, exit); - Br(bcx, cleanup_llbb); - Unreachable(bcx); // anything afterwards should be ignored - return bcx; -} - -pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr_id: ast::NodeId, - label_opt: Option) - -> Block<'blk, 'tcx> { - return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK); -} - -pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr_id: ast::NodeId, - label_opt: Option) - -> Block<'blk, 'tcx> { - return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP); -} - -pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - e: Option<&ast::Expr>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_ret"); - let fcx = bcx.fcx; - let mut bcx = bcx; - let dest = match (fcx.llretslotptr.get(), e) { - (Some(_), Some(e)) => { - let ret_ty = expr_ty(bcx, &*e); - expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot")) - } - _ => expr::Ignore, - }; - match e { - Some(x) => { - bcx = expr::trans_into(bcx, &*x, dest); - match dest { - expr::SaveIn(slot) if fcx.needs_ret_allocas => { - Store(bcx, slot, fcx.llretslotptr.get().unwrap()); - } - _ => {} - } - } - _ => {} - } - let cleanup_llbb = fcx.return_exit_block(); - Br(bcx, cleanup_llbb); - Unreachable(bcx); - return bcx; -} - -pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - sp: Span, - fail_str: InternedString) - -> Block<'blk, 'tcx> { - let ccx = bcx.ccx(); - let _icx = push_ctxt("trans_fail_value"); - - let v_str = C_str_slice(ccx, fail_str); - let loc = bcx.sess().codemap().lookup_char_pos(sp.lo); - let filename = token::intern_and_get_ident(loc.file.name.as_slice()); - let filename = C_str_slice(ccx, filename); - let line = C_uint(ccx, loc.line); - let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false); - let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable); - let args = vec!(expr_file_line); - let did = langcall(bcx, Some(sp), "", PanicFnLangItem); - let bcx = callee::trans_lang_call(bcx, - did, - args.as_slice(), - Some(expr::Ignore)).bcx; - Unreachable(bcx); - return bcx; -} - -pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - sp: Span, - index: ValueRef, - len: ValueRef) - -> Block<'blk, 'tcx> { - let ccx = bcx.ccx(); - let _icx = push_ctxt("trans_fail_bounds_check"); - - // Extract the file/line from the span - let loc = bcx.sess().codemap().lookup_char_pos(sp.lo); - let filename = token::intern_and_get_ident(loc.file.name.as_slice()); - - // Invoke the lang item - let filename = C_str_slice(ccx, filename); - let line = C_uint(ccx, loc.line); - let file_line_const = C_struct(ccx, &[filename, line], false); - let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable); - let args = vec!(file_line, index, len); - let did = langcall(bcx, Some(sp), "", PanicBoundsCheckFnLangItem); - let bcx = callee::trans_lang_call(bcx, - did, - args.as_slice(), - Some(expr::Ignore)).bcx; - Unreachable(bcx); - return bcx; -} diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs deleted file mode 100644 index 4fba0e40510..00000000000 --- a/src/librustc/middle/trans/datum.rs +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * See the section on datums in `doc.rs` for an overview of what - * Datums are and how they are intended to be used. - */ - -pub use self::Expr::*; -pub use self::RvalueMode::*; - -use llvm::ValueRef; -use middle::trans::base::*; -use middle::trans::build::Load; -use middle::trans::common::*; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::expr; -use middle::trans::tvec; -use middle::trans::type_of; -use middle::ty; -use util::ppaux::{ty_to_string}; - -use std::fmt; -use syntax::ast; - -/** - * A `Datum` encapsulates the result of evaluating an expression. It - * describes where the value is stored, what Rust type the value has, - * whether it is addressed by reference, and so forth. Please refer - * the section on datums in `doc.rs` for more details. - */ -#[deriving(Clone)] -pub struct Datum { - /// The llvm value. This is either a pointer to the Rust value or - /// the value itself, depending on `kind` below. - pub val: ValueRef, - - /// The rust type of the value. - pub ty: ty::t, - - /// Indicates whether this is by-ref or by-value. - pub kind: K, -} - -pub struct DatumBlock<'blk, 'tcx: 'blk, K> { - pub bcx: Block<'blk, 'tcx>, - pub datum: Datum, -} - -#[deriving(Show)] -pub enum Expr { - /// a fresh value that was produced and which has no cleanup yet - /// because it has not yet "landed" into its permanent home - RvalueExpr(Rvalue), - - /// `val` is a pointer into memory for which a cleanup is scheduled - /// (and thus has type *T). If you move out of an Lvalue, you must - /// zero out the memory (FIXME #5016). - LvalueExpr, -} - -#[deriving(Clone, Show)] -pub struct Lvalue; - -#[deriving(Show)] -pub struct Rvalue { - pub mode: RvalueMode -} - -impl Rvalue { - pub fn new(m: RvalueMode) -> Rvalue { - Rvalue { mode: m } - } -} - -// Make Datum linear for more type safety. -impl Drop for Rvalue { - fn drop(&mut self) { } -} - -#[deriving(PartialEq, Eq, Hash, Show)] -pub enum RvalueMode { - /// `val` is a pointer to the actual value (and thus has type *T) - ByRef, - - /// `val` is the actual value (*only used for immediates* like ints, ptrs) - ByValue, -} - -pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { - return Datum::new(val, ty, Rvalue::new(ByValue)); -} - -pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - val: ValueRef, - ty: ty::t) - -> DatumBlock<'blk, 'tcx, Rvalue> { - return DatumBlock::new(bcx, immediate_rvalue(val, ty)) -} - - -pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>, - ty: ty::t, - name: &str, - zero: bool, - scope: cleanup::ScopeId, - arg: A, - populate: |A, Block<'blk, 'tcx>, ValueRef| - -> Block<'blk, 'tcx>) - -> DatumBlock<'blk, 'tcx, Lvalue> { - /*! - * Allocates temporary space on the stack using alloca() and - * returns a by-ref Datum pointing to it. The memory will be - * dropped upon exit from `scope`. The callback `populate` should - * initialize the memory. If `zero` is true, the space will be - * zeroed when it is allocated; this is not necessary unless `bcx` - * does not dominate the end of `scope`. - */ - - let scratch = if zero { - alloca_zeroed(bcx, ty, name) - } else { - let llty = type_of::type_of(bcx.ccx(), ty); - alloca(bcx, llty, name) - }; - - // Subtle. Populate the scratch memory *before* scheduling cleanup. - let bcx = populate(arg, bcx, scratch); - bcx.fcx.schedule_lifetime_end(scope, scratch); - bcx.fcx.schedule_drop_mem(scope, scratch, ty); - - DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue)) -} - -pub fn rvalue_scratch_datum(bcx: Block, - ty: ty::t, - name: &str) - -> Datum { - /*! - * Allocates temporary space on the stack using alloca() and - * returns a by-ref Datum pointing to it. If `zero` is true, the - * space will be zeroed when it is allocated; this is normally not - * necessary, but in the case of automatic rooting in match - * statements it is possible to have temporaries that may not get - * initialized if a certain arm is not taken, so we must zero - * them. You must arrange any cleanups etc yourself! - */ - - let llty = type_of::type_of(bcx.ccx(), ty); - let scratch = alloca(bcx, llty, name); - Datum::new(scratch, ty, Rvalue::new(ByRef)) -} - -pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode { - /*! - * Indicates the "appropriate" mode for this value, - * which is either by ref or by value, depending - * on whether type is immediate or not. - */ - - if type_is_immediate(ccx, ty) { - ByValue - } else { - ByRef - } -} - -fn add_rvalue_clean(mode: RvalueMode, - fcx: &FunctionContext, - scope: cleanup::ScopeId, - val: ValueRef, - ty: ty::t) { - match mode { - ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } - ByRef => { - fcx.schedule_lifetime_end(scope, val); - fcx.schedule_drop_mem(scope, val, ty); - } - } -} - -pub trait KindOps { - - /** - * Take appropriate action after the value in `datum` has been - * stored to a new location. - */ - fn post_store<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - val: ValueRef, - ty: ty::t) - -> Block<'blk, 'tcx>; - - /** - * True if this mode is a reference mode, meaning that the datum's - * val field is a pointer to the actual value - */ - fn is_by_ref(&self) -> bool; - - /** - * Converts to an Expr kind - */ - fn to_expr_kind(self) -> Expr; - -} - -impl KindOps for Rvalue { - fn post_store<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - _val: ValueRef, - _ty: ty::t) - -> Block<'blk, 'tcx> { - // No cleanup is scheduled for an rvalue, so we don't have - // to do anything after a move to cancel or duplicate it. - bcx - } - - fn is_by_ref(&self) -> bool { - self.mode == ByRef - } - - fn to_expr_kind(self) -> Expr { - RvalueExpr(self) - } -} - -impl KindOps for Lvalue { - fn post_store<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - val: ValueRef, - ty: ty::t) - -> Block<'blk, 'tcx> { - /*! - * If an lvalue is moved, we must zero out the memory in which - * it resides so as to cancel cleanup. If an @T lvalue is - * copied, we must increment the reference count. - */ - - if ty::type_needs_drop(bcx.tcx(), ty) { - // cancel cleanup of affine values by zeroing out - let () = zero_mem(bcx, val, ty); - bcx - } else { - bcx - } - } - - fn is_by_ref(&self) -> bool { - true - } - - fn to_expr_kind(self) -> Expr { - LvalueExpr - } -} - -impl KindOps for Expr { - fn post_store<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - val: ValueRef, - ty: ty::t) - -> Block<'blk, 'tcx> { - match *self { - LvalueExpr => Lvalue.post_store(bcx, val, ty), - RvalueExpr(ref r) => r.post_store(bcx, val, ty), - } - } - - fn is_by_ref(&self) -> bool { - match *self { - LvalueExpr => Lvalue.is_by_ref(), - RvalueExpr(ref r) => r.is_by_ref() - } - } - - fn to_expr_kind(self) -> Expr { - self - } -} - -impl Datum { - pub fn add_clean(self, - fcx: &FunctionContext, - scope: cleanup::ScopeId) - -> ValueRef { - /*! - * Schedules a cleanup for this datum in the given scope. - * That means that this datum is no longer an rvalue datum; - * hence, this function consumes the datum and returns the - * contained ValueRef. - */ - - add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty); - self.val - } - - pub fn to_lvalue_datum_in_scope<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - name: &str, - scope: cleanup::ScopeId) - -> DatumBlock<'blk, 'tcx, Lvalue> { - /*! - * Returns an lvalue datum (that is, a by ref datum with - * cleanup scheduled). If `self` is not already an lvalue, - * cleanup will be scheduled in the temporary scope for `expr_id`. - */ - let fcx = bcx.fcx; - - match self.kind.mode { - ByRef => { - add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); - DatumBlock::new(bcx, Datum::new(self.val, self.ty, Lvalue)) - } - - ByValue => { - lvalue_scratch_datum( - bcx, self.ty, name, false, scope, self, - |this, bcx, llval| this.store_to(bcx, llval)) - } - } - } - - pub fn to_ref_datum<'blk, 'tcx>(self, bcx: Block<'blk, 'tcx>) - -> DatumBlock<'blk, 'tcx, Rvalue> { - let mut bcx = bcx; - match self.kind.mode { - ByRef => DatumBlock::new(bcx, self), - ByValue => { - let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref"); - bcx = self.store_to(bcx, scratch.val); - DatumBlock::new(bcx, scratch) - } - } - } - - pub fn to_appropriate_datum<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>) - -> DatumBlock<'blk, 'tcx, Rvalue> { - match self.appropriate_rvalue_mode(bcx.ccx()) { - ByRef => { - self.to_ref_datum(bcx) - } - ByValue => { - match self.kind.mode { - ByValue => DatumBlock::new(bcx, self), - ByRef => { - let llval = load_ty(bcx, self.val, self.ty); - DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue))) - } - } - } - } - } -} - -/** - * Methods suitable for "expr" datums that could be either lvalues or - * rvalues. These include coercions into lvalues/rvalues but also a number - * of more general operations. (Some of those operations could be moved to - * the more general `impl Datum`, but it's convenient to have them - * here since we can `match self.kind` rather than having to implement - * generic methods in `KindOps`.) - */ -impl Datum { - fn match_kind(self, - if_lvalue: |Datum| -> R, - if_rvalue: |Datum| -> R) - -> R { - let Datum { val, ty, kind } = self; - match kind { - LvalueExpr => if_lvalue(Datum::new(val, ty, Lvalue)), - RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)), - } - } - - #[allow(dead_code)] // potentially useful - pub fn assert_lvalue(self, bcx: Block) -> Datum { - /*! - * Asserts that this datum *is* an lvalue and returns it. - */ - - self.match_kind( - |d| d, - |_| bcx.sess().bug("assert_lvalue given rvalue")) - } - - pub fn assert_rvalue(self, bcx: Block) -> Datum { - /*! - * Asserts that this datum *is* an lvalue and returns it. - */ - - self.match_kind( - |_| bcx.sess().bug("assert_rvalue given lvalue"), - |r| r) - } - - pub fn store_to_dest<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - dest: expr::Dest, - expr_id: ast::NodeId) - -> Block<'blk, 'tcx> { - match dest { - expr::Ignore => { - self.add_clean_if_rvalue(bcx, expr_id); - bcx - } - expr::SaveIn(addr) => { - self.store_to(bcx, addr) - } - } - } - - pub fn add_clean_if_rvalue<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - expr_id: ast::NodeId) { - /*! - * Arranges cleanup for `self` if it is an rvalue. Use when - * you are done working with a value that may need drop. - */ - - self.match_kind( - |_| { /* Nothing to do, cleanup already arranged */ }, - |r| { - let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); - r.add_clean(bcx.fcx, scope); - }) - } - - pub fn clean<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - name: &'static str, - expr_id: ast::NodeId) - -> Block<'blk, 'tcx> { - /*! - * Ensures that `self` will get cleaned up, if it is not an lvalue - * already. - */ - - self.to_lvalue_datum(bcx, name, expr_id).bcx - } - - pub fn to_lvalue_datum<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - name: &str, - expr_id: ast::NodeId) - -> DatumBlock<'blk, 'tcx, Lvalue> { - debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx())); - - assert!(ty::lltype_is_sized(bcx.tcx(), self.ty), - "Trying to convert unsized value to lval"); - self.match_kind( - |l| DatumBlock::new(bcx, l), - |r| { - let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); - r.to_lvalue_datum_in_scope(bcx, name, scope) - }) - } - - pub fn to_rvalue_datum<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - name: &'static str) - -> DatumBlock<'blk, 'tcx, Rvalue> { - /*! - * Ensures that we have an rvalue datum (that is, a datum with - * no cleanup scheduled). - */ - - self.match_kind( - |l| { - let mut bcx = bcx; - match l.appropriate_rvalue_mode(bcx.ccx()) { - ByRef => { - let scratch = rvalue_scratch_datum(bcx, l.ty, name); - bcx = l.store_to(bcx, scratch.val); - DatumBlock::new(bcx, scratch) - } - ByValue => { - let v = load_ty(bcx, l.val, l.ty); - bcx = l.kind.post_store(bcx, l.val, l.ty); - DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue))) - } - } - }, - |r| DatumBlock::new(bcx, r)) - } - -} - -/** - * Methods suitable only for lvalues. These include the various - * operations to extract components out of compound data structures, - * such as extracting the field from a struct or a particular element - * from an array. - */ -impl Datum { - pub fn to_llref(self) -> ValueRef { - /*! - * Converts a datum into a by-ref value. The datum type must - * be one which is always passed by reference. - */ - - self.val - } - - // Extracts a component of a compound data structure (e.g., a field from a - // struct). Note that if self is an opened, unsized type then the returned - // datum may also be unsized _without the size information_. It is the - // callers responsibility to package the result in some way to make a valid - // datum in that case (e.g., by making a fat pointer or opened pair). - pub fn get_element(&self, bcx: Block, ty: ty::t, - gep: |ValueRef| -> ValueRef) - -> Datum { - let val = match ty::get(self.ty).sty { - _ if ty::type_is_sized(bcx.tcx(), self.ty) => gep(self.val), - ty::ty_open(_) => { - let base = Load(bcx, expr::get_dataptr(bcx, self.val)); - gep(base) - } - _ => bcx.tcx().sess.bug( - format!("Unexpected unsized type in get_element: {}", - bcx.ty_to_string(self.ty)).as_slice()) - }; - Datum { - val: val, - kind: Lvalue, - ty: ty, - } - } - - pub fn get_vec_base_and_len(&self, bcx: Block) -> (ValueRef, ValueRef) { - //! Converts a vector into the slice pair. - - tvec::get_base_and_len(bcx, self.val, self.ty) - } -} - -/** - * Generic methods applicable to any sort of datum. - */ -impl Datum { - pub fn new(val: ValueRef, ty: ty::t, kind: K) -> Datum { - Datum { val: val, ty: ty, kind: kind } - } - - pub fn to_expr_datum(self) -> Datum { - let Datum { val, ty, kind } = self; - Datum { val: val, ty: ty, kind: kind.to_expr_kind() } - } - - pub fn store_to<'blk, 'tcx>(self, - bcx: Block<'blk, 'tcx>, - dst: ValueRef) - -> Block<'blk, 'tcx> { - /*! - * Moves or copies this value into a new home, as appropriate - * depending on the type of the datum. This method consumes - * the datum, since it would be incorrect to go on using the - * datum if the value represented is affine (and hence the value - * is moved). - */ - - self.shallow_copy_raw(bcx, dst); - - self.kind.post_store(bcx, self.val, self.ty) - } - - fn shallow_copy_raw<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - dst: ValueRef) - -> Block<'blk, 'tcx> { - /*! - * Helper function that performs a shallow copy of this value - * into `dst`, which should be a pointer to a memory location - * suitable for `self.ty`. `dst` should contain uninitialized - * memory (either newly allocated, zeroed, or dropped). - * - * This function is private to datums because it leaves memory - * in an unstable state, where the source value has been - * copied but not zeroed. Public methods are `store_to` - * (if you no longer need the source value) or `shallow_copy` - * (if you wish the source value to remain valid). - */ - - let _icx = push_ctxt("copy_to_no_check"); - - if type_is_zero_size(bcx.ccx(), self.ty) { - return bcx; - } - - if self.kind.is_by_ref() { - memcpy_ty(bcx, dst, self.val, self.ty); - } else { - store_ty(bcx, self.val, dst, self.ty); - } - - return bcx; - } - - pub fn shallow_copy<'blk, 'tcx>(&self, - bcx: Block<'blk, 'tcx>, - dst: ValueRef) - -> Block<'blk, 'tcx> { - /*! - * Copies the value into a new location. This function always - * preserves the existing datum as a valid value. Therefore, - * it does not consume `self` and, also, cannot be applied to - * affine values (since they must never be duplicated). - */ - - assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty)); - self.shallow_copy_raw(bcx, dst) - } - - #[allow(dead_code)] // useful for debugging - pub fn to_string(&self, ccx: &CrateContext) -> String { - format!("Datum({}, {}, {})", - ccx.tn().val_to_string(self.val), - ty_to_string(ccx.tcx(), self.ty), - self.kind) - } - - pub fn appropriate_rvalue_mode(&self, ccx: &CrateContext) -> RvalueMode { - /*! See the `appropriate_rvalue_mode()` function */ - - appropriate_rvalue_mode(ccx, self.ty) - } - - pub fn to_llscalarish(self, bcx: Block) -> ValueRef { - /*! - * Converts `self` into a by-value `ValueRef`. Consumes this - * datum (i.e., absolves you of responsibility to cleanup the - * value). For this to work, the value must be something - * scalar-ish (like an int or a pointer) which (1) does not - * require drop glue and (2) is naturally passed around by - * value, and not by reference. - */ - - assert!(!ty::type_needs_drop(bcx.tcx(), self.ty)); - assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); - if self.kind.is_by_ref() { - load_ty(bcx, self.val, self.ty) - } else { - self.val - } - } - - pub fn to_llbool(self, bcx: Block) -> ValueRef { - assert!(ty::type_is_bool(self.ty)) - self.to_llscalarish(bcx) - } -} - -impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> { - pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum) -> DatumBlock<'blk, 'tcx, K> { - DatumBlock { bcx: bcx, datum: datum } - } -} - -impl<'blk, 'tcx, K: KindOps + fmt::Show> DatumBlock<'blk, 'tcx, K> { - pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> { - DatumBlock::new(self.bcx, self.datum.to_expr_datum()) - } -} - -impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> { - pub fn store_to_dest(self, - dest: expr::Dest, - expr_id: ast::NodeId) -> Block<'blk, 'tcx> { - let DatumBlock { bcx, datum } = self; - datum.store_to_dest(bcx, dest, expr_id) - } - - pub fn to_llbool(self) -> Result<'blk, 'tcx> { - let DatumBlock { datum, bcx } = self; - Result::new(bcx, datum.to_llbool(bcx)) - } -} diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs deleted file mode 100644 index de8e80b0275..00000000000 --- a/src/librustc/middle/trans/debuginfo.rs +++ /dev/null @@ -1,4046 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! -# Debug Info Module - -This module serves the purpose of generating debug symbols. We use LLVM's -[source level debugging](http://llvm.org/docs/SourceLevelDebugging.html) -features for generating the debug information. The general principle is this: - -Given the right metadata in the LLVM IR, the LLVM code generator is able to -create DWARF debug symbols for the given code. The -[metadata](http://llvm.org/docs/LangRef.html#metadata-type) is structured much -like DWARF *debugging information entries* (DIE), representing type information -such as datatype layout, function signatures, block layout, variable location -and scope information, etc. It is the purpose of this module to generate correct -metadata and insert it into the LLVM IR. - -As the exact format of metadata trees may change between different LLVM -versions, we now use LLVM -[DIBuilder](http://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) to -create metadata where possible. This will hopefully ease the adaption of this -module to future LLVM versions. - -The public API of the module is a set of functions that will insert the correct -metadata into the LLVM IR when called with the right parameters. The module is -thus driven from an outside client with functions like -`debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`. - -Internally the module will try to reuse already created metadata by utilizing a -cache. The way to get a shared metadata node when needed is thus to just call -the corresponding function in this module: - - let file_metadata = file_metadata(crate_context, path); - -The function will take care of probing the cache for an existing node for that -exact file path. - -All private state used by the module is stored within either the -CrateDebugContext struct (owned by the CrateContext) or the FunctionDebugContext -(owned by the FunctionContext). - -This file consists of three conceptual sections: -1. The public interface of the module -2. Module-internal metadata creation functions -3. Minor utility functions - - -## Recursive Types - -Some kinds of types, such as structs and enums can be recursive. That means that -the type definition of some type X refers to some other type which in turn -(transitively) refers to X. This introduces cycles into the type referral graph. -A naive algorithm doing an on-demand, depth-first traversal of this graph when -describing types, can get trapped in an endless loop when it reaches such a -cycle. - -For example, the following simple type for a singly-linked list... - -``` -struct List { - value: int, - tail: Option>, -} -``` - -will generate the following callstack with a naive DFS algorithm: - -``` -describe(t = List) - describe(t = int) - describe(t = Option>) - describe(t = Box) - describe(t = List) // at the beginning again... - ... -``` - -To break cycles like these, we use "forward declarations". That is, when the -algorithm encounters a possibly recursive type (any struct or enum), it -immediately creates a type description node and inserts it into the cache -*before* describing the members of the type. This type description is just a -stub (as type members are not described and added to it yet) but it allows the -algorithm to already refer to the type. After the stub is inserted into the -cache, the algorithm continues as before. If it now encounters a recursive -reference, it will hit the cache and does not try to describe the type anew. - -This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which -represents a kind of continuation, storing all state needed to continue -traversal at the type members after the type has been registered with the cache. -(This implementation approach might be a tad over-engineered and may change in -the future) - - -## Source Locations and Line Information - -In addition to data type descriptions the debugging information must also allow -to map machine code locations back to source code locations in order to be useful. -This functionality is also handled in this module. The following functions allow -to control source mappings: - -+ set_source_location() -+ clear_source_location() -+ start_emitting_source_locations() - -`set_source_location()` allows to set the current source location. All IR -instructions created after a call to this function will be linked to the given -source location, until another location is specified with -`set_source_location()` or the source location is cleared with -`clear_source_location()`. In the later case, subsequent IR instruction will not -be linked to any source location. As you can see, this is a stateful API -(mimicking the one in LLVM), so be careful with source locations set by previous -calls. It's probably best to not rely on any specific state being present at a -given point in code. - -One topic that deserves some extra attention is *function prologues*. At the -beginning of a function's machine code there are typically a few instructions -for loading argument values into allocas and checking if there's enough stack -space for the function to execute. This *prologue* is not visible in the source -code and LLVM puts a special PROLOGUE END marker into the line table at the -first non-prologue instruction of the function. In order to find out where the -prologue ends, LLVM looks for the first instruction in the function body that is -linked to a source location. So, when generating prologue instructions we have -to make sure that we don't emit source location information until the 'real' -function body begins. For this reason, source location emission is disabled by -default for any new function being translated and is only activated after a call -to the third function from the list above, `start_emitting_source_locations()`. -This function should be called right before regularly starting to translate the -top-level block of the given function. - -There is one exception to the above rule: `llvm.dbg.declare` instruction must be -linked to the source location of the variable being declared. For function -parameters these `llvm.dbg.declare` instructions typically occur in the middle -of the prologue, however, they are ignored by LLVM's prologue detection. The -`create_argument_metadata()` and related functions take care of linking the -`llvm.dbg.declare` instructions to the correct source locations even while -source location emission is still disabled, so there is no need to do anything -special with source location handling here. - -## Unique Type Identification - -In order for link-time optimization to work properly, LLVM needs a unique type -identifier that tells it across compilation units which types are the same as -others. This type identifier is created by TypeMap::get_unique_type_id_of_type() -using the following algorithm: - -(1) Primitive types have their name as ID -(2) Structs, enums and traits have a multipart identifier - - (1) The first part is the SVH (strict version hash) of the crate they were - originally defined in - - (2) The second part is the ast::NodeId of the definition in their original - crate - - (3) The final part is a concatenation of the type IDs of their concrete type - arguments if they are generic types. - -(3) Tuple-, pointer and function types are structurally identified, which means - that they are equivalent if their component types are equivalent (i.e. (int, - int) is the same regardless in which crate it is used). - -This algorithm also provides a stable ID for types that are defined in one crate -but instantiated from metadata within another crate. We just have to take care -to always map crate and node IDs back to the original crate context. - -As a side-effect these unique type IDs also help to solve a problem arising from -lifetime parameters. Since lifetime parameters are completely omitted in -debuginfo, more than one `ty::t` instance may map to the same debuginfo type -metadata, that is, some struct `Struct<'a>` may have N instantiations with -different concrete substitutions for `'a`, and thus there will be N `ty::t` -instances for the type `Struct<'a>` even though it is not generic otherwise. -Unfortunately this means that we cannot use `ty::type_id()` as cheap identifier -for type metadata---we have done this in the past, but it led to unnecessary -metadata duplication in the best case and LLVM assertions in the worst. However, -the unique type ID as described above *can* be used as identifier. Since it is -comparatively expensive to construct, though, `ty::type_id()` is still used -additionally as an optimization for cases where the exact same type has been -seen before (which is most of the time). */ -use self::FunctionDebugContextRepr::*; -use self::VariableAccess::*; -use self::VariableKind::*; -use self::MemberOffset::*; -use self::MemberDescriptionFactory::*; -use self::RecursiveTypeDescription::*; -use self::EnumDiscriminantInfo::*; -use self::DebugLocation::*; - -use driver::config; -use driver::config::{FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; -use llvm; -use llvm::{ModuleRef, ContextRef, ValueRef}; -use llvm::debuginfo::*; -use metadata::csearch; -use middle::subst::{mod, Subst}; -use middle::trans::adt; -use middle::trans::common::*; -use middle::trans::machine; -use middle::trans::_match::{BindingInfo, TrByCopy, TrByMove, TrByRef}; -use middle::trans::type_of; -use middle::trans::type_::Type; -use middle::trans; -use middle::ty; -use middle::pat_util; -use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet}; -use util::ppaux; - -use libc::c_uint; -use std::c_str::{CString, ToCStr}; -use std::cell::{Cell, RefCell}; -use std::ptr; -use std::rc::{Rc, Weak}; -use syntax::util::interner::Interner; -use syntax::codemap::{Span, Pos}; -use syntax::{ast, codemap, ast_util, ast_map}; -use syntax::ast_util::PostExpansionMethod; -use syntax::parse::token; -use syntax::parse::token::special_idents; - -static DW_LANG_RUST: c_uint = 0x9000; - -#[allow(non_upper_case_globals)] -static DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -static DW_TAG_arg_variable: c_uint = 0x101; - -#[allow(non_upper_case_globals)] -static DW_ATE_boolean: c_uint = 0x02; -#[allow(non_upper_case_globals)] -static DW_ATE_float: c_uint = 0x04; -#[allow(non_upper_case_globals)] -static DW_ATE_signed: c_uint = 0x05; -#[allow(non_upper_case_globals)] -static DW_ATE_unsigned: c_uint = 0x07; -#[allow(non_upper_case_globals)] -static DW_ATE_unsigned_char: c_uint = 0x08; - -static UNKNOWN_LINE_NUMBER: c_uint = 0; -static UNKNOWN_COLUMN_NUMBER: c_uint = 0; - -// ptr::null() doesn't work :( -static UNKNOWN_FILE_METADATA: DIFile = (0 as DIFile); -static UNKNOWN_SCOPE_METADATA: DIScope = (0 as DIScope); - -static FLAGS_NONE: c_uint = 0; - -//=----------------------------------------------------------------------------- -// Public Interface of debuginfo module -//=----------------------------------------------------------------------------- - -#[deriving(Show, Hash, Eq, PartialEq, Clone)] -struct UniqueTypeId(ast::Name); - -// The TypeMap is where the CrateDebugContext holds the type metadata nodes -// created so far. The metadata nodes are indexed by UniqueTypeId, and, for -// faster lookup, also by ty::t. The TypeMap is responsible for creating -// UniqueTypeIds. -struct TypeMap { - // The UniqueTypeIds created so far - unique_id_interner: Interner>, - // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. - unique_id_to_metadata: FnvHashMap, - // A map from types to debuginfo metadata. This is a N:1 mapping. - type_to_metadata: FnvHashMap, - // A map from types to UniqueTypeId. This is a N:1 mapping. - type_to_unique_id: FnvHashMap -} - -impl TypeMap { - - fn new() -> TypeMap { - TypeMap { - unique_id_interner: Interner::new(), - type_to_metadata: FnvHashMap::new(), - unique_id_to_metadata: FnvHashMap::new(), - type_to_unique_id: FnvHashMap::new(), - } - } - - // Adds a ty::t to metadata mapping to the TypeMap. The method will fail if - // the mapping already exists. - fn register_type_with_metadata(&mut self, - cx: &CrateContext, - type_: ty::t, - metadata: DIType) { - if self.type_to_metadata.insert(type_, metadata).is_some() { - cx.sess().bug(format!("Type metadata for ty::t '{}' is already in the TypeMap!", - ppaux::ty_to_string(cx.tcx(), type_)).as_slice()); - } - } - - // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will - // fail if the mapping already exists. - fn register_unique_id_with_metadata(&mut self, - cx: &CrateContext, - unique_type_id: UniqueTypeId, - metadata: DIType) { - if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { - let unique_type_id_str = self.get_unique_type_id_as_string(unique_type_id); - cx.sess().bug(format!("Type metadata for unique id '{}' is already in the TypeMap!", - unique_type_id_str.as_slice()).as_slice()); - } - } - - fn find_metadata_for_type(&self, type_: ty::t) -> Option { - self.type_to_metadata.get(&type_).cloned() - } - - fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { - self.unique_id_to_metadata.get(&unique_type_id).cloned() - } - - // Get the string representation of a UniqueTypeId. This method will fail if - // the id is unknown. - fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> Rc { - let UniqueTypeId(interner_key) = unique_type_id; - self.unique_id_interner.get(interner_key) - } - - // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given - // type has been requested before, this is just a table lookup. Otherwise an - // ID will be generated and stored for later lookup. - fn get_unique_type_id_of_type(&mut self, cx: &CrateContext, type_: ty::t) -> UniqueTypeId { - - // basic type -> {:name of the type:} - // tuple -> {tuple_(:param-uid:)*} - // struct -> {struct_:svh: / :node-id:_<(:param-uid:),*> } - // enum -> {enum_:svh: / :node-id:_<(:param-uid:),*> } - // enum variant -> {variant_:variant-name:_:enum-uid:} - // reference (&) -> {& :pointee-uid:} - // mut reference (&mut) -> {&mut :pointee-uid:} - // ptr (*) -> {* :pointee-uid:} - // mut ptr (*mut) -> {*mut :pointee-uid:} - // unique ptr (~) -> {~ :pointee-uid:} - // @-ptr (@) -> {@ :pointee-uid:} - // sized vec ([T, ..x]) -> {[:size:] :element-uid:} - // unsized vec ([T]) -> {[] :element-uid:} - // trait (T) -> {trait_:svh: / :node-id:_<(:param-uid:),*> } - // closure -> { :store-sigil: |(:param-uid:),* <,_...>| -> \ - // :return-type-uid: : (:bounds:)*} - // function -> { fn( (:param-uid:)* <,_...> ) -> \ - // :return-type-uid:} - // unique vec box (~[]) -> {HEAP_VEC_BOX<:pointee-uid:>} - // gc box -> {GC_BOX<:pointee-uid:>} - - match self.type_to_unique_id.get(&type_).cloned() { - Some(unique_type_id) => return unique_type_id, - None => { /* generate one */} - }; - - let mut unique_type_id = String::with_capacity(256); - unique_type_id.push('{'); - - match ty::get(type_).sty { - ty::ty_bool | - ty::ty_char | - ty::ty_str | - ty::ty_int(_) | - ty::ty_uint(_) | - ty::ty_float(_) => { - push_debuginfo_type_name(cx, type_, false, &mut unique_type_id); - }, - ty::ty_enum(def_id, ref substs) => { - unique_type_id.push_str("enum "); - from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id); - }, - ty::ty_struct(def_id, ref substs) => { - unique_type_id.push_str("struct "); - from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id); - }, - ty::ty_tup(ref component_types) if component_types.is_empty() => { - push_debuginfo_type_name(cx, type_, false, &mut unique_type_id); - }, - ty::ty_tup(ref component_types) => { - unique_type_id.push_str("tuple "); - for &component_type in component_types.iter() { - let component_type_id = - self.get_unique_type_id_of_type(cx, component_type); - let component_type_id = - self.get_unique_type_id_as_string(component_type_id); - unique_type_id.push_str(component_type_id.as_slice()); - } - }, - ty::ty_uniq(inner_type) => { - unique_type_id.push('~'); - let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); - let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); - unique_type_id.push_str(inner_type_id.as_slice()); - }, - ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { - unique_type_id.push('*'); - if mutbl == ast::MutMutable { - unique_type_id.push_str("mut"); - } - - let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); - let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); - unique_type_id.push_str(inner_type_id.as_slice()); - }, - ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { - unique_type_id.push('&'); - if mutbl == ast::MutMutable { - unique_type_id.push_str("mut"); - } - - let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); - let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); - unique_type_id.push_str(inner_type_id.as_slice()); - }, - ty::ty_vec(inner_type, optional_length) => { - match optional_length { - Some(len) => { - unique_type_id.push_str(format!("[{}]", len).as_slice()); - } - None => { - unique_type_id.push_str("[]"); - } - }; - - let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); - let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); - unique_type_id.push_str(inner_type_id.as_slice()); - }, - ty::ty_trait(ref trait_data) => { - unique_type_id.push_str("trait "); - - from_def_id_and_substs(self, - cx, - trait_data.principal.def_id, - &trait_data.principal.substs, - &mut unique_type_id); - }, - ty::ty_bare_fn(ty::BareFnTy{ fn_style, abi, ref sig } ) => { - if fn_style == ast::UnsafeFn { - unique_type_id.push_str("unsafe "); - } - - unique_type_id.push_str(abi.name()); - - unique_type_id.push_str(" fn("); - - for ¶meter_type in sig.inputs.iter() { - let parameter_type_id = - self.get_unique_type_id_of_type(cx, parameter_type); - let parameter_type_id = - self.get_unique_type_id_as_string(parameter_type_id); - unique_type_id.push_str(parameter_type_id.as_slice()); - unique_type_id.push(','); - } - - if sig.variadic { - unique_type_id.push_str("..."); - } - - unique_type_id.push_str(")->"); - match sig.output { - ty::FnConverging(ret_ty) => { - let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty); - let return_type_id = self.get_unique_type_id_as_string(return_type_id); - unique_type_id.push_str(return_type_id.as_slice()); - } - ty::FnDiverging => { - unique_type_id.push_str("!"); - } - } - }, - ty::ty_closure(box ref closure_ty) => { - self.get_unique_type_id_of_closure_type(cx, - closure_ty.clone(), - &mut unique_type_id); - }, - ty::ty_unboxed_closure(ref def_id, _, ref substs) => { - let closure_ty = cx.tcx().unboxed_closures.borrow() - .get(def_id).unwrap().closure_type.subst(cx.tcx(), substs); - self.get_unique_type_id_of_closure_type(cx, - closure_ty, - &mut unique_type_id); - }, - _ => { - cx.sess().bug(format!("get_unique_type_id_of_type() - unexpected type: {}, {}", - ppaux::ty_to_string(cx.tcx(), type_).as_slice(), - ty::get(type_).sty).as_slice()) - } - }; - - unique_type_id.push('}'); - - // Trim to size before storing permanently - unique_type_id.shrink_to_fit(); - - let key = self.unique_id_interner.intern(Rc::new(unique_type_id)); - self.type_to_unique_id.insert(type_, UniqueTypeId(key)); - - return UniqueTypeId(key); - - fn from_def_id_and_substs(type_map: &mut TypeMap, - cx: &CrateContext, - def_id: ast::DefId, - substs: &subst::Substs, - output: &mut String) { - // First, find out the 'real' def_id of the type. Items inlined from - // other crates have to be mapped back to their source. - let source_def_id = if def_id.krate == ast::LOCAL_CRATE { - match cx.external_srcs().borrow().get(&def_id.node).cloned() { - Some(source_def_id) => { - // The given def_id identifies the inlined copy of a - // type definition, let's take the source of the copy. - source_def_id - } - None => def_id - } - } else { - def_id - }; - - // Get the crate hash as first part of the identifier. - let crate_hash = if source_def_id.krate == ast::LOCAL_CRATE { - cx.link_meta().crate_hash.clone() - } else { - cx.sess().cstore.get_crate_hash(source_def_id.krate) - }; - - output.push_str(crate_hash.as_str()); - output.push_str("/"); - output.push_str(format!("{:x}", def_id.node).as_slice()); - - // Maybe check that there is no self type here. - - let tps = substs.types.get_slice(subst::TypeSpace); - if tps.len() > 0 { - output.push('<'); - - for &type_parameter in tps.iter() { - let param_type_id = - type_map.get_unique_type_id_of_type(cx, type_parameter); - let param_type_id = - type_map.get_unique_type_id_as_string(param_type_id); - output.push_str(param_type_id.as_slice()); - output.push(','); - } - - output.push('>'); - } - } - } - - fn get_unique_type_id_of_closure_type(&mut self, - cx: &CrateContext, - closure_ty: ty::ClosureTy, - unique_type_id: &mut String) { - let ty::ClosureTy { fn_style, - onceness, - store, - ref bounds, - ref sig, - abi: _ } = closure_ty; - if fn_style == ast::UnsafeFn { - unique_type_id.push_str("unsafe "); - } - - if onceness == ast::Once { - unique_type_id.push_str("once "); - } - - match store { - ty::UniqTraitStore => unique_type_id.push_str("~|"), - ty::RegionTraitStore(_, ast::MutMutable) => { - unique_type_id.push_str("&mut|") - } - ty::RegionTraitStore(_, ast::MutImmutable) => { - unique_type_id.push_str("&|") - } - }; - - for ¶meter_type in sig.inputs.iter() { - let parameter_type_id = - self.get_unique_type_id_of_type(cx, parameter_type); - let parameter_type_id = - self.get_unique_type_id_as_string(parameter_type_id); - unique_type_id.push_str(parameter_type_id.as_slice()); - unique_type_id.push(','); - } - - if sig.variadic { - unique_type_id.push_str("..."); - } - - unique_type_id.push_str("|->"); - - match sig.output { - ty::FnConverging(ret_ty) => { - let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty); - let return_type_id = self.get_unique_type_id_as_string(return_type_id); - unique_type_id.push_str(return_type_id.as_slice()); - } - ty::FnDiverging => { - unique_type_id.push_str("!"); - } - } - - unique_type_id.push(':'); - - for bound in bounds.builtin_bounds.iter() { - match bound { - ty::BoundSend => unique_type_id.push_str("Send"), - ty::BoundSized => unique_type_id.push_str("Sized"), - ty::BoundCopy => unique_type_id.push_str("Copy"), - ty::BoundSync => unique_type_id.push_str("Sync"), - }; - unique_type_id.push('+'); - } - } - - // Get the UniqueTypeId for an enum variant. Enum variants are not really - // types of their own, so they need special handling. We still need a - // UniqueTypeId for them, since to debuginfo they *are* real types. - fn get_unique_type_id_of_enum_variant(&mut self, - cx: &CrateContext, - enum_type: ty::t, - variant_name: &str) - -> UniqueTypeId { - let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); - let enum_variant_type_id = format!("{}::{}", - self.get_unique_type_id_as_string(enum_type_id) - .as_slice(), - variant_name); - let interner_key = self.unique_id_interner.intern(Rc::new(enum_variant_type_id)); - UniqueTypeId(interner_key) - } -} - -// Returns from the enclosing function if the type metadata with the given -// unique id can be found in the type map -macro_rules! return_if_metadata_created_in_meantime( - ($cx: expr, $unique_type_id: expr) => ( - match debug_context($cx).type_map - .borrow() - .find_metadata_for_unique_id($unique_type_id) { - Some(metadata) => return MetadataCreationResult::new(metadata, true), - None => { /* proceed normally */ } - }; - ) -) - - -/// A context object for maintaining all state needed by the debuginfo module. -pub struct CrateDebugContext { - llcontext: ContextRef, - builder: DIBuilderRef, - current_debug_location: Cell, - created_files: RefCell>, - created_enum_disr_types: RefCell>, - - type_map: RefCell, - namespace_map: RefCell, Rc>>, - - // This collection is used to assert that composite types (structs, enums, - // ...) have their members only set once: - composite_types_completed: RefCell>, -} - -impl CrateDebugContext { - pub fn new(llmod: ModuleRef) -> CrateDebugContext { - debug!("CrateDebugContext::new"); - let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) }; - // DIBuilder inherits context from the module, so we'd better use the same one - let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - return CrateDebugContext { - llcontext: llcontext, - builder: builder, - current_debug_location: Cell::new(UnknownLocation), - created_files: RefCell::new(FnvHashMap::new()), - created_enum_disr_types: RefCell::new(DefIdMap::new()), - type_map: RefCell::new(TypeMap::new()), - namespace_map: RefCell::new(FnvHashMap::new()), - composite_types_completed: RefCell::new(FnvHashSet::new()), - }; - } -} - -pub struct FunctionDebugContext { - repr: FunctionDebugContextRepr, -} - -enum FunctionDebugContextRepr { - DebugInfo(Box), - DebugInfoDisabled, - FunctionWithoutDebugInfo, -} - -impl FunctionDebugContext { - fn get_ref<'a>(&'a self, - cx: &CrateContext, - span: Span) - -> &'a FunctionDebugContextData { - match self.repr { - DebugInfo(box ref data) => data, - DebugInfoDisabled => { - cx.sess().span_bug(span, - FunctionDebugContext::debuginfo_disabled_message()); - } - FunctionWithoutDebugInfo => { - cx.sess().span_bug(span, - FunctionDebugContext::should_be_ignored_message()); - } - } - } - - fn debuginfo_disabled_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" - } - - fn should_be_ignored_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext for function that should be \ - ignored by debug info!" - } -} - -struct FunctionDebugContextData { - scope_map: RefCell>, - fn_metadata: DISubprogram, - argument_counter: Cell, - source_locations_enabled: Cell, -} - -enum VariableAccess<'a> { - // The llptr given is an alloca containing the variable's value - DirectVariable { alloca: ValueRef }, - // The llptr given is an alloca containing the start of some pointer chain - // leading to the variable's content. - IndirectVariable { alloca: ValueRef, address_operations: &'a [ValueRef] } -} - -enum VariableKind { - ArgumentVariable(uint /*index*/), - LocalVariable, - CapturedVariable, -} - -/// Create any deferred debug metadata nodes -pub fn finalize(cx: &CrateContext) { - if cx.dbg_cx().is_none() { - return; - } - - debug!("finalize"); - compile_unit_metadata(cx); - unsafe { - llvm::LLVMDIBuilderFinalize(DIB(cx)); - llvm::LLVMDIBuilderDispose(DIB(cx)); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than OS X currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for OS X to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - if cx.sess().target.target.options.is_like_osx { - "Dwarf Version".with_c_str( - |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 2)); - } - - // Prevent bitcode readers from deleting the debug info. - "Debug Info Version".with_c_str( - |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, - llvm::LLVMRustDebugMetadataVersion)); - }; -} - -/// Creates debug information for the given global variable. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_global_var_metadata(cx: &CrateContext, - node_id: ast::NodeId, - global: ValueRef) { - if cx.dbg_cx().is_none() { - return; - } - - // Don't create debuginfo for globals inlined from other crates. The other - // crate should already contain debuginfo for it. More importantly, the - // global might not even exist in un-inlined form anywhere which would lead - // to a linker errors. - if cx.external_srcs().borrow().contains_key(&node_id) { - return; - } - - let var_item = cx.tcx().map.get(node_id); - - let (ident, span) = match var_item { - ast_map::NodeItem(item) => { - match item.node { - ast::ItemStatic(..) => (item.ident, item.span), - ast::ItemConst(..) => (item.ident, item.span), - _ => { - cx.sess() - .span_bug(item.span, - format!("debuginfo::\ - create_global_var_metadata() - - Captured var-id refers to \ - unexpected ast_item variant: {}", - var_item).as_slice()) - } - } - }, - _ => cx.sess().bug(format!("debuginfo::create_global_var_metadata() \ - - Captured var-id refers to unexpected \ - ast_map variant: {}", - var_item).as_slice()) - }; - - let (file_metadata, line_number) = if span != codemap::DUMMY_SP { - let loc = span_start(cx, span); - (file_metadata(cx, loc.file.name.as_slice()), loc.line as c_uint) - } else { - (UNKNOWN_FILE_METADATA, UNKNOWN_LINE_NUMBER) - }; - - let is_local_to_unit = is_node_local_to_unit(cx, node_id); - let variable_type = ty::node_id_to_type(cx.tcx(), node_id); - let type_metadata = type_metadata(cx, variable_type, span); - let namespace_node = namespace_for_item(cx, ast_util::local_def(node_id)); - let var_name = token::get_ident(ident).get().to_string(); - let linkage_name = - namespace_node.mangled_name_of_contained_item(var_name.as_slice()); - let var_scope = namespace_node.scope; - - var_name.as_slice().with_c_str(|var_name| { - linkage_name.as_slice().with_c_str(|linkage_name| { - unsafe { - llvm::LLVMDIBuilderCreateStaticVariable(DIB(cx), - var_scope, - var_name, - linkage_name, - file_metadata, - line_number, - type_metadata, - is_local_to_unit, - global, - ptr::null_mut()); - } - }) - }); -} - -/// Creates debug information for the given local variable. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_local_var_metadata(bcx: Block, local: &ast::Local) { - if fn_should_be_ignored(bcx.fcx) { - return; - } - - let cx = bcx.ccx(); - let def_map = &cx.tcx().def_map; - - pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, path1| { - let var_ident = path1.node; - - let datum = match bcx.fcx.lllocals.borrow().get(&node_id).cloned() { - Some(datum) => datum, - None => { - bcx.sess().span_bug(span, - format!("no entry in lllocals table for {}", - node_id).as_slice()); - } - }; - - let scope_metadata = scope_metadata(bcx.fcx, node_id, span); - - declare_local(bcx, - var_ident, - datum.ty, - scope_metadata, - DirectVariable { alloca: datum.val }, - LocalVariable, - span); - }) -} - -/// Creates debug information for a variable captured in a closure. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_captured_var_metadata(bcx: Block, - node_id: ast::NodeId, - env_data_type: ty::t, - env_pointer: ValueRef, - env_index: uint, - closure_store: ty::TraitStore, - span: Span) { - if fn_should_be_ignored(bcx.fcx) { - return; - } - - let cx = bcx.ccx(); - - let ast_item = cx.tcx().map.find(node_id); - - let variable_ident = match ast_item { - None => { - cx.sess().span_bug(span, "debuginfo::create_captured_var_metadata: node not found"); - } - Some(ast_map::NodeLocal(pat)) | Some(ast_map::NodeArg(pat)) => { - match pat.node { - ast::PatIdent(_, ref path1, _) => { - path1.node - } - _ => { - cx.sess() - .span_bug(span, - format!( - "debuginfo::create_captured_var_metadata() - \ - Captured var-id refers to unexpected \ - ast_map variant: {}", - ast_item).as_slice()); - } - } - } - _ => { - cx.sess() - .span_bug(span, - format!("debuginfo::create_captured_var_metadata() - \ - Captured var-id refers to unexpected \ - ast_map variant: {}", - ast_item).as_slice()); - } - }; - - let variable_type = node_id_type(bcx, node_id); - let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata; - - let llvm_env_data_type = type_of::type_of(cx, env_data_type); - let byte_offset_of_var_in_env = machine::llelement_offset(cx, - llvm_env_data_type, - env_index); - - let address_operations = unsafe { - [llvm::LLVMDIBuilderCreateOpDeref(Type::i64(cx).to_ref()), - llvm::LLVMDIBuilderCreateOpPlus(Type::i64(cx).to_ref()), - C_i64(cx, byte_offset_of_var_in_env as i64), - llvm::LLVMDIBuilderCreateOpDeref(Type::i64(cx).to_ref())] - }; - - let address_op_count = match closure_store { - ty::RegionTraitStore(..) => { - address_operations.len() - } - ty::UniqTraitStore => { - address_operations.len() - 1 - } - }; - - let variable_access = IndirectVariable { - alloca: env_pointer, - address_operations: address_operations[..address_op_count] - }; - - declare_local(bcx, - variable_ident, - variable_type, - scope_metadata, - variable_access, - CapturedVariable, - span); -} - -/// Creates debug information for a local variable introduced in the head of a -/// match-statement arm. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_match_binding_metadata(bcx: Block, - variable_ident: ast::Ident, - binding: BindingInfo) { - if fn_should_be_ignored(bcx.fcx) { - return; - } - - let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span); - let aops = unsafe { - [llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type().to_ref())] - }; - // Regardless of the actual type (`T`) we're always passed the stack slot (alloca) - // for the binding. For ByRef bindings that's a `T*` but for ByMove bindings we - // actually have `T**`. So to get the actual variable we need to dereference once - // more. For ByCopy we just use the stack slot we created for the binding. - let var_type = match binding.trmode { - TrByCopy(llbinding) => DirectVariable { - alloca: llbinding - }, - TrByMove => IndirectVariable { - alloca: binding.llmatch, - address_operations: &aops - }, - TrByRef => DirectVariable { - alloca: binding.llmatch - } - }; - - declare_local(bcx, - variable_ident, - binding.ty, - scope_metadata, - var_type, - LocalVariable, - binding.span); -} - -/// Creates debug information for the given function argument. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_argument_metadata(bcx: Block, arg: &ast::Arg) { - if fn_should_be_ignored(bcx.fcx) { - return; - } - - let fcx = bcx.fcx; - let cx = fcx.ccx; - - let def_map = &cx.tcx().def_map; - let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata; - - pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, path1| { - let llarg = match bcx.fcx.lllocals.borrow().get(&node_id).cloned() { - Some(v) => v, - None => { - bcx.sess().span_bug(span, - format!("no entry in lllocals table for {}", - node_id).as_slice()); - } - }; - - if unsafe { llvm::LLVMIsAAllocaInst(llarg.val) } == ptr::null_mut() { - cx.sess().span_bug(span, "debuginfo::create_argument_metadata() - \ - Referenced variable location is not an alloca!"); - } - - let argument_index = { - let counter = &fcx.debug_context.get_ref(cx, span).argument_counter; - let argument_index = counter.get(); - counter.set(argument_index + 1); - argument_index - }; - - declare_local(bcx, - path1.node, - llarg.ty, - scope_metadata, - DirectVariable { alloca: llarg.val }, - ArgumentVariable(argument_index), - span); - }) -} - -pub fn get_cleanup_debug_loc_for_ast_node(node_id: ast::NodeId, - node_span: Span, - is_block: bool) - -> NodeInfo { - // A debug location needs two things: - // (1) A span (of which only the beginning will actually be used) - // (2) An AST node-id which will be used to look up the lexical scope - // for the location in the functions scope-map - // - // This function will calculate the debug location for compiler-generated - // cleanup calls that are executed when control-flow leaves the - // scope identified by `node_id`. - // - // For everything but block-like things we can simply take id and span of - // the given expression, meaning that from a debugger's view cleanup code is - // executed at the same source location as the statement/expr itself. - // - // Blocks are a special case. Here we want the cleanup to be linked to the - // closing curly brace of the block. The *scope* the cleanup is executed in - // is up to debate: It could either still be *within* the block being - // cleaned up, meaning that locals from the block are still visible in the - // debugger. - // Or it could be in the scope that the block is contained in, so any locals - // from within the block are already considered out-of-scope and thus not - // accessible in the debugger anymore. - // - // The current implementation opts for the second option: cleanup of a block - // already happens in the parent scope of the block. The main reason for - // this decision is that scoping becomes controlflow dependent when variable - // shadowing is involved and it's impossible to decide statically which - // scope is actually left when the cleanup code is executed. - // In practice it shouldn't make much of a difference. - - let cleanup_span = if is_block { - Span { - lo: node_span.hi - codemap::BytePos(1), // closing brace should always be 1 byte... - hi: node_span.hi, - expn_id: node_span.expn_id - } - } else { - node_span - }; - - NodeInfo { - id: node_id, - span: cleanup_span - } -} - -/// Sets the current debug location at the beginning of the span. -/// -/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id -/// parameter is used to reliably find the correct visibility scope for the code -/// position. -pub fn set_source_location(fcx: &FunctionContext, - node_id: ast::NodeId, - span: Span) { - match fcx.debug_context.repr { - DebugInfoDisabled => return, - FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, UnknownLocation); - return; - } - DebugInfo(box ref function_debug_context) => { - let cx = fcx.ccx; - - debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); - - if function_debug_context.source_locations_enabled.get() { - let loc = span_start(cx, span); - let scope = scope_metadata(fcx, node_id, span); - - set_debug_location(cx, DebugLocation::new(scope, - loc.line, - loc.col.to_uint())); - } else { - set_debug_location(cx, UnknownLocation); - } - } - } -} - -/// Clears the current debug location. -/// -/// Instructions generated hereafter won't be assigned a source location. -pub fn clear_source_location(fcx: &FunctionContext) { - if fn_should_be_ignored(fcx) { - return; - } - - set_debug_location(fcx.ccx, UnknownLocation); -} - -/// Enables emitting source locations for the given functions. -/// -/// Since we don't want source locations to be emitted for the function prelude, -/// they are disabled when beginning to translate a new function. This functions -/// switches source location emitting on and must therefore be called before the -/// first real statement/expression of the function is translated. -pub fn start_emitting_source_locations(fcx: &FunctionContext) { - match fcx.debug_context.repr { - DebugInfo(box ref data) => { - data.source_locations_enabled.set(true) - }, - _ => { /* safe to ignore */ } - } -} - -/// Creates the function-specific debug context. -/// -/// Returns the FunctionDebugContext for the function which holds state needed -/// for debug info creation. The function may also return another variant of the -/// FunctionDebugContext enum which indicates why no debuginfo should be created -/// for the function. -pub fn create_function_debug_context(cx: &CrateContext, - fn_ast_id: ast::NodeId, - param_substs: ¶m_substs, - llfn: ValueRef) -> FunctionDebugContext { - if cx.sess().opts.debuginfo == NoDebugInfo { - return FunctionDebugContext { repr: DebugInfoDisabled }; - } - - // Clear the debug location so we don't assign them in the function prelude. - // Do this here already, in case we do an early exit from this function. - set_debug_location(cx, UnknownLocation); - - if fn_ast_id == ast::DUMMY_NODE_ID { - // This is a function not linked to any source location, so don't - // generate debuginfo for it. - return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; - } - - let empty_generics = ast_util::empty_generics(); - - let fnitem = cx.tcx().map.get(fn_ast_id); - - let (ident, fn_decl, generics, top_level_block, span, has_path) = match fnitem { - ast_map::NodeItem(ref item) => { - if contains_nodebug_attribute(item.attrs.as_slice()) { - return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; - } - - match item.node { - ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => { - (item.ident, &**fn_decl, generics, &**top_level_block, item.span, true) - } - _ => { - cx.sess().span_bug(item.span, - "create_function_debug_context: item bound to non-function"); - } - } - } - ast_map::NodeImplItem(ref item) => { - match **item { - ast::MethodImplItem(ref method) => { - if contains_nodebug_attribute(method.attrs.as_slice()) { - return FunctionDebugContext { - repr: FunctionWithoutDebugInfo - }; - } - - (method.pe_ident(), - method.pe_fn_decl(), - method.pe_generics(), - method.pe_body(), - method.span, - true) - } - ast::TypeImplItem(ref typedef) => { - cx.sess().span_bug(typedef.span, - "create_function_debug_context() \ - called on associated type?!") - } - } - } - ast_map::NodeExpr(ref expr) => { - match expr.node { - ast::ExprFnBlock(_, ref fn_decl, ref top_level_block) | - ast::ExprProc(ref fn_decl, ref top_level_block) | - ast::ExprUnboxedFn(_, _, ref fn_decl, ref top_level_block) => { - let name = format!("fn{}", token::gensym("fn")); - let name = token::str_to_ident(name.as_slice()); - (name, &**fn_decl, - // This is not quite right. It should actually inherit - // the generics of the enclosing function. - &empty_generics, - &**top_level_block, - expr.span, - // Don't try to lookup the item path: - false) - } - _ => cx.sess().span_bug(expr.span, - "create_function_debug_context: expected an expr_fn_block here") - } - } - ast_map::NodeTraitItem(ref trait_method) => { - match **trait_method { - ast::ProvidedMethod(ref method) => { - if contains_nodebug_attribute(method.attrs.as_slice()) { - return FunctionDebugContext { - repr: FunctionWithoutDebugInfo - }; - } - - (method.pe_ident(), - method.pe_fn_decl(), - method.pe_generics(), - method.pe_body(), - method.span, - true) - } - _ => { - cx.sess() - .bug(format!("create_function_debug_context: \ - unexpected sort of node: {}", - fnitem).as_slice()) - } - } - } - ast_map::NodeForeignItem(..) | - ast_map::NodeVariant(..) | - ast_map::NodeStructCtor(..) => { - return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; - } - _ => cx.sess().bug(format!("create_function_debug_context: \ - unexpected sort of node: {}", - fnitem).as_slice()) - }; - - // This can be the case for functions inlined from another crate - if span == codemap::DUMMY_SP { - return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; - } - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, loc.file.name.as_slice()); - - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(cx, - fn_ast_id, - &*fn_decl, - param_substs, - span); - llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) - }; - - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let mut function_name = String::from_str(token::get_ident(ident).get()); - let template_parameters = get_template_parameters(cx, - generics, - param_substs, - file_metadata, - &mut function_name); - - // There is no ast_map::Path for ast::ExprFnBlock-type functions. For now, - // just don't put them into a namespace. In the future this could be improved - // somehow (storing a path in the ast_map, or construct a path using the - // enclosing function). - let (linkage_name, containing_scope) = if has_path { - let namespace_node = namespace_for_item(cx, ast_util::local_def(fn_ast_id)); - let linkage_name = namespace_node.mangled_name_of_contained_item( - function_name.as_slice()); - let containing_scope = namespace_node.scope; - (linkage_name, containing_scope) - } else { - (function_name.as_slice().to_string(), file_metadata) - }; - - // Clang sets this parameter to the opening brace of the function's block, - // so let's do this too. - let scope_line = span_start(cx, top_level_block.span).line; - - let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id); - - let fn_metadata = function_name.as_slice().with_c_str(|function_name| { - linkage_name.as_slice().with_c_str(|linkage_name| { - unsafe { - llvm::LLVMDIBuilderCreateFunction( - DIB(cx), - containing_scope, - function_name, - linkage_name, - file_metadata, - loc.line as c_uint, - function_type_metadata, - is_local_to_unit, - true, - scope_line as c_uint, - FlagPrototyped as c_uint, - cx.sess().opts.optimize != config::No, - llfn, - template_parameters, - ptr::null_mut()) - } - }) - }); - - // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = box FunctionDebugContextData { - scope_map: RefCell::new(NodeMap::new()), - fn_metadata: fn_metadata, - argument_counter: Cell::new(1), - source_locations_enabled: Cell::new(false), - }; - - populate_scope_map(cx, - fn_decl.inputs.as_slice(), - &*top_level_block, - fn_metadata, - fn_ast_id, - &mut *fn_debug_context.scope_map.borrow_mut()); - - return FunctionDebugContext { repr: DebugInfo(fn_debug_context) }; - - fn get_function_signature(cx: &CrateContext, - fn_ast_id: ast::NodeId, - fn_decl: &ast::FnDecl, - param_substs: ¶m_substs, - error_reporting_span: Span) -> DIArray { - if cx.sess().opts.debuginfo == LimitedDebugInfo { - return create_DIArray(DIB(cx), &[]); - } - - let mut signature = Vec::with_capacity(fn_decl.inputs.len() + 1); - - // Return type -- llvm::DIBuilder wants this at index 0 - match fn_decl.output { - ast::Return(ref ret_ty) if ret_ty.node == ast::TyTup(vec![]) => - signature.push(ptr::null_mut()), - _ => { - assert_type_for_node_id(cx, fn_ast_id, error_reporting_span); - - let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id); - let return_type = return_type.substp(cx.tcx(), param_substs); - signature.push(type_metadata(cx, return_type, codemap::DUMMY_SP)); - } - } - - // Arguments types - for arg in fn_decl.inputs.iter() { - assert_type_for_node_id(cx, arg.pat.id, arg.pat.span); - let arg_type = ty::node_id_to_type(cx.tcx(), arg.pat.id); - let arg_type = arg_type.substp(cx.tcx(), param_substs); - signature.push(type_metadata(cx, arg_type, codemap::DUMMY_SP)); - } - - return create_DIArray(DIB(cx), signature.as_slice()); - } - - fn get_template_parameters(cx: &CrateContext, - generics: &ast::Generics, - param_substs: ¶m_substs, - file_metadata: DIFile, - name_to_append_suffix_to: &mut String) - -> DIArray { - let self_type = param_substs.substs.self_ty(); - - // Only true for static default methods: - let has_self_type = self_type.is_some(); - - if !generics.is_type_parameterized() && !has_self_type { - return create_DIArray(DIB(cx), &[]); - } - - name_to_append_suffix_to.push('<'); - - // The list to be filled with template parameters: - let mut template_params: Vec = - Vec::with_capacity(generics.ty_params.len() + 1); - - // Handle self type - if has_self_type { - let actual_self_type = self_type.unwrap(); - // Add self type name to <...> clause of function name - let actual_self_type_name = compute_debuginfo_type_name( - cx, - actual_self_type, - true); - - name_to_append_suffix_to.push_str(actual_self_type_name.as_slice()); - - if generics.is_type_parameterized() { - name_to_append_suffix_to.push_str(","); - } - - // Only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == FullDebugInfo { - let actual_self_type_metadata = type_metadata(cx, - actual_self_type, - codemap::DUMMY_SP); - - let ident = special_idents::type_self; - - let param_metadata = token::get_ident(ident).get() - .with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateTemplateTypeParameter( - DIB(cx), - file_metadata, - name, - actual_self_type_metadata, - ptr::null_mut(), - 0, - 0) - } - }); - - template_params.push(param_metadata); - } - } - - // Handle other generic parameters - let actual_types = param_substs.substs.types.get_slice(subst::FnSpace); - for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() { - let actual_type = actual_types[index]; - // Add actual type name to <...> clause of function name - let actual_type_name = compute_debuginfo_type_name(cx, - actual_type, - true); - name_to_append_suffix_to.push_str(actual_type_name.as_slice()); - - if index != generics.ty_params.len() - 1 { - name_to_append_suffix_to.push_str(","); - } - - // Again, only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == FullDebugInfo { - let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP); - let param_metadata = token::get_ident(ident).get() - .with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateTemplateTypeParameter( - DIB(cx), - file_metadata, - name, - actual_type_metadata, - ptr::null_mut(), - 0, - 0) - } - }); - template_params.push(param_metadata); - } - } - - name_to_append_suffix_to.push('>'); - - return create_DIArray(DIB(cx), template_params.as_slice()); - } -} - -//=----------------------------------------------------------------------------- -// Module-Internal debug info creation functions -//=----------------------------------------------------------------------------- - -fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool -{ - // The is_local_to_unit flag indicates whether a function is local to the - // current compilation unit (i.e. if it is *static* in the C-sense). The - // *reachable* set should provide a good approximation of this, as it - // contains everything that might leak out of the current crate (by being - // externally visible or by being inlined into something externally visible). - // It might better to use the `exported_items` set from `driver::CrateAnalysis` - // in the future, but (atm) this set is not available in the translation pass. - !cx.reachable().contains(&node_id) -} - -#[allow(non_snake_case)] -fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { - return unsafe { - llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) - }; -} - -fn compile_unit_metadata(cx: &CrateContext) { - let work_dir = &cx.sess().working_dir; - let compile_unit_name = match cx.sess().local_crate_source_file { - None => fallback_path(cx), - Some(ref abs_path) => { - if abs_path.is_relative() { - cx.sess().warn("debuginfo: Invalid path to crate's local root source file!"); - fallback_path(cx) - } else { - match abs_path.path_relative_from(work_dir) { - Some(ref p) if p.is_relative() => { - // prepend "./" if necessary - let dotdot = b".."; - let prefix = &[dotdot[0], ::std::path::SEP_BYTE]; - let mut path_bytes = p.as_vec().to_vec(); - - if path_bytes.slice_to(2) != prefix && - path_bytes.slice_to(2) != dotdot { - path_bytes.insert(0, prefix[0]); - path_bytes.insert(1, prefix[1]); - } - - path_bytes.as_slice().to_c_str() - } - _ => fallback_path(cx) - } - } - } - }; - - debug!("compile_unit_metadata: {}", compile_unit_name); - let producer = format!("rustc version {}", - (option_env!("CFG_VERSION")).expect("CFG_VERSION")); - - let compile_unit_name = compile_unit_name.as_ptr(); - work_dir.as_vec().with_c_str(|work_dir| { - producer.with_c_str(|producer| { - "".with_c_str(|flags| { - "".with_c_str(|split_name| { - unsafe { - llvm::LLVMDIBuilderCreateCompileUnit( - debug_context(cx).builder, - DW_LANG_RUST, - compile_unit_name, - work_dir, - producer, - cx.sess().opts.optimize != config::No, - flags, - 0, - split_name); - } - }) - }) - }) - }); - - fn fallback_path(cx: &CrateContext) -> CString { - cx.link_meta().crate_name.as_slice().to_c_str() - } -} - -fn declare_local(bcx: Block, - variable_ident: ast::Ident, - variable_type: ty::t, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { - let cx: &CrateContext = bcx.ccx(); - - let filename = span_start(cx, span).file.name.clone(); - let file_metadata = file_metadata(cx, filename.as_slice()); - - let name = token::get_ident(variable_ident); - let loc = span_start(cx, span); - let type_metadata = type_metadata(cx, variable_type, span); - - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable | - CapturedVariable => (0, DW_TAG_auto_variable) - }; - - let (var_alloca, var_metadata) = name.get().with_c_str(|name| { - match variable_access { - DirectVariable { alloca } => ( - alloca, - unsafe { - llvm::LLVMDIBuilderCreateLocalVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name, - file_metadata, - loc.line as c_uint, - type_metadata, - cx.sess().opts.optimize != config::No, - 0, - argument_index) - } - ), - IndirectVariable { alloca, address_operations } => ( - alloca, - unsafe { - llvm::LLVMDIBuilderCreateComplexVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name, - file_metadata, - loc.line as c_uint, - type_metadata, - address_operations.as_ptr(), - address_operations.len() as c_uint, - argument_index) - } - ) - } - }); - - set_debug_location(cx, DebugLocation::new(scope_metadata, - loc.line, - loc.col.to_uint())); - unsafe { - let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( - DIB(cx), - var_alloca, - var_metadata, - bcx.llbb); - - llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); - } - - match variable_kind { - ArgumentVariable(_) | CapturedVariable => { - assert!(!bcx.fcx - .debug_context - .get_ref(cx, span) - .source_locations_enabled - .get()); - set_debug_location(cx, UnknownLocation); - } - _ => { /* nothing to do */ } - } -} - -fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile { - match debug_context(cx).created_files.borrow().get(full_path) { - Some(file_metadata) => return *file_metadata, - None => () - } - - debug!("file_metadata: {}", full_path); - - // FIXME (#9639): This needs to handle non-utf8 paths - let work_dir = cx.sess().working_dir.as_str().unwrap(); - let file_name = - if full_path.starts_with(work_dir) { - full_path.slice(work_dir.len() + 1u, full_path.len()) - } else { - full_path - }; - - let file_metadata = - file_name.with_c_str(|file_name| { - work_dir.with_c_str(|work_dir| { - unsafe { - llvm::LLVMDIBuilderCreateFile(DIB(cx), file_name, work_dir) - } - }) - }); - - let mut created_files = debug_context(cx).created_files.borrow_mut(); - created_files.insert(full_path.to_string(), file_metadata); - return file_metadata; -} - -/// Finds the scope metadata node for the given AST node. -fn scope_metadata(fcx: &FunctionContext, - node_id: ast::NodeId, - error_reporting_span: Span) - -> DIScope { - let scope_map = &fcx.debug_context - .get_ref(fcx.ccx, error_reporting_span) - .scope_map; - match scope_map.borrow().get(&node_id).cloned() { - Some(scope_metadata) => scope_metadata, - None => { - let node = fcx.ccx.tcx().map.get(node_id); - - fcx.ccx.sess().span_bug(error_reporting_span, - format!("debuginfo: Could not find scope info for node {}", - node).as_slice()); - } - } -} - -fn diverging_type_metadata(cx: &CrateContext) -> DIType { - "!".with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateBasicType( - DIB(cx), - name, - bytes_to_bits(0), - bytes_to_bits(0), - DW_ATE_unsigned) - } - }) -} - -fn basic_type_metadata(cx: &CrateContext, t: ty::t) -> DIType { - - debug!("basic_type_metadata: {}", ty::get(t)); - - let (name, encoding) = match ty::get(t).sty { - ty::ty_tup(ref elements) if elements.is_empty() => - ("()".to_string(), DW_ATE_unsigned), - ty::ty_bool => ("bool".to_string(), DW_ATE_boolean), - ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char), - ty::ty_int(int_ty) => match int_ty { - ast::TyI => ("int".to_string(), DW_ATE_signed), - ast::TyI8 => ("i8".to_string(), DW_ATE_signed), - ast::TyI16 => ("i16".to_string(), DW_ATE_signed), - ast::TyI32 => ("i32".to_string(), DW_ATE_signed), - ast::TyI64 => ("i64".to_string(), DW_ATE_signed) - }, - ty::ty_uint(uint_ty) => match uint_ty { - ast::TyU => ("uint".to_string(), DW_ATE_unsigned), - ast::TyU8 => ("u8".to_string(), DW_ATE_unsigned), - ast::TyU16 => ("u16".to_string(), DW_ATE_unsigned), - ast::TyU32 => ("u32".to_string(), DW_ATE_unsigned), - ast::TyU64 => ("u64".to_string(), DW_ATE_unsigned) - }, - ty::ty_float(float_ty) => match float_ty { - ast::TyF32 => ("f32".to_string(), DW_ATE_float), - ast::TyF64 => ("f64".to_string(), DW_ATE_float), - }, - _ => cx.sess().bug("debuginfo::basic_type_metadata - t is invalid type") - }; - - let llvm_type = type_of::type_of(cx, t); - let (size, align) = size_and_align_of(cx, llvm_type); - let ty_metadata = name.with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateBasicType( - DIB(cx), - name, - bytes_to_bits(size), - bytes_to_bits(align), - encoding) - } - }); - - return ty_metadata; -} - -fn pointer_type_metadata(cx: &CrateContext, - pointer_type: ty::t, - pointee_type_metadata: DIType) - -> DIType { - let pointer_llvm_type = type_of::type_of(cx, pointer_type); - let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); - let name = compute_debuginfo_type_name(cx, pointer_type, false); - let ptr_metadata = name.as_slice().with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - bytes_to_bits(pointer_size), - bytes_to_bits(pointer_align), - name) - } - }); - return ptr_metadata; -} - -//=----------------------------------------------------------------------------- -// Common facilities for record-like types (structs, enums, tuples) -//=----------------------------------------------------------------------------- - -enum MemberOffset { - FixedMemberOffset { bytes: uint }, - // For ComputedMemberOffset, the offset is read from the llvm type definition - ComputedMemberOffset -} - -// Description of a type member, which can either be a regular field (as in -// structs or tuples) or an enum variant -struct MemberDescription { - name: String, - llvm_type: Type, - type_metadata: DIType, - offset: MemberOffset, - flags: c_uint -} - -// A factory for MemberDescriptions. It produces a list of member descriptions -// for some record-like type. MemberDescriptionFactories are used to defer the -// creation of type member descriptions in order to break cycles arising from -// recursive type definitions. -enum MemberDescriptionFactory { - StructMDF(StructMemberDescriptionFactory), - TupleMDF(TupleMemberDescriptionFactory), - EnumMDF(EnumMemberDescriptionFactory), - VariantMDF(VariantMemberDescriptionFactory) -} - -impl MemberDescriptionFactory { - fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { - match *self { - StructMDF(ref this) => { - this.create_member_descriptions(cx) - } - TupleMDF(ref this) => { - this.create_member_descriptions(cx) - } - EnumMDF(ref this) => { - this.create_member_descriptions(cx) - } - VariantMDF(ref this) => { - this.create_member_descriptions(cx) - } - } - } -} - -// A description of some recursive type. It can either be already finished (as -// with FinalMetadata) or it is not yet finished, but contains all information -// needed to generate the missing parts of the description. See the documentation -// section on Recursive Types at the top of this file for more information. -enum RecursiveTypeDescription { - UnfinishedMetadata { - unfinished_type: ty::t, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - llvm_type: Type, - member_description_factory: MemberDescriptionFactory, - }, - FinalMetadata(DICompositeType) -} - -fn create_and_register_recursive_type_forward_declaration( - cx: &CrateContext, - unfinished_type: ty::t, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - llvm_type: Type, - member_description_factory: MemberDescriptionFactory) - -> RecursiveTypeDescription { - - // Insert the stub into the TypeMap in order to allow for recursive references - let mut type_map = debug_context(cx).type_map.borrow_mut(); - type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata_stub); - type_map.register_type_with_metadata(cx, unfinished_type, metadata_stub); - - UnfinishedMetadata { - unfinished_type: unfinished_type, - unique_type_id: unique_type_id, - metadata_stub: metadata_stub, - llvm_type: llvm_type, - member_description_factory: member_description_factory, - } -} - -impl RecursiveTypeDescription { - // Finishes up the description of the type in question (mostly by providing - // descriptions of the fields of the given type) and returns the final type metadata. - fn finalize(&self, cx: &CrateContext) -> MetadataCreationResult { - match *self { - FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - llvm_type, - ref member_description_factory, - .. - } => { - // Make sure that we have a forward declaration of the type in - // the TypeMap so that recursive references are possible. This - // will always be the case if the RecursiveTypeDescription has - // been properly created through the - // create_and_register_recursive_type_forward_declaration() function. - { - let type_map = debug_context(cx).type_map.borrow(); - if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || - type_map.find_metadata_for_type(unfinished_type).is_none() { - cx.sess().bug(format!("Forward declaration of potentially recursive type \ - '{}' was not found in TypeMap!", - ppaux::ty_to_string(cx.tcx(), unfinished_type)) - .as_slice()); - } - } - - // ... then create the member descriptions ... - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - // ... and attach them to the stub to complete it. - set_members_of_composite_type(cx, - metadata_stub, - llvm_type, - member_descriptions.as_slice()); - return MetadataCreationResult::new(metadata_stub, true); - } - } - } -} - - -//=----------------------------------------------------------------------------- -// Structs -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a struct -struct StructMemberDescriptionFactory { - fields: Vec, - is_simd: bool, - span: Span, -} - -impl StructMemberDescriptionFactory { - fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { - if self.fields.len() == 0 { - return Vec::new(); - } - - let field_size = if self.is_simd { - machine::llsize_of_alloc(cx, type_of::type_of(cx, self.fields[0].mt.ty)) as uint - } else { - 0xdeadbeef - }; - - self.fields.iter().enumerate().map(|(i, field)| { - let name = if field.name == special_idents::unnamed_field.name { - "".to_string() - } else { - token::get_name(field.name).get().to_string() - }; - - let offset = if self.is_simd { - assert!(field_size != 0xdeadbeef); - FixedMemberOffset { bytes: i * field_size } - } else { - ComputedMemberOffset - }; - - MemberDescription { - name: name, - llvm_type: type_of::type_of(cx, field.mt.ty), - type_metadata: type_metadata(cx, field.mt.ty, self.span), - offset: offset, - flags: FLAGS_NONE, - } - }).collect() - } -} - - -fn prepare_struct_metadata(cx: &CrateContext, - struct_type: ty::t, - def_id: ast::DefId, - substs: &subst::Substs, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription { - let struct_name = compute_debuginfo_type_name(cx, struct_type, false); - let struct_llvm_type = type_of::type_of(cx, struct_type); - - let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - - let struct_metadata_stub = create_struct_stub(cx, - struct_llvm_type, - struct_name.as_slice(), - unique_type_id, - containing_scope); - - let fields = ty::struct_fields(cx.tcx(), def_id, substs); - - create_and_register_recursive_type_forward_declaration( - cx, - struct_type, - unique_type_id, - struct_metadata_stub, - struct_llvm_type, - StructMDF(StructMemberDescriptionFactory { - fields: fields, - is_simd: ty::type_is_simd(cx.tcx(), struct_type), - span: span, - }) - ) -} - - -//=----------------------------------------------------------------------------- -// Tuples -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a tuple -struct TupleMemberDescriptionFactory { - component_types: Vec , - span: Span, -} - -impl TupleMemberDescriptionFactory { - fn create_member_descriptions(&self, cx: &CrateContext) - -> Vec { - self.component_types.iter().map(|&component_type| { - MemberDescription { - name: "".to_string(), - llvm_type: type_of::type_of(cx, component_type), - type_metadata: type_metadata(cx, component_type, self.span), - offset: ComputedMemberOffset, - flags: FLAGS_NONE, - } - }).collect() - } -} - -fn prepare_tuple_metadata(cx: &CrateContext, - tuple_type: ty::t, - component_types: &[ty::t], - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription { - let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); - let tuple_llvm_type = type_of::type_of(cx, tuple_type); - - create_and_register_recursive_type_forward_declaration( - cx, - tuple_type, - unique_type_id, - create_struct_stub(cx, - tuple_llvm_type, - tuple_name.as_slice(), - unique_type_id, - UNKNOWN_SCOPE_METADATA), - tuple_llvm_type, - TupleMDF(TupleMemberDescriptionFactory { - component_types: component_types.to_vec(), - span: span, - }) - ) -} - - -//=----------------------------------------------------------------------------- -// Enums -//=----------------------------------------------------------------------------- - -// Describes the members of an enum value: An enum is described as a union of -// structs in DWARF. This MemberDescriptionFactory provides the description for -// the members of this union; so for every variant of the given enum, this factory -// will produce one MemberDescription (all with no name and a fixed offset of -// zero bytes). -struct EnumMemberDescriptionFactory { - enum_type: ty::t, - type_rep: Rc, - variants: Rc>>, - discriminant_type_metadata: Option, - containing_scope: DIScope, - file_metadata: DIFile, - span: Span, -} - -impl EnumMemberDescriptionFactory { - fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { - match *self.type_rep { - adt::General(_, ref struct_defs, _) => { - let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata - .expect("")); - - struct_defs - .iter() - .enumerate() - .map(|(i, struct_def)| { - let (variant_type_metadata, - variant_llvm_type, - member_desc_factory) = - describe_enum_variant(cx, - self.enum_type, - struct_def, - &*(*self.variants)[i], - discriminant_info, - self.containing_scope, - self.span); - - let member_descriptions = member_desc_factory - .create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - variant_llvm_type, - member_descriptions.as_slice()); - MemberDescription { - name: "".to_string(), - llvm_type: variant_llvm_type, - type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: FLAGS_NONE - } - }).collect() - }, - adt::Univariant(ref struct_def, _) => { - assert!(self.variants.len() <= 1); - - if self.variants.len() == 0 { - vec![] - } else { - let (variant_type_metadata, - variant_llvm_type, - member_description_factory) = - describe_enum_variant(cx, - self.enum_type, - struct_def, - &*(*self.variants)[0], - NoDiscriminant, - self.containing_scope, - self.span); - - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - variant_llvm_type, - member_descriptions.as_slice()); - vec![ - MemberDescription { - name: "".to_string(), - llvm_type: variant_llvm_type, - type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: FLAGS_NONE - } - ] - } - } - adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => { - // As far as debuginfo is concerned, the pointer this enum - // represents is still wrapped in a struct. This is to make the - // DWARF representation of enums uniform. - - // First create a description of the artificial wrapper struct: - let non_null_variant = &(*self.variants)[non_null_variant_index as uint]; - let non_null_variant_name = token::get_name(non_null_variant.name); - - // The llvm type and metadata of the pointer - let non_null_llvm_type = type_of::type_of(cx, nnty); - let non_null_type_metadata = type_metadata(cx, nnty, self.span); - - // The type of the artificial struct wrapping the pointer - let artificial_struct_llvm_type = Type::struct_(cx, - &[non_null_llvm_type], - false); - - // For the metadata of the wrapper struct, we need to create a - // MemberDescription of the struct's single field. - let sole_struct_member_description = MemberDescription { - name: match non_null_variant.arg_names { - Some(ref names) => token::get_ident(names[0]).get().to_string(), - None => "".to_string() - }, - llvm_type: non_null_llvm_type, - type_metadata: non_null_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: FLAGS_NONE - }; - - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - self.enum_type, - non_null_variant_name.get()); - - // Now we can create the metadata of the artificial struct - let artificial_struct_metadata = - composite_type_metadata(cx, - artificial_struct_llvm_type, - non_null_variant_name.get(), - unique_type_id, - &[sole_struct_member_description], - self.containing_scope, - self.file_metadata, - codemap::DUMMY_SP); - - // Encode the information about the null variant in the union - // member's name. - let null_variant_index = (1 - non_null_variant_index) as uint; - let null_variant_name = token::get_name((*self.variants)[null_variant_index].name); - let union_member_name = format!("RUST$ENCODED$ENUM${}${}", - 0u, - null_variant_name); - - // Finally create the (singleton) list of descriptions of union - // members. - vec![ - MemberDescription { - name: union_member_name, - llvm_type: artificial_struct_llvm_type, - type_metadata: artificial_struct_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: FLAGS_NONE - } - ] - }, - adt::StructWrappedNullablePointer { nonnull: ref struct_def, - nndiscr, - ptrfield, ..} => { - // Create a description of the non-null variant - let (variant_type_metadata, variant_llvm_type, member_description_factory) = - describe_enum_variant(cx, - self.enum_type, - struct_def, - &*(*self.variants)[nndiscr as uint], - OptimizedDiscriminant(ptrfield), - self.containing_scope, - self.span); - - let variant_member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - variant_llvm_type, - variant_member_descriptions.as_slice()); - - // Encode the information about the null variant in the union - // member's name. - let null_variant_index = (1 - nndiscr) as uint; - let null_variant_name = token::get_name((*self.variants)[null_variant_index].name); - let discrfield = match ptrfield { - adt::ThinPointer(field) => format!("{}", field), - adt::FatPointer(field) => format!("{}", field) - }; - let union_member_name = format!("RUST$ENCODED$ENUM${}${}", - discrfield, - null_variant_name); - - // Create the (singleton) list of descriptions of union members. - vec![ - MemberDescription { - name: union_member_name, - llvm_type: variant_llvm_type, - type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: FLAGS_NONE - } - ] - }, - adt::CEnum(..) => cx.sess().span_bug(self.span, "This should be unreachable.") - } - } -} - -// Creates MemberDescriptions for the fields of a single enum variant. -struct VariantMemberDescriptionFactory { - args: Vec<(String, ty::t)> , - discriminant_type_metadata: Option, - span: Span, -} - -impl VariantMemberDescriptionFactory { - fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { - self.args.iter().enumerate().map(|(i, &(ref name, ty))| { - MemberDescription { - name: name.to_string(), - llvm_type: type_of::type_of(cx, ty), - type_metadata: match self.discriminant_type_metadata { - Some(metadata) if i == 0 => metadata, - _ => type_metadata(cx, ty, self.span) - }, - offset: ComputedMemberOffset, - flags: FLAGS_NONE - } - }).collect() - } -} - -enum EnumDiscriminantInfo { - RegularDiscriminant(DIType), - OptimizedDiscriminant(adt::PointerField), - NoDiscriminant -} - -// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type -// of the variant, and (3) a MemberDescriptionFactory for producing the -// descriptions of the fields of the variant. This is a rudimentary version of a -// full RecursiveTypeDescription. -fn describe_enum_variant(cx: &CrateContext, - enum_type: ty::t, - struct_def: &adt::Struct, - variant_info: &ty::VariantInfo, - discriminant_info: EnumDiscriminantInfo, - containing_scope: DIScope, - span: Span) - -> (DICompositeType, Type, MemberDescriptionFactory) { - let variant_llvm_type = - Type::struct_(cx, struct_def.fields - .iter() - .map(|&t| type_of::type_of(cx, t)) - .collect::>() - .as_slice(), - struct_def.packed); - // Could do some consistency checks here: size, align, field count, discr type - - let variant_name = token::get_name(variant_info.name); - let variant_name = variant_name.get(); - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - enum_type, - variant_name); - - let metadata_stub = create_struct_stub(cx, - variant_llvm_type, - variant_name, - unique_type_id, - containing_scope); - - // Get the argument names from the enum variant info - let mut arg_names: Vec<_> = match variant_info.arg_names { - Some(ref names) => { - names.iter() - .map(|ident| { - token::get_ident(*ident).get().to_string().into_string() - }).collect() - } - None => variant_info.args.iter().map(|_| "".to_string()).collect() - }; - - // If this is not a univariant enum, there is also the discriminant field. - match discriminant_info { - RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), - _ => { /* do nothing */ } - }; - - // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args: Vec<(String, ty::t)> = arg_names.iter() - .zip(struct_def.fields.iter()) - .map(|(s, &t)| (s.to_string(), t)) - .collect(); - - let member_description_factory = - VariantMDF(VariantMemberDescriptionFactory { - args: args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant(discriminant_type_metadata) => { - Some(discriminant_type_metadata) - } - _ => None - }, - span: span, - }); - - (metadata_stub, variant_llvm_type, member_description_factory) -} - -fn prepare_enum_metadata(cx: &CrateContext, - enum_type: ty::t, - enum_def_id: ast::DefId, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription { - let enum_name = compute_debuginfo_type_name(cx, enum_type, false); - - let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, enum_def_id); - let loc = span_start(cx, definition_span); - let file_metadata = file_metadata(cx, loc.file.name.as_slice()); - - let variants = ty::enum_variants(cx.tcx(), enum_def_id); - - let enumerators_metadata: Vec = variants - .iter() - .map(|v| { - token::get_name(v.name).get().with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateEnumerator( - DIB(cx), - name, - v.disr_val as u64) - } - }) - }) - .collect(); - - let discriminant_type_metadata = |inttype| { - // We can reuse the type of the discriminant for all monomorphized - // instances of an enum because it doesn't depend on any type parameters. - // The def_id, uniquely identifying the enum's polytype acts as key in - // this cache. - let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types - .borrow() - .get(&enum_def_id).cloned(); - match cached_discriminant_type_metadata { - Some(discriminant_type_metadata) => discriminant_type_metadata, - None => { - let discriminant_llvm_type = adt::ll_inttype(cx, inttype); - let (discriminant_size, discriminant_align) = - size_and_align_of(cx, discriminant_llvm_type); - let discriminant_base_type_metadata = type_metadata(cx, - adt::ty_of_inttype(inttype), - codemap::DUMMY_SP); - let discriminant_name = get_enum_discriminant_name(cx, enum_def_id); - - let discriminant_type_metadata = discriminant_name.get().with_c_str(|name| { - unsafe { - llvm::LLVMDIBuilderCreateEnumerationType( - DIB(cx), - containing_scope, - name, - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(discriminant_size), - bytes_to_bits(discriminant_align), - create_DIArray(DIB(cx), enumerators_metadata.as_slice()), - discriminant_base_type_metadata) - } - }); - - debug_context(cx).created_enum_disr_types - .borrow_mut() - .insert(enum_def_id, discriminant_type_metadata); - - discriminant_type_metadata - } - } - }; - - let type_rep = adt::represent_type(cx, enum_type); - - let discriminant_type_metadata = match *type_rep { - adt::CEnum(inttype, _, _) => { - return FinalMetadata(discriminant_type_metadata(inttype)) - }, - adt::RawNullablePointer { .. } | - adt::StructWrappedNullablePointer { .. } | - adt::Univariant(..) => None, - adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)), - }; - - let enum_llvm_type = type_of::type_of(cx, enum_type); - let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); - - let unique_type_id_str = debug_context(cx) - .type_map - .borrow() - .get_unique_type_id_as_string(unique_type_id); - - let enum_metadata = enum_name.as_slice().with_c_str(|enum_name| { - unique_type_id_str.as_slice().with_c_str(|unique_type_id_str| { - unsafe { - llvm::LLVMDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - enum_name, - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(enum_type_size), - bytes_to_bits(enum_type_align), - 0, // Flags - ptr::null_mut(), - 0, // RuntimeLang - unique_type_id_str) - } - }) - }); - - return create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - enum_metadata, - enum_llvm_type, - EnumMDF(EnumMemberDescriptionFactory { - enum_type: enum_type, - type_rep: type_rep.clone(), - variants: variants, - discriminant_type_metadata: discriminant_type_metadata, - containing_scope: containing_scope, - file_metadata: file_metadata, - span: span, - }), - ); - - fn get_enum_discriminant_name(cx: &CrateContext, - def_id: ast::DefId) - -> token::InternedString { - let name = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx().map.get_path_elem(def_id.node).name() - } else { - csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name() - }; - - token::get_name(name) - } -} - -/// Creates debug information for a composite type, that is, anything that -/// results in a LLVM struct. -/// -/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata(cx: &CrateContext, - composite_llvm_type: Type, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription], - containing_scope: DIScope, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: DIFile, - _definition_span: Span) - -> DICompositeType { - // Create the (empty) struct metadata node ... - let composite_type_metadata = create_struct_stub(cx, - composite_llvm_type, - composite_type_name, - composite_type_unique_id, - containing_scope); - // ... and immediately create and add the member descriptions. - set_members_of_composite_type(cx, - composite_type_metadata, - composite_llvm_type, - member_descriptions); - - return composite_type_metadata; -} - -fn set_members_of_composite_type(cx: &CrateContext, - composite_type_metadata: DICompositeType, - composite_llvm_type: Type, - member_descriptions: &[MemberDescription]) { - // In some rare cases LLVM metadata uniquing would lead to an existing type - // description being used instead of a new one created in create_struct_stub. - // This would cause a hard to trace assertion in DICompositeType::SetTypeArray(). - // The following check makes sure that we get a better error message if this - // should happen again due to some regression. - { - let mut composite_types_completed = - debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { - let (llvm_version_major, llvm_version_minor) = unsafe { - (llvm::LLVMVersionMajor(), llvm::LLVMVersionMinor()) - }; - - let actual_llvm_version = llvm_version_major * 1000000 + llvm_version_minor * 1000; - let min_supported_llvm_version = 3 * 1000000 + 4 * 1000; - - if actual_llvm_version < min_supported_llvm_version { - cx.sess().warn(format!("This version of rustc was built with LLVM \ - {}.{}. Rustc just ran into a known \ - debuginfo corruption problem thatoften \ - occurs with LLVM versions below 3.4. \ - Please use a rustc built with anewer \ - version of LLVM.", - llvm_version_major, - llvm_version_minor).as_slice()); - } else { - cx.sess().bug("debuginfo::set_members_of_composite_type() - \ - Already completed forward declaration re-encountered."); - } - } else { - composite_types_completed.insert(composite_type_metadata); - } - } - - let member_metadata: Vec = member_descriptions - .iter() - .enumerate() - .map(|(i, member_description)| { - let (member_size, member_align) = size_and_align_of(cx, member_description.llvm_type); - let member_offset = match member_description.offset { - FixedMemberOffset { bytes } => bytes as u64, - ComputedMemberOffset => machine::llelement_offset(cx, composite_llvm_type, i) - }; - - member_description.name.as_slice().with_c_str(|member_name| { - unsafe { - llvm::LLVMDIBuilderCreateMemberType( - DIB(cx), - composite_type_metadata, - member_name, - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(member_size), - bytes_to_bits(member_align), - bytes_to_bits(member_offset), - member_description.flags, - member_description.type_metadata) - } - }) - }) - .collect(); - - unsafe { - let type_array = create_DIArray(DIB(cx), member_metadata.as_slice()); - llvm::LLVMDICompositeTypeSetTypeArray(composite_type_metadata, type_array); - } -} - -// A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any -// caching, does not add any fields to the struct. This can be done later with -// set_members_of_composite_type(). -fn create_struct_stub(cx: &CrateContext, - struct_llvm_type: Type, - struct_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type); - - let unique_type_id_str = debug_context(cx).type_map - .borrow() - .get_unique_type_id_as_string(unique_type_id); - let metadata_stub = unsafe { - struct_type_name.with_c_str(|name| { - unique_type_id_str.as_slice().with_c_str(|unique_type_id| { - // LLVMDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMDIBuilderCreateStructType( - DIB(cx), - containing_scope, - name, - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(struct_size), - bytes_to_bits(struct_align), - 0, - ptr::null_mut(), - empty_array, - 0, - ptr::null_mut(), - unique_type_id) - }) - }) - }; - - return metadata_stub; -} - -fn fixed_vec_metadata(cx: &CrateContext, - unique_type_id: UniqueTypeId, - element_type: ty::t, - len: uint, - span: Span) - -> MetadataCreationResult { - let element_type_metadata = type_metadata(cx, element_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let element_llvm_type = type_of::type_of(cx, element_type); - let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); - - let subrange = unsafe { - llvm::LLVMDIBuilderGetOrCreateSubrange( - DIB(cx), - 0, - len as i64) - }; - - let subscripts = create_DIArray(DIB(cx), &[subrange]); - let metadata = unsafe { - llvm::LLVMDIBuilderCreateArrayType( - DIB(cx), - bytes_to_bits(element_type_size * (len as u64)), - bytes_to_bits(element_type_align), - element_type_metadata, - subscripts) - }; - - return MetadataCreationResult::new(metadata, false); -} - -fn vec_slice_metadata(cx: &CrateContext, - vec_type: ty::t, - element_type: ty::t, - unique_type_id: UniqueTypeId, - span: Span) - -> MetadataCreationResult { - let data_ptr_type = ty::mk_ptr(cx.tcx(), ty::mt { - ty: element_type, - mutbl: ast::MutImmutable - }); - - let element_type_metadata = type_metadata(cx, data_ptr_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let slice_llvm_type = type_of::type_of(cx, vec_type); - let slice_type_name = compute_debuginfo_type_name(cx, vec_type, true); - - let member_llvm_types = slice_llvm_type.field_types(); - assert!(slice_layout_is_correct(cx, - member_llvm_types.as_slice(), - element_type)); - let member_descriptions = [ - MemberDescription { - name: "data_ptr".to_string(), - llvm_type: member_llvm_types[0], - type_metadata: element_type_metadata, - offset: ComputedMemberOffset, - flags: FLAGS_NONE - }, - MemberDescription { - name: "length".to_string(), - llvm_type: member_llvm_types[1], - type_metadata: type_metadata(cx, ty::mk_uint(), span), - offset: ComputedMemberOffset, - flags: FLAGS_NONE - }, - ]; - - assert!(member_descriptions.len() == member_llvm_types.len()); - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, loc.file.name.as_slice()); - - let metadata = composite_type_metadata(cx, - slice_llvm_type, - slice_type_name.as_slice(), - unique_type_id, - &member_descriptions, - UNKNOWN_SCOPE_METADATA, - file_metadata, - span); - return MetadataCreationResult::new(metadata, false); - - fn slice_layout_is_correct(cx: &CrateContext, - member_llvm_types: &[Type], - element_type: ty::t) - -> bool { - member_llvm_types.len() == 2 && - member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && - member_llvm_types[1] == cx.int_type() - } -} - -fn subroutine_type_metadata(cx: &CrateContext, - unique_type_id: UniqueTypeId, - signature: &ty::FnSig, - span: Span) - -> MetadataCreationResult { - let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs.len() + 1); - - // return type - signature_metadata.push(match signature.output { - ty::FnConverging(ret_ty) => match ty::get(ret_ty).sty { - ty::ty_tup(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, ret_ty, span) - }, - ty::FnDiverging => diverging_type_metadata(cx) - }); - - // regular arguments - for &argument_type in signature.inputs.iter() { - signature_metadata.push(type_metadata(cx, argument_type, span)); - } - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - return MetadataCreationResult::new( - unsafe { - llvm::LLVMDIBuilderCreateSubroutineType( - DIB(cx), - UNKNOWN_FILE_METADATA, - create_DIArray(DIB(cx), signature_metadata.as_slice())) - }, - false); -} - -// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., Box, -// &Trait), trait_object_type should be the whole thing (e.g, Box) and -// trait_type should be the actual trait (e.g., Trait). Where the trait is part -// of a DST struct, there is no trait_object_type and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata(cx: &CrateContext, - trait_type: ty::t, - trait_object_type: Option, - unique_type_id: UniqueTypeId) - -> DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // But it does not describe the trait's methods. - - let def_id = match ty::get(trait_type).sty { - ty::ty_trait(box ty::TyTrait { ref principal, .. }) => principal.def_id, - _ => { - let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type); - cx.sess().bug(format!("debuginfo: Unexpected trait-object type in \ - trait_pointer_metadata(): {}", - pp_type_name.as_slice()).as_slice()); - } - }; - - let trait_object_type = trait_object_type.unwrap_or(trait_type); - let trait_type_name = - compute_debuginfo_type_name(cx, trait_object_type, false); - - let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - - let trait_llvm_type = type_of::type_of(cx, trait_object_type); - - composite_type_metadata(cx, - trait_llvm_type, - trait_type_name.as_slice(), - unique_type_id, - &[], - containing_scope, - UNKNOWN_FILE_METADATA, - codemap::DUMMY_SP) -} - -fn type_metadata(cx: &CrateContext, - t: ty::t, - usage_site_span: Span) - -> DIType { - // Get the unique type id of this type. - let unique_type_id = { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - // First, try to find the type in TypeMap. If we have seen it before, we - // can exit early here. - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - return metadata; - }, - None => { - // The ty::t is not in the TypeMap but maybe we have already seen - // an equivalent type (e.g. only differing in region arguments). - // In order to find out, generate the unique type id and look - // that up. - let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); - match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => { - // There is already an equivalent type in the TypeMap. - // Register this ty::t as an alias in the cache and - // return the cached metadata. - type_map.register_type_with_metadata(cx, t, metadata); - return metadata; - }, - None => { - // There really is no type metadata for this type, so - // proceed by creating it. - unique_type_id - } - } - } - } - }; - - debug!("type_metadata: {}", ty::get(t)); - - let sty = &ty::get(t).sty; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { - ty::ty_bool | - ty::ty_char | - ty::ty_int(_) | - ty::ty_uint(_) | - ty::ty_float(_) => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::ty_tup(ref elements) if elements.is_empty() => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::ty_enum(def_id, _) => { - prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx) - } - ty::ty_vec(typ, Some(len)) => { - fixed_vec_metadata(cx, unique_type_id, typ, len, usage_site_span) - } - // FIXME Can we do better than this for unsized vec/str fields? - ty::ty_vec(typ, None) => fixed_vec_metadata(cx, unique_type_id, typ, 0, usage_site_span), - ty::ty_str => fixed_vec_metadata(cx, unique_type_id, ty::mk_i8(), 0, usage_site_span), - ty::ty_trait(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, t, None, unique_type_id), - false) - } - ty::ty_uniq(ty) | ty::ty_ptr(ty::mt{ty, ..}) | ty::ty_rptr(_, ty::mt{ty, ..}) => { - match ty::get(ty).sty { - ty::ty_vec(typ, None) => { - vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span) - } - ty::ty_str => { - vec_slice_metadata(cx, t, ty::mk_u8(), unique_type_id, usage_site_span) - } - ty::ty_trait(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false) - } - _ => { - let pointee_metadata = type_metadata(cx, ty, usage_site_span); - - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return metadata, - None => { /* proceed normally */ } - }; - - MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), - false) - } - } - } - ty::ty_bare_fn(ref barefnty) => { - subroutine_type_metadata(cx, unique_type_id, &barefnty.sig, usage_site_span) - } - ty::ty_closure(ref closurety) => { - subroutine_type_metadata(cx, unique_type_id, &closurety.sig, usage_site_span) - } - ty::ty_unboxed_closure(ref def_id, _, ref substs) => { - let sig = cx.tcx().unboxed_closures.borrow() - .get(def_id).unwrap().closure_type.sig.subst(cx.tcx(), substs); - subroutine_type_metadata(cx, unique_type_id, &sig, usage_site_span) - } - ty::ty_struct(def_id, ref substs) => { - prepare_struct_metadata(cx, - t, - def_id, - substs, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::ty_tup(ref elements) => { - prepare_tuple_metadata(cx, - t, - elements.as_slice(), - unique_type_id, - usage_site_span).finalize(cx) - } - _ => { - cx.sess().bug(format!("debuginfo: unexpected type in type_metadata: {}", - sty).as_slice()) - } - }; - - { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - - if already_stored_in_typemap { - // Also make sure that we already have a TypeMap entry entry for the unique type id. - let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => metadata, - None => { - let unique_type_id_str = - type_map.get_unique_type_id_as_string(unique_type_id); - let error_message = format!("Expected type metadata for unique \ - type id '{}' to already be in \ - the debuginfo::TypeMap but it \ - was not. (ty::t = {})", - unique_type_id_str.as_slice(), - ppaux::ty_to_string(cx.tcx(), t)); - cx.sess().span_bug(usage_site_span, error_message.as_slice()); - } - }; - - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - if metadata != metadata_for_uid { - let unique_type_id_str = - type_map.get_unique_type_id_as_string(unique_type_id); - let error_message = format!("Mismatch between ty::t and \ - UniqueTypeId maps in \ - debuginfo::TypeMap. \ - UniqueTypeId={}, ty::t={}", - unique_type_id_str.as_slice(), - ppaux::ty_to_string(cx.tcx(), t)); - cx.sess().span_bug(usage_site_span, error_message.as_slice()); - } - } - None => { - type_map.register_type_with_metadata(cx, t, metadata); - } - } - } else { - type_map.register_type_with_metadata(cx, t, metadata); - type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata); - } - } - - metadata -} - -struct MetadataCreationResult { - metadata: DIType, - already_stored_in_typemap: bool -} - -impl MetadataCreationResult { - fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { - MetadataCreationResult { - metadata: metadata, - already_stored_in_typemap: already_stored_in_typemap - } - } -} - -#[deriving(PartialEq)] -enum DebugLocation { - KnownLocation { scope: DIScope, line: uint, col: uint }, - UnknownLocation -} - -impl DebugLocation { - fn new(scope: DIScope, line: uint, col: uint) -> DebugLocation { - KnownLocation { - scope: scope, - line: line, - col: col, - } - } -} - -fn set_debug_location(cx: &CrateContext, debug_location: DebugLocation) { - if debug_location == debug_context(cx).current_debug_location.get() { - return; - } - - let metadata_node; - - match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - let elements = [C_i32(cx, line as i32), C_i32(cx, col as i32), - scope, ptr::null_mut()]; - unsafe { - metadata_node = llvm::LLVMMDNodeInContext(debug_context(cx).llcontext, - elements.as_ptr(), - elements.len() as c_uint); - } - } - UnknownLocation => { - debug!("clearing debug location "); - metadata_node = ptr::null_mut(); - } - }; - - unsafe { - llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); - } - - debug_context(cx).current_debug_location.set(debug_location); -} - -//=----------------------------------------------------------------------------- -// Utility Functions -//=----------------------------------------------------------------------------- - -fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { - attributes.iter().any(|attr| { - let meta_item: &ast::MetaItem = &*attr.node.value; - match meta_item.node { - ast::MetaWord(ref value) => value.get() == "no_debug", - _ => false - } - }) -} - -/// Return codemap::Loc corresponding to the beginning of the span -fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { - cx.sess().codemap().lookup_char_pos(span.lo) -} - -fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { - (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) -} - -fn bytes_to_bits(bytes: u64) -> u64 { - bytes * 8 -} - -#[inline] -fn debug_context<'a>(cx: &'a CrateContext) -> &'a CrateDebugContext { - let debug_context: &'a CrateDebugContext = cx.dbg_cx().as_ref().unwrap(); - debug_context -} - -#[inline] -#[allow(non_snake_case)] -fn DIB(cx: &CrateContext) -> DIBuilderRef { - cx.dbg_cx().as_ref().unwrap().builder -} - -fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { - match fcx.debug_context.repr { - DebugInfo(_) => false, - _ => true - } -} - -fn assert_type_for_node_id(cx: &CrateContext, - node_id: ast::NodeId, - error_reporting_span: Span) { - if !cx.tcx().node_types.borrow().contains_key(&node_id) { - cx.sess().span_bug(error_reporting_span, - "debuginfo: Could not find type for node id!"); - } -} - -fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) - -> (DIScope, Span) { - let containing_scope = namespace_for_item(cx, def_id).scope; - let definition_span = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx().map.span(def_id.node) - } else { - // For external items there is no span information - codemap::DUMMY_SP - }; - - (containing_scope, definition_span) -} - -// This procedure builds the *scope map* for a given function, which maps any -// given ast::NodeId in the function's AST to the correct DIScope metadata instance. -// -// This builder procedure walks the AST in execution order and keeps track of -// what belongs to which scope, creating DIScope DIEs along the way, and -// introducing *artificial* lexical scope descriptors where necessary. These -// artificial scopes allow GDB to correctly handle name shadowing. -fn populate_scope_map(cx: &CrateContext, - args: &[ast::Arg], - fn_entry_block: &ast::Block, - fn_metadata: DISubprogram, - fn_ast_id: ast::NodeId, - scope_map: &mut NodeMap) { - let def_map = &cx.tcx().def_map; - - struct ScopeStackEntry { - scope_metadata: DIScope, - ident: Option - } - - let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, - ident: None }); - scope_map.insert(fn_ast_id, fn_metadata); - - // Push argument identifiers onto the stack so arguments integrate nicely - // with variable shadowing. - for arg in args.iter() { - pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { - scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, - ident: Some(path1.node) }); - scope_map.insert(node_id, fn_metadata); - }) - } - - // Clang creates a separate scope for function bodies, so let's do this too. - with_new_scope(cx, - fn_entry_block.span, - &mut scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, fn_entry_block, scope_stack, scope_map); - }); - - // local helper functions for walking the AST. - fn with_new_scope(cx: &CrateContext, - scope_span: Span, - scope_stack: &mut Vec , - scope_map: &mut NodeMap, - inner_walk: |&CrateContext, - &mut Vec , - &mut NodeMap|) { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); - let file_metadata = file_metadata(cx, loc.file.name.as_slice()); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_uint() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, - ident: None }); - - inner_walk(cx, scope_stack, scope_map); - - // pop artificial scopes - while scope_stack.last().unwrap().ident.is_some() { - scope_stack.pop(); - } - - if scope_stack.last().unwrap().scope_metadata != scope_metadata { - cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); - } - - scope_stack.pop(); - } - - fn walk_block(cx: &CrateContext, - block: &ast::Block, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); - - // The interesting things here are statements and the concluding expression. - for statement in block.stmts.iter() { - scope_map.insert(ast_util::stmt_id(&**statement), - scope_stack.last().unwrap().scope_metadata); - - match statement.node { - ast::StmtDecl(ref decl, _) => - walk_decl(cx, &**decl, scope_stack, scope_map), - ast::StmtExpr(ref exp, _) | - ast::StmtSemi(ref exp, _) => - walk_expr(cx, &**exp, scope_stack, scope_map), - ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). - } - } - - for exp in block.expr.iter() { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - - fn walk_decl(cx: &CrateContext, - decl: &ast::Decl, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - match *decl { - codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { - scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); - - walk_pattern(cx, &*local.pat, scope_stack, scope_map); - - for exp in local.init.iter() { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - _ => () - } - } - - fn walk_pattern(cx: &CrateContext, - pat: &ast::Pat, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - let def_map = &cx.tcx().def_map; - - // Unfortunately, we cannot just use pat_util::pat_bindings() or - // ast_util::walk_pat() here because we have to visit *all* nodes in - // order to put them into the scope map. The above functions don't do that. - match pat.node { - ast::PatIdent(_, ref path1, ref sub_pat_opt) => { - - // Check if this is a binding. If so we need to put it on the - // scope stack and maybe introduce an artificial scope - if pat_util::pat_is_binding(def_map, &*pat) { - - let ident = path1.node; - - // LLVM does not properly generate 'DW_AT_start_scope' fields - // for variable DIEs. For this reason we have to introduce - // an artificial scope at bindings whenever a variable with - // the same name is declared in *any* parent scope. - // - // Otherwise the following error occurs: - // - // let x = 10; - // - // do_something(); // 'gdb print x' correctly prints 10 - // - // { - // do_something(); // 'gdb print x' prints 0, because it - // // already reads the uninitialized 'x' - // // from the next line... - // let x = 100; - // do_something(); // 'gdb print x' correctly prints 100 - // } - - // Is there already a binding with that name? - // N.B.: this comparison must be UNhygienic... because - // gdb knows nothing about the context, so any two - // variables with the same name will cause the problem. - let need_new_scope = scope_stack - .iter() - .any(|entry| entry.ident.iter().any(|i| i.name == ident.name)); - - if need_new_scope { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); - let file_metadata = file_metadata(cx, - loc.file - .name - .as_slice()); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_uint() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { - scope_metadata: scope_metadata, - ident: Some(ident) - }); - - } else { - // Push a new entry anyway so the name can be found - let prev_metadata = scope_stack.last().unwrap().scope_metadata; - scope_stack.push(ScopeStackEntry { - scope_metadata: prev_metadata, - ident: Some(ident) - }); - } - } - - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in sub_pat_opt.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatWild(_) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - } - - ast::PatEnum(_, ref sub_pats_opt) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pats in sub_pats_opt.iter() { - for p in sub_pats.iter() { - walk_pattern(cx, &**p, scope_stack, scope_map); - } - } - } - - ast::PatStruct(_, ref field_pats, _) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for &codemap::Spanned { - node: ast::FieldPat { pat: ref sub_pat, .. }, - .. - } in field_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatTup(ref sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in sub_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - ast::PatLit(ref exp) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - ast::PatRange(ref exp1, ref exp2) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp1, scope_stack, scope_map); - walk_expr(cx, &**exp2, scope_stack, scope_map); - } - - ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in front_sub_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - for sub_pat in middle_sub_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - - for sub_pat in back_sub_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatMac(_) => { - cx.sess().span_bug(pat.span, "debuginfo::populate_scope_map() - \ - Found unexpanded macro."); - } - } - } - - fn walk_expr(cx: &CrateContext, - exp: &ast::Expr, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); - - match exp.node { - ast::ExprLit(_) | - ast::ExprBreak(_) | - ast::ExprAgain(_) | - ast::ExprPath(_) => {} - - ast::ExprCast(ref sub_exp, _) | - ast::ExprAddrOf(_, ref sub_exp) | - ast::ExprField(ref sub_exp, _, _) | - ast::ExprTupField(ref sub_exp, _, _) | - ast::ExprParen(ref sub_exp) => - walk_expr(cx, &**sub_exp, scope_stack, scope_map), - - ast::ExprBox(ref place, ref sub_expr) => { - walk_expr(cx, &**place, scope_stack, scope_map); - walk_expr(cx, &**sub_expr, scope_stack, scope_map); - } - - ast::ExprRet(ref exp_opt) => match *exp_opt { - Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), - None => () - }, - - ast::ExprUnary(_, ref sub_exp) => { - walk_expr(cx, &**sub_exp, scope_stack, scope_map); - } - - ast::ExprAssignOp(_, ref lhs, ref rhs) | - ast::ExprIndex(ref lhs, ref rhs) | - ast::ExprBinary(_, ref lhs, ref rhs) => { - walk_expr(cx, &**lhs, scope_stack, scope_map); - walk_expr(cx, &**rhs, scope_stack, scope_map); - } - - ast::ExprSlice(ref base, ref start, ref end, _) => { - walk_expr(cx, &**base, scope_stack, scope_map); - start.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map)); - end.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map)); - } - - ast::ExprVec(ref init_expressions) | - ast::ExprTup(ref init_expressions) => { - for ie in init_expressions.iter() { - walk_expr(cx, &**ie, scope_stack, scope_map); - } - } - - ast::ExprAssign(ref sub_exp1, ref sub_exp2) | - ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { - walk_expr(cx, &**sub_exp1, scope_stack, scope_map); - walk_expr(cx, &**sub_exp2, scope_stack, scope_map); - } - - ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); - - with_new_scope(cx, - then_block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**then_block, scope_stack, scope_map); - }); - - match *opt_else_exp { - Some(ref else_exp) => - walk_expr(cx, &**else_exp, scope_stack, scope_map), - _ => () - } - } - - ast::ExprIfLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ - Found unexpanded if-let."); - } - - ast::ExprWhile(ref cond_exp, ref loop_body, _) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); - - with_new_scope(cx, - loop_body.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**loop_body, scope_stack, scope_map); - }) - } - - ast::ExprWhileLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ - Found unexpanded while-let."); - } - - ast::ExprForLoop(ref pattern, ref head, ref body, _) => { - walk_expr(cx, &**head, scope_stack, scope_map); - - with_new_scope(cx, - exp.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - scope_map.insert(exp.id, - scope_stack.last() - .unwrap() - .scope_metadata); - walk_pattern(cx, - &**pattern, - scope_stack, - scope_map); - walk_block(cx, &**body, scope_stack, scope_map); - }) - } - - ast::ExprMac(_) => { - cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ - Found unexpanded macro."); - } - - ast::ExprLoop(ref block, _) | - ast::ExprBlock(ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**block, scope_stack, scope_map); - }) - } - - ast::ExprFnBlock(_, ref decl, ref block) | - ast::ExprProc(ref decl, ref block) | - ast::ExprUnboxedFn(_, _, ref decl, ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for &ast::Arg { pat: ref pattern, .. } in decl.inputs.iter() { - walk_pattern(cx, &**pattern, scope_stack, scope_map); - } - - walk_block(cx, &**block, scope_stack, scope_map); - }) - } - - ast::ExprCall(ref fn_exp, ref args) => { - walk_expr(cx, &**fn_exp, scope_stack, scope_map); - - for arg_exp in args.iter() { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } - - ast::ExprMethodCall(_, _, ref args) => { - for arg_exp in args.iter() { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } - - ast::ExprMatch(ref discriminant_exp, ref arms, _) => { - walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); - - // For each arm we have to first walk the pattern as these might - // introduce new artificial scopes. It should be sufficient to - // walk only one pattern per arm, as they all must contain the - // same binding names. - - for arm_ref in arms.iter() { - let arm_span = arm_ref.pats[0].span; - - with_new_scope(cx, - arm_span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for pat in arm_ref.pats.iter() { - walk_pattern(cx, &**pat, scope_stack, scope_map); - } - - for guard_exp in arm_ref.guard.iter() { - walk_expr(cx, &**guard_exp, scope_stack, scope_map) - } - - walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); - }) - } - } - - ast::ExprStruct(_, ref fields, ref base_exp) => { - for &ast::Field { expr: ref exp, .. } in fields.iter() { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - match *base_exp { - Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), - None => () - } - } - - ast::ExprInlineAsm(ast::InlineAsm { ref inputs, - ref outputs, - .. }) => { - // inputs, outputs: Vec<(String, P)> - for &(_, ref exp) in inputs.iter() { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - - for &(_, ref exp, _) in outputs.iter() { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - } - } -} - - -//=----------------------------------------------------------------------------- -// Type Names for Debug Info -//=----------------------------------------------------------------------------- - -// Compute the name of the type as it should be stored in debuginfo. Does not do -// any caching, i.e. calling the function twice with the same type will also do -// the work twice. The `qualified` parameter only affects the first level of the -// type name, further levels (i.e. type parameters) are always fully qualified. -fn compute_debuginfo_type_name(cx: &CrateContext, - t: ty::t, - qualified: bool) - -> String { - let mut result = String::with_capacity(64); - push_debuginfo_type_name(cx, t, qualified, &mut result); - result -} - -// Pushes the name of the type as it should be stored in debuginfo on the -// `output` String. See also compute_debuginfo_type_name(). -fn push_debuginfo_type_name(cx: &CrateContext, - t: ty::t, - qualified: bool, - output: &mut String) { - match ty::get(t).sty { - ty::ty_bool => output.push_str("bool"), - ty::ty_char => output.push_str("char"), - ty::ty_str => output.push_str("str"), - ty::ty_int(ast::TyI) => output.push_str("int"), - ty::ty_int(ast::TyI8) => output.push_str("i8"), - ty::ty_int(ast::TyI16) => output.push_str("i16"), - ty::ty_int(ast::TyI32) => output.push_str("i32"), - ty::ty_int(ast::TyI64) => output.push_str("i64"), - ty::ty_uint(ast::TyU) => output.push_str("uint"), - ty::ty_uint(ast::TyU8) => output.push_str("u8"), - ty::ty_uint(ast::TyU16) => output.push_str("u16"), - ty::ty_uint(ast::TyU32) => output.push_str("u32"), - ty::ty_uint(ast::TyU64) => output.push_str("u64"), - ty::ty_float(ast::TyF32) => output.push_str("f32"), - ty::ty_float(ast::TyF64) => output.push_str("f64"), - ty::ty_struct(def_id, ref substs) | - ty::ty_enum(def_id, ref substs) => { - push_item_name(cx, def_id, qualified, output); - push_type_params(cx, substs, output); - }, - ty::ty_tup(ref component_types) => { - output.push('('); - for &component_type in component_types.iter() { - push_debuginfo_type_name(cx, component_type, true, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::ty_uniq(inner_type) => { - output.push_str("Box<"); - push_debuginfo_type_name(cx, inner_type, true, output); - output.push('>'); - }, - ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { - output.push('*'); - match mutbl { - ast::MutImmutable => output.push_str("const "), - ast::MutMutable => output.push_str("mut "), - } - - push_debuginfo_type_name(cx, inner_type, true, output); - }, - ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { - output.push('&'); - if mutbl == ast::MutMutable { - output.push_str("mut "); - } - - push_debuginfo_type_name(cx, inner_type, true, output); - }, - ty::ty_vec(inner_type, optional_length) => { - output.push('['); - push_debuginfo_type_name(cx, inner_type, true, output); - - match optional_length { - Some(len) => { - output.push_str(format!(", ..{}", len).as_slice()); - } - None => { /* nothing to do */ } - }; - - output.push(']'); - }, - ty::ty_trait(ref trait_data) => { - push_item_name(cx, trait_data.principal.def_id, false, output); - push_type_params(cx, &trait_data.principal.substs, output); - }, - ty::ty_bare_fn(ty::BareFnTy{ fn_style, abi, ref sig } ) => { - if fn_style == ast::UnsafeFn { - output.push_str("unsafe "); - } - - if abi != ::syntax::abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - if sig.inputs.len() > 0 { - for ¶meter_type in sig.inputs.iter() { - push_debuginfo_type_name(cx, parameter_type, true, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if sig.inputs.len() > 0 { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - match sig.output { - ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} - ty::FnConverging(result_type) => { - output.push_str(" -> "); - push_debuginfo_type_name(cx, result_type, true, output); - } - ty::FnDiverging => { - output.push_str(" -> !"); - } - } - }, - ty::ty_closure(box ty::ClosureTy { fn_style, - onceness, - store, - ref sig, - .. // omitting bounds ... - }) => { - if fn_style == ast::UnsafeFn { - output.push_str("unsafe "); - } - - if onceness == ast::Once { - output.push_str("once "); - } - - let param_list_closing_char; - match store { - ty::UniqTraitStore => { - output.push_str("proc("); - param_list_closing_char = ')'; - } - ty::RegionTraitStore(_, ast::MutMutable) => { - output.push_str("&mut|"); - param_list_closing_char = '|'; - } - ty::RegionTraitStore(_, ast::MutImmutable) => { - output.push_str("&|"); - param_list_closing_char = '|'; - } - }; - - if sig.inputs.len() > 0 { - for ¶meter_type in sig.inputs.iter() { - push_debuginfo_type_name(cx, parameter_type, true, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if sig.inputs.len() > 0 { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(param_list_closing_char); - - match sig.output { - ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} - ty::FnConverging(result_type) => { - output.push_str(" -> "); - push_debuginfo_type_name(cx, result_type, true, output); - } - ty::FnDiverging => { - output.push_str(" -> !"); - } - } - }, - ty::ty_unboxed_closure(..) => { - output.push_str("closure"); - } - ty::ty_err | - ty::ty_infer(_) | - ty::ty_open(_) | - ty::ty_param(_) => { - cx.sess().bug(format!("debuginfo: Trying to create type name for \ - unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t)).as_slice()); - } - } - - fn push_item_name(cx: &CrateContext, - def_id: ast::DefId, - qualified: bool, - output: &mut String) { - ty::with_path(cx.tcx(), def_id, |mut path| { - if qualified { - if def_id.krate == ast::LOCAL_CRATE { - output.push_str(crate_root_namespace(cx)); - output.push_str("::"); - } - - let mut path_element_count = 0u; - for path_element in path { - let name = token::get_name(path_element.name()); - output.push_str(name.get()); - output.push_str("::"); - path_element_count += 1; - } - - if path_element_count == 0 { - cx.sess().bug("debuginfo: Encountered empty item path!"); - } - - output.pop(); - output.pop(); - } else { - let name = token::get_name(path.last() - .expect("debuginfo: Empty item path?") - .name()); - output.push_str(name.get()); - } - }); - } - - // Pushes the type parameters in the given `Substs` to the output string. - // This ignores region parameters, since they can't reliably be - // reconstructed for items from non-local crates. For local crates, this - // would be possible but with inlining and LTO we have to use the least - // common denominator - otherwise we would run into conflicts. - fn push_type_params(cx: &CrateContext, - substs: &subst::Substs, - output: &mut String) { - if substs.types.is_empty() { - return; - } - - output.push('<'); - - for &type_parameter in substs.types.iter() { - push_debuginfo_type_name(cx, type_parameter, true, output); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } -} - - -//=----------------------------------------------------------------------------- -// Namespace Handling -//=----------------------------------------------------------------------------- - -struct NamespaceTreeNode { - name: ast::Name, - scope: DIScope, - parent: Option>, -} - -impl NamespaceTreeNode { - fn mangled_name_of_contained_item(&self, item_name: &str) -> String { - fn fill_nested(node: &NamespaceTreeNode, output: &mut String) { - match node.parent { - Some(ref parent) => fill_nested(&*parent.upgrade().unwrap(), output), - None => {} - } - let string = token::get_name(node.name); - output.push_str(format!("{}", string.get().len()).as_slice()); - output.push_str(string.get()); - } - - let mut name = String::from_str("_ZN"); - fill_nested(self, &mut name); - name.push_str(format!("{}", item_name.len()).as_slice()); - name.push_str(item_name); - name.push('E'); - name - } -} - -fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str { - cx.link_meta().crate_name.as_slice() -} - -fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc { - ty::with_path(cx.tcx(), def_id, |path| { - // prepend crate name if not already present - let krate = if def_id.krate == ast::LOCAL_CRATE { - let crate_namespace_ident = token::str_to_ident(crate_root_namespace(cx)); - Some(ast_map::PathMod(crate_namespace_ident.name)) - } else { - None - }; - let mut path = krate.into_iter().chain(path).peekable(); - - let mut current_key = Vec::new(); - let mut parent_node: Option> = None; - - // Create/Lookup namespace for each element of the path. - loop { - // Emulate a for loop so we can use peek below. - let path_element = match path.next() { - Some(e) => e, - None => break - }; - // Ignore the name of the item (the last path element). - if path.peek().is_none() { - break; - } - - let name = path_element.name(); - current_key.push(name); - - let existing_node = debug_context(cx).namespace_map.borrow() - .get(¤t_key).cloned(); - let current_node = match existing_node { - Some(existing_node) => existing_node, - None => { - // create and insert - let parent_scope = match parent_node { - Some(ref node) => node.scope, - None => ptr::null_mut() - }; - let namespace_name = token::get_name(name); - let scope = namespace_name.get().with_c_str(|namespace_name| { - unsafe { - llvm::LLVMDIBuilderCreateNameSpace( - DIB(cx), - parent_scope, - namespace_name, - // cannot reconstruct file ... - ptr::null_mut(), - // ... or line information, but that's not so important. - 0) - } - }); - - let node = Rc::new(NamespaceTreeNode { - name: name, - scope: scope, - parent: parent_node.map(|parent| parent.downgrade()), - }); - - debug_context(cx).namespace_map.borrow_mut() - .insert(current_key.clone(), node.clone()); - - node - } - }; - - parent_node = Some(current_node); - } - - match parent_node { - Some(node) => node, - None => { - cx.sess().bug(format!("debuginfo::namespace_for_item(): \ - path too short for {}", - def_id).as_slice()); - } - } - }) -} diff --git a/src/librustc/middle/trans/doc.rs b/src/librustc/middle/trans/doc.rs deleted file mode 100644 index 013483d0003..00000000000 --- a/src/librustc/middle/trans/doc.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -# Documentation for the trans module - -This module contains high-level summaries of how the various modules -in trans work. It is a work in progress. For detailed comments, -naturally, you can refer to the individual modules themselves. - -## The Expr module - -The expr module handles translation of expressions. The most general -translation routine is `trans()`, which will translate an expression -into a datum. `trans_into()` is also available, which will translate -an expression and write the result directly into memory, sometimes -avoiding the need for a temporary stack slot. Finally, -`trans_to_lvalue()` is available if you'd like to ensure that the -result has cleanup scheduled. - -Internally, each of these functions dispatches to various other -expression functions depending on the kind of expression. We divide -up expressions into: - -- **Datum expressions:** Those that most naturally yield values. - Examples would be `22`, `box x`, or `a + b` (when not overloaded). -- **DPS expressions:** Those that most naturally write into a location - in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`. -- **Statement expressions:** That that do not generate a meaningful - result. Examples would be `while { ... }` or `return 44`. - -## The Datum module - -A `Datum` encapsulates the result of evaluating a Rust expression. It -contains a `ValueRef` indicating the result, a `ty::t` describing -the Rust type, but also a *kind*. The kind indicates whether the datum -has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of -rvalues -- whether or not the value is "by ref" or "by value". - -The datum API is designed to try and help you avoid memory errors like -forgetting to arrange cleanup or duplicating a value. The type of the -datum incorporates the kind, and thus reflects whether it has cleanup -scheduled: - -- `Datum` -- by ref, cleanup scheduled -- `Datum` -- by value or by ref, no cleanup scheduled -- `Datum` -- either `Datum` or `Datum` - -Rvalue and expr datums are noncopyable, and most of the methods on -datums consume the datum itself (with some notable exceptions). This -reflects the fact that datums may represent affine values which ought -to be consumed exactly once, and if you were to try to (for example) -store an affine value multiple times, you would be duplicating it, -which would certainly be a bug. - -Some of the datum methods, however, are designed to work only on -copyable values such as ints or pointers. Those methods may borrow the -datum (`&self`) rather than consume it, but they always include -assertions on the type of the value represented to check that this -makes sense. An example is `shallow_copy()`, which duplicates -a datum value. - -Translating an expression always yields a `Datum` result, but -the methods `to_[lr]value_datum()` can be used to coerce a -`Datum` into a `Datum` or `Datum` as -needed. Coercing to an lvalue is fairly common, and generally occurs -whenever it is necessary to inspect a value and pull out its -subcomponents (for example, a match, or indexing expression). Coercing -to an rvalue is more unusual; it occurs when moving values from place -to place, such as in an assignment expression or parameter passing. - -### Lvalues in detail - -An lvalue datum is one for which cleanup has been scheduled. Lvalue -datums are always located in memory, and thus the `ValueRef` for an -LLVM value is always a pointer to the actual Rust value. This means -that if the Datum has a Rust type of `int`, then the LLVM type of the -`ValueRef` will be `int*` (pointer to int). - -Because lvalues already have cleanups scheduled, the memory must be -zeroed to prevent the cleanup from taking place (presuming that the -Rust type needs drop in the first place, otherwise it doesn't -matter). The Datum code automatically performs this zeroing when the -value is stored to a new location, for example. - -Lvalues usually result from evaluating lvalue expressions. For -example, evaluating a local variable `x` yields an lvalue, as does a -reference to a field like `x.f` or an index `x[i]`. - -Lvalue datums can also arise by *converting* an rvalue into an lvalue. -This is done with the `to_lvalue_datum` method defined on -`Datum`. Basically this method just schedules cleanup if the -datum is an rvalue, possibly storing the value into a stack slot first -if needed. Converting rvalues into lvalues occurs in constructs like -`&foo()` or `match foo() { ref x => ... }`, where the user is -implicitly requesting a temporary. - -Somewhat surprisingly, not all lvalue expressions yield lvalue datums -when trans'd. Ultimately the reason for this is to micro-optimize -the resulting LLVM. For example, consider the following code: - - fn foo() -> Box { ... } - let x = *foo(); - -The expression `*foo()` is an lvalue, but if you invoke `expr::trans`, -it will return an rvalue datum. See `deref_once` in expr.rs for -more details. - -### Rvalues in detail - -Rvalues datums are values with no cleanup scheduled. One must be -careful with rvalue datums to ensure that cleanup is properly -arranged, usually by converting to an lvalue datum or by invoking the -`add_clean` method. - -### Scratch datums - -Sometimes you need some temporary scratch space. The functions -`[lr]value_scratch_datum()` can be used to get temporary stack -space. As their name suggests, they yield lvalues and rvalues -respectively. That is, the slot from `lvalue_scratch_datum` will have -cleanup arranged, and the slot from `rvalue_scratch_datum` does not. - -## The Cleanup module - -The cleanup module tracks what values need to be cleaned up as scopes -are exited, either via panic or just normal control flow. The basic -idea is that the function context maintains a stack of cleanup scopes -that are pushed/popped as we traverse the AST tree. There is typically -at least one cleanup scope per AST node; some AST nodes may introduce -additional temporary scopes. - -Cleanup items can be scheduled into any of the scopes on the stack. -Typically, when a scope is popped, we will also generate the code for -each of its cleanups at that time. This corresponds to a normal exit -from a block (for example, an expression completing evaluation -successfully without panic). However, it is also possible to pop a -block *without* executing its cleanups; this is typically used to -guard intermediate values that must be cleaned up on panic, but not -if everything goes right. See the section on custom scopes below for -more details. - -Cleanup scopes come in three kinds: -- **AST scopes:** each AST node in a function body has a corresponding - AST scope. We push the AST scope when we start generate code for an AST - node and pop it once the AST node has been fully generated. -- **Loop scopes:** loops have an additional cleanup scope. Cleanups are - never scheduled into loop scopes; instead, they are used to record the - basic blocks that we should branch to when a `continue` or `break` statement - is encountered. -- **Custom scopes:** custom scopes are typically used to ensure cleanup - of intermediate values. - -### When to schedule cleanup - -Although the cleanup system is intended to *feel* fairly declarative, -it's still important to time calls to `schedule_clean()` correctly. -Basically, you should not schedule cleanup for memory until it has -been initialized, because if an unwind should occur before the memory -is fully initialized, then the cleanup will run and try to free or -drop uninitialized memory. If the initialization itself produces -byproducts that need to be freed, then you should use temporary custom -scopes to ensure that those byproducts will get freed on unwind. For -example, an expression like `box foo()` will first allocate a box in the -heap and then call `foo()` -- if `foo()` should panic, this box needs -to be *shallowly* freed. - -### Long-distance jumps - -In addition to popping a scope, which corresponds to normal control -flow exiting the scope, we may also *jump out* of a scope into some -earlier scope on the stack. This can occur in response to a `return`, -`break`, or `continue` statement, but also in response to panic. In -any of these cases, we will generate a series of cleanup blocks for -each of the scopes that is exited. So, if the stack contains scopes A -... Z, and we break out of a loop whose corresponding cleanup scope is -X, we would generate cleanup blocks for the cleanups in X, Y, and Z. -After cleanup is done we would branch to the exit point for scope X. -But if panic should occur, we would generate cleanups for all the -scopes from A to Z and then resume the unwind process afterwards. - -To avoid generating tons of code, we cache the cleanup blocks that we -create for breaks, returns, unwinds, and other jumps. Whenever a new -cleanup is scheduled, though, we must clear these cached blocks. A -possible improvement would be to keep the cached blocks but simply -generate a new block which performs the additional cleanup and then -branches to the existing cached blocks. - -### AST and loop cleanup scopes - -AST cleanup scopes are pushed when we begin and end processing an AST -node. They are used to house cleanups related to rvalue temporary that -get referenced (e.g., due to an expression like `&Foo()`). Whenever an -AST scope is popped, we always trans all the cleanups, adding the cleanup -code after the postdominator of the AST node. - -AST nodes that represent breakable loops also push a loop scope; the -loop scope never has any actual cleanups, it's just used to point to -the basic blocks where control should flow after a "continue" or -"break" statement. Popping a loop scope never generates code. - -### Custom cleanup scopes - -Custom cleanup scopes are used for a variety of purposes. The most -common though is to handle temporary byproducts, where cleanup only -needs to occur on panic. The general strategy is to push a custom -cleanup scope, schedule *shallow* cleanups into the custom scope, and -then pop the custom scope (without transing the cleanups) when -execution succeeds normally. This way the cleanups are only trans'd on -unwind, and only up until the point where execution succeeded, at -which time the complete value should be stored in an lvalue or some -other place where normal cleanup applies. - -To spell it out, here is an example. Imagine an expression `box expr`. -We would basically: - -1. Push a custom cleanup scope C. -2. Allocate the box. -3. Schedule a shallow free in the scope C. -4. Trans `expr` into the box. -5. Pop the scope C. -6. Return the box as an rvalue. - -This way, if a panic occurs while transing `expr`, the custom -cleanup scope C is pushed and hence the box will be freed. The trans -code for `expr` itself is responsible for freeing any other byproducts -that may be in play. - -*/ diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs deleted file mode 100644 index 4a2ea9c5f63..00000000000 --- a/src/librustc/middle/trans/expr.rs +++ /dev/null @@ -1,2248 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - * # Translation of Expressions - * - * Public entry points: - * - * - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression, - * storing the result into `dest`. This is the preferred form, if you - * can manage it. - * - * - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding - * `Datum` with the result. You can then store the datum, inspect - * the value, etc. This may introduce temporaries if the datum is a - * structural type. - * - * - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an - * expression and ensures that the result has a cleanup associated with it, - * creating a temporary stack slot if necessary. - * - * - `trans_local_var -> Datum`: looks up a local variable or upvar. - * - * See doc.rs for more comments. - */ - -#![allow(non_camel_case_types)] - -pub use self::cast_kind::*; -pub use self::Dest::*; -use self::lazy_binop_ty::*; - -use back::abi; -use llvm; -use llvm::{ValueRef}; -use middle::def; -use middle::mem_categorization::Typer; -use middle::subst; -use middle::subst::Subst; -use middle::trans::_match; -use middle::trans::adt; -use middle::trans::asm; -use middle::trans::base::*; -use middle::trans::base; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::closure; -use middle::trans::common::*; -use middle::trans::consts; -use middle::trans::controlflow; -use middle::trans::datum::*; -use middle::trans::debuginfo; -use middle::trans::glue; -use middle::trans::machine; -use middle::trans::meth; -use middle::trans::inline; -use middle::trans::tvec; -use middle::trans::type_of; -use middle::ty::{struct_fields, tup_fields}; -use middle::ty::{AdjustDerefRef, AdjustAddEnv, AutoUnsafe}; -use middle::ty::{AutoPtr}; -use middle::ty; -use middle::typeck; -use middle::typeck::MethodCall; -use util::common::indenter; -use util::ppaux::Repr; -use middle::trans::machine::{llsize_of, llsize_of_alloc}; -use middle::trans::type_::Type; - -use syntax::ast; -use syntax::codemap; -use syntax::print::pprust::{expr_to_string}; -use syntax::ptr::P; -use std::rc::Rc; - -// Destinations - -// These are passed around by the code generating functions to track the -// destination of a computation's value. - -#[deriving(PartialEq)] -pub enum Dest { - SaveIn(ValueRef), - Ignore, -} - -impl Dest { - pub fn to_string(&self, ccx: &CrateContext) -> String { - match *self { - SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)), - Ignore => "Ignore".to_string() - } - } -} - -pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - dest: Dest) - -> Block<'blk, 'tcx> { - /*! - * This function is equivalent to `trans(bcx, expr).store_to_dest(dest)` - * but it may generate better optimized LLVM code. - */ - - let mut bcx = bcx; - - if bcx.tcx().adjustments.borrow().contains_key(&expr.id) { - // use trans, which may be less efficient but - // which will perform the adjustments: - let datum = unpack_datum!(bcx, trans(bcx, expr)); - return datum.store_to_dest(bcx, dest, expr.id) - } - - debug!("trans_into() expr={}", expr.repr(bcx.tcx())); - - let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id, - expr.span, - false); - bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc); - - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - let kind = ty::expr_kind(bcx.tcx(), expr); - bcx = match kind { - ty::LvalueExpr | ty::RvalueDatumExpr => { - trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id) - } - ty::RvalueDpsExpr => { - trans_rvalue_dps_unadjusted(bcx, expr, dest) - } - ty::RvalueStmtExpr => { - trans_rvalue_stmt_unadjusted(bcx, expr) - } - }; - - bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id) -} - -pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - /*! - * Translates an expression, returning a datum (and new block) - * encapsulating the result. When possible, it is preferred to - * use `trans_into`, as that may avoid creating a temporary on - * the stack. - */ - - debug!("trans(expr={})", bcx.expr_to_string(expr)); - - let mut bcx = bcx; - let fcx = bcx.fcx; - - let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id, - expr.span, - false); - fcx.push_ast_cleanup_scope(cleanup_debug_loc); - let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr)); - let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum)); - bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id); - return DatumBlock::new(bcx, datum); -} - -pub fn get_len(bcx: Block, fat_ptr: ValueRef) -> ValueRef { - GEPi(bcx, fat_ptr, &[0u, abi::slice_elt_len]) -} - -pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef { - GEPi(bcx, fat_ptr, &[0u, abi::slice_elt_base]) -} - -fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'blk, 'tcx, Expr> { - /*! - * Helper for trans that apply adjustments from `expr` to `datum`, - * which should be the unadjusted translation of `expr`. - */ - - let mut bcx = bcx; - let mut datum = datum; - let adjustment = match bcx.tcx().adjustments.borrow().get(&expr.id).cloned() { - None => { - return DatumBlock::new(bcx, datum); - } - Some(adj) => { adj } - }; - debug!("unadjusted datum for expr {}: {}", - expr.id, datum.to_string(bcx.ccx())); - match adjustment { - AdjustAddEnv(..) => { - datum = unpack_datum!(bcx, add_env(bcx, expr, datum)); - } - AdjustDerefRef(ref adj) => { - let (autoderefs, use_autoref) = match adj.autoref { - // Extracting a value from a box counts as a deref, but if we are - // just converting Box<[T, ..n]> to Box<[T]> we aren't really doing - // a deref (and wouldn't if we could treat Box like a normal struct). - Some(ty::AutoUnsizeUniq(..)) => (adj.autoderefs - 1, true), - // We are a bit paranoid about adjustments and thus might have a re- - // borrow here which merely derefs and then refs again (it might have - // a different region or mutability, but we don't care here. It might - // also be just in case we need to unsize. But if there are no nested - // adjustments then it should be a no-op). - Some(ty::AutoPtr(_, _, None)) if adj.autoderefs == 1 => { - match ty::get(datum.ty).sty { - // Don't skip a conversion from Box to &T, etc. - ty::ty_rptr(..) => { - let method_call = MethodCall::autoderef(expr.id, adj.autoderefs-1); - let method = bcx.tcx().method_map.borrow().get(&method_call).is_some(); - if method { - // Don't skip an overloaded deref. - (adj.autoderefs, true) - } else { - (adj.autoderefs - 1, false) - } - } - _ => (adj.autoderefs, true), - } - } - _ => (adj.autoderefs, true) - }; - - if autoderefs > 0 { - // Schedule cleanup. - let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "auto_deref", expr.id)); - datum = unpack_datum!( - bcx, deref_multiple(bcx, expr, lval.to_expr_datum(), autoderefs)); - } - - // (You might think there is a more elegant way to do this than a - // use_autoref bool, but then you remember that the borrow checker exists). - match (use_autoref, &adj.autoref) { - (true, &Some(ref a)) => { - datum = unpack_datum!(bcx, apply_autoref(a, - bcx, - expr, - datum)); - } - _ => {} - } - } - } - debug!("after adjustments, datum={}", datum.to_string(bcx.ccx())); - return DatumBlock::new(bcx, datum); - - fn apply_autoref<'blk, 'tcx>(autoref: &ty::AutoRef, - bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let mut datum = datum; - - let datum = match autoref { - &AutoPtr(_, _, ref a) | &AutoUnsafe(_, ref a) => { - debug!(" AutoPtr"); - match a { - &Some(box ref a) => datum = unpack_datum!(bcx, - apply_autoref(a, bcx, expr, datum)), - _ => {} - } - unpack_datum!(bcx, ref_ptr(bcx, expr, datum)) - } - &ty::AutoUnsize(ref k) => { - debug!(" AutoUnsize"); - unpack_datum!(bcx, unsize_expr(bcx, expr, datum, k)) - } - - &ty::AutoUnsizeUniq(ty::UnsizeLength(len)) => { - debug!(" AutoUnsizeUniq(UnsizeLength)"); - unpack_datum!(bcx, unsize_unique_vec(bcx, expr, datum, len)) - } - &ty::AutoUnsizeUniq(ref k) => { - debug!(" AutoUnsizeUniq"); - unpack_datum!(bcx, unsize_unique_expr(bcx, expr, datum, k)) - } - }; - - DatumBlock::new(bcx, datum) - } - - fn ref_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'blk, 'tcx, Expr> { - if !ty::type_is_sized(bcx.tcx(), datum.ty) { - debug!("Taking address of unsized type {}", - bcx.ty_to_string(datum.ty)); - ref_fat_ptr(bcx, expr, datum) - } else { - debug!("Taking address of sized type {}", - bcx.ty_to_string(datum.ty)); - auto_ref(bcx, datum, expr) - } - } - - // Retrieve the information we are losing (making dynamic) in an unsizing - // adjustment. - // When making a dtor, we need to do different things depending on the - // ownership of the object.. mk_ty is a function for turning unsized_type - // into a type to be destructed. If we want to end up with a Box pointer, - // then mk_ty should make a Box pointer (T -> Box), if we want a - // borrowed reference then it should be T -> &T. - fn unsized_info<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - kind: &ty::UnsizeKind, - id: ast::NodeId, - unsized_ty: ty::t, - mk_ty: |ty::t| -> ty::t) -> ValueRef { - match kind { - &ty::UnsizeLength(len) => C_uint(bcx.ccx(), len), - &ty::UnsizeStruct(box ref k, tp_index) => match ty::get(unsized_ty).sty { - ty::ty_struct(_, ref substs) => { - let ty_substs = substs.types.get_slice(subst::TypeSpace); - // The dtor for a field treats it like a value, so mk_ty - // should just be the identity function. - unsized_info(bcx, k, id, ty_substs[tp_index], |t| t) - } - _ => bcx.sess().bug(format!("UnsizeStruct with bad sty: {}", - bcx.ty_to_string(unsized_ty)).as_slice()) - }, - &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => { - let substs = principal.substs.with_self_ty(unsized_ty); - let trait_ref = - Rc::new(ty::TraitRef { def_id: principal.def_id, - substs: substs }); - let trait_ref = - trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs); - let box_ty = mk_ty(unsized_ty); - PointerCast(bcx, - meth::get_vtable(bcx, box_ty, trait_ref), - Type::vtable_ptr(bcx.ccx())) - } - } - } - - fn unsize_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - k: &ty::UnsizeKind) - -> DatumBlock<'blk, 'tcx, Expr> { - let tcx = bcx.tcx(); - let datum_ty = datum.ty; - let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span); - let dest_ty = ty::mk_open(tcx, unsized_ty); - // Closures for extracting and manipulating the data and payload parts of - // the fat pointer. - let base = match k { - &ty::UnsizeStruct(..) => - |bcx, val| PointerCast(bcx, - val, - type_of::type_of(bcx.ccx(), unsized_ty).ptr_to()), - &ty::UnsizeLength(..) => - |bcx, val| GEPi(bcx, val, &[0u, 0u]), - &ty::UnsizeVtable(..) => - |_bcx, val| PointerCast(bcx, val, Type::i8p(bcx.ccx())) - }; - let info = |bcx, _val| unsized_info(bcx, - k, - expr.id, - ty::deref_or_dont(datum_ty), - |t| ty::mk_rptr(tcx, - ty::ReStatic, - ty::mt{ - ty: t, - mutbl: ast::MutImmutable - })); - into_fat_ptr(bcx, expr, datum, dest_ty, base, info) - } - - fn ref_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'blk, 'tcx, Expr> { - let tcx = bcx.tcx(); - let dest_ty = ty::close_type(tcx, datum.ty); - let base = |bcx, val| Load(bcx, get_dataptr(bcx, val)); - let len = |bcx, val| Load(bcx, get_len(bcx, val)); - into_fat_ptr(bcx, expr, datum, dest_ty, base, len) - } - - fn into_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - dest_ty: ty::t, - base: |Block<'blk, 'tcx>, ValueRef| -> ValueRef, - info: |Block<'blk, 'tcx>, ValueRef| -> ValueRef) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - - // Arrange cleanup - let lval = unpack_datum!(bcx, - datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id)); - let base = base(bcx, lval.val); - let info = info(bcx, lval.val); - - let scratch = rvalue_scratch_datum(bcx, dest_ty, "__fat_ptr"); - Store(bcx, base, get_dataptr(bcx, scratch.val)); - Store(bcx, info, get_len(bcx, scratch.val)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - - fn unsize_unique_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - len: uint) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let tcx = bcx.tcx(); - - let datum_ty = datum.ty; - // Arrange cleanup - let lval = unpack_datum!(bcx, - datum.to_lvalue_datum(bcx, "unsize_unique_vec", expr.id)); - - let ll_len = C_uint(bcx.ccx(), len); - let unit_ty = ty::sequence_element_type(tcx, ty::type_content(datum_ty)); - let vec_ty = ty::mk_uniq(tcx, ty::mk_vec(tcx, unit_ty, None)); - let scratch = rvalue_scratch_datum(bcx, vec_ty, "__unsize_unique"); - - let base = get_dataptr(bcx, scratch.val); - let base = PointerCast(bcx, - base, - type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); - bcx = lval.store_to(bcx, base); - - Store(bcx, ll_len, get_len(bcx, scratch.val)); - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - - fn unsize_unique_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - k: &ty::UnsizeKind) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let tcx = bcx.tcx(); - - let datum_ty = datum.ty; - let unboxed_ty = match ty::get(datum_ty).sty { - ty::ty_uniq(t) => t, - _ => bcx.sess().bug(format!("Expected ty_uniq, found {}", - bcx.ty_to_string(datum_ty)).as_slice()) - }; - let result_ty = ty::mk_uniq(tcx, ty::unsize_ty(tcx, unboxed_ty, k, expr.span)); - - let lval = unpack_datum!(bcx, - datum.to_lvalue_datum(bcx, "unsize_unique_expr", expr.id)); - - let scratch = rvalue_scratch_datum(bcx, result_ty, "__uniq_fat_ptr"); - let llbox_ty = type_of::type_of(bcx.ccx(), datum_ty); - let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to()); - bcx = lval.store_to(bcx, base); - - let info = unsized_info(bcx, k, expr.id, unboxed_ty, |t| ty::mk_uniq(tcx, t)); - Store(bcx, info, get_len(bcx, scratch.val)); - - let scratch = unpack_datum!(bcx, - scratch.to_expr_datum().to_lvalue_datum(bcx, - "fresh_uniq_fat_ptr", - expr.id)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - - fn add_env<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum) - -> DatumBlock<'blk, 'tcx, Expr> { - // This is not the most efficient thing possible; since closures - // are two words it'd be better if this were compiled in - // 'dest' mode, but I can't find a nice way to structure the - // code and keep it DRY that accommodates that use case at the - // moment. - - let closure_ty = expr_ty_adjusted(bcx, expr); - let fn_ptr = datum.to_llscalarish(bcx); - let def = ty::resolve_expr(bcx.tcx(), expr); - closure::make_closure_from_bare_fn(bcx, closure_ty, def, fn_ptr) - } -} - -pub fn trans_to_lvalue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - name: &str) - -> DatumBlock<'blk, 'tcx, Lvalue> { - /*! - * Translates an expression in "lvalue" mode -- meaning that it - * returns a reference to the memory that the expr represents. - * - * If this expression is an rvalue, this implies introducing a - * temporary. In other words, something like `x().f` is - * translated into roughly the equivalent of - * - * { tmp = x(); tmp.f } - */ - - let mut bcx = bcx; - let datum = unpack_datum!(bcx, trans(bcx, expr)); - return datum.to_lvalue_datum(bcx, name, expr.id); -} - -fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - /*! - * A version of `trans` that ignores adjustments. You almost - * certainly do not want to call this directly. - */ - - let mut bcx = bcx; - - debug!("trans_unadjusted(expr={})", bcx.expr_to_string(expr)); - let _indenter = indenter(); - - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - - return match ty::expr_kind(bcx.tcx(), expr) { - ty::LvalueExpr | ty::RvalueDatumExpr => { - let datum = unpack_datum!(bcx, { - trans_datum_unadjusted(bcx, expr) - }); - - DatumBlock {bcx: bcx, datum: datum} - } - - ty::RvalueStmtExpr => { - bcx = trans_rvalue_stmt_unadjusted(bcx, expr); - nil(bcx, expr_ty(bcx, expr)) - } - - ty::RvalueDpsExpr => { - let ty = expr_ty(bcx, expr); - if type_is_zero_size(bcx.ccx(), ty) { - bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore); - nil(bcx, ty) - } else { - let scratch = rvalue_scratch_datum(bcx, ty, ""); - bcx = trans_rvalue_dps_unadjusted( - bcx, expr, SaveIn(scratch.val)); - - // Note: this is not obviously a good idea. It causes - // immediate values to be loaded immediately after a - // return from a call or other similar expression, - // which in turn leads to alloca's having shorter - // lifetimes and hence larger stack frames. However, - // in turn it can lead to more register pressure. - // Still, in practice it seems to increase - // performance, since we have fewer problems with - // morestack churn. - let scratch = unpack_datum!( - bcx, scratch.to_appropriate_datum(bcx)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - } - }; - - fn nil<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ty: ty::t) - -> DatumBlock<'blk, 'tcx, Expr> { - let llval = C_undef(type_of::type_of(bcx.ccx(), ty)); - let datum = immediate_rvalue(llval, ty); - DatumBlock::new(bcx, datum.to_expr_datum()) - } -} - -fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let fcx = bcx.fcx; - let _icx = push_ctxt("trans_datum_unadjusted"); - - match expr.node { - ast::ExprParen(ref e) => { - trans(bcx, &**e) - } - ast::ExprPath(_) => { - trans_def(bcx, expr, bcx.def(expr.id)) - } - ast::ExprField(ref base, ident, _) => { - trans_rec_field(bcx, &**base, ident.node) - } - ast::ExprTupField(ref base, idx, _) => { - trans_rec_tup_field(bcx, &**base, idx.node) - } - ast::ExprIndex(ref base, ref idx) => { - trans_index(bcx, expr, &**base, &**idx, MethodCall::expr(expr.id)) - } - ast::ExprSlice(ref base, ref start, ref end, _) => { - let _icx = push_ctxt("trans_slice"); - let ccx = bcx.ccx(); - - let method_call = MethodCall::expr(expr.id); - let method_ty = ccx.tcx() - .method_map - .borrow() - .get(&method_call) - .map(|method| method.ty); - let base_datum = unpack_datum!(bcx, trans(bcx, &**base)); - - let mut args = vec![]; - start.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); - end.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); - - let result_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty.unwrap())).unwrap(); - let scratch = rvalue_scratch_datum(bcx, result_ty, "trans_slice"); - - unpack_result!(bcx, - trans_overloaded_op(bcx, - expr, - method_call, - base_datum, - args, - Some(SaveIn(scratch.val)))); - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - ast::ExprBox(_, ref contents) => { - // Special case for `Box` - let box_ty = expr_ty(bcx, expr); - let contents_ty = expr_ty(bcx, &**contents); - match ty::get(box_ty).sty { - ty::ty_uniq(..) => { - trans_uniq_expr(bcx, box_ty, &**contents, contents_ty) - } - _ => bcx.sess().span_bug(expr.span, - "expected unique box") - } - - } - ast::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, &**lit), - ast::ExprBinary(op, ref lhs, ref rhs) => { - trans_binary(bcx, expr, op, &**lhs, &**rhs) - } - ast::ExprUnary(op, ref x) => { - trans_unary(bcx, expr, op, &**x) - } - ast::ExprAddrOf(_, ref x) => { - match x.node { - ast::ExprRepeat(..) | ast::ExprVec(..) => { - // Special case for slices. - let cleanup_debug_loc = - debuginfo::get_cleanup_debug_loc_for_ast_node(x.id, x.span, false); - fcx.push_ast_cleanup_scope(cleanup_debug_loc); - let datum = unpack_datum!( - bcx, tvec::trans_slice_vec(bcx, expr, &**x)); - bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, x.id); - DatumBlock::new(bcx, datum) - } - _ => { - trans_addr_of(bcx, expr, &**x) - } - } - } - ast::ExprCast(ref val, _) => { - // Datum output mode means this is a scalar cast: - trans_imm_cast(bcx, &**val, expr.id) - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("trans_rvalue_datum_unadjusted reached \ - fall-through case: {}", - expr.node).as_slice()); - } - } -} - -fn trans_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - base: &ast::Expr, - get_idx: |&'blk ty::ctxt<'tcx>, &[ty::field]| -> uint) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let _icx = push_ctxt("trans_rec_field"); - - let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field")); - let bare_ty = ty::unopen_type(base_datum.ty); - let repr = adt::represent_type(bcx.ccx(), bare_ty); - with_field_tys(bcx.tcx(), bare_ty, None, |discr, field_tys| { - let ix = get_idx(bcx.tcx(), field_tys); - let d = base_datum.get_element( - bcx, - field_tys[ix].mt.ty, - |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix)); - - if ty::type_is_sized(bcx.tcx(), d.ty) { - DatumBlock { datum: d.to_expr_datum(), bcx: bcx } - } else { - let scratch = rvalue_scratch_datum(bcx, ty::mk_open(bcx.tcx(), d.ty), ""); - Store(bcx, d.val, get_dataptr(bcx, scratch.val)); - let info = Load(bcx, get_len(bcx, base_datum.val)); - Store(bcx, info, get_len(bcx, scratch.val)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) - - } - }) - -} - -/// Translates `base.field`. -fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - base: &ast::Expr, - field: ast::Ident) - -> DatumBlock<'blk, 'tcx, Expr> { - trans_field(bcx, base, |tcx, field_tys| ty::field_idx_strict(tcx, field.name, field_tys)) -} - -/// Translates `base.`. -fn trans_rec_tup_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - base: &ast::Expr, - idx: uint) - -> DatumBlock<'blk, 'tcx, Expr> { - trans_field(bcx, base, |_, _| idx) -} - -fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - index_expr: &ast::Expr, - base: &ast::Expr, - idx: &ast::Expr, - method_call: MethodCall) - -> DatumBlock<'blk, 'tcx, Expr> { - //! Translates `base[idx]`. - - let _icx = push_ctxt("trans_index"); - let ccx = bcx.ccx(); - let mut bcx = bcx; - - // Check for overloaded index. - let method_ty = ccx.tcx() - .method_map - .borrow() - .get(&method_call) - .map(|method| method.ty); - let elt_datum = match method_ty { - Some(method_ty) => { - let base_datum = unpack_datum!(bcx, trans(bcx, base)); - - // Translate index expression. - let ix_datum = unpack_datum!(bcx, trans(bcx, idx)); - - let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap(); - let elt_ty = match ty::deref(ref_ty, true) { - None => { - bcx.tcx().sess.span_bug(index_expr.span, - "index method didn't return a \ - dereferenceable type?!") - } - Some(elt_tm) => elt_tm.ty, - }; - - // Overloaded. Evaluate `trans_overloaded_op`, which will - // invoke the user's index() method, which basically yields - // a `&T` pointer. We can then proceed down the normal - // path (below) to dereference that `&T`. - let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_index_elt"); - unpack_result!(bcx, - trans_overloaded_op(bcx, - index_expr, - method_call, - base_datum, - vec![(ix_datum, idx.id)], - Some(SaveIn(scratch.val)))); - let datum = scratch.to_expr_datum(); - if ty::type_is_sized(bcx.tcx(), elt_ty) { - Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr) - } else { - Datum::new(datum.val, ty::mk_open(bcx.tcx(), elt_ty), LvalueExpr) - } - } - None => { - let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, - base, - "index")); - - // Translate index expression and cast to a suitable LLVM integer. - // Rust is less strict than LLVM in this regard. - let ix_datum = unpack_datum!(bcx, trans(bcx, idx)); - let ix_val = ix_datum.to_llscalarish(bcx); - let ix_size = machine::llbitsize_of_real(bcx.ccx(), - val_ty(ix_val)); - let int_size = machine::llbitsize_of_real(bcx.ccx(), - ccx.int_type()); - let ix_val = { - if ix_size < int_size { - if ty::type_is_signed(expr_ty(bcx, idx)) { - SExt(bcx, ix_val, ccx.int_type()) - } else { ZExt(bcx, ix_val, ccx.int_type()) } - } else if ix_size > int_size { - Trunc(bcx, ix_val, ccx.int_type()) - } else { - ix_val - } - }; - - let vt = - tvec::vec_types(bcx, - ty::sequence_element_type(bcx.tcx(), - base_datum.ty)); - base::maybe_name_value(bcx.ccx(), vt.llunit_size, "unit_sz"); - - let (base, len) = base_datum.get_vec_base_and_len(bcx); - - debug!("trans_index: base {}", bcx.val_to_string(base)); - debug!("trans_index: len {}", bcx.val_to_string(len)); - - let bounds_check = ICmp(bcx, llvm::IntUGE, ix_val, len); - let expect = ccx.get_intrinsic(&("llvm.expect.i1")); - let expected = Call(bcx, - expect, - &[bounds_check, C_bool(ccx, false)], - None); - bcx = with_cond(bcx, expected, |bcx| { - controlflow::trans_fail_bounds_check(bcx, - index_expr.span, - ix_val, - len) - }); - let elt = InBoundsGEP(bcx, base, &[ix_val]); - let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to()); - Datum::new(elt, vt.unit_ty, LvalueExpr) - } - }; - - DatumBlock::new(bcx, elt_datum) -} - -fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ref_expr: &ast::Expr, - def: def::Def) - -> DatumBlock<'blk, 'tcx, Expr> { - //! Translates a reference to a path. - - let _icx = push_ctxt("trans_def_lvalue"); - match def { - def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) | - def::DefStruct(_) | def::DefVariant(..) => { - trans_def_fn_unadjusted(bcx, ref_expr, def) - } - def::DefStatic(did, _) => { - // There are two things that may happen here: - // 1) If the static item is defined in this crate, it will be - // translated using `get_item_val`, and we return a pointer to - // the result. - // 2) If the static item is defined in another crate then we add - // (or reuse) a declaration of an external global, and return a - // pointer to that. - let const_ty = expr_ty(bcx, ref_expr); - - fn get_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, did: ast::DefId, - const_ty: ty::t) -> ValueRef { - // For external constants, we don't inline. - if did.krate == ast::LOCAL_CRATE { - // Case 1. - - // The LLVM global has the type of its initializer, - // which may not be equal to the enum's type for - // non-C-like enums. - let val = base::get_item_val(bcx.ccx(), did.node); - let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); - PointerCast(bcx, val, pty) - } else { - // Case 2. - base::get_extern_const(bcx.ccx(), did, const_ty) - } - } - let val = get_val(bcx, did, const_ty); - DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr)) - } - def::DefConst(did) => { - // First, inline any external constants into the local crate so we - // can be sure to get the LLVM value corresponding to it. - let did = inline::maybe_instantiate_inline(bcx.ccx(), did); - if did.krate != ast::LOCAL_CRATE { - bcx.tcx().sess.span_bug(ref_expr.span, - "cross crate constant could not \ - be inlined"); - } - let val = base::get_item_val(bcx.ccx(), did.node); - - // Next, we need to crate a ByRef rvalue datum to return. We can't - // use the normal .to_ref_datum() function because the type of - // `val` is not actually the same as `const_ty`. - // - // To get around this, we make a custom alloca slot with the - // appropriate type (const_ty), and then we cast it to a pointer of - // typeof(val), store the value, and then hand this slot over to - // the datum infrastructure. - let const_ty = expr_ty(bcx, ref_expr); - let llty = type_of::type_of(bcx.ccx(), const_ty); - let slot = alloca(bcx, llty, "const"); - let pty = Type::from_ref(unsafe { llvm::LLVMTypeOf(val) }).ptr_to(); - Store(bcx, val, PointerCast(bcx, slot, pty)); - - let datum = Datum::new(slot, const_ty, Rvalue::new(ByRef)); - DatumBlock::new(bcx, datum.to_expr_datum()) - } - _ => { - DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum()) - } - } -} - -fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr) - -> Block<'blk, 'tcx> { - let mut bcx = bcx; - let _icx = push_ctxt("trans_rvalue_stmt"); - - if bcx.unreachable.get() { - return bcx; - } - - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - - match expr.node { - ast::ExprParen(ref e) => { - trans_into(bcx, &**e, Ignore) - } - ast::ExprBreak(label_opt) => { - controlflow::trans_break(bcx, expr.id, label_opt) - } - ast::ExprAgain(label_opt) => { - controlflow::trans_cont(bcx, expr.id, label_opt) - } - ast::ExprRet(ref ex) => { - controlflow::trans_ret(bcx, ex.as_ref().map(|e| &**e)) - } - ast::ExprWhile(ref cond, ref body, _) => { - controlflow::trans_while(bcx, expr.id, &**cond, &**body) - } - ast::ExprForLoop(ref pat, ref head, ref body, _) => { - controlflow::trans_for(bcx, - expr_info(expr), - &**pat, - &**head, - &**body) - } - ast::ExprLoop(ref body, _) => { - controlflow::trans_loop(bcx, expr.id, &**body) - } - ast::ExprAssign(ref dst, ref src) => { - let src_datum = unpack_datum!(bcx, trans(bcx, &**src)); - let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign")); - - if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) { - // If there are destructors involved, make sure we - // are copying from an rvalue, since that cannot possible - // alias an lvalue. We are concerned about code like: - // - // a = a - // - // but also - // - // a = a.b - // - // where e.g. a : Option and a.b : - // Option. In that case, freeing `a` before the - // assignment may also free `a.b`! - // - // We could avoid this intermediary with some analysis - // to determine whether `dst` may possibly own `src`. - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - let src_datum = unpack_datum!( - bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign")); - bcx = glue::drop_ty(bcx, - dst_datum.val, - dst_datum.ty, - Some(NodeInfo { id: expr.id, span: expr.span })); - src_datum.store_to(bcx, dst_datum.val) - } else { - src_datum.store_to(bcx, dst_datum.val) - } - } - ast::ExprAssignOp(op, ref dst, ref src) => { - trans_assign_op(bcx, expr, op, &**dst, &**src) - } - ast::ExprInlineAsm(ref a) => { - asm::trans_inline_asm(bcx, a) - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("trans_rvalue_stmt_unadjusted reached \ - fall-through case: {}", - expr.node).as_slice()); - } - } -} - -fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - dest: Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); - let mut bcx = bcx; - let tcx = bcx.tcx(); - - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); - - match expr.node { - ast::ExprParen(ref e) => { - trans_into(bcx, &**e, dest) - } - ast::ExprPath(_) => { - trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest) - } - ast::ExprIf(ref cond, ref thn, ref els) => { - controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest) - } - ast::ExprMatch(ref discr, ref arms, _) => { - _match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest) - } - ast::ExprBlock(ref blk) => { - controlflow::trans_block(bcx, &**blk, dest) - } - ast::ExprStruct(_, ref fields, ref base) => { - trans_struct(bcx, - fields.as_slice(), - base.as_ref().map(|e| &**e), - expr.span, - expr.id, - dest) - } - ast::ExprTup(ref args) => { - let numbered_fields: Vec<(uint, &ast::Expr)> = - args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect(); - trans_adt(bcx, - expr_ty(bcx, expr), - 0, - numbered_fields.as_slice(), - None, - dest, - Some(NodeInfo { id: expr.id, span: expr.span })) - } - ast::ExprLit(ref lit) => { - match lit.node { - ast::LitStr(ref s, _) => { - tvec::trans_lit_str(bcx, expr, (*s).clone(), dest) - } - _ => { - bcx.tcx() - .sess - .span_bug(expr.span, - "trans_rvalue_dps_unadjusted shouldn't be \ - translating this type of literal") - } - } - } - ast::ExprVec(..) | ast::ExprRepeat(..) => { - tvec::trans_fixed_vstore(bcx, expr, dest) - } - ast::ExprFnBlock(_, ref decl, ref body) | - ast::ExprProc(ref decl, ref body) => { - let expr_ty = expr_ty(bcx, expr); - let store = ty::ty_closure_store(expr_ty); - debug!("translating block function {} with type {}", - expr_to_string(expr), expr_ty.repr(tcx)); - closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest) - } - ast::ExprUnboxedFn(_, _, ref decl, ref body) => { - closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest) - } - ast::ExprCall(ref f, ref args) => { - if bcx.tcx().is_method_call(expr.id) { - trans_overloaded_call(bcx, - expr, - &**f, - args.as_slice(), - Some(dest)) - } else { - callee::trans_call(bcx, - expr, - &**f, - callee::ArgExprs(args.as_slice()), - dest) - } - } - ast::ExprMethodCall(_, _, ref args) => { - callee::trans_method_call(bcx, - expr, - &*args[0], - callee::ArgExprs(args.as_slice()), - dest) - } - ast::ExprBinary(_, ref lhs, ref rhs) => { - // if not overloaded, would be RvalueDatumExpr - let lhs = unpack_datum!(bcx, trans(bcx, &**lhs)); - let rhs_datum = unpack_datum!(bcx, trans(bcx, &**rhs)); - trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs, - vec![(rhs_datum, rhs.id)], Some(dest)).bcx - } - ast::ExprUnary(_, ref subexpr) => { - // if not overloaded, would be RvalueDatumExpr - let arg = unpack_datum!(bcx, trans(bcx, &**subexpr)); - trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), - arg, Vec::new(), Some(dest)).bcx - } - ast::ExprIndex(ref base, ref idx) => { - // if not overloaded, would be RvalueDatumExpr - let base = unpack_datum!(bcx, trans(bcx, &**base)); - let idx_datum = unpack_datum!(bcx, trans(bcx, &**idx)); - trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), base, - vec![(idx_datum, idx.id)], Some(dest)).bcx - } - ast::ExprCast(ref val, _) => { - // DPS output mode means this is a trait cast: - if ty::type_is_trait(node_id_type(bcx, expr.id)) { - let trait_ref = - bcx.tcx().object_cast_map.borrow() - .get(&expr.id) - .map(|t| (*t).clone()) - .unwrap(); - let trait_ref = - trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs); - let datum = unpack_datum!(bcx, trans(bcx, &**val)); - meth::trans_trait_cast(bcx, datum, expr.id, - trait_ref, dest) - } else { - bcx.tcx().sess.span_bug(expr.span, - "expr_cast of non-trait"); - } - } - ast::ExprAssignOp(op, ref dst, ref src) => { - trans_assign_op(bcx, expr, op, &**dst, &**src) - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("trans_rvalue_dps_unadjusted reached fall-through \ - case: {}", - expr.node).as_slice()); - } - } -} - -fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ref_expr: &ast::Expr, - def: def::Def, - dest: Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_def_dps_unadjusted"); - - let lldest = match dest { - SaveIn(lldest) => lldest, - Ignore => { return bcx; } - }; - - match def { - def::DefVariant(tid, vid, _) => { - let variant_info = ty::enum_variant_with_id(bcx.tcx(), tid, vid); - if variant_info.args.len() > 0u { - // N-ary variant. - let llfn = callee::trans_fn_ref(bcx, vid, ExprId(ref_expr.id)); - Store(bcx, llfn, lldest); - return bcx; - } else { - // Nullary variant. - let ty = expr_ty(bcx, ref_expr); - let repr = adt::represent_type(bcx.ccx(), ty); - adt::trans_set_discr(bcx, &*repr, lldest, - variant_info.disr_val); - return bcx; - } - } - def::DefStruct(_) => { - let ty = expr_ty(bcx, ref_expr); - match ty::get(ty).sty { - ty::ty_struct(did, _) if ty::has_dtor(bcx.tcx(), did) => { - let repr = adt::represent_type(bcx.ccx(), ty); - adt::trans_set_discr(bcx, &*repr, lldest, 0); - } - _ => {} - } - bcx - } - _ => { - bcx.tcx().sess.span_bug(ref_expr.span, format!( - "Non-DPS def {} referened by {}", - def, bcx.node_id_to_string(ref_expr.id)).as_slice()); - } - } -} - -fn trans_def_fn_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ref_expr: &ast::Expr, - def: def::Def) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_def_datum_unadjusted"); - - let llfn = match def { - def::DefFn(did, _) | - def::DefStruct(did) | def::DefVariant(_, did, _) | - def::DefStaticMethod(did, def::FromImpl(_)) | - def::DefMethod(did, _, def::FromImpl(_)) => { - callee::trans_fn_ref(bcx, did, ExprId(ref_expr.id)) - } - def::DefStaticMethod(impl_did, def::FromTrait(trait_did)) | - def::DefMethod(impl_did, _, def::FromTrait(trait_did)) => { - meth::trans_static_method_callee(bcx, impl_did, - trait_did, ref_expr.id) - } - _ => { - bcx.tcx().sess.span_bug(ref_expr.span, format!( - "trans_def_fn_unadjusted invoked on: {} for {}", - def, - ref_expr.repr(bcx.tcx())).as_slice()); - } - }; - - let fn_ty = expr_ty(bcx, ref_expr); - DatumBlock::new(bcx, Datum::new(llfn, fn_ty, RvalueExpr(Rvalue::new(ByValue)))) -} - -pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - def: def::Def) - -> Datum { - /*! - * Translates a reference to a local variable or argument. - * This always results in an lvalue datum. - */ - - let _icx = push_ctxt("trans_local_var"); - - match def { - def::DefUpvar(nid, _, _) => { - // Can't move upvars, so this is never a ZeroMemLastUse. - let local_ty = node_id_type(bcx, nid); - match bcx.fcx.llupvars.borrow().get(&nid) { - Some(&val) => Datum::new(val, local_ty, Lvalue), - None => { - bcx.sess().bug(format!( - "trans_local_var: no llval for upvar {} found", - nid).as_slice()); - } - } - } - def::DefLocal(nid) => { - let datum = match bcx.fcx.lllocals.borrow().get(&nid) { - Some(&v) => v, - None => { - bcx.sess().bug(format!( - "trans_local_var: no datum for local/arg {} found", - nid).as_slice()); - } - }; - debug!("take_local(nid={}, v={}, ty={})", - nid, bcx.val_to_string(datum.val), bcx.ty_to_string(datum.ty)); - datum - } - _ => { - bcx.sess().unimpl(format!( - "unsupported def type in trans_local_var: {}", - def).as_slice()); - } - } -} - -pub fn with_field_tys(tcx: &ty::ctxt, - ty: ty::t, - node_id_opt: Option, - op: |ty::Disr, (&[ty::field])| -> R) - -> R { - /*! - * Helper for enumerating the field types of structs, enums, or records. - * The optional node ID here is the node ID of the path identifying the enum - * variant in use. If none, this cannot possibly an enum variant (so, if it - * is and `node_id_opt` is none, this function panics). - */ - - match ty::get(ty).sty { - ty::ty_struct(did, ref substs) => { - op(0, struct_fields(tcx, did, substs).as_slice()) - } - - ty::ty_tup(ref v) => { - op(0, tup_fields(v.as_slice()).as_slice()) - } - - ty::ty_enum(_, ref substs) => { - // We want the *variant* ID here, not the enum ID. - match node_id_opt { - None => { - tcx.sess.bug(format!( - "cannot get field types from the enum type {} \ - without a node ID", - ty.repr(tcx)).as_slice()); - } - Some(node_id) => { - let def = tcx.def_map.borrow()[node_id].clone(); - match def { - def::DefVariant(enum_id, variant_id, _) => { - let variant_info = ty::enum_variant_with_id( - tcx, enum_id, variant_id); - op(variant_info.disr_val, - struct_fields(tcx, - variant_id, - substs).as_slice()) - } - _ => { - tcx.sess.bug("resolve didn't map this expr to a \ - variant ID") - } - } - } - } - } - - _ => { - tcx.sess.bug(format!( - "cannot get field types from the type {}", - ty.repr(tcx)).as_slice()); - } - } -} - -fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - fields: &[ast::Field], - base: Option<&ast::Expr>, - expr_span: codemap::Span, - expr_id: ast::NodeId, - dest: Dest) -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_rec"); - - let ty = node_id_type(bcx, expr_id); - let tcx = bcx.tcx(); - with_field_tys(tcx, ty, Some(expr_id), |discr, field_tys| { - let mut need_base = Vec::from_elem(field_tys.len(), true); - - let numbered_fields = fields.iter().map(|field| { - let opt_pos = - field_tys.iter().position(|field_ty| - field_ty.name == field.ident.node.name); - match opt_pos { - Some(i) => { - need_base[i] = false; - (i, &*field.expr) - } - None => { - tcx.sess.span_bug(field.span, - "Couldn't find field in struct type") - } - } - }).collect::>(); - let optbase = match base { - Some(base_expr) => { - let mut leftovers = Vec::new(); - for (i, b) in need_base.iter().enumerate() { - if *b { - leftovers.push((i, field_tys[i].mt.ty)) - } - } - Some(StructBaseInfo {expr: base_expr, - fields: leftovers }) - } - None => { - if need_base.iter().any(|b| *b) { - tcx.sess.span_bug(expr_span, "missing fields and no base expr") - } - None - } - }; - - trans_adt(bcx, - ty, - discr, - numbered_fields.as_slice(), - optbase, - dest, - Some(NodeInfo { id: expr_id, span: expr_span })) - }) -} - -/** - * Information that `trans_adt` needs in order to fill in the fields - * of a struct copied from a base struct (e.g., from an expression - * like `Foo { a: b, ..base }`. - * - * Note that `fields` may be empty; the base expression must always be - * evaluated for side-effects. - */ -pub struct StructBaseInfo<'a> { - /// The base expression; will be evaluated after all explicit fields. - expr: &'a ast::Expr, - /// The indices of fields to copy paired with their types. - fields: Vec<(uint, ty::t)> -} - -/** - * Constructs an ADT instance: - * - * - `fields` should be a list of field indices paired with the - * expression to store into that field. The initializers will be - * evaluated in the order specified by `fields`. - * - * - `optbase` contains information on the base struct (if any) from - * which remaining fields are copied; see comments on `StructBaseInfo`. - */ -pub fn trans_adt<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - ty: ty::t, - discr: ty::Disr, - fields: &[(uint, &ast::Expr)], - optbase: Option, - dest: Dest, - source_location: Option) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_adt"); - let fcx = bcx.fcx; - let repr = adt::represent_type(bcx.ccx(), ty); - - match source_location { - Some(src_loc) => debuginfo::set_source_location(bcx.fcx, - src_loc.id, - src_loc.span), - None => {} - }; - - // If we don't care about the result, just make a - // temporary stack slot - let addr = match dest { - SaveIn(pos) => pos, - Ignore => alloc_ty(bcx, ty, "temp"), - }; - - // This scope holds intermediates that must be cleaned should - // panic occur before the ADT as a whole is ready. - let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); - - // First we trans the base, if we have one, to the dest - for base in optbase.iter() { - assert_eq!(discr, 0); - - match ty::expr_kind(bcx.tcx(), &*base.expr) { - ty::RvalueDpsExpr | ty::RvalueDatumExpr if !ty::type_needs_drop(bcx.tcx(), ty) => { - bcx = trans_into(bcx, &*base.expr, SaveIn(addr)); - }, - ty::RvalueStmtExpr => bcx.tcx().sess.bug("unexpected expr kind for struct base expr"), - _ => { - let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base")); - for &(i, t) in base.fields.iter() { - let datum = base_datum.get_element( - bcx, t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); - assert!(ty::type_is_sized(bcx.tcx(), datum.ty)); - let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); - bcx = datum.store_to(bcx, dest); - } - } - } - } - - match source_location { - Some(src_loc) => debuginfo::set_source_location(bcx.fcx, - src_loc.id, - src_loc.span), - None => {} - }; - - if ty::type_is_simd(bcx.tcx(), ty) { - // This is the constructor of a SIMD type, such types are - // always primitive machine types and so do not have a - // destructor or require any clean-up. - let llty = type_of::type_of(bcx.ccx(), ty); - - // keep a vector as a register, and running through the field - // `insertelement`ing them directly into that register - // (i.e. avoid GEPi and `store`s to an alloca) . - let mut vec_val = C_undef(llty); - - for &(i, ref e) in fields.iter() { - let block_datum = trans(bcx, &**e); - bcx = block_datum.bcx; - let position = C_uint(bcx.ccx(), i); - let value = block_datum.datum.to_llscalarish(bcx); - vec_val = InsertElement(bcx, vec_val, value, position); - } - Store(bcx, vec_val, addr); - } else { - // Now, we just overwrite the fields we've explicitly specified - for &(i, ref e) in fields.iter() { - let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); - let e_ty = expr_ty_adjusted(bcx, &**e); - bcx = trans_into(bcx, &**e, SaveIn(dest)); - let scope = cleanup::CustomScope(custom_cleanup_scope); - fcx.schedule_lifetime_end(scope, dest); - fcx.schedule_drop_mem(scope, dest, e_ty); - } - } - - adt::trans_set_discr(bcx, &*repr, addr, discr); - - fcx.pop_custom_cleanup_scope(custom_cleanup_scope); - - // If we don't care about the result drop the temporary we made - match dest { - SaveIn(_) => bcx, - Ignore => { - bcx = glue::drop_ty(bcx, addr, ty, source_location); - base::call_lifetime_end(bcx, addr); - bcx - } - } -} - - -fn trans_immediate_lit<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - lit: &ast::Lit) - -> DatumBlock<'blk, 'tcx, Expr> { - // must not be a string constant, that is a RvalueDpsExpr - let _icx = push_ctxt("trans_immediate_lit"); - let ty = expr_ty(bcx, expr); - let v = consts::const_lit(bcx.ccx(), expr, lit); - immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock() -} - -fn trans_unary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - op: ast::UnOp, - sub_expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let ccx = bcx.ccx(); - let mut bcx = bcx; - let _icx = push_ctxt("trans_unary_datum"); - - let method_call = MethodCall::expr(expr.id); - - // The only overloaded operator that is translated to a datum - // is an overloaded deref, since it is always yields a `&T`. - // Otherwise, we should be in the RvalueDpsExpr path. - assert!( - op == ast::UnDeref || - !ccx.tcx().method_map.borrow().contains_key(&method_call)); - - let un_ty = expr_ty(bcx, expr); - - match op { - ast::UnNot => { - let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); - let llresult = Not(bcx, datum.to_llscalarish(bcx)); - immediate_rvalue_bcx(bcx, llresult, un_ty).to_expr_datumblock() - } - ast::UnNeg => { - let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); - let val = datum.to_llscalarish(bcx); - let llneg = { - if ty::type_is_fp(un_ty) { - FNeg(bcx, val) - } else { - Neg(bcx, val) - } - }; - immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock() - } - ast::UnUniq => { - trans_uniq_expr(bcx, un_ty, sub_expr, expr_ty(bcx, sub_expr)) - } - ast::UnDeref => { - let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); - deref_once(bcx, expr, datum, method_call) - } - } -} - -fn trans_uniq_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - box_ty: ty::t, - contents: &ast::Expr, - contents_ty: ty::t) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_uniq_expr"); - let fcx = bcx.fcx; - assert!(ty::type_is_sized(bcx.tcx(), contents_ty)); - let llty = type_of::type_of(bcx.ccx(), contents_ty); - let size = llsize_of(bcx.ccx(), llty); - let align = C_uint(bcx.ccx(), type_of::align_of(bcx.ccx(), contents_ty)); - let llty_ptr = llty.ptr_to(); - let Result { bcx, val } = malloc_raw_dyn(bcx, llty_ptr, box_ty, size, align); - // Unique boxes do not allocate for zero-size types. The standard library - // may assume that `free` is never called on the pointer returned for - // `Box`. - let bcx = if llsize_of_alloc(bcx.ccx(), llty) == 0 { - trans_into(bcx, contents, SaveIn(val)) - } else { - let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); - fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope), - val, cleanup::HeapExchange, contents_ty); - let bcx = trans_into(bcx, contents, SaveIn(val)); - fcx.pop_custom_cleanup_scope(custom_cleanup_scope); - bcx - }; - immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock() -} - -fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - subexpr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_addr_of"); - let mut bcx = bcx; - let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of")); - match ty::get(sub_datum.ty).sty { - ty::ty_open(_) => { - // Opened DST value, close to a fat pointer - debug!("Closing fat pointer {}", bcx.ty_to_string(sub_datum.ty)); - - let scratch = rvalue_scratch_datum(bcx, - ty::close_type(bcx.tcx(), sub_datum.ty), - "fat_addr_of"); - let base = Load(bcx, get_dataptr(bcx, sub_datum.val)); - Store(bcx, base, get_dataptr(bcx, scratch.val)); - - let len = Load(bcx, get_len(bcx, sub_datum.val)); - Store(bcx, len, get_len(bcx, scratch.val)); - - DatumBlock::new(bcx, scratch.to_expr_datum()) - } - _ => { - // Sized value, ref to a thin pointer - let ty = expr_ty(bcx, expr); - immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock() - } - } -} - -// Important to get types for both lhs and rhs, because one might be _|_ -// and the other not. -fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - binop_expr: &ast::Expr, - binop_ty: ty::t, - op: ast::BinOp, - lhs_t: ty::t, - lhs: ValueRef, - rhs_t: ty::t, - rhs: ValueRef) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_eager_binop"); - - let tcx = bcx.tcx(); - let is_simd = ty::type_is_simd(tcx, lhs_t); - let intype = { - if is_simd { ty::simd_type(tcx, lhs_t) } - else { lhs_t } - }; - let is_float = ty::type_is_fp(intype); - let is_signed = ty::type_is_signed(intype); - - let rhs = base::cast_shift_expr_rhs(bcx, op, lhs, rhs); - - let mut bcx = bcx; - let val = match op { - ast::BiAdd => { - if is_float { FAdd(bcx, lhs, rhs) } - else { Add(bcx, lhs, rhs) } - } - ast::BiSub => { - if is_float { FSub(bcx, lhs, rhs) } - else { Sub(bcx, lhs, rhs) } - } - ast::BiMul => { - if is_float { FMul(bcx, lhs, rhs) } - else { Mul(bcx, lhs, rhs) } - } - ast::BiDiv => { - if is_float { - FDiv(bcx, lhs, rhs) - } else { - // Only zero-check integers; fp /0 is NaN - bcx = base::fail_if_zero_or_overflows(bcx, binop_expr.span, - op, lhs, rhs, rhs_t); - if is_signed { - SDiv(bcx, lhs, rhs) - } else { - UDiv(bcx, lhs, rhs) - } - } - } - ast::BiRem => { - if is_float { - FRem(bcx, lhs, rhs) - } else { - // Only zero-check integers; fp %0 is NaN - bcx = base::fail_if_zero_or_overflows(bcx, binop_expr.span, - op, lhs, rhs, rhs_t); - if is_signed { - SRem(bcx, lhs, rhs) - } else { - URem(bcx, lhs, rhs) - } - } - } - ast::BiBitOr => Or(bcx, lhs, rhs), - ast::BiBitAnd => And(bcx, lhs, rhs), - ast::BiBitXor => Xor(bcx, lhs, rhs), - ast::BiShl => Shl(bcx, lhs, rhs), - ast::BiShr => { - if is_signed { - AShr(bcx, lhs, rhs) - } else { LShr(bcx, lhs, rhs) } - } - ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { - if ty::type_is_scalar(rhs_t) { - unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op)) - } else if is_simd { - base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op) - } else { - bcx.tcx().sess.span_bug(binop_expr.span, "comparison operator unsupported for type") - } - } - _ => { - bcx.tcx().sess.span_bug(binop_expr.span, "unexpected binop"); - } - }; - - immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock() -} - -// refinement types would obviate the need for this -enum lazy_binop_ty { - lazy_and, - lazy_or, -} - -fn trans_lazy_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - binop_expr: &ast::Expr, - op: lazy_binop_ty, - a: &ast::Expr, - b: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_lazy_binop"); - let binop_ty = expr_ty(bcx, binop_expr); - let fcx = bcx.fcx; - - let DatumBlock {bcx: past_lhs, datum: lhs} = trans(bcx, a); - let lhs = lhs.to_llscalarish(past_lhs); - - if past_lhs.unreachable.get() { - return immediate_rvalue_bcx(past_lhs, lhs, binop_ty).to_expr_datumblock(); - } - - let join = fcx.new_id_block("join", binop_expr.id); - let before_rhs = fcx.new_id_block("before_rhs", b.id); - - match op { - lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb), - lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb) - } - - let DatumBlock {bcx: past_rhs, datum: rhs} = trans(before_rhs, b); - let rhs = rhs.to_llscalarish(past_rhs); - - if past_rhs.unreachable.get() { - return immediate_rvalue_bcx(join, lhs, binop_ty).to_expr_datumblock(); - } - - Br(past_rhs, join.llbb); - let phi = Phi(join, Type::i1(bcx.ccx()), &[lhs, rhs], - &[past_lhs.llbb, past_rhs.llbb]); - - return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock(); -} - -fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - op: ast::BinOp, - lhs: &ast::Expr, - rhs: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_binary"); - let ccx = bcx.ccx(); - - // if overloaded, would be RvalueDpsExpr - assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); - - match op { - ast::BiAnd => { - trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs) - } - ast::BiOr => { - trans_lazy_binop(bcx, expr, lazy_or, lhs, rhs) - } - _ => { - let mut bcx = bcx; - let lhs_datum = unpack_datum!(bcx, trans(bcx, lhs)); - let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs)); - let binop_ty = expr_ty(bcx, expr); - - debug!("trans_binary (expr {}): lhs_datum={}", - expr.id, - lhs_datum.to_string(ccx)); - let lhs_ty = lhs_datum.ty; - let lhs = lhs_datum.to_llscalarish(bcx); - - debug!("trans_binary (expr {}): rhs_datum={}", - expr.id, - rhs_datum.to_string(ccx)); - let rhs_ty = rhs_datum.ty; - let rhs = rhs_datum.to_llscalarish(bcx); - trans_eager_binop(bcx, expr, binop_ty, op, - lhs_ty, lhs, rhs_ty, rhs) - } - } -} - -fn trans_overloaded_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - method_call: MethodCall, - lhs: Datum, - rhs: Vec<(Datum, ast::NodeId)>, - dest: Option) - -> Result<'blk, 'tcx> { - let method_ty = (*bcx.tcx().method_map.borrow())[method_call].ty; - callee::trans_call_inner(bcx, - Some(expr_info(expr)), - monomorphize_type(bcx, method_ty), - |bcx, arg_cleanup_scope| { - meth::trans_method_callee(bcx, - method_call, - None, - arg_cleanup_scope) - }, - callee::ArgOverloadedOp(lhs, rhs), - dest) -} - -fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - callee: &'a ast::Expr, - args: &'a [P], - dest: Option) - -> Block<'blk, 'tcx> { - let method_call = MethodCall::expr(expr.id); - let method_type = (*bcx.tcx() - .method_map - .borrow())[method_call] - .ty; - let mut all_args = vec!(callee); - all_args.extend(args.iter().map(|e| &**e)); - unpack_result!(bcx, - callee::trans_call_inner(bcx, - Some(expr_info(expr)), - monomorphize_type(bcx, - method_type), - |bcx, arg_cleanup_scope| { - meth::trans_method_callee( - bcx, - method_call, - None, - arg_cleanup_scope) - }, - callee::ArgOverloadedCall(all_args), - dest)); - bcx -} - -fn int_cast(bcx: Block, - lldsttype: Type, - llsrctype: Type, - llsrc: ValueRef, - signed: bool) - -> ValueRef { - let _icx = push_ctxt("int_cast"); - unsafe { - let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype.to_ref()); - let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype.to_ref()); - return if dstsz == srcsz { - BitCast(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - TruncOrBitCast(bcx, llsrc, lldsttype) - } else if signed { - SExtOrBitCast(bcx, llsrc, lldsttype) - } else { - ZExtOrBitCast(bcx, llsrc, lldsttype) - }; - } -} - -fn float_cast(bcx: Block, - lldsttype: Type, - llsrctype: Type, - llsrc: ValueRef) - -> ValueRef { - let _icx = push_ctxt("float_cast"); - let srcsz = llsrctype.float_width(); - let dstsz = lldsttype.float_width(); - return if dstsz > srcsz { - FPExt(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - FPTrunc(bcx, llsrc, lldsttype) - } else { llsrc }; -} - -#[deriving(PartialEq, Show)] -pub enum cast_kind { - cast_pointer, - cast_integral, - cast_float, - cast_enum, - cast_other, -} - -pub fn cast_type_kind(tcx: &ty::ctxt, t: ty::t) -> cast_kind { - match ty::get(t).sty { - ty::ty_char => cast_integral, - ty::ty_float(..) => cast_float, - ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => { - if ty::type_is_sized(tcx, mt.ty) { - cast_pointer - } else { - cast_other - } - } - ty::ty_bare_fn(..) => cast_pointer, - ty::ty_int(..) => cast_integral, - ty::ty_uint(..) => cast_integral, - ty::ty_bool => cast_integral, - ty::ty_enum(..) => cast_enum, - _ => cast_other - } -} - -fn cast_is_noop(t_in: ty::t, t_out: ty::t) -> bool { - match (ty::deref(t_in, true), ty::deref(t_out, true)) { - (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => { - t_in == t_out - } - _ => false - } -} - -fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - id: ast::NodeId) - -> DatumBlock<'blk, 'tcx, Expr> { - let _icx = push_ctxt("trans_cast"); - let mut bcx = bcx; - let ccx = bcx.ccx(); - - let t_in = expr_ty(bcx, expr); - let t_out = node_id_type(bcx, id); - let k_in = cast_type_kind(bcx.tcx(), t_in); - let k_out = cast_type_kind(bcx.tcx(), t_out); - let s_in = k_in == cast_integral && ty::type_is_signed(t_in); - let ll_t_in = type_of::arg_type_of(ccx, t_in); - let ll_t_out = type_of::arg_type_of(ccx, t_out); - - // Convert the value to be cast into a ValueRef, either by-ref or - // by-value as appropriate given its type: - let mut datum = unpack_datum!(bcx, trans(bcx, expr)); - - if cast_is_noop(datum.ty, t_out) { - datum.ty = t_out; - return DatumBlock::new(bcx, datum); - } - - let newval = match (k_in, k_out) { - (cast_integral, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) - } - (cast_float, cast_float) => { - let llexpr = datum.to_llscalarish(bcx); - float_cast(bcx, ll_t_out, ll_t_in, llexpr) - } - (cast_integral, cast_float) => { - let llexpr = datum.to_llscalarish(bcx); - if s_in { - SIToFP(bcx, llexpr, ll_t_out) - } else { UIToFP(bcx, llexpr, ll_t_out) } - } - (cast_float, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - if ty::type_is_signed(t_out) { - FPToSI(bcx, llexpr, ll_t_out) - } else { FPToUI(bcx, llexpr, ll_t_out) } - } - (cast_integral, cast_pointer) => { - let llexpr = datum.to_llscalarish(bcx); - IntToPtr(bcx, llexpr, ll_t_out) - } - (cast_pointer, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - PtrToInt(bcx, llexpr, ll_t_out) - } - (cast_pointer, cast_pointer) => { - let llexpr = datum.to_llscalarish(bcx); - PointerCast(bcx, llexpr, ll_t_out) - } - (cast_enum, cast_integral) | - (cast_enum, cast_float) => { - let mut bcx = bcx; - let repr = adt::represent_type(ccx, t_in); - let datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id)); - let llexpr_ptr = datum.to_llref(); - let lldiscrim_a = - adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx))); - match k_out { - cast_integral => int_cast(bcx, ll_t_out, - val_ty(lldiscrim_a), - lldiscrim_a, true), - cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), - _ => { - ccx.sess().bug(format!("translating unsupported cast: \ - {} ({}) -> {} ({})", - t_in.repr(bcx.tcx()), - k_in, - t_out.repr(bcx.tcx()), - k_out).as_slice()) - } - } - } - _ => ccx.sess().bug(format!("translating unsupported cast: \ - {} ({}) -> {} ({})", - t_in.repr(bcx.tcx()), - k_in, - t_out.repr(bcx.tcx()), - k_out).as_slice()) - }; - return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock(); -} - -fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - op: ast::BinOp, - dst: &ast::Expr, - src: &ast::Expr) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_assign_op"); - let mut bcx = bcx; - - debug!("trans_assign_op(expr={})", bcx.expr_to_string(expr)); - - // User-defined operator methods cannot be used with `+=` etc right now - assert!(!bcx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); - - // Evaluate LHS (destination), which should be an lvalue - let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op")); - assert!(!ty::type_needs_drop(bcx.tcx(), dst_datum.ty)); - let dst_ty = dst_datum.ty; - let dst = load_ty(bcx, dst_datum.val, dst_datum.ty); - - // Evaluate RHS - let rhs_datum = unpack_datum!(bcx, trans(bcx, &*src)); - let rhs_ty = rhs_datum.ty; - let rhs = rhs_datum.to_llscalarish(bcx); - - // Perform computation and store the result - let result_datum = unpack_datum!( - bcx, trans_eager_binop(bcx, expr, dst_datum.ty, op, - dst_ty, dst, rhs_ty, rhs)); - return result_datum.store_to(bcx, dst_datum.val); -} - -fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - datum: Datum, - expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - - // Ensure cleanup of `datum` if not already scheduled and obtain - // a "by ref" pointer. - let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id)); - - // Compute final type. Note that we are loose with the region and - // mutability, since those things don't matter in trans. - let referent_ty = lv_datum.ty; - let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty); - - // Get the pointer. - let llref = lv_datum.to_llref(); - - // Construct the resulting datum, using what was the "by ref" - // ValueRef of type `referent_ty` to be the "by value" ValueRef - // of type `&referent_ty`. - DatumBlock::new(bcx, Datum::new(llref, ptr_ty, RvalueExpr(Rvalue::new(ByValue)))) -} - -fn deref_multiple<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - times: uint) - -> DatumBlock<'blk, 'tcx, Expr> { - let mut bcx = bcx; - let mut datum = datum; - for i in range(0, times) { - let method_call = MethodCall::autoderef(expr.id, i); - datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, method_call)); - } - DatumBlock { bcx: bcx, datum: datum } -} - -fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - method_call: MethodCall) - -> DatumBlock<'blk, 'tcx, Expr> { - let ccx = bcx.ccx(); - - debug!("deref_once(expr={}, datum={}, method_call={})", - expr.repr(bcx.tcx()), - datum.to_string(ccx), - method_call); - - let mut bcx = bcx; - - // Check for overloaded deref. - let method_ty = ccx.tcx().method_map.borrow() - .get(&method_call).map(|method| method.ty); - let datum = match method_ty { - Some(method_ty) => { - // Overloaded. Evaluate `trans_overloaded_op`, which will - // invoke the user's deref() method, which basically - // converts from the `Smaht` pointer that we have into - // a `&T` pointer. We can then proceed down the normal - // path (below) to dereference that `&T`. - let datum = match method_call.adjustment { - // Always perform an AutoPtr when applying an overloaded auto-deref - typeck::AutoDeref(_) => unpack_datum!(bcx, auto_ref(bcx, datum, expr)), - _ => datum - }; - - let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap(); - let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref"); - - unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call, - datum, Vec::new(), Some(SaveIn(scratch.val)))); - scratch.to_expr_datum() - } - None => { - // Not overloaded. We already have a pointer we know how to deref. - datum - } - }; - - let r = match ty::get(datum.ty).sty { - ty::ty_uniq(content_ty) => { - if ty::type_is_sized(bcx.tcx(), content_ty) { - deref_owned_pointer(bcx, expr, datum, content_ty) - } else { - // A fat pointer and an opened DST value have the same - // representation just different types. Since there is no - // temporary for `*e` here (because it is unsized), we cannot - // emulate the sized object code path for running drop glue and - // free. Instead, we schedule cleanup for `e`, turning it into - // an lvalue. - let datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); - - let datum = Datum::new(datum.val, ty::mk_open(bcx.tcx(), content_ty), LvalueExpr); - DatumBlock::new(bcx, datum) - } - } - - ty::ty_ptr(ty::mt { ty: content_ty, .. }) | - ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => { - if ty::type_is_sized(bcx.tcx(), content_ty) { - let ptr = datum.to_llscalarish(bcx); - - // Always generate an lvalue datum, even if datum.mode is - // an rvalue. This is because datum.mode is only an - // rvalue for non-owning pointers like &T or *T, in which - // case cleanup *is* scheduled elsewhere, by the true - // owner (or, in the case of *T, by the user). - DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) - } else { - // A fat pointer and an opened DST value have the same representation - // just different types. - DatumBlock::new(bcx, Datum::new(datum.val, - ty::mk_open(bcx.tcx(), content_ty), - LvalueExpr)) - } - } - - _ => { - bcx.tcx().sess.span_bug( - expr.span, - format!("deref invoked on expr of illegal type {}", - datum.ty.repr(bcx.tcx())).as_slice()); - } - }; - - debug!("deref_once(expr={}, method_call={}, result={})", - expr.id, method_call, r.datum.to_string(ccx)); - - return r; - - fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - datum: Datum, - content_ty: ty::t) - -> DatumBlock<'blk, 'tcx, Expr> { - /*! - * We microoptimize derefs of owned pointers a bit here. - * Basically, the idea is to make the deref of an rvalue - * result in an rvalue. This helps to avoid intermediate stack - * slots in the resulting LLVM. The idea here is that, if the - * `Box` pointer is an rvalue, then we can schedule a *shallow* - * free of the `Box` pointer, and then return a ByRef rvalue - * into the pointer. Because the free is shallow, it is legit - * to return an rvalue, because we know that the contents are - * not yet scheduled to be freed. The language rules ensure that the - * contents will be used (or moved) before the free occurs. - */ - - match datum.kind { - RvalueExpr(Rvalue { mode: ByRef }) => { - let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); - let ptr = Load(bcx, datum.val); - if !type_is_zero_size(bcx.ccx(), content_ty) { - bcx.fcx.schedule_free_value(scope, ptr, cleanup::HeapExchange, content_ty); - } - } - RvalueExpr(Rvalue { mode: ByValue }) => { - let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); - if !type_is_zero_size(bcx.ccx(), content_ty) { - bcx.fcx.schedule_free_value(scope, datum.val, cleanup::HeapExchange, - content_ty); - } - } - LvalueExpr => { } - } - - // If we had an rvalue in, we produce an rvalue out. - let (llptr, kind) = match datum.kind { - LvalueExpr => { - (Load(bcx, datum.val), LvalueExpr) - } - RvalueExpr(Rvalue { mode: ByRef }) => { - (Load(bcx, datum.val), RvalueExpr(Rvalue::new(ByRef))) - } - RvalueExpr(Rvalue { mode: ByValue }) => { - (datum.val, RvalueExpr(Rvalue::new(ByRef))) - } - }; - - let datum = Datum { ty: content_ty, val: llptr, kind: kind }; - DatumBlock { bcx: bcx, datum: datum } - } -} diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs deleted file mode 100644 index 940319d050b..00000000000 --- a/src/librustc/middle/trans/foreign.rs +++ /dev/null @@ -1,974 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use back::{link}; -use llvm::{ValueRef, CallConv, get_param}; -use llvm; -use middle::weak_lang_items; -use middle::trans::base::{llvm_linkage_by_name, push_ctxt}; -use middle::trans::base; -use middle::trans::build::*; -use middle::trans::cabi; -use middle::trans::common::*; -use middle::trans::machine; -use middle::trans::type_::Type; -use middle::trans::type_of::*; -use middle::trans::type_of; -use middle::ty::FnSig; -use middle::ty; -use middle::subst::Subst; -use std::cmp; -use libc::c_uint; -use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi}; -use syntax::abi::{RustIntrinsic, Rust, RustCall, Stdcall, Fastcall, System}; -use syntax::codemap::Span; -use syntax::parse::token::{InternedString, special_idents}; -use syntax::parse::token; -use syntax::{ast}; -use syntax::{attr, ast_map}; -use util::ppaux::Repr; - -/////////////////////////////////////////////////////////////////////////// -// Type definitions - -struct ForeignTypes { - /// Rust signature of the function - fn_sig: ty::FnSig, - - /// Adapter object for handling native ABI rules (trust me, you - /// don't want to know) - fn_ty: cabi::FnType, - - /// LLVM types that will appear on the foreign function - llsig: LlvmSignature, -} - -struct LlvmSignature { - // LLVM versions of the types of this function's arguments. - llarg_tys: Vec , - - // LLVM version of the type that this function returns. Note that - // this *may not be* the declared return type of the foreign - // function, because the foreign function may opt to return via an - // out pointer. - llret_ty: Type, - - /// True if there is a return value (not bottom, not unit) - ret_def: bool, -} - - -/////////////////////////////////////////////////////////////////////////// -// Calls to external functions - -pub fn llvm_calling_convention(ccx: &CrateContext, - abi: Abi) -> CallConv { - match ccx.sess().target.target.adjust_abi(abi) { - RustIntrinsic => { - // Intrinsics are emitted at the call site - ccx.sess().bug("asked to register intrinsic fn"); - } - - Rust => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with Rust ABI"); - } - - RustCall => { - // FIXME(#3678) Implement linking to foreign fns with Rust ABI - ccx.sess().unimpl("foreign functions with RustCall ABI"); - } - - // It's the ABI's job to select this, not us. - System => ccx.sess().bug("system abi should be selected elsewhere"), - - Stdcall => llvm::X86StdcallCallConv, - Fastcall => llvm::X86FastcallCallConv, - C => llvm::CCallConv, - Win64 => llvm::X86_64_Win64, - - // These API constants ought to be more specific... - Cdecl => llvm::CCallConv, - Aapcs => llvm::CCallConv, - } -} - -pub fn register_static(ccx: &CrateContext, - foreign_item: &ast::ForeignItem) -> ValueRef { - let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id); - let llty = type_of::type_of(ccx, ty); - - let ident = link_name(foreign_item); - match attr::first_attr_value_str_by_name(foreign_item.attrs.as_slice(), - "linkage") { - // If this is a static with a linkage specified, then we need to handle - // it a little specially. The typesystem prevents things like &T and - // extern "C" fn() from being non-null, so we can't just declare a - // static and call it a day. Some linkages (like weak) will make it such - // that the static actually has a null value. - Some(name) => { - let linkage = match llvm_linkage_by_name(name.get()) { - Some(linkage) => linkage, - None => { - ccx.sess().span_fatal(foreign_item.span, - "invalid linkage specified"); - } - }; - let llty2 = match ty::get(ty).sty { - ty::ty_ptr(ref mt) => type_of::type_of(ccx, mt.ty), - _ => { - ccx.sess().span_fatal(foreign_item.span, - "must have type `*T` or `*mut T`"); - } - }; - unsafe { - // Declare a symbol `foo` with the desired linkage. - let g1 = ident.get().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(), buf) - }); - llvm::SetLinkage(g1, linkage); - - // Declare an internal global `extern_with_linkage_foo` which - // is initialized with the address of `foo`. If `foo` is - // discarded during linking (for example, if `foo` has weak - // linkage and there are no definitions), then - // `extern_with_linkage_foo` will instead be initialized to - // zero. - let mut real_name = "_rust_extern_with_linkage_".to_string(); - real_name.push_str(ident.get()); - let g2 = real_name.with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) - }); - llvm::SetLinkage(g2, llvm::InternalLinkage); - llvm::LLVMSetInitializer(g2, g1); - g2 - } - } - None => unsafe { - // Generate an external declaration. - ident.get().with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) - }) - } - } -} - -pub fn register_foreign_item_fn(ccx: &CrateContext, abi: Abi, fty: ty::t, - name: &str) -> ValueRef { - /*! - * Registers a foreign function found in a library. - * Just adds a LLVM global. - */ - - debug!("register_foreign_item_fn(abi={}, \ - ty={}, \ - name={})", - abi.repr(ccx.tcx()), - fty.repr(ccx.tcx()), - name); - - let cc = llvm_calling_convention(ccx, abi); - - // Register the function as a C extern fn - let tys = foreign_types_for_fn_ty(ccx, fty); - - // Make sure the calling convention is right for variadic functions - // (should've been caught if not in typeck) - if tys.fn_sig.variadic { - assert!(cc == llvm::CCallConv); - } - - // Create the LLVM value for the C extern fn - let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); - - let llfn = base::get_extern_fn(ccx, - &mut *ccx.externs().borrow_mut(), - name, - cc, - llfn_ty, - fty); - add_argument_attributes(&tys, llfn); - - llfn -} - -pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - callee_ty: ty::t, - llfn: ValueRef, - llretptr: ValueRef, - llargs_rust: &[ValueRef], - passed_arg_tys: Vec ) - -> Block<'blk, 'tcx> { - /*! - * Prepares a call to a native function. This requires adapting - * from the Rust argument passing rules to the native rules. - * - * # Parameters - * - * - `callee_ty`: Rust type for the function we are calling - * - `llfn`: the function pointer we are calling - * - `llretptr`: where to store the return value of the function - * - `llargs_rust`: a list of the argument values, prepared - * as they would be if calling a Rust function - * - `passed_arg_tys`: Rust type for the arguments. Normally we - * can derive these from callee_ty but in the case of variadic - * functions passed_arg_tys will include the Rust type of all - * the arguments including the ones not specified in the fn's signature. - */ - - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - - debug!("trans_native_call(callee_ty={}, \ - llfn={}, \ - llretptr={})", - callee_ty.repr(tcx), - ccx.tn().val_to_string(llfn), - ccx.tn().val_to_string(llretptr)); - - let (fn_abi, fn_sig) = match ty::get(callee_ty).sty { - ty::ty_bare_fn(ref fn_ty) => (fn_ty.abi, fn_ty.sig.clone()), - _ => ccx.sess().bug("trans_native_call called on non-function type") - }; - let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys.as_slice()); - let fn_type = cabi::compute_abi_info(ccx, - llsig.llarg_tys.as_slice(), - llsig.llret_ty, - llsig.ret_def); - - let arg_tys: &[cabi::ArgType] = fn_type.arg_tys.as_slice(); - - let mut llargs_foreign = Vec::new(); - - // If the foreign ABI expects return value by pointer, supply the - // pointer that Rust gave us. Sometimes we have to bitcast - // because foreign fns return slightly different (but equivalent) - // views on the same type (e.g., i64 in place of {i32,i32}). - if fn_type.ret_ty.is_indirect() { - match fn_type.ret_ty.cast { - Some(ty) => { - let llcastedretptr = - BitCast(bcx, llretptr, ty.ptr_to()); - llargs_foreign.push(llcastedretptr); - } - None => { - llargs_foreign.push(llretptr); - } - } - } - - for (i, &llarg_rust) in llargs_rust.iter().enumerate() { - let mut llarg_rust = llarg_rust; - - if arg_tys[i].is_ignore() { - continue; - } - - // Does Rust pass this argument by pointer? - let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]); - - debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}", - i, - ccx.tn().val_to_string(llarg_rust), - rust_indirect, - ccx.tn().type_to_string(arg_tys[i].ty)); - - // Ensure that we always have the Rust value indirectly, - // because it makes bitcasting easier. - if !rust_indirect { - let scratch = - base::alloca(bcx, - type_of::type_of(ccx, passed_arg_tys[i]), - "__arg"); - base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]); - llarg_rust = scratch; - } - - debug!("llarg_rust={} (after indirection)", - ccx.tn().val_to_string(llarg_rust)); - - // Check whether we need to do any casting - match arg_tys[i].cast { - Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()), - None => () - } - - debug!("llarg_rust={} (after casting)", - ccx.tn().val_to_string(llarg_rust)); - - // Finally, load the value if needed for the foreign ABI - let foreign_indirect = arg_tys[i].is_indirect(); - let llarg_foreign = if foreign_indirect { - llarg_rust - } else { - if ty::type_is_bool(passed_arg_tys[i]) { - let val = LoadRangeAssert(bcx, llarg_rust, 0, 2, llvm::False); - Trunc(bcx, val, Type::i1(bcx.ccx())) - } else { - Load(bcx, llarg_rust) - } - }; - - debug!("argument {}, llarg_foreign={}", - i, ccx.tn().val_to_string(llarg_foreign)); - - // fill padding with undef value - match arg_tys[i].pad { - Some(ty) => llargs_foreign.push(C_undef(ty)), - None => () - } - llargs_foreign.push(llarg_foreign); - } - - let cc = llvm_calling_convention(ccx, fn_abi); - - // A function pointer is called without the declaration available, so we have to apply - // any attributes with ABI implications directly to the call instruction. - let mut attrs = llvm::AttrBuilder::new(); - - // Add attributes that are always applicable, independent of the concrete foreign ABI - if fn_type.ret_ty.is_indirect() { - let llret_sz = machine::llsize_of_real(ccx, fn_type.ret_ty.ty); - - // The outptr can be noalias and nocapture because it's entirely - // invisible to the program. We also know it's nonnull as well - // as how many bytes we can dereference - attrs.arg(1, llvm::NoAliasAttribute) - .arg(1, llvm::NoCaptureAttribute) - .arg(1, llvm::DereferenceableAttribute(llret_sz)); - }; - - // Add attributes that depend on the concrete foreign ABI - let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 }; - match fn_type.ret_ty.attr { - Some(attr) => { attrs.arg(arg_idx, attr); }, - _ => () - } - - arg_idx += 1; - for arg_ty in fn_type.arg_tys.iter() { - if arg_ty.is_ignore() { - continue; - } - // skip padding - if arg_ty.pad.is_some() { arg_idx += 1; } - - match arg_ty.attr { - Some(attr) => { attrs.arg(arg_idx, attr); }, - _ => {} - } - - arg_idx += 1; - } - - let llforeign_retval = CallWithConv(bcx, - llfn, - llargs_foreign.as_slice(), - cc, - Some(attrs)); - - // If the function we just called does not use an outpointer, - // store the result into the rust outpointer. Cast the outpointer - // type to match because some ABIs will use a different type than - // the Rust type. e.g., a {u32,u32} struct could be returned as - // u64. - if llsig.ret_def && !fn_type.ret_ty.is_indirect() { - let llrust_ret_ty = llsig.llret_ty; - let llforeign_ret_ty = match fn_type.ret_ty.cast { - Some(ty) => ty, - None => fn_type.ret_ty.ty - }; - - debug!("llretptr={}", ccx.tn().val_to_string(llretptr)); - debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval)); - debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty)); - debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty)); - - if llrust_ret_ty == llforeign_ret_ty { - match fn_sig.output { - ty::FnConverging(result_ty) => { - base::store_ty(bcx, llforeign_retval, llretptr, result_ty) - } - ty::FnDiverging => {} - } - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); - Store(bcx, llforeign_retval, llscratch); - let llscratch_i8 = BitCast(bcx, llscratch, Type::i8(ccx).ptr_to()); - let llretptr_i8 = BitCast(bcx, llretptr, Type::i8(ccx).ptr_to()); - let llrust_size = machine::llsize_of_store(ccx, llrust_ret_ty); - let llforeign_align = machine::llalign_of_min(ccx, llforeign_ret_ty); - let llrust_align = machine::llalign_of_min(ccx, llrust_ret_ty); - let llalign = cmp::min(llforeign_align, llrust_align); - debug!("llrust_size={}", llrust_size); - base::call_memcpy(bcx, llretptr_i8, llscratch_i8, - C_uint(ccx, llrust_size), llalign as u32); - } - } - - return bcx; -} - -pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { - let _icx = push_ctxt("foreign::trans_foreign_mod"); - for foreign_item in foreign_mod.items.iter() { - let lname = link_name(&**foreign_item); - - match foreign_item.node { - ast::ForeignItemFn(..) => { - match foreign_mod.abi { - Rust | RustIntrinsic => {} - abi => { - let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id); - register_foreign_item_fn(ccx, abi, ty, - lname.get().as_slice()); - // Unlike for other items, we shouldn't call - // `base::update_linkage` here. Foreign items have - // special linkage requirements, which are handled - // inside `foreign::register_*`. - } - } - } - _ => {} - } - - ccx.item_symbols().borrow_mut().insert(foreign_item.id, - lname.get().to_string()); - } -} - -/////////////////////////////////////////////////////////////////////////// -// Rust functions with foreign ABIs -// -// These are normal Rust functions defined with foreign ABIs. For -// now, and perhaps forever, we translate these using a "layer of -// indirection". That is, given a Rust declaration like: -// -// extern "C" fn foo(i: u32) -> u32 { ... } -// -// we will generate a function like: -// -// S foo(T i) { -// S r; -// foo0(&r, NULL, i); -// return r; -// } -// -// #[inline_always] -// void foo0(uint32_t *r, void *env, uint32_t i) { ... } -// -// Here the (internal) `foo0` function follows the Rust ABI as normal, -// where the `foo` function follows the C ABI. We rely on LLVM to -// inline the one into the other. Of course we could just generate the -// correct code in the first place, but this is much simpler. - -pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext, - t: ty::t, - name: &str) - -> ValueRef { - let tys = foreign_types_for_fn_ty(ccx, t); - let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); - let cconv = match ty::get(t).sty { - ty::ty_bare_fn(ref fn_ty) => { - llvm_calling_convention(ccx, fn_ty.abi) - } - _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi") - }; - let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::FnConverging(ty::mk_nil(ccx.tcx()))); - add_argument_attributes(&tys, llfn); - debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", - ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); - llfn -} - -pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext, - sp: Span, - sym: String, - node_id: ast::NodeId) - -> ValueRef { - let _icx = push_ctxt("foreign::register_foreign_fn"); - - let tys = foreign_types_for_id(ccx, node_id); - let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); - let t = ty::node_id_to_type(ccx.tcx(), node_id); - let cconv = match ty::get(t).sty { - ty::ty_bare_fn(ref fn_ty) => { - llvm_calling_convention(ccx, fn_ty.abi) - } - _ => panic!("expected bare fn in register_rust_fn_with_foreign_abi") - }; - let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty); - add_argument_attributes(&tys, llfn); - debug!("register_rust_fn_with_foreign_abi(node_id={}, llfn_ty={}, llfn={})", - node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); - llfn -} - -pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, - decl: &ast::FnDecl, - body: &ast::Block, - attrs: &[ast::Attribute], - llwrapfn: ValueRef, - param_substs: ¶m_substs, - id: ast::NodeId, - hash: Option<&str>) { - let _icx = push_ctxt("foreign::build_foreign_fn"); - - let fnty = ty::node_id_to_type(ccx.tcx(), id); - let mty = fnty.subst(ccx.tcx(), ¶m_substs.substs); - let tys = foreign_types_for_fn_ty(ccx, mty); - - unsafe { // unsafe because we call LLVM operations - // Build up the Rust function (`foo0` above). - let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash); - - // Build up the foreign wrapper (`foo` above). - return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty); - } - - fn build_rust_fn(ccx: &CrateContext, - decl: &ast::FnDecl, - body: &ast::Block, - param_substs: ¶m_substs, - attrs: &[ast::Attribute], - id: ast::NodeId, - hash: Option<&str>) - -> ValueRef { - let _icx = push_ctxt("foreign::foreign::build_rust_fn"); - let tcx = ccx.tcx(); - let t = ty::node_id_to_type(tcx, id).subst( - ccx.tcx(), ¶m_substs.substs); - - let ps = ccx.tcx().map.with_path(id, |path| { - let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name)); - link::mangle(path.chain(abi.into_iter()), hash) - }); - - // Compute the type that the function would have if it were just a - // normal Rust function. This will be the type of the wrappee fn. - match ty::get(t).sty { - ty::ty_bare_fn(ref f) => { - assert!(f.abi != Rust && f.abi != RustIntrinsic); - } - _ => { - ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \ - expected a bare fn ty", - ccx.tcx().map.path_to_string(id), - t.repr(tcx)).as_slice()); - } - }; - - debug!("build_rust_fn: path={} id={} t={}", - ccx.tcx().map.path_to_string(id), - id, t.repr(tcx)); - - let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice()); - base::set_llvm_fn_attrs(ccx, attrs, llfn); - base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]); - llfn - } - - unsafe fn build_wrap_fn(ccx: &CrateContext, - llrustfn: ValueRef, - llwrapfn: ValueRef, - tys: &ForeignTypes, - t: ty::t) { - let _icx = push_ctxt( - "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn"); - let tcx = ccx.tcx(); - - debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})", - ccx.tn().val_to_string(llrustfn), - ccx.tn().val_to_string(llwrapfn), - t.repr(ccx.tcx())); - - // Avoid all the Rust generation stuff and just generate raw - // LLVM here. - // - // We want to generate code like this: - // - // S foo(T i) { - // S r; - // foo0(&r, NULL, i); - // return r; - // } - - let the_block = - "the block".with_c_str( - |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, s)); - - let builder = ccx.builder(); - builder.position_at_end(the_block); - - // Array for the arguments we will pass to the rust function. - let mut llrust_args = Vec::new(); - let mut next_foreign_arg_counter: c_uint = 0; - let next_foreign_arg: |pad: bool| -> c_uint = |pad: bool| { - next_foreign_arg_counter += if pad { - 2 - } else { - 1 - }; - next_foreign_arg_counter - 1 - }; - - // If there is an out pointer on the foreign function - let foreign_outptr = { - if tys.fn_ty.ret_ty.is_indirect() { - Some(get_param(llwrapfn, next_foreign_arg(false))) - } else { - None - } - }; - - // Push Rust return pointer, using null if it will be unused. - let rust_uses_outptr = match tys.fn_sig.output { - ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty), - ty::FnDiverging => false - }; - let return_alloca: Option; - let llrust_ret_ty = tys.llsig.llret_ty; - let llrust_retptr_ty = llrust_ret_ty.ptr_to(); - if rust_uses_outptr { - // Rust expects to use an outpointer. If the foreign fn - // also uses an outpointer, we can reuse it, but the types - // may vary, so cast first to the Rust type. If the - // foreign fn does NOT use an outpointer, we will have to - // alloca some scratch space on the stack. - match foreign_outptr { - Some(llforeign_outptr) => { - debug!("out pointer, foreign={}", - ccx.tn().val_to_string(llforeign_outptr)); - let llrust_retptr = - builder.bitcast(llforeign_outptr, llrust_retptr_ty); - debug!("out pointer, foreign={} (casted)", - ccx.tn().val_to_string(llrust_retptr)); - llrust_args.push(llrust_retptr); - return_alloca = None; - } - - None => { - let slot = builder.alloca(llrust_ret_ty, "return_alloca"); - debug!("out pointer, \ - allocad={}, \ - llrust_ret_ty={}, \ - return_ty={}", - ccx.tn().val_to_string(slot), - ccx.tn().type_to_string(llrust_ret_ty), - tys.fn_sig.output.repr(tcx)); - llrust_args.push(slot); - return_alloca = Some(slot); - } - } - } else { - // Rust does not expect an outpointer. If the foreign fn - // does use an outpointer, then we will do a store of the - // value that the Rust fn returns. - return_alloca = None; - }; - - // Build up the arguments to the call to the rust function. - // Careful to adapt for cases where the native convention uses - // a pointer and Rust does not or vice versa. - for i in range(0, tys.fn_sig.inputs.len()) { - let rust_ty = tys.fn_sig.inputs[i]; - let llrust_ty = tys.llsig.llarg_tys[i]; - let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty); - let llforeign_arg_ty = tys.fn_ty.arg_tys[i]; - let foreign_indirect = llforeign_arg_ty.is_indirect(); - - if llforeign_arg_ty.is_ignore() { - debug!("skipping ignored arg #{}", i); - llrust_args.push(C_undef(llrust_ty)); - continue; - } - - // skip padding - let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some()); - let mut llforeign_arg = get_param(llwrapfn, foreign_index); - - debug!("llforeign_arg {}{}: {}", "#", - i, ccx.tn().val_to_string(llforeign_arg)); - debug!("rust_indirect = {}, foreign_indirect = {}", - rust_indirect, foreign_indirect); - - // Ensure that the foreign argument is indirect (by - // pointer). It makes adapting types easier, since we can - // always just bitcast pointers. - if !foreign_indirect { - llforeign_arg = if ty::type_is_bool(rust_ty) { - let lltemp = builder.alloca(Type::bool(ccx), ""); - builder.store(builder.zext(llforeign_arg, Type::bool(ccx)), lltemp); - lltemp - } else { - let lltemp = builder.alloca(val_ty(llforeign_arg), ""); - builder.store(llforeign_arg, lltemp); - lltemp - } - } - - // If the types in the ABI and the Rust types don't match, - // bitcast the llforeign_arg pointer so it matches the types - // Rust expects. - if llforeign_arg_ty.cast.is_some() { - assert!(!foreign_indirect); - llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to()); - } - - let llrust_arg = if rust_indirect { - llforeign_arg - } else { - if ty::type_is_bool(rust_ty) { - let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False); - builder.trunc(tmp, Type::i1(ccx)) - } else { - builder.load(llforeign_arg) - } - }; - - debug!("llrust_arg {}{}: {}", "#", - i, ccx.tn().val_to_string(llrust_arg)); - llrust_args.push(llrust_arg); - } - - // Perform the call itself - debug!("calling llrustfn = {}, t = {}", - ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx())); - let attributes = base::get_fn_llvm_attributes(ccx, t); - let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes)); - - // Get the return value where the foreign fn expects it. - let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast { - Some(ty) => ty, - None => tys.fn_ty.ret_ty.ty - }; - match foreign_outptr { - None if !tys.llsig.ret_def => { - // Function returns `()` or `bot`, which in Rust is the LLVM - // type "{}" but in foreign ABIs is "Void". - builder.ret_void(); - } - - None if rust_uses_outptr => { - // Rust uses an outpointer, but the foreign ABI does not. Load. - let llrust_outptr = return_alloca.unwrap(); - let llforeign_outptr_casted = - builder.bitcast(llrust_outptr, llforeign_ret_ty.ptr_to()); - let llforeign_retval = builder.load(llforeign_outptr_casted); - builder.ret(llforeign_retval); - } - - None if llforeign_ret_ty != llrust_ret_ty => { - // Neither ABI uses an outpointer, but the types don't - // quite match. Must cast. Probably we should try and - // examine the types and use a concrete llvm cast, but - // right now we just use a temp memory location and - // bitcast the pointer, which is the same thing the - // old wrappers used to do. - let lltemp = builder.alloca(llforeign_ret_ty, ""); - let lltemp_casted = builder.bitcast(lltemp, llrust_ret_ty.ptr_to()); - builder.store(llrust_ret_val, lltemp_casted); - let llforeign_retval = builder.load(lltemp); - builder.ret(llforeign_retval); - } - - None => { - // Neither ABI uses an outpointer, and the types - // match. Easy peasy. - builder.ret(llrust_ret_val); - } - - Some(llforeign_outptr) if !rust_uses_outptr => { - // Foreign ABI requires an out pointer, but Rust doesn't. - // Store Rust return value. - let llforeign_outptr_casted = - builder.bitcast(llforeign_outptr, llrust_retptr_ty); - builder.store(llrust_ret_val, llforeign_outptr_casted); - builder.ret_void(); - } - - Some(_) => { - // Both ABIs use outpointers. Easy peasy. - builder.ret_void(); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// General ABI Support -// -// This code is kind of a confused mess and needs to be reworked given -// the massive simplifications that have occurred. - -pub fn link_name(i: &ast::ForeignItem) -> InternedString { - match attr::first_attr_value_str_by_name(i.attrs.as_slice(), "link_name") { - Some(ln) => ln.clone(), - None => match weak_lang_items::link_name(i.attrs.as_slice()) { - Some(name) => name, - None => token::get_ident(i.ident), - } - } -} - -fn foreign_signature(ccx: &CrateContext, fn_sig: &ty::FnSig, arg_tys: &[ty::t]) - -> LlvmSignature { - /*! - * The ForeignSignature is the LLVM types of the arguments/return type - * of a function. Note that these LLVM types are not quite the same - * as the LLVM types would be for a native Rust function because foreign - * functions just plain ignore modes. They also don't pass aggregate - * values by pointer like we do. - */ - - let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect(); - let (llret_ty, ret_def) = match fn_sig.output { - ty::FnConverging(ret_ty) => - (type_of::arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)), - ty::FnDiverging => - (Type::nil(ccx), false) - }; - LlvmSignature { - llarg_tys: llarg_tys, - llret_ty: llret_ty, - ret_def: ret_def - } -} - -fn foreign_types_for_id(ccx: &CrateContext, - id: ast::NodeId) -> ForeignTypes { - foreign_types_for_fn_ty(ccx, ty::node_id_to_type(ccx.tcx(), id)) -} - -fn foreign_types_for_fn_ty(ccx: &CrateContext, - ty: ty::t) -> ForeignTypes { - let fn_sig = match ty::get(ty).sty { - ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(), - _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type") - }; - let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs.as_slice()); - let fn_ty = cabi::compute_abi_info(ccx, - llsig.llarg_tys.as_slice(), - llsig.llret_ty, - llsig.ret_def); - debug!("foreign_types_for_fn_ty(\ - ty={}, \ - llsig={} -> {}, \ - fn_ty={} -> {}, \ - ret_def={}", - ty.repr(ccx.tcx()), - ccx.tn().types_to_str(llsig.llarg_tys.as_slice()), - ccx.tn().type_to_string(llsig.llret_ty), - ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::>().as_slice()), - ccx.tn().type_to_string(fn_ty.ret_ty.ty), - llsig.ret_def); - - ForeignTypes { - fn_sig: fn_sig, - llsig: llsig, - fn_ty: fn_ty - } -} - -fn lltype_for_fn_from_foreign_types(ccx: &CrateContext, tys: &ForeignTypes) -> Type { - let mut llargument_tys = Vec::new(); - - let ret_ty = tys.fn_ty.ret_ty; - let llreturn_ty = if ret_ty.is_indirect() { - llargument_tys.push(ret_ty.ty.ptr_to()); - Type::void(ccx) - } else { - match ret_ty.cast { - Some(ty) => ty, - None => ret_ty.ty - } - }; - - for &arg_ty in tys.fn_ty.arg_tys.iter() { - if arg_ty.is_ignore() { - continue; - } - // add padding - match arg_ty.pad { - Some(ty) => llargument_tys.push(ty), - None => () - } - - let llarg_ty = if arg_ty.is_indirect() { - arg_ty.ty.ptr_to() - } else { - match arg_ty.cast { - Some(ty) => ty, - None => arg_ty.ty - } - }; - - llargument_tys.push(llarg_ty); - } - - if tys.fn_sig.variadic { - Type::variadic_func(llargument_tys.as_slice(), &llreturn_ty) - } else { - Type::func(llargument_tys.as_slice(), &llreturn_ty) - } -} - -pub fn lltype_for_foreign_fn(ccx: &CrateContext, ty: ty::t) -> Type { - lltype_for_fn_from_foreign_types(ccx, &foreign_types_for_fn_ty(ccx, ty)) -} - -fn add_argument_attributes(tys: &ForeignTypes, - llfn: ValueRef) { - let mut i = if tys.fn_ty.ret_ty.is_indirect() { - 1i - } else { - 0i - }; - - match tys.fn_ty.ret_ty.attr { - Some(attr) => unsafe { - llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64); - }, - None => {} - } - - i += 1; - - for &arg_ty in tys.fn_ty.arg_tys.iter() { - if arg_ty.is_ignore() { - continue; - } - // skip padding - if arg_ty.pad.is_some() { i += 1; } - - match arg_ty.attr { - Some(attr) => unsafe { - llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64); - }, - None => () - } - - i += 1; - } -} diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs deleted file mode 100644 index 6c3e3f4364e..00000000000 --- a/src/librustc/middle/trans/glue.rs +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! -// -// Code relating to taking, dropping, etc as well as type descriptors. - - -use back::abi; -use back::link::*; -use llvm::{ValueRef, True, get_param}; -use llvm; -use middle::lang_items::ExchangeFreeFnLangItem; -use middle::subst; -use middle::subst::Subst; -use middle::trans::adt; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::common::*; -use middle::trans::datum; -use middle::trans::debuginfo; -use middle::trans::expr; -use middle::trans::machine::*; -use middle::trans::tvec; -use middle::trans::type_::Type; -use middle::trans::type_of::{type_of, sizing_type_of, align_of}; -use middle::ty; -use util::ppaux::{ty_to_short_str, Repr}; -use util::ppaux; - -use arena::TypedArena; -use std::c_str::ToCStr; -use libc::c_uint; -use syntax::ast; -use syntax::parse::token; - -pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, - size: ValueRef, align: ValueRef) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_exchange_free"); - let ccx = cx.ccx(); - callee::trans_lang_call(cx, - langcall(cx, None, "", ExchangeFreeFnLangItem), - &[PointerCast(cx, v, Type::i8p(ccx)), size, align], - Some(expr::Ignore)).bcx -} - -pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, - size: u64, align: u32) -> Block<'blk, 'tcx> { - trans_exchange_free_dyn(cx, v, C_uint(cx.ccx(), size), - C_uint(cx.ccx(), align)) -} - -pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ptr: ValueRef, - content_ty: ty::t) -> Block<'blk, 'tcx> { - assert!(ty::type_is_sized(bcx.ccx().tcx(), content_ty)); - let sizing_type = sizing_type_of(bcx.ccx(), content_ty); - let content_size = llsize_of_alloc(bcx.ccx(), sizing_type); - - // `Box` does not allocate. - if content_size != 0 { - let content_align = align_of(bcx.ccx(), content_ty); - trans_exchange_free(bcx, ptr, content_size, content_align) - } else { - bcx - } -} - -pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t { - let tcx = ccx.tcx(); - // Even if there is no dtor for t, there might be one deeper down and we - // might need to pass in the vtable ptr. - if !ty::type_is_sized(tcx, t) { - return t - } - if !ty::type_needs_drop(tcx, t) { - return ty::mk_i8(); - } - match ty::get(t).sty { - ty::ty_uniq(typ) if !ty::type_needs_drop(tcx, typ) - && ty::type_is_sized(tcx, typ) => { - let llty = sizing_type_of(ccx, typ); - // `Box` does not allocate. - if llsize_of_alloc(ccx, llty) == 0 { - ty::mk_i8() - } else { - t - } - } - _ => t - } -} - -pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - t: ty::t, - source_location: Option) - -> Block<'blk, 'tcx> { - // NB: v is an *alias* of type t here, not a direct value. - debug!("drop_ty(t={})", t.repr(bcx.tcx())); - let _icx = push_ctxt("drop_ty"); - if ty::type_needs_drop(bcx.tcx(), t) { - let ccx = bcx.ccx(); - let glue = get_drop_glue(ccx, t); - let glue_type = get_drop_glue_type(ccx, t); - let ptr = if glue_type != t { - PointerCast(bcx, v, type_of(ccx, glue_type).ptr_to()) - } else { - v - }; - - match source_location { - Some(sl) => debuginfo::set_source_location(bcx.fcx, sl.id, sl.span), - None => debuginfo::clear_source_location(bcx.fcx) - }; - - Call(bcx, glue, &[ptr], None); - } - bcx -} - -pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - t: ty::t, - source_location: Option) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("drop_ty_immediate"); - let vp = alloca(bcx, type_of(bcx.ccx(), t), ""); - Store(bcx, v, vp); - drop_ty(bcx, vp, t, source_location) -} - -pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { - debug!("make drop glue for {}", ppaux::ty_to_string(ccx.tcx(), t)); - let t = get_drop_glue_type(ccx, t); - debug!("drop glue type {}", ppaux::ty_to_string(ccx.tcx(), t)); - match ccx.drop_glues().borrow().get(&t) { - Some(&glue) => return glue, - _ => { } - } - - let llty = if ty::type_is_sized(ccx.tcx(), t) { - type_of(ccx, t).ptr_to() - } else { - type_of(ccx, ty::mk_uniq(ccx.tcx(), t)).ptr_to() - }; - - let llfnty = Type::glue_fn(ccx, llty); - - let (glue, new_sym) = match ccx.available_drop_glues().borrow().get(&t) { - Some(old_sym) => { - let glue = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil(ccx.tcx())); - (glue, None) - }, - None => { - let (sym, glue) = declare_generic_glue(ccx, t, llfnty, "drop"); - (glue, Some(sym)) - }, - }; - - ccx.drop_glues().borrow_mut().insert(t, glue); - - // To avoid infinite recursion, don't `make_drop_glue` until after we've - // added the entry to the `drop_glues` cache. - match new_sym { - Some(sym) => { - ccx.available_drop_glues().borrow_mut().insert(t, sym); - // We're creating a new drop glue, so also generate a body. - make_generic_glue(ccx, t, glue, make_drop_glue, "drop"); - }, - None => {}, - } - - glue -} - -fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - t: ty::t, - v0: ValueRef, - dtor_did: ast::DefId, - class_did: ast::DefId, - substs: &subst::Substs) - -> Block<'blk, 'tcx> { - let repr = adt::represent_type(bcx.ccx(), t); - let struct_data = if ty::type_is_sized(bcx.tcx(), t) { - v0 - } else { - let llval = GEPi(bcx, v0, &[0, abi::slice_elt_base]); - Load(bcx, llval) - }; - let drop_flag = unpack_datum!(bcx, adt::trans_drop_flag_ptr(bcx, &*repr, struct_data)); - with_cond(bcx, load_ty(bcx, drop_flag.val, ty::mk_bool()), |cx| { - trans_struct_drop(cx, t, v0, dtor_did, class_did, substs) - }) -} - -fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - t: ty::t, - v0: ValueRef, - dtor_did: ast::DefId, - class_did: ast::DefId, - substs: &subst::Substs) - -> Block<'blk, 'tcx> { - let repr = adt::represent_type(bcx.ccx(), t); - - // Find and call the actual destructor - let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, t, - class_did, substs); - - // The first argument is the "self" argument for drop - let params = unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr)); - ty.element_type().func_params() - }; - - let fty = ty::lookup_item_type(bcx.tcx(), dtor_did).ty.subst(bcx.tcx(), substs); - let self_ty = match ty::get(fty).sty { - ty::ty_bare_fn(ref f) => { - assert!(f.sig.inputs.len() == 1); - f.sig.inputs[0] - } - _ => bcx.sess().bug(format!("Expected function type, found {}", - bcx.ty_to_string(fty)).as_slice()) - }; - - let (struct_data, info) = if ty::type_is_sized(bcx.tcx(), t) { - (v0, None) - } else { - let data = GEPi(bcx, v0, &[0, abi::slice_elt_base]); - let info = GEPi(bcx, v0, &[0, abi::slice_elt_len]); - (Load(bcx, data), Some(Load(bcx, info))) - }; - - adt::fold_variants(bcx, &*repr, struct_data, |variant_cx, st, value| { - // Be sure to put all of the fields into a scope so we can use an invoke - // instruction to call the user destructor but still call the field - // destructors if the user destructor panics. - let field_scope = variant_cx.fcx.push_custom_cleanup_scope(); - - // Class dtors have no explicit args, so the params should - // just consist of the environment (self). - assert_eq!(params.len(), 1); - let self_arg = if ty::type_is_fat_ptr(bcx.tcx(), self_ty) { - // The dtor expects a fat pointer, so make one, even if we have to fake it. - let boxed_ty = ty::mk_open(bcx.tcx(), t); - let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_self"); - Store(bcx, value, GEPi(bcx, scratch.val, &[0, abi::slice_elt_base])); - Store(bcx, - // If we just had a thin pointer, make a fat pointer by sticking - // null where we put the unsizing info. This works because t - // is a sized type, so we will only unpack the fat pointer, never - // use the fake info. - info.unwrap_or(C_null(Type::i8p(bcx.ccx()))), - GEPi(bcx, scratch.val, &[0, abi::slice_elt_len])); - PointerCast(variant_cx, scratch.val, params[0]) - } else { - PointerCast(variant_cx, value, params[0]) - }; - let args = vec!(self_arg); - - // Add all the fields as a value which needs to be cleaned at the end of - // this scope. Iterate in reverse order so a Drop impl doesn't reverse - // the order in which fields get dropped. - for (i, ty) in st.fields.iter().enumerate().rev() { - let llfld_a = adt::struct_field_ptr(variant_cx, &*st, value, i, false); - - let val = if ty::type_is_sized(bcx.tcx(), *ty) { - llfld_a - } else { - let boxed_ty = ty::mk_open(bcx.tcx(), *ty); - let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_field"); - Store(bcx, llfld_a, GEPi(bcx, scratch.val, &[0, abi::slice_elt_base])); - Store(bcx, info.unwrap(), GEPi(bcx, scratch.val, &[0, abi::slice_elt_len])); - scratch.val - }; - variant_cx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope), - val, *ty); - } - - let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID, - &[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil(bcx.tcx())); - let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false); - - variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope); - variant_cx - }) -} - -fn size_and_align_of_dst(bcx: Block, t :ty::t, info: ValueRef) -> (ValueRef, ValueRef) { - debug!("calculate size of DST: {}; with lost info: {}", - bcx.ty_to_string(t), bcx.val_to_string(info)); - if ty::type_is_sized(bcx.tcx(), t) { - let sizing_type = sizing_type_of(bcx.ccx(), t); - let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type)); - let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t)); - return (size, align); - } - match ty::get(t).sty { - ty::ty_struct(id, ref substs) => { - let ccx = bcx.ccx(); - // First get the size of all statically known fields. - // Don't use type_of::sizing_type_of because that expects t to be sized. - assert!(!ty::type_is_simd(bcx.tcx(), t)); - let repr = adt::represent_type(ccx, t); - let sizing_type = adt::sizing_type_of(ccx, &*repr, true); - let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type)); - let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type)); - - // Recurse to get the size of the dynamically sized field (must be - // the last field). - let fields = ty::struct_fields(bcx.tcx(), id, substs); - let last_field = fields[fields.len()-1]; - let field_ty = last_field.mt.ty; - let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); - - // Return the sum of sizes and max of aligns. - let size = Add(bcx, sized_size, unsized_size); - let align = Select(bcx, - ICmp(bcx, llvm::IntULT, sized_align, unsized_align), - sized_align, - unsized_align); - (size, align) - } - ty::ty_trait(..) => { - // info points to the vtable and the second entry in the vtable is the - // dynamic size of the object. - let info = PointerCast(bcx, info, Type::int(bcx.ccx()).ptr_to()); - let size_ptr = GEPi(bcx, info, &[1u]); - let align_ptr = GEPi(bcx, info, &[2u]); - (Load(bcx, size_ptr), Load(bcx, align_ptr)) - } - ty::ty_vec(unit_ty, None) => { - // The info in this case is the length of the vec, so the size is that - // times the unit size. - let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); - let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); - (Mul(bcx, info, C_uint(bcx.ccx(), unit_size)), C_uint(bcx.ccx(), 8u)) - } - _ => bcx.sess().bug(format!("Unexpected unsized type, found {}", - bcx.ty_to_string(t)).as_slice()) - } -} - -fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t) - -> Block<'blk, 'tcx> { - // NB: v0 is an *alias* of type t here, not a direct value. - let _icx = push_ctxt("make_drop_glue"); - match ty::get(t).sty { - ty::ty_uniq(content_ty) => { - match ty::get(content_ty).sty { - ty::ty_vec(ty, None) => { - tvec::make_drop_glue_unboxed(bcx, v0, ty, true) - } - ty::ty_str => { - let unit_ty = ty::sequence_element_type(bcx.tcx(), content_ty); - tvec::make_drop_glue_unboxed(bcx, v0, unit_ty, true) - } - ty::ty_trait(..) => { - let lluniquevalue = GEPi(bcx, v0, &[0, abi::trt_field_box]); - // Only drop the value when it is non-null - let concrete_ptr = Load(bcx, lluniquevalue); - with_cond(bcx, IsNotNull(bcx, concrete_ptr), |bcx| { - let dtor_ptr = Load(bcx, GEPi(bcx, v0, &[0, abi::trt_field_vtable])); - let dtor = Load(bcx, dtor_ptr); - Call(bcx, - dtor, - &[PointerCast(bcx, lluniquevalue, Type::i8p(bcx.ccx()))], - None); - bcx - }) - } - ty::ty_struct(..) if !ty::type_is_sized(bcx.tcx(), content_ty) => { - let llval = GEPi(bcx, v0, &[0, abi::slice_elt_base]); - let llbox = Load(bcx, llval); - let not_null = IsNotNull(bcx, llbox); - with_cond(bcx, not_null, |bcx| { - let bcx = drop_ty(bcx, v0, content_ty, None); - let info = GEPi(bcx, v0, &[0, abi::slice_elt_len]); - let info = Load(bcx, info); - let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info); - trans_exchange_free_dyn(bcx, llbox, llsize, llalign) - }) - } - _ => { - assert!(ty::type_is_sized(bcx.tcx(), content_ty)); - let llval = v0; - let llbox = Load(bcx, llval); - let not_null = IsNotNull(bcx, llbox); - with_cond(bcx, not_null, |bcx| { - let bcx = drop_ty(bcx, llbox, content_ty, None); - trans_exchange_free_ty(bcx, llbox, content_ty) - }) - } - } - } - ty::ty_struct(did, ref substs) | ty::ty_enum(did, ref substs) => { - let tcx = bcx.tcx(); - match ty::ty_dtor(tcx, did) { - ty::TraitDtor(dtor, true) => { - // FIXME(16758) Since the struct is unsized, it is hard to - // find the drop flag (which is at the end of the struct). - // Lets just ignore the flag and pretend everything will be - // OK. - if ty::type_is_sized(bcx.tcx(), t) { - trans_struct_drop_flag(bcx, t, v0, dtor, did, substs) - } else { - // Give the user a heads up that we are doing something - // stupid and dangerous. - bcx.sess().warn(format!("Ignoring drop flag in destructor for {}\ - because the struct is unsized. See issue\ - #16758", - bcx.ty_to_string(t)).as_slice()); - trans_struct_drop(bcx, t, v0, dtor, did, substs) - } - } - ty::TraitDtor(dtor, false) => { - trans_struct_drop(bcx, t, v0, dtor, did, substs) - } - ty::NoDtor => { - // No dtor? Just the default case - iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) - } - } - } - ty::ty_unboxed_closure(..) => iter_structural_ty(bcx, - v0, - t, - |bb, vv, tt| drop_ty(bb, vv, tt, None)), - ty::ty_closure(ref f) if f.store == ty::UniqTraitStore => { - let box_cell_v = GEPi(bcx, v0, &[0u, abi::fn_field_box]); - let env = Load(bcx, box_cell_v); - let env_ptr_ty = Type::at_box(bcx.ccx(), Type::i8(bcx.ccx())).ptr_to(); - let env = PointerCast(bcx, env, env_ptr_ty); - with_cond(bcx, IsNotNull(bcx, env), |bcx| { - let dtor_ptr = GEPi(bcx, env, &[0u, abi::box_field_drop_glue]); - let dtor = Load(bcx, dtor_ptr); - Call(bcx, dtor, &[PointerCast(bcx, box_cell_v, Type::i8p(bcx.ccx()))], None); - bcx - }) - } - ty::ty_trait(..) => { - // No need to do a null check here (as opposed to the Box tvec::make_drop_glue_unboxed(bcx, v0, ty, false), - _ => { - assert!(ty::type_is_sized(bcx.tcx(), t)); - if ty::type_needs_drop(bcx.tcx(), t) && - ty::type_is_structural(t) { - iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) - } else { - bcx - } - } - } -} - -// Generates the declaration for (but doesn't emit) a type descriptor. -pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info { - // If emit_tydescs already ran, then we shouldn't be creating any new - // tydescs. - assert!(!ccx.finished_tydescs().get()); - - let llty = type_of(ccx, t); - - if ccx.sess().count_type_sizes() { - println!("{}\t{}", llsize_of_real(ccx, llty), - ppaux::ty_to_string(ccx.tcx(), t)); - } - - let llsize = llsize_of(ccx, llty); - let llalign = llalign_of(ccx, llty); - let name = mangle_internal_name_by_type_and_seq(ccx, t, "tydesc"); - debug!("+++ declare_tydesc {} {}", ppaux::ty_to_string(ccx.tcx(), t), name); - let gvar = name.as_slice().with_c_str(|buf| { - unsafe { - llvm::LLVMAddGlobal(ccx.llmod(), ccx.tydesc_type().to_ref(), buf) - } - }); - note_unique_llvm_symbol(ccx, name); - - let ty_name = token::intern_and_get_ident( - ppaux::ty_to_string(ccx.tcx(), t).as_slice()); - let ty_name = C_str_slice(ccx, ty_name); - - debug!("--- declare_tydesc {}", ppaux::ty_to_string(ccx.tcx(), t)); - tydesc_info { - ty: t, - tydesc: gvar, - size: llsize, - align: llalign, - name: ty_name, - } -} - -fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type, - name: &str) -> (String, ValueRef) { - let _icx = push_ctxt("declare_generic_glue"); - let fn_nm = mangle_internal_name_by_type_and_seq( - ccx, - t, - format!("glue_{}", name).as_slice()); - let llfn = decl_cdecl_fn(ccx, fn_nm.as_slice(), llfnty, ty::mk_nil(ccx.tcx())); - note_unique_llvm_symbol(ccx, fn_nm.clone()); - return (fn_nm, llfn); -} - -fn make_generic_glue(ccx: &CrateContext, - t: ty::t, - llfn: ValueRef, - helper: for<'blk, 'tcx> |Block<'blk, 'tcx>, ValueRef, ty::t| - -> Block<'blk, 'tcx>, - name: &str) - -> ValueRef { - let _icx = push_ctxt("make_generic_glue"); - let glue_name = format!("glue {} {}", name, ty_to_short_str(ccx.tcx(), t)); - let _s = StatRecorder::new(ccx, glue_name); - - let arena = TypedArena::new(); - let empty_param_substs = param_substs::empty(); - let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, - ty::FnConverging(ty::mk_nil(ccx.tcx())), - &empty_param_substs, None, &arena); - - let bcx = init_function(&fcx, false, ty::FnConverging(ty::mk_nil(ccx.tcx()))); - - update_linkage(ccx, llfn, None, OriginalTranslation); - - ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u); - // All glue functions take values passed *by alias*; this is a - // requirement since in many contexts glue is invoked indirectly and - // the caller has no idea if it's dealing with something that can be - // passed by value. - // - // llfn is expected be declared to take a parameter of the appropriate - // type, so we don't need to explicitly cast the function parameter. - - let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint); - let bcx = helper(bcx, llrawptr0, t); - finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil(ccx.tcx()))); - - llfn -} - -pub fn emit_tydescs(ccx: &CrateContext) { - let _icx = push_ctxt("emit_tydescs"); - // As of this point, allow no more tydescs to be created. - ccx.finished_tydescs().set(true); - let glue_fn_ty = Type::generic_glue_fn(ccx).ptr_to(); - for (_, ti) in ccx.tydescs().borrow().iter() { - // Each of the glue functions needs to be cast to a generic type - // before being put into the tydesc because we only have a singleton - // tydesc type. Then we'll recast each function to its real type when - // calling it. - let drop_glue = unsafe { - llvm::LLVMConstPointerCast(get_drop_glue(ccx, ti.ty), glue_fn_ty.to_ref()) - }; - ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() + 1); - - let tydesc = C_named_struct(ccx.tydesc_type(), - &[ti.size, // size - ti.align, // align - drop_glue, // drop_glue - ti.name]); // name - - unsafe { - let gvar = ti.tydesc; - llvm::LLVMSetInitializer(gvar, tydesc); - llvm::LLVMSetGlobalConstant(gvar, True); - llvm::SetLinkage(gvar, llvm::InternalLinkage); - } - }; -} diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs deleted file mode 100644 index 2b54ac68ffa..00000000000 --- a/src/librustc/middle/trans/inline.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{AvailableExternallyLinkage, InternalLinkage, SetLinkage}; -use metadata::csearch; -use middle::astencode; -use middle::trans::base::{push_ctxt, trans_item, get_item_val, trans_fn}; -use middle::trans::common::*; -use middle::ty; - -use syntax::ast; -use syntax::ast_util::{local_def, PostExpansionMethod}; - -fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) - -> Option { - let _icx = push_ctxt("maybe_instantiate_inline"); - match ccx.external().borrow().get(&fn_id) { - Some(&Some(node_id)) => { - // Already inline - debug!("maybe_instantiate_inline({}): already inline as node id {}", - ty::item_path_str(ccx.tcx(), fn_id), node_id); - return Some(local_def(node_id)); - } - Some(&None) => { - return None; // Not inlinable - } - None => { - // Not seen yet - } - } - - let csearch_result = - csearch::maybe_get_item_ast( - ccx.tcx(), fn_id, - |a,b,c,d| astencode::decode_inlined_item(a, b, c, d)); - - let inline_def = match csearch_result { - csearch::not_found => { - ccx.external().borrow_mut().insert(fn_id, None); - return None; - } - csearch::found(&ast::IIItem(ref item)) => { - ccx.external().borrow_mut().insert(fn_id, Some(item.id)); - ccx.external_srcs().borrow_mut().insert(item.id, fn_id); - - ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); - trans_item(ccx, &**item); - - let linkage = match item.node { - ast::ItemFn(_, _, _, ref generics, _) => { - if generics.is_type_parameterized() { - // Generics have no symbol, so they can't be given any - // linkage. - None - } else { - if ccx.sess().opts.cg.codegen_units == 1 { - // We could use AvailableExternallyLinkage here, - // but InternalLinkage allows LLVM to optimize more - // aggressively (at the cost of sometimes - // duplicating code). - Some(InternalLinkage) - } else { - // With multiple compilation units, duplicated code - // is more of a problem. Also, `codegen_units > 1` - // means the user is okay with losing some - // performance. - Some(AvailableExternallyLinkage) - } - } - } - ast::ItemConst(..) => None, - _ => unreachable!(), - }; - - match linkage { - Some(linkage) => { - let g = get_item_val(ccx, item.id); - SetLinkage(g, linkage); - } - None => {} - } - - local_def(item.id) - } - csearch::found(&ast::IIForeign(ref item)) => { - ccx.external().borrow_mut().insert(fn_id, Some(item.id)); - ccx.external_srcs().borrow_mut().insert(item.id, fn_id); - local_def(item.id) - } - csearch::found_parent(parent_id, &ast::IIItem(ref item)) => { - ccx.external().borrow_mut().insert(parent_id, Some(item.id)); - ccx.external_srcs().borrow_mut().insert(item.id, parent_id); - - let mut my_id = 0; - match item.node { - ast::ItemEnum(_, _) => { - let vs_here = ty::enum_variants(ccx.tcx(), local_def(item.id)); - let vs_there = ty::enum_variants(ccx.tcx(), parent_id); - for (here, there) in vs_here.iter().zip(vs_there.iter()) { - if there.id == fn_id { my_id = here.id.node; } - ccx.external().borrow_mut().insert(there.id, Some(here.id.node)); - } - } - ast::ItemStruct(ref struct_def, _) => { - match struct_def.ctor_id { - None => {} - Some(ctor_id) => { - ccx.external().borrow_mut().insert(fn_id, Some(ctor_id)); - my_id = ctor_id; - } - } - } - _ => ccx.sess().bug("maybe_instantiate_inline: item has a \ - non-enum, non-struct parent") - } - trans_item(ccx, &**item); - local_def(my_id) - } - csearch::found_parent(_, _) => { - ccx.sess().bug("maybe_get_item_ast returned a found_parent \ - with a non-item parent"); - } - csearch::found(&ast::IITraitItem(_, ref trait_item)) => { - match *trait_item { - ast::RequiredMethod(_) => ccx.sess().bug("found RequiredMethod IITraitItem"), - ast::ProvidedMethod(ref mth) => { - ccx.external().borrow_mut().insert(fn_id, Some(mth.id)); - ccx.external_srcs().borrow_mut().insert(mth.id, fn_id); - - ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); - - // If this is a default method, we can't look up the - // impl type. But we aren't going to translate anyways, so - // don't. - local_def(mth.id) - } - ast::TypeTraitItem(_) => { - ccx.sess().bug("found TypeTraitItem IITraitItem") - } - } - } - csearch::found(&ast::IIImplItem(impl_did, ref impl_item)) => { - match *impl_item { - ast::MethodImplItem(ref mth) => { - ccx.external().borrow_mut().insert(fn_id, Some(mth.id)); - ccx.external_srcs().borrow_mut().insert(mth.id, fn_id); - - ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); - - let impl_tpt = ty::lookup_item_type(ccx.tcx(), impl_did); - let unparameterized = impl_tpt.generics.types.is_empty() && - mth.pe_generics().ty_params.is_empty(); - - if unparameterized { - let llfn = get_item_val(ccx, mth.id); - trans_fn(ccx, - &*mth.pe_fn_decl(), - &*mth.pe_body(), - llfn, - ¶m_substs::empty(), - mth.id, - &[]); - // Use InternalLinkage so LLVM can optimize more - // aggressively. - SetLinkage(llfn, InternalLinkage); - } - local_def(mth.id) - } - ast::TypeImplItem(_) => { - ccx.sess().bug("found TypeImplItem IIImplItem") - } - } - } - }; - - return Some(inline_def); -} - -pub fn get_local_instance(ccx: &CrateContext, fn_id: ast::DefId) - -> Option { - if fn_id.krate == ast::LOCAL_CRATE { - Some(fn_id) - } else { - instantiate_inline(ccx, fn_id) - } -} - -pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) -> ast::DefId { - get_local_instance(ccx, fn_id).unwrap_or(fn_id) -} diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs deleted file mode 100644 index 13a10258f80..00000000000 --- a/src/librustc/middle/trans/intrinsic.rs +++ /dev/null @@ -1,623 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use llvm; -use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef}; -use middle::subst; -use middle::subst::FnSpace; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::common::*; -use middle::trans::datum::*; -use middle::trans::expr; -use middle::trans::glue; -use middle::trans::type_of::*; -use middle::trans::type_of; -use middle::trans::machine; -use middle::trans::machine::llsize_of; -use middle::trans::type_::Type; -use middle::ty; -use syntax::abi::RustIntrinsic; -use syntax::ast; -use syntax::parse::token; -use util::ppaux::ty_to_string; - -pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Option { - let name = match token::get_ident(item.ident).get() { - "sqrtf32" => "llvm.sqrt.f32", - "sqrtf64" => "llvm.sqrt.f64", - "powif32" => "llvm.powi.f32", - "powif64" => "llvm.powi.f64", - "sinf32" => "llvm.sin.f32", - "sinf64" => "llvm.sin.f64", - "cosf32" => "llvm.cos.f32", - "cosf64" => "llvm.cos.f64", - "powf32" => "llvm.pow.f32", - "powf64" => "llvm.pow.f64", - "expf32" => "llvm.exp.f32", - "expf64" => "llvm.exp.f64", - "exp2f32" => "llvm.exp2.f32", - "exp2f64" => "llvm.exp2.f64", - "logf32" => "llvm.log.f32", - "logf64" => "llvm.log.f64", - "log10f32" => "llvm.log10.f32", - "log10f64" => "llvm.log10.f64", - "log2f32" => "llvm.log2.f32", - "log2f64" => "llvm.log2.f64", - "fmaf32" => "llvm.fma.f32", - "fmaf64" => "llvm.fma.f64", - "fabsf32" => "llvm.fabs.f32", - "fabsf64" => "llvm.fabs.f64", - "copysignf32" => "llvm.copysign.f32", - "copysignf64" => "llvm.copysign.f64", - "floorf32" => "llvm.floor.f32", - "floorf64" => "llvm.floor.f64", - "ceilf32" => "llvm.ceil.f32", - "ceilf64" => "llvm.ceil.f64", - "truncf32" => "llvm.trunc.f32", - "truncf64" => "llvm.trunc.f64", - "rintf32" => "llvm.rint.f32", - "rintf64" => "llvm.rint.f64", - "nearbyintf32" => "llvm.nearbyint.f32", - "nearbyintf64" => "llvm.nearbyint.f64", - "roundf32" => "llvm.round.f32", - "roundf64" => "llvm.round.f64", - "ctpop8" => "llvm.ctpop.i8", - "ctpop16" => "llvm.ctpop.i16", - "ctpop32" => "llvm.ctpop.i32", - "ctpop64" => "llvm.ctpop.i64", - "bswap16" => "llvm.bswap.i16", - "bswap32" => "llvm.bswap.i32", - "bswap64" => "llvm.bswap.i64", - "assume" => "llvm.assume", - _ => return None - }; - Some(ccx.get_intrinsic(&name)) -} - -/// Performs late verification that intrinsics are used correctly. At present, -/// the only intrinsic that needs such verification is `transmute`. -pub fn check_intrinsics(ccx: &CrateContext) { - for transmute_restriction in ccx.tcx() - .transmute_restrictions - .borrow() - .iter() { - let llfromtype = type_of::sizing_type_of(ccx, - transmute_restriction.from); - let lltotype = type_of::sizing_type_of(ccx, - transmute_restriction.to); - let from_type_size = machine::llbitsize_of_real(ccx, llfromtype); - let to_type_size = machine::llbitsize_of_real(ccx, lltotype); - if from_type_size != to_type_size { - ccx.sess() - .span_err(transmute_restriction.span, - format!("transmute called on types with different sizes: \ - {} ({} bit{}) to {} ({} bit{})", - ty_to_string(ccx.tcx(), transmute_restriction.from), - from_type_size as uint, - if from_type_size == 1 { - "" - } else { - "s" - }, - ty_to_string(ccx.tcx(), transmute_restriction.to), - to_type_size as uint, - if to_type_size == 1 { - "" - } else { - "s" - }).as_slice()); - } - if ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || - ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { - ccx.sess() - .add_lint(::lint::builtin::FAT_PTR_TRANSMUTES, - transmute_restriction.id, - transmute_restriction.span, - format!("Transmuting fat pointer types; {} to {}.\ - Beware of relying on the compiler's representation", - ty_to_string(ccx.tcx(), transmute_restriction.from), - ty_to_string(ccx.tcx(), transmute_restriction.to))); - } - } - ccx.sess().abort_if_errors(); -} - -pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::NodeId, - callee_ty: ty::t, cleanup_scope: cleanup::CustomScopeIndex, - args: callee::CallArgs, dest: expr::Dest, - substs: subst::Substs, call_info: NodeInfo) - -> Result<'blk, 'tcx> { - - let fcx = bcx.fcx; - let ccx = fcx.ccx; - let tcx = bcx.tcx(); - - let ret_ty = match ty::get(callee_ty).sty { - ty::ty_bare_fn(ref f) => f.sig.output, - _ => panic!("expected bare_fn in trans_intrinsic_call") - }; - let foreign_item = tcx.map.expect_foreign_item(node); - let name = token::get_ident(foreign_item.ident); - - // For `transmute` we can just trans the input expr directly into dest - if name.get() == "transmute" { - let llret_ty = type_of::type_of(ccx, ret_ty.unwrap()); - match args { - callee::ArgExprs(arg_exprs) => { - assert_eq!(arg_exprs.len(), 1); - - let (in_type, out_type) = (*substs.types.get(FnSpace, 0), - *substs.types.get(FnSpace, 1)); - let llintype = type_of::type_of(ccx, in_type); - let llouttype = type_of::type_of(ccx, out_type); - - let in_type_size = machine::llbitsize_of_real(ccx, llintype); - let out_type_size = machine::llbitsize_of_real(ccx, llouttype); - - // This should be caught by the intrinsicck pass - assert_eq!(in_type_size, out_type_size); - - // We need to cast the dest so the types work out - let dest = match dest { - expr::SaveIn(d) => expr::SaveIn(PointerCast(bcx, d, llintype.ptr_to())), - expr::Ignore => expr::Ignore - }; - bcx = expr::trans_into(bcx, &*arg_exprs[0], dest); - - fcx.pop_custom_cleanup_scope(cleanup_scope); - - return match dest { - expr::SaveIn(d) => Result::new(bcx, d), - expr::Ignore => Result::new(bcx, C_undef(llret_ty.ptr_to())) - }; - - } - - _ => { - ccx.sess().bug("expected expr as argument for transmute"); - } - } - } - - // Push the arguments. - let mut llargs = Vec::new(); - bcx = callee::trans_args(bcx, - args, - callee_ty, - &mut llargs, - cleanup::CustomScope(cleanup_scope), - false, - RustIntrinsic); - - fcx.pop_custom_cleanup_scope(cleanup_scope); - - // These are the only intrinsic functions that diverge. - if name.get() == "abort" { - let llfn = ccx.get_intrinsic(&("llvm.trap")); - Call(bcx, llfn, &[], None); - Unreachable(bcx); - return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to())); - } else if name.get() == "unreachable" { - Unreachable(bcx); - return Result::new(bcx, C_nil(ccx)); - } - - let ret_ty = match ret_ty { - ty::FnConverging(ret_ty) => ret_ty, - ty::FnDiverging => unreachable!() - }; - - let llret_ty = type_of::type_of(ccx, ret_ty); - - // Get location to store the result. If the user does - // not care about the result, just make a stack slot - let llresult = match dest { - expr::SaveIn(d) => d, - expr::Ignore => { - if !type_is_zero_size(ccx, ret_ty) { - alloc_ty(bcx, ret_ty, "intrinsic_result") - } else { - C_undef(llret_ty.ptr_to()) - } - } - }; - - let simple = get_simple_intrinsic(ccx, &*foreign_item); - let llval = match (simple, name.get()) { - (Some(llfn), _) => { - Call(bcx, llfn, llargs.as_slice(), None) - } - (_, "breakpoint") => { - let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); - Call(bcx, llfn, &[], None) - } - (_, "size_of") => { - let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)) - } - (_, "min_align_of") => { - let tp_ty = *substs.types.get(FnSpace, 0); - C_uint(ccx, type_of::align_of(ccx, tp_ty)) - } - (_, "pref_align_of") => { - let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)) - } - (_, "move_val_init") => { - // Create a datum reflecting the value being moved. - // Use `appropriate_mode` so that the datum is by ref - // if the value is non-immediate. Note that, with - // intrinsics, there are no argument cleanups to - // concern ourselves with, so we can use an rvalue datum. - let tp_ty = *substs.types.get(FnSpace, 0); - let mode = appropriate_rvalue_mode(ccx, tp_ty); - let src = Datum { - val: llargs[1], - ty: tp_ty, - kind: Rvalue::new(mode) - }; - bcx = src.store_to(bcx, llargs[0]); - C_nil(ccx) - } - (_, "get_tydesc") => { - let tp_ty = *substs.types.get(FnSpace, 0); - let static_ti = get_tydesc(ccx, tp_ty); - - // FIXME (#3730): ideally this shouldn't need a cast, - // but there's a circularity between translating rust types to llvm - // types and having a tydesc type available. So I can't directly access - // the llvm type of intrinsic::TyDesc struct. - PointerCast(bcx, static_ti.tydesc, llret_ty) - } - (_, "type_id") => { - let hash = ty::hash_crate_independent( - ccx.tcx(), - *substs.types.get(FnSpace, 0), - &ccx.link_meta().crate_hash); - // NB: This needs to be kept in lockstep with the TypeId struct in - // the intrinsic module - C_named_struct(llret_ty, &[C_u64(ccx, hash)]) - } - (_, "init") => { - let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - if return_type_is_void(ccx, tp_ty) { - C_nil(ccx) - } else { - C_null(lltp_ty) - } - } - // Effectively no-ops - (_, "uninit") | (_, "forget") => { - C_nil(ccx) - } - (_, "needs_drop") => { - let tp_ty = *substs.types.get(FnSpace, 0); - C_bool(ccx, ty::type_needs_drop(ccx.tcx(), tp_ty)) - } - (_, "owns_managed") => { - let tp_ty = *substs.types.get(FnSpace, 0); - C_bool(ccx, ty::type_contents(ccx.tcx(), tp_ty).owns_managed()) - } - (_, "offset") => { - let ptr = llargs[0]; - let offset = llargs[1]; - InBoundsGEP(bcx, ptr, &[offset]) - } - - (_, "copy_nonoverlapping_memory") => { - copy_intrinsic(bcx, false, false, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - (_, "copy_memory") => { - copy_intrinsic(bcx, true, false, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - (_, "set_memory") => { - memset_intrinsic(bcx, false, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - - (_, "volatile_copy_nonoverlapping_memory") => { - copy_intrinsic(bcx, false, true, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - (_, "volatile_copy_memory") => { - copy_intrinsic(bcx, true, true, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - (_, "volatile_set_memory") => { - memset_intrinsic(bcx, true, *substs.types.get(FnSpace, 0), - llargs[0], llargs[1], llargs[2]) - } - (_, "volatile_load") => { - VolatileLoad(bcx, llargs[0]) - }, - (_, "volatile_store") => { - VolatileStore(bcx, llargs[1], llargs[0]); - C_nil(ccx) - }, - - (_, "ctlz8") => count_zeros_intrinsic(bcx, "llvm.ctlz.i8", llargs[0]), - (_, "ctlz16") => count_zeros_intrinsic(bcx, "llvm.ctlz.i16", llargs[0]), - (_, "ctlz32") => count_zeros_intrinsic(bcx, "llvm.ctlz.i32", llargs[0]), - (_, "ctlz64") => count_zeros_intrinsic(bcx, "llvm.ctlz.i64", llargs[0]), - (_, "cttz8") => count_zeros_intrinsic(bcx, "llvm.cttz.i8", llargs[0]), - (_, "cttz16") => count_zeros_intrinsic(bcx, "llvm.cttz.i16", llargs[0]), - (_, "cttz32") => count_zeros_intrinsic(bcx, "llvm.cttz.i32", llargs[0]), - (_, "cttz64") => count_zeros_intrinsic(bcx, "llvm.cttz.i64", llargs[0]), - - (_, "i8_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "i16_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "i32_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "i64_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "u8_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "u16_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "u32_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "u64_add_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "i8_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "i16_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "i32_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "i64_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "u8_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "u16_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "u32_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "u64_sub_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "i8_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "i16_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "i32_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "i64_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "u8_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i8", ret_ty, - llargs[0], llargs[1]), - (_, "u16_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i16", ret_ty, - llargs[0], llargs[1]), - (_, "u32_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i32", ret_ty, - llargs[0], llargs[1]), - (_, "u64_mul_with_overflow") => - with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i64", ret_ty, - llargs[0], llargs[1]), - - (_, "return_address") => { - if !fcx.caller_expects_out_pointer { - tcx.sess.span_err(call_info.span, - "invalid use of `return_address` intrinsic: function \ - does not use out pointer"); - C_null(Type::i8p(ccx)) - } else { - PointerCast(bcx, llvm::get_param(fcx.llfn, 0), Type::i8p(ccx)) - } - } - - // This requires that atomic intrinsics follow a specific naming pattern: - // "atomic_[_]", and no ordering means SeqCst - (_, name) if name.starts_with("atomic_") => { - let split: Vec<&str> = name.split('_').collect(); - assert!(split.len() >= 2, "Atomic intrinsic not correct format"); - - let order = if split.len() == 2 { - llvm::SequentiallyConsistent - } else { - match split[2] { - "relaxed" => llvm::Monotonic, - "acq" => llvm::Acquire, - "rel" => llvm::Release, - "acqrel" => llvm::AcquireRelease, - _ => ccx.sess().fatal("unknown ordering in atomic intrinsic") - } - }; - - match split[1] { - "cxchg" => { - // See include/llvm/IR/Instructions.h for their implementation - // of this, I assume that it's good enough for us to use for - // now. - let strongest_failure_ordering = match order { - llvm::NotAtomic | llvm::Unordered => - ccx.sess().fatal("cmpxchg must be atomic"), - - llvm::Monotonic | llvm::Release => - llvm::Monotonic, - - llvm::Acquire | llvm::AcquireRelease => - llvm::Acquire, - - llvm::SequentiallyConsistent => - llvm::SequentiallyConsistent - }; - - let res = AtomicCmpXchg(bcx, llargs[0], llargs[1], - llargs[2], order, - strongest_failure_ordering); - if unsafe { llvm::LLVMVersionMinor() >= 5 } { - ExtractValue(bcx, res, 0) - } else { - res - } - } - - "load" => { - AtomicLoad(bcx, llargs[0], order) - } - "store" => { - AtomicStore(bcx, llargs[1], llargs[0], order); - C_nil(ccx) - } - - "fence" => { - AtomicFence(bcx, order); - C_nil(ccx) - } - - // These are all AtomicRMW ops - op => { - let atom_op = match op { - "xchg" => llvm::AtomicXchg, - "xadd" => llvm::AtomicAdd, - "xsub" => llvm::AtomicSub, - "and" => llvm::AtomicAnd, - "nand" => llvm::AtomicNand, - "or" => llvm::AtomicOr, - "xor" => llvm::AtomicXor, - "max" => llvm::AtomicMax, - "min" => llvm::AtomicMin, - "umax" => llvm::AtomicUMax, - "umin" => llvm::AtomicUMin, - _ => ccx.sess().fatal("unknown atomic operation") - }; - - AtomicRMW(bcx, atom_op, llargs[0], llargs[1], order) - } - } - - } - - (_, _) => ccx.sess().span_bug(foreign_item.span, "unknown intrinsic") - }; - - if val_ty(llval) != Type::void(ccx) && - machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { - store_ty(bcx, llval, llresult, ret_ty); - } - - // If we made a temporary stack slot, let's clean it up - match dest { - expr::Ignore => { - bcx = glue::drop_ty(bcx, llresult, ret_ty, Some(call_info)); - } - expr::SaveIn(_) => {} - } - - Result::new(bcx, llresult) -} - -fn copy_intrinsic(bcx: Block, allow_overlap: bool, volatile: bool, - tp_ty: ty::t, dst: ValueRef, src: ValueRef, count: ValueRef) -> ValueRef { - let ccx = bcx.ccx(); - let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); - let size = machine::llsize_of(ccx, lltp_ty); - let int_size = machine::llbitsize_of_real(ccx, ccx.int_type()); - let name = if allow_overlap { - if int_size == 32 { - "llvm.memmove.p0i8.p0i8.i32" - } else { - "llvm.memmove.p0i8.p0i8.i64" - } - } else { - if int_size == 32 { - "llvm.memcpy.p0i8.p0i8.i32" - } else { - "llvm.memcpy.p0i8.p0i8.i64" - } - }; - - let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); - let src_ptr = PointerCast(bcx, src, Type::i8p(ccx)); - let llfn = ccx.get_intrinsic(&name); - - Call(bcx, llfn, &[dst_ptr, src_ptr, Mul(bcx, size, count), align, - C_bool(ccx, volatile)], None) -} - -fn memset_intrinsic(bcx: Block, volatile: bool, tp_ty: ty::t, - dst: ValueRef, val: ValueRef, count: ValueRef) -> ValueRef { - let ccx = bcx.ccx(); - let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); - let size = machine::llsize_of(ccx, lltp_ty); - let name = if machine::llbitsize_of_real(ccx, ccx.int_type()) == 32 { - "llvm.memset.p0i8.i32" - } else { - "llvm.memset.p0i8.i64" - }; - - let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); - let llfn = ccx.get_intrinsic(&name); - - Call(bcx, llfn, &[dst_ptr, val, Mul(bcx, size, count), align, - C_bool(ccx, volatile)], None) -} - -fn count_zeros_intrinsic(bcx: Block, name: &'static str, val: ValueRef) -> ValueRef { - let y = C_bool(bcx.ccx(), false); - let llfn = bcx.ccx().get_intrinsic(&name); - Call(bcx, llfn, &[val, y], None) -} - -fn with_overflow_intrinsic(bcx: Block, name: &'static str, t: ty::t, - a: ValueRef, b: ValueRef) -> ValueRef { - let llfn = bcx.ccx().get_intrinsic(&name); - - // Convert `i1` to a `bool`, and write it to the out parameter - let val = Call(bcx, llfn, &[a, b], None); - let result = ExtractValue(bcx, val, 0); - let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); - let ret = C_undef(type_of::type_of(bcx.ccx(), t)); - let ret = InsertValue(bcx, ret, result, 0); - let ret = InsertValue(bcx, ret, overflow, 1); - - ret -} diff --git a/src/librustc/middle/trans/llrepr.rs b/src/librustc/middle/trans/llrepr.rs deleted file mode 100644 index 28e97e0f658..00000000000 --- a/src/librustc/middle/trans/llrepr.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use middle::trans::context::CrateContext; -use middle::trans::type_::Type; -use llvm::ValueRef; - -pub trait LlvmRepr for Sized? { - fn llrepr(&self, ccx: &CrateContext) -> String; -} - -impl LlvmRepr for [T] { - fn llrepr(&self, ccx: &CrateContext) -> String { - let reprs: Vec = self.iter().map(|t| t.llrepr(ccx)).collect(); - format!("[{}]", reprs.connect(",")) - } -} - -impl LlvmRepr for Type { - fn llrepr(&self, ccx: &CrateContext) -> String { - ccx.tn().type_to_string(*self) - } -} - -impl LlvmRepr for ValueRef { - fn llrepr(&self, ccx: &CrateContext) -> String { - ccx.tn().val_to_string(*self) - } -} - - diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs deleted file mode 100644 index f576dcc60c0..00000000000 --- a/src/librustc/middle/trans/machine.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Information concerning the machine representation of various types. - -#![allow(non_camel_case_types)] - -use llvm; -use llvm::{ValueRef}; -use llvm::False; -use middle::trans::common::*; - -use middle::trans::type_::Type; - -pub type llbits = u64; -pub type llsize = u64; -pub type llalign = u32; - -// ______________________________________________________________________ -// compute sizeof / alignof - -// Returns the number of bytes clobbered by a Store to this type. -pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> llsize { - unsafe { - return llvm::LLVMStoreSizeOfType(cx.td().lltd, ty.to_ref()); - } -} - -// Returns the number of bytes between successive elements of type T in an -// array of T. This is the "ABI" size. It includes any ABI-mandated padding. -pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> llsize { - unsafe { - return llvm::LLVMABISizeOfType(cx.td().lltd, ty.to_ref()); - } -} - -// Returns, as near as we can figure, the "real" size of a type. As in, the -// bits in this number of bytes actually carry data related to the datum -// with the type. Not junk, padding, accidentally-damaged words, or -// whatever. Rounds up to the nearest byte though, so if you have a 1-bit -// value, we return 1 here, not 0. Most of rustc works in bytes. Be warned -// that LLVM *does* distinguish between e.g. a 1-bit value and an 8-bit value -// at the codegen level! In general you should prefer `llbitsize_of_real` -// below. -pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> llsize { - unsafe { - let nbits = llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()); - if nbits & 7 != 0 { - // Not an even number of bytes, spills into "next" byte. - 1 + (nbits >> 3) - } else { - nbits >> 3 - } - } -} - -/// Returns the "real" size of the type in bits. -pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> llbits { - unsafe { - llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) - } -} - -/// Returns the size of the type as an LLVM constant integer value. -pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { - // Once upon a time, this called LLVMSizeOf, which does a - // getelementptr(1) on a null pointer and casts to an int, in - // order to obtain the type size as a value without requiring the - // target data layout. But we have the target data layout, so - // there's no need for that contrivance. The instruction - // selection DAG generator would flatten that GEP(1) node into a - // constant of the type's alloc size, so let's save it some work. - return C_uint(cx, llsize_of_alloc(cx, ty)); -} - -// Returns the "default" size of t (see above), or 1 if the size would -// be zero. This is important for things like vectors that expect -// space to be consumed. -pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { - if llbitsize_of_real(cx, ty) == 0 { - unsafe { llvm::LLVMConstInt(cx.int_type().to_ref(), 1, False) } - } else { - llsize_of(cx, ty) - } -} - -// Returns the preferred alignment of the given type for the current target. -// The preferred alignment may be larger than the alignment used when -// packing the type into structs. This will be used for things like -// allocations inside a stack frame, which LLVM has a free hand in. -pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> llalign { - unsafe { - return llvm::LLVMPreferredAlignmentOfType(cx.td().lltd, ty.to_ref()); - } -} - -// Returns the minimum alignment of a type required by the platform. -// This is the alignment that will be used for struct fields, arrays, -// and similar ABI-mandated things. -pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> llalign { - unsafe { - return llvm::LLVMABIAlignmentOfType(cx.td().lltd, ty.to_ref()); - } -} - -// Returns the "default" alignment of t, which is calculated by casting -// null to a record containing a single-bit followed by a t value, then -// doing gep(0,1) to get at the trailing (and presumably padded) t cell. -pub fn llalign_of(cx: &CrateContext, ty: Type) -> ValueRef { - unsafe { - return llvm::LLVMConstIntCast( - llvm::LLVMAlignOf(ty.to_ref()), cx.int_type().to_ref(), False); - } -} - -pub fn llelement_offset(cx: &CrateContext, struct_ty: Type, element: uint) -> u64 { - unsafe { - return llvm::LLVMOffsetOfElement(cx.td().lltd, struct_ty.to_ref(), - element as u32); - } -} diff --git a/src/librustc/middle/trans/macros.rs b/src/librustc/middle/trans/macros.rs deleted file mode 100644 index 313280cb7a8..00000000000 --- a/src/librustc/middle/trans/macros.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![macro_escape] - -macro_rules! unpack_datum( - ($bcx: ident, $inp: expr) => ( - { - let db = $inp; - $bcx = db.bcx; - db.datum - } - ) -) - -macro_rules! unpack_result( - ($bcx: ident, $inp: expr) => ( - { - let db = $inp; - $bcx = db.bcx; - db.val - } - ) -) diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs deleted file mode 100644 index 3cd16d3b018..00000000000 --- a/src/librustc/middle/trans/meth.rs +++ /dev/null @@ -1,778 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use back::abi; -use llvm; -use llvm::ValueRef; -use metadata::csearch; -use middle::subst::{Subst,Substs}; -use middle::subst::VecPerParamSpace; -use middle::subst; -use middle::traits; -use middle::trans::base::*; -use middle::trans::build::*; -use middle::trans::callee::*; -use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::common::*; -use middle::trans::datum::*; -use middle::trans::expr::{SaveIn, Ignore}; -use middle::trans::expr; -use middle::trans::glue; -use middle::trans::machine; -use middle::trans::type_::Type; -use middle::trans::type_of::*; -use middle::ty; -use middle::typeck; -use middle::typeck::MethodCall; -use util::ppaux::Repr; - -use std::c_str::ToCStr; -use std::rc::Rc; -use syntax::abi::{Rust, RustCall}; -use syntax::parse::token; -use syntax::{ast, ast_map, attr, visit}; -use syntax::ast_util::PostExpansionMethod; -use syntax::codemap::DUMMY_SP; - -// drop_glue pointer, size, align. -static VTABLE_OFFSET: uint = 3; - -/** -The main "translation" pass for methods. Generates code -for non-monomorphized methods only. Other methods will -be generated once they are invoked with specific type parameters, -see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`. -*/ -pub fn trans_impl(ccx: &CrateContext, - name: ast::Ident, - impl_items: &[ast::ImplItem], - generics: &ast::Generics, - id: ast::NodeId) { - let _icx = push_ctxt("meth::trans_impl"); - let tcx = ccx.tcx(); - - debug!("trans_impl(name={}, id={})", name.repr(tcx), id); - - // Both here and below with generic methods, be sure to recurse and look for - // items that we need to translate. - if !generics.ty_params.is_empty() { - let mut v = TransItemVisitor{ ccx: ccx }; - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - visit::walk_method_helper(&mut v, &**method); - } - ast::TypeImplItem(_) => {} - } - } - return; - } - for impl_item in impl_items.iter() { - match *impl_item { - ast::MethodImplItem(ref method) => { - if method.pe_generics().ty_params.len() == 0u { - let trans_everywhere = attr::requests_inline(method.attrs.as_slice()); - for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) { - let llfn = get_item_val(ccx, method.id); - trans_fn(ccx, - method.pe_fn_decl(), - method.pe_body(), - llfn, - ¶m_substs::empty(), - method.id, - &[]); - update_linkage(ccx, - llfn, - Some(method.id), - if is_origin { OriginalTranslation } else { InlinedCopy }); - } - } - let mut v = TransItemVisitor { - ccx: ccx, - }; - visit::walk_method_helper(&mut v, &**method); - } - ast::TypeImplItem(_) => {} - } - } -} - -pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - method_call: MethodCall, - self_expr: Option<&ast::Expr>, - arg_cleanup_scope: cleanup::ScopeId) - -> Callee<'blk, 'tcx> { - let _icx = push_ctxt("meth::trans_method_callee"); - - let (origin, method_ty) = - bcx.tcx().method_map - .borrow() - .get(&method_call) - .map(|method| (method.origin.clone(), method.ty)) - .unwrap(); - - match origin { - typeck::MethodStatic(did) | - typeck::MethodStaticUnboxedClosure(did) => { - Callee { - bcx: bcx, - data: Fn(callee::trans_fn_ref(bcx, - did, - MethodCall(method_call))), - } - } - - typeck::MethodTypeParam(typeck::MethodParam { - ref trait_ref, - method_num - }) => { - let trait_ref = - Rc::new(trait_ref.subst(bcx.tcx(), - &bcx.fcx.param_substs.substs)); - let span = bcx.tcx().map.span(method_call.expr_id); - let origin = fulfill_obligation(bcx.ccx(), - span, - (*trait_ref).clone()); - debug!("origin = {}", origin.repr(bcx.tcx())); - trans_monomorphized_callee(bcx, method_call, trait_ref.def_id, - method_num, origin) - } - - typeck::MethodTraitObject(ref mt) => { - let self_expr = match self_expr { - Some(self_expr) => self_expr, - None => { - bcx.sess().span_bug(bcx.tcx().map.span(method_call.expr_id), - "self expr wasn't provided for trait object \ - callee (trying to call overloaded op?)") - } - }; - trans_trait_callee(bcx, - monomorphize_type(bcx, method_ty), - mt.real_index, - self_expr, - arg_cleanup_scope) - } - } -} - -pub fn trans_static_method_callee(bcx: Block, - method_id: ast::DefId, - trait_id: ast::DefId, - expr_id: ast::NodeId) - -> ValueRef -{ - let _icx = push_ctxt("meth::trans_static_method_callee"); - let ccx = bcx.ccx(); - - debug!("trans_static_method_callee(method_id={}, trait_id={}, \ - expr_id={})", - method_id, - ty::item_path_str(bcx.tcx(), trait_id), - expr_id); - - let mname = if method_id.krate == ast::LOCAL_CRATE { - match bcx.tcx().map.get(method_id.node) { - ast_map::NodeTraitItem(method) => { - let ident = match *method { - ast::RequiredMethod(ref m) => m.ident, - ast::ProvidedMethod(ref m) => m.pe_ident(), - ast::TypeTraitItem(_) => { - bcx.tcx().sess.bug("trans_static_method_callee() on \ - an associated type?!") - } - }; - ident.name - } - _ => panic!("callee is not a trait method") - } - } else { - csearch::get_item_path(bcx.tcx(), method_id).last().unwrap().name() - }; - debug!("trans_static_method_callee: method_id={}, expr_id={}, \ - name={}", method_id, expr_id, token::get_name(mname)); - - // Find the substitutions for the fn itself. This includes - // type parameters that belong to the trait but also some that - // belong to the method: - let rcvr_substs = node_id_substs(bcx, ExprId(expr_id)); - let subst::SeparateVecsPerParamSpace { - types: rcvr_type, - selfs: rcvr_self, - assocs: rcvr_assoc, - fns: rcvr_method - } = rcvr_substs.types.split(); - - // Lookup the precise impl being called. To do that, we need to - // create a trait reference identifying the self type and other - // input type parameters. To create that trait reference, we have - // to pick apart the type parameters to identify just those that - // pertain to the trait. This is easiest to explain by example: - // - // trait Convert { - // fn from(n: U) -> Option; - // } - // ... - // let f = as Convert>::from::(...) - // - // Here, in this call, which I've written with explicit UFCS - // notation, the set of type parameters will be: - // - // rcvr_type: [] <-- nothing declared on the trait itself - // rcvr_self: [Vec] <-- the self type - // rcvr_method: [String] <-- method type parameter - // - // So we create a trait reference using the first two, - // basically corresponding to ` as Convert>`. - // The remaining type parameters (`rcvr_method`) will be used below. - let trait_substs = - Substs::erased(VecPerParamSpace::new(rcvr_type, - rcvr_self, - rcvr_assoc, - Vec::new())); - debug!("trait_substs={}", trait_substs.repr(bcx.tcx())); - let trait_ref = Rc::new(ty::TraitRef { def_id: trait_id, - substs: trait_substs }); - let vtbl = fulfill_obligation(bcx.ccx(), - DUMMY_SP, - trait_ref); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - traits::VtableImpl(traits::VtableImplData { - impl_def_id: impl_did, - substs: impl_substs, - nested: _ }) => - { - assert!(impl_substs.types.all(|t| !ty::type_needs_infer(*t))); - - // Create the substitutions that are in scope. This combines - // the type parameters from the impl with those declared earlier. - // To see what I mean, consider a possible impl: - // - // impl Convert for Vec { - // fn from(n: U) { ... } - // } - // - // Recall that we matched ` as Convert>`. Trait - // resolution will have given us a substitution - // containing `impl_substs=[[T=int],[],[]]` (the type - // parameters defined on the impl). We combine - // that with the `rcvr_method` from before, which tells us - // the type parameters from the *method*, to yield - // `callee_substs=[[T=int],[],[U=String]]`. - let subst::SeparateVecsPerParamSpace { - types: impl_type, - selfs: impl_self, - assocs: impl_assoc, - fns: _ - } = impl_substs.types.split(); - let callee_substs = - Substs::erased(VecPerParamSpace::new(impl_type, - impl_self, - impl_assoc, - rcvr_method)); - - let mth_id = method_with_name(ccx, impl_did, mname); - let llfn = trans_fn_ref_with_substs(bcx, mth_id, ExprId(expr_id), - callee_substs); - - let callee_ty = node_id_type(bcx, expr_id); - let llty = type_of_fn_from_ty(ccx, callee_ty).ptr_to(); - PointerCast(bcx, llfn, llty) - } - _ => { - bcx.tcx().sess.bug( - format!("static call to invalid vtable: {}", - vtbl.repr(bcx.tcx())).as_slice()); - } - } -} - -fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name) - -> ast::DefId { - match ccx.impl_method_cache().borrow().get(&(impl_id, name)).cloned() { - Some(m) => return m, - None => {} - } - - let impl_items = ccx.tcx().impl_items.borrow(); - let impl_items = - impl_items.get(&impl_id) - .expect("could not find impl while translating"); - let meth_did = impl_items.iter() - .find(|&did| { - ty::impl_or_trait_item(ccx.tcx(), did.def_id()).name() == name - }).expect("could not find method while \ - translating"); - - ccx.impl_method_cache().borrow_mut().insert((impl_id, name), - meth_did.def_id()); - meth_did.def_id() -} - -fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - method_call: MethodCall, - trait_id: ast::DefId, - n_method: uint, - vtable: traits::Vtable<()>) - -> Callee<'blk, 'tcx> { - let _icx = push_ctxt("meth::trans_monomorphized_callee"); - match vtable { - traits::VtableImpl(vtable_impl) => { - let ccx = bcx.ccx(); - let impl_did = vtable_impl.impl_def_id; - let mname = match ty::trait_item(ccx.tcx(), trait_id, n_method) { - ty::MethodTraitItem(method) => method.name, - ty::TypeTraitItem(_) => { - bcx.tcx().sess.bug("can't monomorphize an associated \ - type") - } - }; - let mth_id = method_with_name(bcx.ccx(), impl_did, mname); - - // create a concatenated set of substitutions which includes - // those from the impl and those from the method: - let callee_substs = - combine_impl_and_methods_tps( - bcx, MethodCall(method_call), vtable_impl.substs); - - // translate the function - let llfn = trans_fn_ref_with_substs(bcx, - mth_id, - MethodCall(method_call), - callee_substs); - - Callee { bcx: bcx, data: Fn(llfn) } - } - traits::VtableUnboxedClosure(closure_def_id, substs) => { - // The substitutions should have no type parameters remaining - // after passing through fulfill_obligation - let llfn = trans_fn_ref_with_substs(bcx, - closure_def_id, - MethodCall(method_call), - substs); - - Callee { - bcx: bcx, - data: Fn(llfn), - } - } - _ => { - bcx.tcx().sess.bug( - "vtable_param left in monomorphized function's vtable substs"); - } - } -} - -fn combine_impl_and_methods_tps(bcx: Block, - node: ExprOrMethodCall, - rcvr_substs: subst::Substs) - -> subst::Substs -{ - /*! - * Creates a concatenated set of substitutions which includes - * those from the impl and those from the method. This are - * some subtle complications here. Statically, we have a list - * of type parameters like `[T0, T1, T2, M1, M2, M3]` where - * `Tn` are type parameters that appear on the receiver. For - * example, if the receiver is a method parameter `A` with a - * bound like `trait` then `Tn` would be `[B,C,D]`. - * - * The weird part is that the type `A` might now be bound to - * any other type, such as `foo`. In that case, the vector - * we want is: `[X, M1, M2, M3]`. Therefore, what we do now is - * to slice off the method type parameters and append them to - * the type parameters from the type that the receiver is - * mapped to. - */ - - let ccx = bcx.ccx(); - - let node_substs = node_id_substs(bcx, node); - - debug!("rcvr_substs={}", rcvr_substs.repr(ccx.tcx())); - debug!("node_substs={}", node_substs.repr(ccx.tcx())); - - // Break apart the type parameters from the node and type - // parameters from the receiver. - let node_method = node_substs.types.split().fns; - let subst::SeparateVecsPerParamSpace { - types: rcvr_type, - selfs: rcvr_self, - assocs: rcvr_assoc, - fns: rcvr_method - } = rcvr_substs.types.clone().split(); - assert!(rcvr_method.is_empty()); - subst::Substs { - regions: subst::ErasedRegions, - types: subst::VecPerParamSpace::new(rcvr_type, rcvr_self, rcvr_assoc, node_method) - } -} - -fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - method_ty: ty::t, - n_method: uint, - self_expr: &ast::Expr, - arg_cleanup_scope: cleanup::ScopeId) - -> Callee<'blk, 'tcx> { - /*! - * Create a method callee where the method is coming from a trait - * object (e.g., Box type). In this case, we must pull the fn - * pointer out of the vtable that is packaged up with the object. - * Objects are represented as a pair, so we first evaluate the self - * expression and then extract the self data and vtable out of the - * pair. - */ - - let _icx = push_ctxt("meth::trans_trait_callee"); - let mut bcx = bcx; - - // Translate self_datum and take ownership of the value by - // converting to an rvalue. - let self_datum = unpack_datum!( - bcx, expr::trans(bcx, self_expr)); - - let llval = if ty::type_needs_drop(bcx.tcx(), self_datum.ty) { - let self_datum = unpack_datum!( - bcx, self_datum.to_rvalue_datum(bcx, "trait_callee")); - - // Convert to by-ref since `trans_trait_callee_from_llval` wants it - // that way. - let self_datum = unpack_datum!( - bcx, self_datum.to_ref_datum(bcx)); - - // Arrange cleanup in case something should go wrong before the - // actual call occurs. - self_datum.add_clean(bcx.fcx, arg_cleanup_scope) - } else { - // We don't have to do anything about cleanups for &Trait and &mut Trait. - assert!(self_datum.kind.is_by_ref()); - self_datum.val - }; - - trans_trait_callee_from_llval(bcx, method_ty, n_method, llval) -} - -pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - callee_ty: ty::t, - n_method: uint, - llpair: ValueRef) - -> Callee<'blk, 'tcx> { - /*! - * Same as `trans_trait_callee()` above, except that it is given - * a by-ref pointer to the object pair. - */ - - let _icx = push_ctxt("meth::trans_trait_callee"); - let ccx = bcx.ccx(); - - // Load the data pointer from the object. - debug!("(translating trait callee) loading second index from pair"); - let llboxptr = GEPi(bcx, llpair, &[0u, abi::trt_field_box]); - let llbox = Load(bcx, llboxptr); - let llself = PointerCast(bcx, llbox, Type::i8p(ccx)); - - // Load the function from the vtable and cast it to the expected type. - debug!("(translating trait callee) loading method"); - // Replace the self type (&Self or Box) with an opaque pointer. - let llcallee_ty = match ty::get(callee_ty).sty { - ty::ty_bare_fn(ref f) if f.abi == Rust || f.abi == RustCall => { - type_of_rust_fn(ccx, - Some(Type::i8p(ccx)), - f.sig.inputs.slice_from(1), - f.sig.output, - f.abi) - } - _ => { - ccx.sess().bug("meth::trans_trait_callee given non-bare-rust-fn"); - } - }; - let llvtable = Load(bcx, - PointerCast(bcx, - GEPi(bcx, llpair, - &[0u, abi::trt_field_vtable]), - Type::vtable(ccx).ptr_to().ptr_to())); - let mptr = Load(bcx, GEPi(bcx, llvtable, &[0u, n_method + VTABLE_OFFSET])); - let mptr = PointerCast(bcx, mptr, llcallee_ty.ptr_to()); - - return Callee { - bcx: bcx, - data: TraitItem(MethodData { - llfn: mptr, - llself: llself, - }) - }; -} - -/// Creates a returns a dynamic vtable for the given type and vtable origin. -/// This is used only for objects. -/// -/// The `trait_ref` encodes the erased self type. Hence if we are -/// making an object `Foo` from a value of type `Foo`, then -/// `trait_ref` would map `T:Trait`, but `box_ty` would be -/// `Foo`. This `box_ty` is primarily used to encode the destructor. -/// This will hopefully change now that DST is underway. -pub fn get_vtable(bcx: Block, - box_ty: ty::t, - trait_ref: Rc) - -> ValueRef -{ - debug!("get_vtable(box_ty={}, trait_ref={})", - box_ty.repr(bcx.tcx()), - trait_ref.repr(bcx.tcx())); - - let tcx = bcx.tcx(); - let ccx = bcx.ccx(); - let _icx = push_ctxt("meth::get_vtable"); - - // Check the cache. - let cache_key = (box_ty, trait_ref.clone()); - match ccx.vtables().borrow().get(&cache_key) { - Some(&val) => { return val } - None => { } - } - - // Not in the cache. Build it. - let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| { - let vtable = fulfill_obligation(bcx.ccx(), - DUMMY_SP, - trait_ref.clone()); - match vtable { - traits::VtableBuiltin(_) => { - Vec::new().into_iter() - } - traits::VtableImpl( - traits::VtableImplData { - impl_def_id: id, - substs, - nested: _ }) => { - emit_vtable_methods(bcx, id, substs).into_iter() - } - traits::VtableUnboxedClosure(closure_def_id, substs) => { - // Look up closure type - let self_ty = ty::node_id_to_type(bcx.tcx(), closure_def_id.node); - // Apply substitutions from closure param environment. - // The substitutions should have no type parameters - // remaining after passing through fulfill_obligation - let self_ty = self_ty.subst(bcx.tcx(), &substs); - - let mut llfn = trans_fn_ref_with_substs( - bcx, - closure_def_id, - ExprId(0), - substs.clone()); - - { - let unboxed_closures = bcx.tcx() - .unboxed_closures - .borrow(); - let closure_info = - unboxed_closures.get(&closure_def_id) - .expect("get_vtable(): didn't find \ - unboxed closure"); - if closure_info.kind == ty::FnOnceUnboxedClosureKind { - // Untuple the arguments and create an unboxing shim. - let (new_inputs, new_output) = match ty::get(self_ty).sty { - ty::ty_unboxed_closure(_, _, ref substs) => { - let mut new_inputs = vec![self_ty.clone()]; - match ty::get(closure_info.closure_type - .sig - .inputs[0]).sty { - ty::ty_tup(ref elements) => { - for element in elements.iter() { - new_inputs.push(element.subst(bcx.tcx(), substs)); - } - } - _ => { - bcx.tcx().sess.bug("get_vtable(): closure \ - type wasn't a tuple") - } - } - (new_inputs, - closure_info.closure_type.sig.output.subst(bcx.tcx(), substs)) - }, - _ => bcx.tcx().sess.bug("get_vtable(): def wasn't an unboxed closure") - }; - - let closure_type = ty::BareFnTy { - fn_style: closure_info.closure_type.fn_style, - abi: Rust, - sig: ty::FnSig { - binder_id: closure_info.closure_type - .sig - .binder_id, - inputs: new_inputs, - output: new_output, - variadic: false, - }, - }; - debug!("get_vtable(): closure type is {}", - closure_type.repr(bcx.tcx())); - llfn = trans_unboxing_shim(bcx, - llfn, - &closure_type, - closure_def_id, - &substs); - } - } - - (vec!(llfn)).into_iter() - } - traits::VtableParam(..) => { - bcx.sess().bug( - format!("resolved vtable for {} to bad vtable {} in trans", - trait_ref.repr(bcx.tcx()), - vtable.repr(bcx.tcx())).as_slice()); - } - } - }); - - let size_ty = sizing_type_of(ccx, trait_ref.self_ty()); - let size = machine::llsize_of_alloc(ccx, size_ty); - let ll_size = C_uint(ccx, size); - let align = align_of(ccx, trait_ref.self_ty()); - let ll_align = C_uint(ccx, align); - - // Generate a destructor for the vtable. - let drop_glue = glue::get_drop_glue(ccx, box_ty); - let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods); - - ccx.vtables().borrow_mut().insert(cache_key, vtable); - vtable -} - -/// Helper function to declare and initialize the vtable. -pub fn make_vtable>(ccx: &CrateContext, - drop_glue: ValueRef, - size: ValueRef, - align: ValueRef, - ptrs: I) - -> ValueRef { - let _icx = push_ctxt("meth::make_vtable"); - - let head = vec![drop_glue, size, align]; - let components: Vec<_> = head.into_iter().chain(ptrs).collect(); - - unsafe { - let tbl = C_struct(ccx, components.as_slice(), false); - let sym = token::gensym("vtable"); - let vt_gvar = format!("vtable{}", sym.uint()).with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), buf) - }); - llvm::LLVMSetInitializer(vt_gvar, tbl); - llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True); - llvm::SetLinkage(vt_gvar, llvm::InternalLinkage); - vt_gvar - } -} - -fn emit_vtable_methods(bcx: Block, - impl_id: ast::DefId, - substs: subst::Substs) - -> Vec { - let ccx = bcx.ccx(); - let tcx = ccx.tcx(); - - let trt_id = match ty::impl_trait_ref(tcx, impl_id) { - Some(t_id) => t_id.def_id, - None => ccx.sess().bug("make_impl_vtable: don't know how to \ - make a vtable for a type impl!") - }; - - ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trt_id); - - let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id); - trait_item_def_ids.iter().flat_map(|method_def_id| { - let method_def_id = method_def_id.def_id(); - let name = ty::impl_or_trait_item(tcx, method_def_id).name(); - // The substitutions we have are on the impl, so we grab - // the method type from the impl to substitute into. - let m_id = method_with_name(ccx, impl_id, name); - let ti = ty::impl_or_trait_item(tcx, m_id); - match ti { - ty::MethodTraitItem(m) => { - debug!("(making impl vtable) emitting method {} at subst {}", - m.repr(tcx), - substs.repr(tcx)); - if m.generics.has_type_params(subst::FnSpace) || - ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) { - debug!("(making impl vtable) method has self or type \ - params: {}", - token::get_name(name)); - Some(C_null(Type::nil(ccx).ptr_to())).into_iter() - } else { - let mut fn_ref = trans_fn_ref_with_substs( - bcx, - m_id, - ExprId(0), - substs.clone()); - if m.explicit_self == ty::ByValueExplicitSelfCategory { - fn_ref = trans_unboxing_shim(bcx, - fn_ref, - &m.fty, - m_id, - &substs); - } - Some(fn_ref).into_iter() - } - } - ty::TypeTraitItem(_) => { - None.into_iter() - } - } - }).collect() -} - -pub fn trans_trait_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - datum: Datum, - id: ast::NodeId, - trait_ref: Rc, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - /*! - * Generates the code to convert from a pointer (`Box`, `&T`, etc) - * into an object (`Box`, `&Trait`, etc). This means creating a - * pair where the first word is the vtable and the second word is - * the pointer. - */ - - let mut bcx = bcx; - let _icx = push_ctxt("meth::trans_trait_cast"); - - let lldest = match dest { - Ignore => { - return datum.clean(bcx, "trait_trait_cast", id); - } - SaveIn(dest) => dest - }; - - debug!("trans_trait_cast: trait_ref={}", - trait_ref.repr(bcx.tcx())); - - let datum_ty = datum.ty; - let llbox_ty = type_of(bcx.ccx(), datum_ty); - - // Store the pointer into the first half of pair. - let llboxdest = GEPi(bcx, lldest, &[0u, abi::trt_field_box]); - let llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to()); - bcx = datum.store_to(bcx, llboxdest); - - // Store the vtable into the second half of pair. - let vtable = get_vtable(bcx, datum_ty, trait_ref); - let llvtabledest = GEPi(bcx, lldest, &[0u, abi::trt_field_vtable]); - let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to()); - Store(bcx, vtable, llvtabledest); - - bcx -} diff --git a/src/librustc/middle/trans/mod.rs b/src/librustc/middle/trans/mod.rs deleted file mode 100644 index fe7697447ac..00000000000 --- a/src/librustc/middle/trans/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub mod doc; -pub mod macros; -pub mod inline; -pub mod monomorphize; -pub mod controlflow; -pub mod glue; -pub mod datum; -pub mod callee; -pub mod expr; -pub mod common; -pub mod context; -pub mod consts; -pub mod type_of; -pub mod build; -pub mod builder; -pub mod base; -pub mod _match; -pub mod closure; -pub mod tvec; -pub mod meth; -pub mod cabi; -pub mod cabi_x86; -pub mod cabi_x86_64; -pub mod cabi_x86_win64; -pub mod cabi_arm; -pub mod cabi_mips; -pub mod foreign; -pub mod intrinsic; -pub mod debuginfo; -pub mod machine; -pub mod adt; -pub mod asm; -pub mod type_; -pub mod value; -pub mod basic_block; -pub mod llrepr; -pub mod cleanup; diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs deleted file mode 100644 index 1e8d54b249f..00000000000 --- a/src/librustc/middle/trans/monomorphize.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use back::link::exported_name; -use driver::session; -use llvm::ValueRef; -use llvm; -use middle::subst; -use middle::subst::Subst; -use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint}; -use middle::trans::base::{trans_enum_variant, push_ctxt, get_item_val}; -use middle::trans::base::{trans_fn, decl_internal_rust_fn}; -use middle::trans::base; -use middle::trans::common::*; -use middle::trans::foreign; -use middle::ty; -use util::ppaux::Repr; - -use syntax::abi; -use syntax::ast; -use syntax::ast_map; -use syntax::ast_util::{local_def, PostExpansionMethod}; -use syntax::attr; -use std::hash::{sip, Hash}; - -pub fn monomorphic_fn(ccx: &CrateContext, - fn_id: ast::DefId, - real_substs: &subst::Substs, - ref_id: Option) - -> (ValueRef, bool) { - debug!("monomorphic_fn(\ - fn_id={}, \ - real_substs={}, \ - ref_id={})", - fn_id.repr(ccx.tcx()), - real_substs.repr(ccx.tcx()), - ref_id); - - assert!(real_substs.types.all(|t| { - !ty::type_needs_infer(*t) && !ty::type_has_params(*t) - })); - - let _icx = push_ctxt("monomorphic_fn"); - - let hash_id = MonoId { - def: fn_id, - params: real_substs.types.clone() - }; - - match ccx.monomorphized().borrow().get(&hash_id) { - Some(&val) => { - debug!("leaving monomorphic fn {}", - ty::item_path_str(ccx.tcx(), fn_id)); - return (val, false); - } - None => () - } - - let psubsts = param_substs { - substs: (*real_substs).clone(), - }; - - debug!("monomorphic_fn(\ - fn_id={}, \ - psubsts={}, \ - hash_id={})", - fn_id.repr(ccx.tcx()), - psubsts.repr(ccx.tcx()), - hash_id); - - let tpt = ty::lookup_item_type(ccx.tcx(), fn_id); - let llitem_ty = tpt.ty; - - let map_node = session::expect( - ccx.sess(), - ccx.tcx().map.find(fn_id.node), - || { - format!("while monomorphizing {}, couldn't find it in \ - the item map (may have attempted to monomorphize \ - an item defined in a different crate?)", - fn_id) - }); - - match map_node { - ast_map::NodeForeignItem(_) => { - if ccx.tcx().map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic { - // Foreign externs don't have to be monomorphized. - return (get_item_val(ccx, fn_id.node), true); - } - } - _ => {} - } - - debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx())); - let mono_ty = llitem_ty.subst(ccx.tcx(), real_substs); - - ccx.stats().n_monos.set(ccx.stats().n_monos.get() + 1); - - let depth; - { - let mut monomorphizing = ccx.monomorphizing().borrow_mut(); - depth = match monomorphizing.get(&fn_id) { - Some(&d) => d, None => 0 - }; - - // Random cut-off -- code that needs to instantiate the same function - // recursively more than thirty times can probably safely be assumed - // to be causing an infinite expansion. - if depth > ccx.sess().recursion_limit.get() { - ccx.sess().span_fatal(ccx.tcx().map.span(fn_id.node), - "reached the recursion limit during monomorphization"); - } - - monomorphizing.insert(fn_id, depth + 1); - } - - let hash; - let s = { - let mut state = sip::SipState::new(); - hash_id.hash(&mut state); - mono_ty.hash(&mut state); - - hash = format!("h{}", state.result()); - ccx.tcx().map.with_path(fn_id.node, |path| { - exported_name(path, hash.as_slice()) - }) - }; - - debug!("monomorphize_fn mangled to {}", s); - - // This shouldn't need to option dance. - let mut hash_id = Some(hash_id); - let mk_lldecl = |abi: abi::Abi| { - let lldecl = if abi != abi::Rust { - foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, s.as_slice()) - } else { - decl_internal_rust_fn(ccx, mono_ty, s.as_slice()) - }; - - ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); - lldecl - }; - let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| { - base::update_linkage(ccx, lldecl, None, base::OriginalTranslation); - set_llvm_fn_attrs(ccx, attrs, lldecl); - - let is_first = !ccx.available_monomorphizations().borrow().contains(&s); - if is_first { - ccx.available_monomorphizations().borrow_mut().insert(s.clone()); - } - - let trans_everywhere = attr::requests_inline(attrs); - if trans_everywhere && !is_first { - llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage); - } - - // If `true`, then `lldecl` should be given a function body. - // Otherwise, it should be left as a declaration of an external - // function, with no definition in the current compilation unit. - trans_everywhere || is_first - }; - - let lldecl = match map_node { - ast_map::NodeItem(i) => { - match *i { - ast::Item { - node: ast::ItemFn(ref decl, _, abi, _, ref body), - .. - } => { - let d = mk_lldecl(abi); - let needs_body = setup_lldecl(d, i.attrs.as_slice()); - if needs_body { - if abi != abi::Rust { - foreign::trans_rust_fn_with_foreign_abi( - ccx, &**decl, &**body, &[], d, &psubsts, fn_id.node, - Some(hash.as_slice())); - } else { - trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, &[]); - } - } - - d - } - _ => { - ccx.sess().bug("Can't monomorphize this kind of item") - } - } - } - ast_map::NodeVariant(v) => { - let parent = ccx.tcx().map.get_parent(fn_id.node); - let tvs = ty::enum_variants(ccx.tcx(), local_def(parent)); - let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap(); - let d = mk_lldecl(abi::Rust); - set_inline_hint(d); - match v.node.kind { - ast::TupleVariantKind(ref args) => { - trans_enum_variant(ccx, - parent, - &*v, - args.as_slice(), - this_tv.disr_val, - &psubsts, - d); - } - ast::StructVariantKind(_) => - ccx.sess().bug("can't monomorphize struct variants"), - } - d - } - ast_map::NodeImplItem(ii) => { - match *ii { - ast::MethodImplItem(ref mth) => { - let d = mk_lldecl(abi::Rust); - let needs_body = setup_lldecl(d, mth.attrs.as_slice()); - if needs_body { - trans_fn(ccx, - mth.pe_fn_decl(), - mth.pe_body(), - d, - &psubsts, - mth.id, - &[]); - } - d - } - ast::TypeImplItem(_) => { - ccx.sess().bug("can't monomorphize an associated type") - } - } - } - ast_map::NodeTraitItem(method) => { - match *method { - ast::ProvidedMethod(ref mth) => { - let d = mk_lldecl(abi::Rust); - let needs_body = setup_lldecl(d, mth.attrs.as_slice()); - if needs_body { - trans_fn(ccx, mth.pe_fn_decl(), mth.pe_body(), d, - &psubsts, mth.id, &[]); - } - d - } - _ => { - ccx.sess().bug(format!("can't monomorphize a {}", - map_node).as_slice()) - } - } - } - ast_map::NodeStructCtor(struct_def) => { - let d = mk_lldecl(abi::Rust); - set_inline_hint(d); - base::trans_tuple_struct(ccx, - struct_def.fields.as_slice(), - struct_def.ctor_id.expect("ast-mapped tuple struct \ - didn't have a ctor id"), - &psubsts, - d); - d - } - - // Ugh -- but this ensures any new variants won't be forgotten - ast_map::NodeForeignItem(..) | - ast_map::NodeLifetime(..) | - ast_map::NodeExpr(..) | - ast_map::NodeStmt(..) | - ast_map::NodeArg(..) | - ast_map::NodeBlock(..) | - ast_map::NodePat(..) | - ast_map::NodeLocal(..) => { - ccx.sess().bug(format!("can't monomorphize a {}", - map_node).as_slice()) - } - }; - - ccx.monomorphizing().borrow_mut().insert(fn_id, depth); - - debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id)); - (lldecl, true) -} - -#[deriving(PartialEq, Eq, Hash, Show)] -pub struct MonoId { - pub def: ast::DefId, - pub params: subst::VecPerParamSpace -} diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs deleted file mode 100644 index cf1aeca7916..00000000000 --- a/src/librustc/middle/trans/tvec.rs +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_camel_case_types)] - -use back::abi; -use llvm; -use llvm::{ValueRef}; -use middle::trans::base::*; -use middle::trans::base; -use middle::trans::build::*; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; -use middle::trans::common::*; -use middle::trans::datum::*; -use middle::trans::expr::{Dest, Ignore, SaveIn}; -use middle::trans::expr; -use middle::trans::glue; -use middle::trans::machine; -use middle::trans::machine::{nonzero_llsize_of, llsize_of_alloc}; -use middle::trans::type_::Type; -use middle::trans::type_of; -use middle::ty; -use util::ppaux::ty_to_string; - -use syntax::ast; -use syntax::parse::token::InternedString; - -fn get_len(bcx: Block, vptr: ValueRef) -> ValueRef { - let _icx = push_ctxt("tvec::get_lenl"); - Load(bcx, expr::get_len(bcx, vptr)) -} - -fn get_dataptr(bcx: Block, vptr: ValueRef) -> ValueRef { - let _icx = push_ctxt("tvec::get_dataptr"); - Load(bcx, expr::get_dataptr(bcx, vptr)) -} - -pub fn pointer_add_byte(bcx: Block, ptr: ValueRef, bytes: ValueRef) -> ValueRef { - let _icx = push_ctxt("tvec::pointer_add_byte"); - let old_ty = val_ty(ptr); - let bptr = PointerCast(bcx, ptr, Type::i8p(bcx.ccx())); - return PointerCast(bcx, InBoundsGEP(bcx, bptr, &[bytes]), old_ty); -} - -pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - vptr: ValueRef, - unit_ty: ty::t, - should_deallocate: bool) - -> Block<'blk, 'tcx> { - let not_null = IsNotNull(bcx, vptr); - with_cond(bcx, not_null, |bcx| { - let ccx = bcx.ccx(); - let tcx = bcx.tcx(); - let _icx = push_ctxt("tvec::make_drop_glue_unboxed"); - - let dataptr = get_dataptr(bcx, vptr); - let bcx = if ty::type_needs_drop(tcx, unit_ty) { - let len = get_len(bcx, vptr); - iter_vec_raw(bcx, dataptr, unit_ty, len, |bb, vv, tt| glue::drop_ty(bb, vv, tt, None)) - } else { - bcx - }; - - if should_deallocate { - let llty = type_of::type_of(ccx, unit_ty); - let unit_size = llsize_of_alloc(ccx, llty); - if unit_size != 0 { - let len = get_len(bcx, vptr); - let not_empty = ICmp(bcx, llvm::IntNE, len, C_uint(ccx, 0u)); - with_cond(bcx, not_empty, |bcx| { - let llalign = C_uint(ccx, machine::llalign_of_min(ccx, llty)); - let size = Mul(bcx, C_uint(ccx, unit_size), len); - glue::trans_exchange_free_dyn(bcx, dataptr, size, llalign) - }) - } else { - bcx - } - } else { - bcx - } - }) -} - -pub struct VecTypes { - pub unit_ty: ty::t, - pub llunit_ty: Type, - pub llunit_size: ValueRef, - pub llunit_alloc_size: u64 -} - -impl VecTypes { - pub fn to_string(&self, ccx: &CrateContext) -> String { - format!("VecTypes {{unit_ty={}, llunit_ty={}, \ - llunit_size={}, llunit_alloc_size={}}}", - ty_to_string(ccx.tcx(), self.unit_ty), - ccx.tn().type_to_string(self.llunit_ty), - ccx.tn().val_to_string(self.llunit_size), - self.llunit_alloc_size) - } -} - -pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - expr: &ast::Expr, - dest: expr::Dest) - -> Block<'blk, 'tcx> { - //! - // - // [...] allocates a fixed-size array and moves it around "by value". - // In this case, it means that the caller has already given us a location - // to store the array of the suitable size, so all we have to do is - // generate the content. - - debug!("trans_fixed_vstore(expr={}, dest={})", - bcx.expr_to_string(expr), dest.to_string(bcx.ccx())); - - let vt = vec_types_from_expr(bcx, expr); - - return match dest { - Ignore => write_content(bcx, &vt, expr, expr, dest), - SaveIn(lldest) => { - // lldest will have type *[T x N], but we want the type *T, - // so use GEP to convert: - let lldest = GEPi(bcx, lldest, &[0, 0]); - write_content(bcx, &vt, expr, expr, SaveIn(lldest)) - } - }; -} - -pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - slice_expr: &ast::Expr, - content_expr: &ast::Expr) - -> DatumBlock<'blk, 'tcx, Expr> { - /*! - * &[...] allocates memory on the stack and writes the values into it, - * returning the vector (the caller must make the reference). "..." is - * similar except that the memory can be statically allocated and we return - * a reference (strings are always by-ref). - */ - - let fcx = bcx.fcx; - let ccx = fcx.ccx; - let mut bcx = bcx; - - debug!("trans_slice_vec(slice_expr={})", - bcx.expr_to_string(slice_expr)); - - let vec_ty = node_id_type(bcx, slice_expr.id); - - // Handle the "..." case (returns a slice since strings are always unsized): - match content_expr.node { - ast::ExprLit(ref lit) => { - match lit.node { - ast::LitStr(ref s, _) => { - let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); - bcx = trans_lit_str(bcx, - content_expr, - s.clone(), - SaveIn(scratch.val)); - return DatumBlock::new(bcx, scratch.to_expr_datum()); - } - _ => {} - } - } - _ => {} - } - - // Handle the &[...] case: - let vt = vec_types_from_expr(bcx, content_expr); - let count = elements_required(bcx, content_expr); - debug!(" vt={}, count={}", vt.to_string(ccx), count); - let llcount = C_uint(ccx, count); - - let fixed_ty = ty::mk_vec(bcx.tcx(), - vt.unit_ty, - Some(count)); - let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); - - let llfixed = if count == 0 { - // Just create a zero-sized alloca to preserve - // the non-null invariant of the inner slice ptr - let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); - BitCast(bcx, llfixed, llfixed_ty) - } else { - // Make a fixed-length backing array and allocate it on the stack. - let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); - - // Arrange for the backing array to be cleaned up. - let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty); - let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id); - fcx.schedule_lifetime_end(cleanup_scope, llfixed_casted); - fcx.schedule_drop_mem(cleanup_scope, llfixed_casted, fixed_ty); - - // Generate the content into the backing array. - bcx = write_content(bcx, &vt, slice_expr, - content_expr, SaveIn(llfixed)); - - llfixed_casted - }; - - immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock() -} - -pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lit_expr: &ast::Expr, - str_lit: InternedString, - dest: Dest) - -> Block<'blk, 'tcx> { - /*! - * Literal strings translate to slices into static memory. This is - * different from trans_slice_vstore() above because it doesn't need to copy - * the content anywhere. - */ - - debug!("trans_lit_str(lit_expr={}, dest={})", - bcx.expr_to_string(lit_expr), - dest.to_string(bcx.ccx())); - - match dest { - Ignore => bcx, - SaveIn(lldest) => { - unsafe { - let bytes = str_lit.get().len(); - let llbytes = C_uint(bcx.ccx(), bytes); - let llcstr = C_cstr(bcx.ccx(), str_lit, false); - let llcstr = llvm::LLVMConstPointerCast(llcstr, Type::i8p(bcx.ccx()).to_ref()); - Store(bcx, llcstr, GEPi(bcx, lldest, &[0u, abi::slice_elt_base])); - Store(bcx, llbytes, GEPi(bcx, lldest, &[0u, abi::slice_elt_len])); - bcx - } - } - } -} - -pub fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - vt: &VecTypes, - vstore_expr: &ast::Expr, - content_expr: &ast::Expr, - dest: Dest) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("tvec::write_content"); - let fcx = bcx.fcx; - let mut bcx = bcx; - - debug!("write_content(vt={}, dest={}, vstore_expr={})", - vt.to_string(bcx.ccx()), - dest.to_string(bcx.ccx()), - bcx.expr_to_string(vstore_expr)); - - match content_expr.node { - ast::ExprLit(ref lit) => { - match lit.node { - ast::LitStr(ref s, _) => { - match dest { - Ignore => return bcx, - SaveIn(lldest) => { - let bytes = s.get().len(); - let llbytes = C_uint(bcx.ccx(), bytes); - let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false); - base::call_memcpy(bcx, - lldest, - llcstr, - llbytes, - 1); - return bcx; - } - } - } - _ => { - bcx.tcx().sess.span_bug(content_expr.span, - "unexpected evec content"); - } - } - } - ast::ExprVec(ref elements) => { - match dest { - Ignore => { - for element in elements.iter() { - bcx = expr::trans_into(bcx, &**element, Ignore); - } - } - - SaveIn(lldest) => { - let temp_scope = fcx.push_custom_cleanup_scope(); - for (i, element) in elements.iter().enumerate() { - let lleltptr = GEPi(bcx, lldest, &[i]); - debug!("writing index {} with lleltptr={}", - i, bcx.val_to_string(lleltptr)); - bcx = expr::trans_into(bcx, &**element, - SaveIn(lleltptr)); - let scope = cleanup::CustomScope(temp_scope); - fcx.schedule_lifetime_end(scope, lleltptr); - fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty); - } - fcx.pop_custom_cleanup_scope(temp_scope); - } - } - return bcx; - } - ast::ExprRepeat(ref element, ref count_expr) => { - match dest { - Ignore => { - return expr::trans_into(bcx, &**element, Ignore); - } - SaveIn(lldest) => { - match ty::eval_repeat_count(bcx.tcx(), &**count_expr) { - 0 => bcx, - 1 => expr::trans_into(bcx, &**element, SaveIn(lldest)), - count => { - let elem = unpack_datum!(bcx, expr::trans(bcx, &**element)); - assert!(!ty::type_moves_by_default(bcx.tcx(), elem.ty)); - - let bcx = iter_vec_loop(bcx, lldest, vt, - C_uint(bcx.ccx(), count), - |set_bcx, lleltptr, _| { - elem.shallow_copy(set_bcx, lleltptr) - }); - - elem.add_clean_if_rvalue(bcx, element.id); - bcx - } - } - } - } - } - _ => { - bcx.tcx().sess.span_bug(content_expr.span, - "unexpected vec content"); - } - } -} - -pub fn vec_types_from_expr(bcx: Block, vec_expr: &ast::Expr) -> VecTypes { - let vec_ty = node_id_type(bcx, vec_expr.id); - vec_types(bcx, ty::sequence_element_type(bcx.tcx(), vec_ty)) -} - -pub fn vec_types(bcx: Block, unit_ty: ty::t) -> VecTypes { - let ccx = bcx.ccx(); - let llunit_ty = type_of::type_of(ccx, unit_ty); - let llunit_size = nonzero_llsize_of(ccx, llunit_ty); - let llunit_alloc_size = llsize_of_alloc(ccx, llunit_ty); - - VecTypes { - unit_ty: unit_ty, - llunit_ty: llunit_ty, - llunit_size: llunit_size, - llunit_alloc_size: llunit_alloc_size - } -} - -pub fn elements_required(bcx: Block, content_expr: &ast::Expr) -> uint { - //! Figure out the number of elements we need to store this content - - match content_expr.node { - ast::ExprLit(ref lit) => { - match lit.node { - ast::LitStr(ref s, _) => s.get().len(), - _ => { - bcx.tcx().sess.span_bug(content_expr.span, - "unexpected evec content") - } - } - }, - ast::ExprVec(ref es) => es.len(), - ast::ExprRepeat(_, ref count_expr) => { - ty::eval_repeat_count(bcx.tcx(), &**count_expr) - } - _ => bcx.tcx().sess.span_bug(content_expr.span, - "unexpected vec content") - } -} - -pub fn get_fixed_base_and_len(bcx: Block, - llval: ValueRef, - vec_length: uint) - -> (ValueRef, ValueRef) { - /*! - * Converts a fixed-length vector into the slice pair. - * The vector should be stored in `llval` which should be by ref. - */ - - let ccx = bcx.ccx(); - - let base = expr::get_dataptr(bcx, llval); - let len = C_uint(ccx, vec_length); - (base, len) -} - -fn get_slice_base_and_len(bcx: Block, - llval: ValueRef) - -> (ValueRef, ValueRef) { - let base = Load(bcx, GEPi(bcx, llval, &[0u, abi::slice_elt_base])); - let len = Load(bcx, GEPi(bcx, llval, &[0u, abi::slice_elt_len])); - (base, len) -} - -pub fn get_base_and_len(bcx: Block, - llval: ValueRef, - vec_ty: ty::t) - -> (ValueRef, ValueRef) { - /*! - * Converts a vector into the slice pair. The vector should be - * stored in `llval` which should be by-reference. If you have a - * datum, you would probably prefer to call - * `Datum::get_base_and_len()` which will handle any conversions - * for you. - */ - - let ccx = bcx.ccx(); - - match ty::get(vec_ty).sty { - ty::ty_vec(_, Some(n)) => get_fixed_base_and_len(bcx, llval, n), - ty::ty_open(ty) => match ty::get(ty).sty { - ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), - _ => ccx.sess().bug("unexpected type in get_base_and_len") - }, - - // Only used for pattern matching. - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { - ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), - ty::ty_vec(_, Some(n)) => { - let base = GEPi(bcx, Load(bcx, llval), &[0u, 0u]); - (base, C_uint(ccx, n)) - } - _ => ccx.sess().bug("unexpected type in get_base_and_len"), - }, - _ => ccx.sess().bug("unexpected type in get_base_and_len"), - } -} - -pub type iter_vec_block<'a, 'blk, 'tcx> = - |Block<'blk, 'tcx>, ValueRef, ty::t|: 'a -> Block<'blk, 'tcx>; - -pub fn iter_vec_loop<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - data_ptr: ValueRef, - vt: &VecTypes, - count: ValueRef, - f: iter_vec_block<'a, 'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("tvec::iter_vec_loop"); - let fcx = bcx.fcx; - - let next_bcx = fcx.new_temp_block("expr_repeat: while next"); - let loop_bcx = fcx.new_temp_block("expr_repeat"); - let cond_bcx = fcx.new_temp_block("expr_repeat: loop cond"); - let body_bcx = fcx.new_temp_block("expr_repeat: body: set"); - let inc_bcx = fcx.new_temp_block("expr_repeat: body: inc"); - Br(bcx, loop_bcx.llbb); - - let loop_counter = { - // i = 0 - let i = alloca(loop_bcx, bcx.ccx().int_type(), "__i"); - Store(loop_bcx, C_uint(bcx.ccx(), 0u), i); - - Br(loop_bcx, cond_bcx.llbb); - i - }; - - { // i < count - let lhs = Load(cond_bcx, loop_counter); - let rhs = count; - let cond_val = ICmp(cond_bcx, llvm::IntULT, lhs, rhs); - - CondBr(cond_bcx, cond_val, body_bcx.llbb, next_bcx.llbb); - } - - { // loop body - let i = Load(body_bcx, loop_counter); - let lleltptr = if vt.llunit_alloc_size == 0 { - data_ptr - } else { - InBoundsGEP(body_bcx, data_ptr, &[i]) - }; - let body_bcx = f(body_bcx, lleltptr, vt.unit_ty); - - Br(body_bcx, inc_bcx.llbb); - } - - { // i += 1 - let i = Load(inc_bcx, loop_counter); - let plusone = Add(inc_bcx, i, C_uint(bcx.ccx(), 1u)); - Store(inc_bcx, plusone, loop_counter); - - Br(inc_bcx, cond_bcx.llbb); - } - - next_bcx -} - -pub fn iter_vec_raw<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - data_ptr: ValueRef, - unit_ty: ty::t, - len: ValueRef, - f: iter_vec_block<'a, 'blk, 'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("tvec::iter_vec_raw"); - let fcx = bcx.fcx; - - let vt = vec_types(bcx, unit_ty); - let fill = Mul(bcx, len, vt.llunit_size); - - if vt.llunit_alloc_size == 0 { - // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) - iter_vec_loop(bcx, data_ptr, &vt, fill, f) - } else { - // Calculate the last pointer address we want to handle. - // FIXME (#3729): Optimize this when the size of the unit type is - // statically known to not use pointer casts, which tend to confuse - // LLVM. - let data_end_ptr = pointer_add_byte(bcx, data_ptr, fill); - - // Now perform the iteration. - let header_bcx = fcx.new_temp_block("iter_vec_loop_header"); - Br(bcx, header_bcx.llbb); - let data_ptr = - Phi(header_bcx, val_ty(data_ptr), &[data_ptr], &[bcx.llbb]); - let not_yet_at_end = - ICmp(header_bcx, llvm::IntULT, data_ptr, data_end_ptr); - let body_bcx = fcx.new_temp_block("iter_vec_loop_body"); - let next_bcx = fcx.new_temp_block("iter_vec_next"); - CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb); - let body_bcx = f(body_bcx, data_ptr, vt.unit_ty); - AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr, - &[C_int(bcx.ccx(), 1i)]), - body_bcx.llbb); - Br(body_bcx, header_bcx.llbb); - next_bcx - } -} diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs deleted file mode 100644 index 69861290b8d..00000000000 --- a/src/librustc/middle/trans/type_.rs +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use llvm; -use llvm::{TypeRef, Bool, False, True, TypeKind, ValueRef}; -use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; - -use middle::trans::context::CrateContext; -use util::nodemap::FnvHashMap; - -use syntax::ast; - -use std::c_str::ToCStr; -use std::mem; -use std::cell::RefCell; - -use libc::c_uint; - -#[deriving(Clone, PartialEq, Show)] -#[repr(C)] -pub struct Type { - rf: TypeRef -} - -macro_rules! ty ( - ($e:expr) => ( Type::from_ref(unsafe { $e })) -) - -/** - * Wrapper for LLVM TypeRef - */ -impl Type { - #[inline(always)] - pub fn from_ref(r: TypeRef) -> Type { - Type { - rf: r - } - } - - #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler - pub fn to_ref(&self) -> TypeRef { - self.rf - } - - pub fn void(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMVoidTypeInContext(ccx.llcx())) - } - - pub fn nil(ccx: &CrateContext) -> Type { - Type::empty_struct(ccx) - } - - pub fn metadata(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx())) - } - - pub fn i1(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt1TypeInContext(ccx.llcx())) - } - - pub fn i8(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt8TypeInContext(ccx.llcx())) - } - - pub fn i16(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt16TypeInContext(ccx.llcx())) - } - - pub fn i32(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt32TypeInContext(ccx.llcx())) - } - - pub fn i64(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMInt64TypeInContext(ccx.llcx())) - } - - pub fn f32(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMFloatTypeInContext(ccx.llcx())) - } - - pub fn f64(ccx: &CrateContext) -> Type { - ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx())) - } - - pub fn bool(ccx: &CrateContext) -> Type { - Type::i8(ccx) - } - - pub fn char(ccx: &CrateContext) -> Type { - Type::i32(ccx) - } - - pub fn i8p(ccx: &CrateContext) -> Type { - Type::i8(ccx).ptr_to() - } - - pub fn int(ccx: &CrateContext) -> Type { - match ccx.tcx().sess.target.target.target_word_size.as_slice() { - "32" => Type::i32(ccx), - "64" => Type::i64(ccx), - tws => panic!("Unsupported target word size for int: {}", tws), - } - } - - pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type { - match t { - ast::TyI => ccx.int_type(), - ast::TyI8 => Type::i8(ccx), - ast::TyI16 => Type::i16(ccx), - ast::TyI32 => Type::i32(ccx), - ast::TyI64 => Type::i64(ccx) - } - } - - pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type { - match t { - ast::TyU => ccx.int_type(), - ast::TyU8 => Type::i8(ccx), - ast::TyU16 => Type::i16(ccx), - ast::TyU32 => Type::i32(ccx), - ast::TyU64 => Type::i64(ccx) - } - } - - pub fn float_from_ty(ccx: &CrateContext, t: ast::FloatTy) -> Type { - match t { - ast::TyF32 => Type::f32(ccx), - ast::TyF64 => Type::f64(ccx), - } - } - - pub fn func(args: &[Type], ret: &Type) -> Type { - let vec : &[TypeRef] = unsafe { mem::transmute(args) }; - ty!(llvm::LLVMFunctionType(ret.to_ref(), vec.as_ptr(), - args.len() as c_uint, False)) - } - - pub fn variadic_func(args: &[Type], ret: &Type) -> Type { - let vec : &[TypeRef] = unsafe { mem::transmute(args) }; - ty!(llvm::LLVMFunctionType(ret.to_ref(), vec.as_ptr(), - args.len() as c_uint, True)) - } - - pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type { - let els : &[TypeRef] = unsafe { mem::transmute(els) }; - ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(), - els.len() as c_uint, - packed as Bool)) - } - - pub fn named_struct(ccx: &CrateContext, name: &str) -> Type { - ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx(), s))) - } - - pub fn empty_struct(ccx: &CrateContext) -> Type { - Type::struct_(ccx, &[], false) - } - - pub fn vtable(ccx: &CrateContext) -> Type { - Type::array(&Type::i8p(ccx).ptr_to(), 1) - } - - pub fn generic_glue_fn(cx: &CrateContext) -> Type { - match cx.tn().find_type("glue_fn") { - Some(ty) => return ty, - None => () - } - - let ty = Type::glue_fn(cx, Type::i8p(cx)); - cx.tn().associate_type("glue_fn", &ty); - - ty - } - - pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type { - Type::func(&[t], &Type::void(ccx)) - } - - pub fn tydesc(ccx: &CrateContext, str_slice_ty: Type) -> Type { - let mut tydesc = Type::named_struct(ccx, "tydesc"); - let glue_fn_ty = Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(); - - let int_ty = Type::int(ccx); - - // Must mirror: - // - // std::unstable::intrinsics::TyDesc - - let elems = [int_ty, // size - int_ty, // align - glue_fn_ty, // drop - str_slice_ty]; // name - tydesc.set_struct_body(&elems, false); - - tydesc - } - - pub fn array(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) - } - - pub fn vector(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) - } - - pub fn vec(ccx: &CrateContext, ty: &Type) -> Type { - Type::struct_(ccx, - &[Type::array(ty, 0), Type::int(ccx)], - false) - } - - pub fn opaque_vec(ccx: &CrateContext) -> Type { - Type::vec(ccx, &Type::i8(ccx)) - } - - // The box pointed to by @T. - pub fn at_box(ccx: &CrateContext, ty: Type) -> Type { - Type::struct_(ccx, &[ - ccx.int_type(), Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(), - Type::i8p(ccx), Type::i8p(ccx), ty - ], false) - } - - pub fn vtable_ptr(ccx: &CrateContext) -> Type { - Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to() - } - - pub fn opaque_trait(ccx: &CrateContext) -> Type { - Type::struct_(ccx, &[Type::opaque_trait_data(ccx).ptr_to(), Type::vtable_ptr(ccx)], false) - } - - pub fn opaque_trait_data(ccx: &CrateContext) -> Type { - Type::i8(ccx) - } - - pub fn kind(&self) -> TypeKind { - unsafe { - llvm::LLVMGetTypeKind(self.to_ref()) - } - } - - pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { - unsafe { - let vec : &[TypeRef] = mem::transmute(els); - llvm::LLVMStructSetBody(self.to_ref(), vec.as_ptr(), - els.len() as c_uint, packed as Bool) - } - } - - pub fn ptr_to(&self) -> Type { - ty!(llvm::LLVMPointerType(self.to_ref(), 0)) - } - - pub fn is_packed(&self) -> bool { - unsafe { - llvm::LLVMIsPackedStruct(self.to_ref()) == True - } - } - - pub fn element_type(&self) -> Type { - unsafe { - Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) - } - } - - pub fn array_length(&self) -> uint { - unsafe { - llvm::LLVMGetArrayLength(self.to_ref()) as uint - } - } - - pub fn field_types(&self) -> Vec { - unsafe { - let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint; - if n_elts == 0 { - return Vec::new(); - } - let mut elts = Vec::from_elem(n_elts, Type { rf: 0 as TypeRef }); - llvm::LLVMGetStructElementTypes(self.to_ref(), - elts.as_mut_ptr() as *mut TypeRef); - elts - } - } - - pub fn return_type(&self) -> Type { - ty!(llvm::LLVMGetReturnType(self.to_ref())) - } - - pub fn func_params(&self) -> Vec { - unsafe { - let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as uint; - let mut args = Vec::from_elem(n_args, Type { rf: 0 as TypeRef }); - llvm::LLVMGetParamTypes(self.to_ref(), - args.as_mut_ptr() as *mut TypeRef); - args - } - } - - pub fn float_width(&self) -> uint { - match self.kind() { - Float => 32, - Double => 64, - X86_FP80 => 80, - FP128 | PPC_FP128 => 128, - _ => panic!("llvm_float_width called on a non-float type") - } - } -} - - -/* Memory-managed object interface to type handles. */ - -pub struct TypeNames { - named_types: RefCell>, -} - -impl TypeNames { - pub fn new() -> TypeNames { - TypeNames { - named_types: RefCell::new(FnvHashMap::new()) - } - } - - pub fn associate_type(&self, s: &str, t: &Type) { - assert!(self.named_types.borrow_mut().insert(s.to_string(), - t.to_ref()).is_none()); - } - - pub fn find_type(&self, s: &str) -> Option { - self.named_types.borrow().get(s).map(|x| Type::from_ref(*x)) - } - - pub fn type_to_string(&self, ty: Type) -> String { - llvm::build_string(|s| unsafe { - llvm::LLVMWriteTypeToString(ty.to_ref(), s); - }).expect("non-UTF8 type description from LLVM") - } - - pub fn types_to_str(&self, tys: &[Type]) -> String { - let strs: Vec = tys.iter().map(|t| self.type_to_string(*t)).collect(); - format!("[{}]", strs.connect(",")) - } - - pub fn val_to_string(&self, val: ValueRef) -> String { - llvm::build_string(|s| unsafe { - llvm::LLVMWriteValueToString(val, s); - }).expect("nun-UTF8 value description from LLVM") - } -} diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs deleted file mode 100644 index 1fff29255da..00000000000 --- a/src/librustc/middle/trans/type_of.rs +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_camel_case_types)] - -pub use self::named_ty::*; - -use middle::subst; -use middle::trans::adt; -use middle::trans::common::*; -use middle::trans::foreign; -use middle::trans::machine; -use middle::ty; -use util::ppaux; -use util::ppaux::Repr; - -use middle::trans::type_::Type; - -use std::num::Int; -use syntax::abi; -use syntax::ast; - -// LLVM doesn't like objects that are too big. Issue #17913 -fn ensure_array_fits_in_address_space(ccx: &CrateContext, - llet: Type, - size: machine::llsize, - scapegoat: ty::t) { - let esz = machine::llsize_of_alloc(ccx, llet); - match esz.checked_mul(size) { - Some(n) if n < ccx.max_obj_size() => {} - _ => { ccx.report_overbig_object(scapegoat) } - } -} - -pub fn arg_is_indirect(ccx: &CrateContext, arg_ty: ty::t) -> bool { - !type_is_immediate(ccx, arg_ty) -} - -pub fn return_uses_outptr(ccx: &CrateContext, ty: ty::t) -> bool { - !type_is_immediate(ccx, ty) -} - -pub fn type_of_explicit_arg(ccx: &CrateContext, arg_ty: ty::t) -> Type { - let llty = arg_type_of(ccx, arg_ty); - if arg_is_indirect(ccx, arg_ty) { - llty.ptr_to() - } else { - llty - } -} - -/// Yields the types of the "real" arguments for this function. For most -/// functions, these are simply the types of the arguments. For functions with -/// the `RustCall` ABI, however, this untuples the arguments of the function. -pub fn untuple_arguments_if_necessary(ccx: &CrateContext, - inputs: &[ty::t], - abi: abi::Abi) - -> Vec { - if abi != abi::RustCall { - return inputs.iter().map(|x| (*x).clone()).collect() - } - - if inputs.len() == 0 { - return Vec::new() - } - - let mut result = Vec::new(); - for (i, &arg_prior_to_tuple) in inputs.iter().enumerate() { - if i < inputs.len() - 1 { - result.push(arg_prior_to_tuple); - } - } - - match ty::get(inputs[inputs.len() - 1]).sty { - ty::ty_tup(ref tupled_arguments) => { - debug!("untuple_arguments_if_necessary(): untupling arguments"); - for &tupled_argument in tupled_arguments.iter() { - result.push(tupled_argument); - } - } - _ => { - ccx.tcx().sess.bug("argument to function with \"rust-call\" ABI \ - is neither a tuple nor unit") - } - } - - result -} - -pub fn type_of_rust_fn(cx: &CrateContext, - llenvironment_type: Option, - inputs: &[ty::t], - output: ty::FnOutput, - abi: abi::Abi) - -> Type { - let mut atys: Vec = Vec::new(); - - // First, munge the inputs, if this has the `rust-call` ABI. - let inputs = untuple_arguments_if_necessary(cx, inputs, abi); - - // Arg 0: Output pointer. - // (if the output type is non-immediate) - let lloutputtype = match output { - ty::FnConverging(output) => { - let use_out_pointer = return_uses_outptr(cx, output); - let lloutputtype = arg_type_of(cx, output); - // Use the output as the actual return value if it's immediate. - if use_out_pointer { - atys.push(lloutputtype.ptr_to()); - Type::void(cx) - } else if return_type_is_void(cx, output) { - Type::void(cx) - } else { - lloutputtype - } - } - ty::FnDiverging => Type::void(cx) - }; - - // Arg 1: Environment - match llenvironment_type { - None => {} - Some(llenvironment_type) => atys.push(llenvironment_type), - } - - // ... then explicit args. - let input_tys = inputs.iter().map(|&arg_ty| type_of_explicit_arg(cx, arg_ty)); - atys.extend(input_tys); - - Type::func(atys.as_slice(), &lloutputtype) -} - -// Given a function type and a count of ty params, construct an llvm type -pub fn type_of_fn_from_ty(cx: &CrateContext, fty: ty::t) -> Type { - match ty::get(fty).sty { - ty::ty_closure(ref f) => { - type_of_rust_fn(cx, - Some(Type::i8p(cx)), - f.sig.inputs.as_slice(), - f.sig.output, - f.abi) - } - ty::ty_bare_fn(ref f) => { - if f.abi == abi::Rust || f.abi == abi::RustCall { - type_of_rust_fn(cx, - None, - f.sig.inputs.as_slice(), - f.sig.output, - f.abi) - } else { - foreign::lltype_for_foreign_fn(cx, fty) - } - } - _ => { - cx.sess().bug("type_of_fn_from_ty given non-closure, non-bare-fn") - } - } -} - -// A "sizing type" is an LLVM type, the size and alignment of which are -// guaranteed to be equivalent to what you would get out of `type_of()`. It's -// useful because: -// -// (1) It may be cheaper to compute the sizing type than the full type if all -// you're interested in is the size and/or alignment; -// -// (2) It won't make any recursive calls to determine the structure of the -// type behind pointers. This can help prevent infinite loops for -// recursive types. For example, enum types rely on this behavior. - -pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { - match cx.llsizingtypes().borrow().get(&t).cloned() { - Some(t) => return t, - None => () - } - - let llsizingty = match ty::get(t).sty { - _ if !ty::lltype_is_sized(cx.tcx(), t) => { - cx.sess().bug(format!("trying to take the sizing type of {}, an unsized type", - ppaux::ty_to_string(cx.tcx(), t)).as_slice()) - } - - ty::ty_bool => Type::bool(cx), - ty::ty_char => Type::char(cx), - ty::ty_int(t) => Type::int_from_ty(cx, t), - ty::ty_uint(t) => Type::uint_from_ty(cx, t), - ty::ty_float(t) => Type::float_from_ty(cx, t), - - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) | ty::ty_ptr(ty::mt{ty, ..}) => { - if ty::type_is_sized(cx.tcx(), ty) { - Type::i8p(cx) - } else { - Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false) - } - } - - ty::ty_bare_fn(..) => Type::i8p(cx), - ty::ty_closure(..) => Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false), - - ty::ty_vec(ty, Some(size)) => { - let llty = sizing_type_of(cx, ty); - let size = size as u64; - ensure_array_fits_in_address_space(cx, llty, size, t); - Type::array(&llty, size) - } - - ty::ty_tup(ref tys) if tys.is_empty() => { - Type::nil(cx) - } - - ty::ty_tup(..) | ty::ty_enum(..) | ty::ty_unboxed_closure(..) => { - let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &*repr, false) - } - - ty::ty_struct(..) => { - if ty::type_is_simd(cx.tcx(), t) { - let llet = type_of(cx, ty::simd_type(cx.tcx(), t)); - let n = ty::simd_size(cx.tcx(), t) as u64; - ensure_array_fits_in_address_space(cx, llet, n, t); - Type::vector(&llet, n) - } else { - let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &*repr, false) - } - } - - ty::ty_open(_) => { - Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false) - } - - ty::ty_infer(..) | ty::ty_param(..) | ty::ty_err(..) => { - cx.sess().bug(format!("fictitious type {} in sizing_type_of()", - ppaux::ty_to_string(cx.tcx(), t)).as_slice()) - } - ty::ty_vec(_, None) | ty::ty_trait(..) | ty::ty_str => panic!("unreachable") - }; - - cx.llsizingtypes().borrow_mut().insert(t, llsizingty); - llsizingty -} - -pub fn arg_type_of(cx: &CrateContext, t: ty::t) -> Type { - if ty::type_is_bool(t) { - Type::i1(cx) - } else { - type_of(cx, t) - } -} - -// NB: If you update this, be sure to update `sizing_type_of()` as well. -pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { - fn type_of_unsize_info(cx: &CrateContext, t: ty::t) -> Type { - // It is possible to end up here with a sized type. This happens with a - // struct which might be unsized, but is monomorphised to a sized type. - // In this case we'll fake a fat pointer with no unsize info (we use 0). - // However, its still a fat pointer, so we need some type use. - if ty::type_is_sized(cx.tcx(), t) { - return Type::i8p(cx); - } - - match ty::get(ty::unsized_part_of_type(cx.tcx(), t)).sty { - ty::ty_str | ty::ty_vec(..) => Type::uint_from_ty(cx, ast::TyU), - ty::ty_trait(_) => Type::vtable_ptr(cx), - _ => panic!("Unexpected type returned from unsized_part_of_type : {}", - t.repr(cx.tcx())) - } - } - - // Check the cache. - match cx.lltypes().borrow().get(&t) { - Some(&llty) => return llty, - None => () - } - - debug!("type_of {} {}", t.repr(cx.tcx()), ty::get(t).sty); - - // Replace any typedef'd types with their equivalent non-typedef - // type. This ensures that all LLVM nominal types that contain - // Rust types are defined as the same LLVM types. If we don't do - // this then, e.g. `Option<{myfield: bool}>` would be a different - // type than `Option`. - let t_norm = ty::normalize_ty(cx.tcx(), t); - - if t != t_norm { - let llty = type_of(cx, t_norm); - debug!("--> normalized {} {} to {} {} llty={}", - t.repr(cx.tcx()), - t, - t_norm.repr(cx.tcx()), - t_norm, - cx.tn().type_to_string(llty)); - cx.lltypes().borrow_mut().insert(t, llty); - return llty; - } - - let mut llty = match ty::get(t).sty { - ty::ty_bool => Type::bool(cx), - ty::ty_char => Type::char(cx), - ty::ty_int(t) => Type::int_from_ty(cx, t), - ty::ty_uint(t) => Type::uint_from_ty(cx, t), - ty::ty_float(t) => Type::float_from_ty(cx, t), - ty::ty_enum(did, ref substs) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. This - // avoids creating more than one copy of the enum when one - // of the enum's variants refers to the enum itself. - let repr = adt::represent_type(cx, t); - let tps = substs.types.get_slice(subst::TypeSpace); - let name = llvm_type_name(cx, an_enum, did, tps); - adt::incomplete_type_of(cx, &*repr, name.as_slice()) - } - ty::ty_unboxed_closure(did, _, ref substs) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. - let repr = adt::represent_type(cx, t); - // Unboxed closures can have substitutions in all spaces - // inherited from their environment, so we use entire - // contents of the VecPerParamSpace to to construct the llvm - // name - let name = llvm_type_name(cx, an_unboxed_closure, did, substs.types.as_slice()); - adt::incomplete_type_of(cx, &*repr, name.as_slice()) - } - - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) | ty::ty_ptr(ty::mt{ty, ..}) => { - match ty::get(ty).sty { - ty::ty_str => { - // This means we get a nicer name in the output (str is always - // unsized). - cx.tn().find_type("str_slice").unwrap() - } - ty::ty_trait(..) => Type::opaque_trait(cx), - _ if !ty::type_is_sized(cx.tcx(), ty) => { - let p_ty = type_of(cx, ty).ptr_to(); - Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, ty)], false) - } - _ => type_of(cx, ty).ptr_to(), - } - } - - ty::ty_vec(ty, Some(size)) => { - let size = size as u64; - let llty = type_of(cx, ty); - ensure_array_fits_in_address_space(cx, llty, size, t); - Type::array(&llty, size) - } - ty::ty_vec(ty, None) => { - type_of(cx, ty) - } - - ty::ty_trait(..) => { - Type::opaque_trait_data(cx) - } - - ty::ty_str => Type::i8(cx), - - ty::ty_bare_fn(_) => { - type_of_fn_from_ty(cx, t).ptr_to() - } - ty::ty_closure(_) => { - let fn_ty = type_of_fn_from_ty(cx, t).ptr_to(); - Type::struct_(cx, &[fn_ty, Type::i8p(cx)], false) - } - ty::ty_tup(ref tys) if tys.is_empty() => Type::nil(cx), - ty::ty_tup(..) => { - let repr = adt::represent_type(cx, t); - adt::type_of(cx, &*repr) - } - ty::ty_struct(did, ref substs) => { - if ty::type_is_simd(cx.tcx(), t) { - let llet = type_of(cx, ty::simd_type(cx.tcx(), t)); - let n = ty::simd_size(cx.tcx(), t) as u64; - ensure_array_fits_in_address_space(cx, llet, n, t); - Type::vector(&llet, n) - } else { - // Only create the named struct, but don't fill it in. We fill it - // in *after* placing it into the type cache. This prevents - // infinite recursion with recursive struct types. - let repr = adt::represent_type(cx, t); - let tps = substs.types.get_slice(subst::TypeSpace); - let name = llvm_type_name(cx, a_struct, did, tps); - adt::incomplete_type_of(cx, &*repr, name.as_slice()) - } - } - - ty::ty_open(t) => match ty::get(t).sty { - ty::ty_struct(..) => { - let p_ty = type_of(cx, t).ptr_to(); - Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) - } - ty::ty_vec(ty, None) => { - let p_ty = type_of(cx, ty).ptr_to(); - Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) - } - ty::ty_str => { - let p_ty = Type::i8p(cx); - Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) - } - ty::ty_trait(..) => Type::opaque_trait(cx), - _ => cx.sess().bug(format!("ty_open with sized type: {}", - ppaux::ty_to_string(cx.tcx(), t)).as_slice()) - }, - - ty::ty_infer(..) => cx.sess().bug("type_of with ty_infer"), - ty::ty_param(..) => cx.sess().bug("type_of with ty_param"), - ty::ty_err(..) => cx.sess().bug("type_of with ty_err"), - }; - - debug!("--> mapped t={} {} to llty={}", - t.repr(cx.tcx()), - t, - cx.tn().type_to_string(llty)); - - cx.lltypes().borrow_mut().insert(t, llty); - - // If this was an enum or struct, fill in the type now. - match ty::get(t).sty { - ty::ty_enum(..) | ty::ty_struct(..) | ty::ty_unboxed_closure(..) - if !ty::type_is_simd(cx.tcx(), t) => { - let repr = adt::represent_type(cx, t); - adt::finish_type_of(cx, &*repr, &mut llty); - } - _ => () - } - - return llty; -} - -pub fn align_of(cx: &CrateContext, t: ty::t) -> machine::llalign { - let llty = sizing_type_of(cx, t); - machine::llalign_of_min(cx, llty) -} - -// Want refinements! (Or case classes, I guess -pub enum named_ty { - a_struct, - an_enum, - an_unboxed_closure, -} - -pub fn llvm_type_name(cx: &CrateContext, - what: named_ty, - did: ast::DefId, - tps: &[ty::t]) - -> String -{ - let name = match what { - a_struct => "struct", - an_enum => "enum", - an_unboxed_closure => return "closure".to_string(), - }; - - let base = ty::item_path_str(cx.tcx(), did); - let strings: Vec = tps.iter().map(|t| t.repr(cx.tcx())).collect(); - let tstr = if strings.is_empty() { - base - } else { - format!("{}<{}>", base, strings) - }; - - if did.krate == 0 { - format!("{}.{}", name, tstr) - } else { - format!("{}.{}[{}{}]", name, tstr, "#", did.krate) - } -} - -pub fn type_of_dtor(ccx: &CrateContext, self_ty: ty::t) -> Type { - let self_ty = type_of(ccx, self_ty).ptr_to(); - Type::func(&[self_ty], &Type::void(ccx)) -} diff --git a/src/librustc/middle/trans/value.rs b/src/librustc/middle/trans/value.rs deleted file mode 100644 index dfa4ae6b285..00000000000 --- a/src/librustc/middle/trans/value.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use llvm::{UseRef, ValueRef}; -use middle::trans::basic_block::BasicBlock; -use middle::trans::common::Block; -use libc::c_uint; - -pub struct Value(pub ValueRef); - -macro_rules! opt_val ( ($e:expr) => ( - unsafe { - match $e { - p if p.is_not_null() => Some(Value(p)), - _ => None - } - } -)) - -/** - * Wrapper for LLVM ValueRef - */ -impl Value { - /// Returns the native ValueRef - pub fn get(&self) -> ValueRef { - let Value(v) = *self; v - } - - /// Returns the BasicBlock that contains this value - pub fn get_parent(self) -> Option { - unsafe { - match llvm::LLVMGetInstructionParent(self.get()) { - p if p.is_not_null() => Some(BasicBlock(p)), - _ => None - } - } - } - - /// Removes this value from its containing BasicBlock - pub fn erase_from_parent(self) { - unsafe { - llvm::LLVMInstructionEraseFromParent(self.get()); - } - } - - /// Returns the single dominating store to this value, if any - /// This only performs a search for a trivially dominating store. The store - /// must be the only user of this value, and there must not be any conditional - /// branches between the store and the given block. - pub fn get_dominating_store(self, bcx: Block) -> Option { - match self.get_single_user().and_then(|user| user.as_store_inst()) { - Some(store) => { - store.get_parent().and_then(|store_bb| { - let mut bb = BasicBlock(bcx.llbb); - let mut ret = Some(store); - while bb.get() != store_bb.get() { - match bb.get_single_predecessor() { - Some(pred) => bb = pred, - None => { ret = None; break } - } - } - ret - }) - } - _ => None - } - } - - /// Returns the first use of this value, if any - pub fn get_first_use(self) -> Option { - unsafe { - match llvm::LLVMGetFirstUse(self.get()) { - u if u.is_not_null() => Some(Use(u)), - _ => None - } - } - } - - /// Tests if there are no uses of this value - pub fn has_no_uses(self) -> bool { - self.get_first_use().is_none() - } - - /// Returns the single user of this value - /// If there are no users or multiple users, this returns None - pub fn get_single_user(self) -> Option { - let mut iter = self.user_iter(); - match (iter.next(), iter.next()) { - (Some(first), None) => Some(first), - _ => None - } - } - - /// Returns an iterator for the users of this value - pub fn user_iter(self) -> Users { - Users { - next: self.get_first_use() - } - } - - /// Returns the requested operand of this instruction - /// Returns None, if there's no operand at the given index - pub fn get_operand(self, i: uint) -> Option { - opt_val!(llvm::LLVMGetOperand(self.get(), i as c_uint)) - } - - /// Returns the Store represent by this value, if any - pub fn as_store_inst(self) -> Option { - opt_val!(llvm::LLVMIsAStoreInst(self.get())) - } - - /// Tests if this value is a terminator instruction - pub fn is_a_terminator_inst(self) -> bool { - unsafe { - llvm::LLVMIsATerminatorInst(self.get()).is_not_null() - } - } -} - -pub struct Use(UseRef); - -/** - * Wrapper for LLVM UseRef - */ -impl Use { - pub fn get(&self) -> UseRef { - let Use(v) = *self; v - } - - pub fn get_user(self) -> Value { - unsafe { - Value(llvm::LLVMGetUser(self.get())) - } - } - - pub fn get_next_use(self) -> Option { - unsafe { - match llvm::LLVMGetNextUse(self.get()) { - u if u.is_not_null() => Some(Use(u)), - _ => None - } - } - } -} - -/// Iterator for the users of a value -pub struct Users { - next: Option -} - -impl Iterator for Users { - fn next(&mut self) -> Option { - let current = self.next; - - self.next = current.and_then(|u| u.get_next_use()); - - current.map(|u| u.get_user()) - } -} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 52b29ff8b2e..5ec89ffb9e7 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -37,7 +37,7 @@ pub use self::sty::*; pub use self::IntVarValue::*; use back::svh::Svh; -use driver::session::Session; +use session::Session; use lint; use metadata::csearch; use middle::const_eval; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d58b0322433..33705343dab 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -82,7 +82,7 @@ use self::Expectation::*; use self::IsBinopAssignment::*; use self::TupleArgumentsFlag::*; -use driver::session::Session; +use session::Session; use middle::const_eval; use middle::def; use middle::lang_items::IteratorItem; diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index f9dffc04ceb..a75e3a78e4b 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -67,7 +67,6 @@ pub mod region_inference; pub mod resolve; mod skolemize; pub mod sub; -pub mod test; pub mod type_variable; pub mod unify; @@ -996,6 +995,16 @@ impl TypeTrace { pub fn span(&self) -> Span { self.origin.span() } + + pub fn dummy() -> TypeTrace { + TypeTrace { + origin: Misc(codemap::DUMMY_SP), + values: Types(ty::expected_found { + expected: ty::mk_err(), + found: ty::mk_err(), + }) + } + } } impl Repr for TypeTrace { diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs deleted file mode 100644 index 769fdef0625..00000000000 --- a/src/librustc/middle/typeck/infer/test.rs +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -# Standalone Tests for the Inference Module - -*/ - -// This is only used by tests, hence allow dead code. -#![allow(dead_code)] - -use driver::config; -use driver::diagnostic; -use driver::diagnostic::Emitter; -use driver::driver; -use driver::session; -use middle::lang_items; -use middle::region; -use middle::resolve; -use middle::resolve_lifetime; -use middle::stability; -use middle::ty; -use middle::typeck::infer::combine::Combine; -use middle::typeck::infer; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::glb::Glb; -use syntax::codemap; -use syntax::codemap::{Span, CodeMap, DUMMY_SP}; -use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help}; -use syntax::{ast, ast_map}; -use util::ppaux::{ty_to_string, UserString}; - -use arena::TypedArena; - -struct Env<'a, 'tcx: 'a> { - infcx: &'a infer::InferCtxt<'a, 'tcx>, -} - -struct RH<'a> { - id: ast::NodeId, - sub: &'a [RH<'a>] -} - -static EMPTY_SOURCE_STR: &'static str = "#![no_std]"; - -struct ExpectErrorEmitter { - messages: Vec -} - -fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { - match lvl { - Bug | Fatal | Error => { } - Warning | Note | Help => { return; } - } - - debug!("Error: {}", msg); - match e.messages.iter().position(|m| msg.contains(m.as_slice())) { - Some(i) => { - e.messages.remove(i); - } - None => { - panic!("Unexpected error: {} Expected: {}", - msg, e.messages); - } - } -} - -impl Emitter for ExpectErrorEmitter { - fn emit(&mut self, - _cmsp: Option<(&codemap::CodeMap, Span)>, - msg: &str, - _: Option<&str>, - lvl: Level) - { - remove_message(self, msg, lvl); - } - - fn custom_emit(&mut self, - _cm: &codemap::CodeMap, - _sp: RenderSpan, - msg: &str, - lvl: Level) - { - remove_message(self, msg, lvl); - } -} - -fn errors(msgs: &[&str]) -> (Box, uint) { - let v = msgs.iter().map(|m| m.to_string()).collect(); - (box ExpectErrorEmitter { messages: v } as Box, msgs.len()) -} - -fn test_env(_test_name: &str, - source_string: &str, - (emitter, expected_err_count): (Box, uint), - body: |Env|) { - let options = - config::basic_options(); - let codemap = - CodeMap::new(); - let diagnostic_handler = - diagnostic::mk_handler(emitter); - let span_diagnostic_handler = - diagnostic::mk_span_handler(diagnostic_handler, codemap); - - let sess = session::build_session_(options, None, span_diagnostic_handler); - let krate_config = Vec::new(); - let input = driver::StrInput(source_string.to_string()); - let krate = driver::phase_1_parse_input(&sess, krate_config, &input); - let krate = driver::phase_2_configure_and_expand(&sess, krate, "test", None) - .expect("phase 2 aborted"); - - let mut forest = ast_map::Forest::new(krate); - let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest); - let krate = ast_map.krate(); - - // run just enough stuff to build a tcx: - let lang_items = lang_items::collect_language_items(krate, &sess); - let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } = - resolve::resolve_crate(&sess, &lang_items, krate); - let named_region_map = resolve_lifetime::krate(&sess, krate); - let region_map = region::resolve_crate(&sess, krate); - let stability_index = stability::Index::build(krate); - let type_arena = TypedArena::new(); - let tcx = ty::mk_ctxt(sess, - &type_arena, - def_map, - named_region_map, - ast_map, - freevars, - capture_mode_map, - region_map, - lang_items, - stability_index); - let infcx = infer::new_infer_ctxt(&tcx); - body(Env { infcx: &infcx }); - infcx.resolve_regions_and_report_errors(); - assert_eq!(tcx.sess.err_count(), expected_err_count); -} - -impl<'a, 'tcx> Env<'a, 'tcx> { - pub fn create_region_hierarchy(&self, rh: &RH) { - for child_rh in rh.sub.iter() { - self.create_region_hierarchy(child_rh); - self.infcx.tcx.region_maps.record_encl_scope(child_rh.id, rh.id); - } - } - - pub fn create_simple_region_hierarchy(&self) { - // creates a region hierarchy where 1 is root, 10 and 11 are - // children of 1, etc - self.create_region_hierarchy( - &RH {id: 1, - sub: &[RH {id: 10, - sub: &[]}, - RH {id: 11, - sub: &[]}]}); - } - - pub fn lookup_item(&self, names: &[String]) -> ast::NodeId { - return match search_mod(self, &self.infcx.tcx.map.krate().module, 0, names) { - Some(id) => id, - None => { - panic!("no item found: `{}`", names.connect("::")); - } - }; - - fn search_mod(this: &Env, - m: &ast::Mod, - idx: uint, - names: &[String]) - -> Option { - assert!(idx < names.len()); - for item in m.items.iter() { - if item.ident.user_string(this.infcx.tcx) == names[idx] { - return search(this, &**item, idx+1, names); - } - } - return None; - } - - fn search(this: &Env, - it: &ast::Item, - idx: uint, - names: &[String]) - -> Option { - if idx == names.len() { - return Some(it.id); - } - - return match it.node { - ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemFn(..) | - ast::ItemForeignMod(..) | ast::ItemTy(..) => { - None - } - - ast::ItemEnum(..) | ast::ItemStruct(..) | - ast::ItemTrait(..) | ast::ItemImpl(..) | - ast::ItemMac(..) => { - None - } - - ast::ItemMod(ref m) => { - search_mod(this, m, idx, names) - } - }; - } - } - - pub fn make_subtype(&self, a: ty::t, b: ty::t) -> bool { - match infer::mk_subty(self.infcx, true, infer::Misc(DUMMY_SP), a, b) { - Ok(_) => true, - Err(ref e) => panic!("Encountered error: {}", - ty::type_err_to_str(self.infcx.tcx, e)) - } - } - - pub fn is_subtype(&self, a: ty::t, b: ty::t) -> bool { - match infer::can_mk_subty(self.infcx, a, b) { - Ok(_) => true, - Err(_) => false - } - } - - pub fn assert_subtype(&self, a: ty::t, b: ty::t) { - if !self.is_subtype(a, b) { - panic!("{} is not a subtype of {}, but it should be", - self.ty_to_string(a), - self.ty_to_string(b)); - } - } - - pub fn assert_not_subtype(&self, a: ty::t, b: ty::t) { - if self.is_subtype(a, b) { - panic!("{} is a subtype of {}, but it shouldn't be", - self.ty_to_string(a), - self.ty_to_string(b)); - } - } - - pub fn assert_eq(&self, a: ty::t, b: ty::t) { - self.assert_subtype(a, b); - self.assert_subtype(b, a); - } - - pub fn ty_to_string(&self, a: ty::t) -> String { - ty_to_string(self.infcx.tcx, a) - } - - pub fn t_fn(&self, - binder_id: ast::NodeId, - input_tys: &[ty::t], - output_ty: ty::t) - -> ty::t - { - ty::mk_ctor_fn(self.infcx.tcx, binder_id, input_tys, output_ty) - } - - pub fn t_int(&self) -> ty::t { - ty::mk_int() - } - - pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)), - self.t_int()) - } - - pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), self.t_int()) - } - - pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, - ty::ReFree(ty::FreeRegion {scope_id: nid, - bound_region: ty::BrAnon(id)}), - self.t_int()) - } - - pub fn t_rptr_static(&self) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, self.t_int()) - } - - pub fn dummy_type_trace(&self) -> infer::TypeTrace { - infer::TypeTrace { - origin: infer::Misc(DUMMY_SP), - values: infer::Types(ty::expected_found { - expected: ty::mk_err(), - found: ty::mk_err(), - }) - } - } - - pub fn lub(&self) -> Lub<'a, 'tcx> { - let trace = self.dummy_type_trace(); - Lub(self.infcx.combine_fields(true, trace)) - } - - pub fn glb(&self) -> Glb<'a, 'tcx> { - let trace = self.dummy_type_trace(); - Glb(self.infcx.combine_fields(true, trace)) - } - - pub fn resolve_regions(&self) { - self.infcx.resolve_regions_and_report_errors(); - } - - pub fn make_lub_ty(&self, t1: ty::t, t2: ty::t) -> ty::t { - match self.lub().tys(t1, t2) { - Ok(t) => t, - Err(ref e) => panic!("unexpected error computing LUB: {}", - ty::type_err_to_str(self.infcx.tcx, e)) - } - } - - /// Checks that `LUB(t1,t2) == t_lub` - pub fn check_lub(&self, t1: ty::t, t2: ty::t, t_lub: ty::t) { - match self.lub().tys(t1, t2) { - Ok(t) => { - self.assert_eq(t, t_lub); - } - Err(ref e) => { - panic!("unexpected error in LUB: {}", - ty::type_err_to_str(self.infcx.tcx, e)) - } - } - } - - /// Checks that `GLB(t1,t2) == t_glb` - pub fn check_glb(&self, t1: ty::t, t2: ty::t, t_glb: ty::t) { - debug!("check_glb(t1={}, t2={}, t_glb={})", - self.ty_to_string(t1), - self.ty_to_string(t2), - self.ty_to_string(t_glb)); - match self.glb().tys(t1, t2) { - Err(e) => { - panic!("unexpected error computing LUB: {}", e) - } - Ok(t) => { - self.assert_eq(t, t_glb); - - // sanity check for good measure: - self.assert_subtype(t, t1); - self.assert_subtype(t, t2); - } - } - } - - /// Checks that `LUB(t1,t2)` is undefined - pub fn check_no_lub(&self, t1: ty::t, t2: ty::t) { - match self.lub().tys(t1, t2) { - Err(_) => {} - Ok(t) => { - panic!("unexpected success computing LUB: {}", self.ty_to_string(t)) - } - } - } - - /// Checks that `GLB(t1,t2)` is undefined - pub fn check_no_glb(&self, t1: ty::t, t2: ty::t) { - match self.glb().tys(t1, t2) { - Err(_) => {} - Ok(t) => { - panic!("unexpected success computing GLB: {}", self.ty_to_string(t)) - } - } - } -} - -#[test] -fn contravariant_region_ptr_ok() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - env.create_simple_region_hierarchy(); - let t_rptr1 = env.t_rptr_scope(1); - let t_rptr10 = env.t_rptr_scope(10); - env.assert_eq(t_rptr1, t_rptr1); - env.assert_eq(t_rptr10, t_rptr10); - env.make_subtype(t_rptr1, t_rptr10); - }) -} - -#[test] -fn contravariant_region_ptr_err() { - test_env("contravariant_region_ptr", - EMPTY_SOURCE_STR, - errors(&["lifetime mismatch"]), - |env| { - env.create_simple_region_hierarchy(); - let t_rptr1 = env.t_rptr_scope(1); - let t_rptr10 = env.t_rptr_scope(10); - env.assert_eq(t_rptr1, t_rptr1); - env.assert_eq(t_rptr10, t_rptr10); - - // will cause an error when regions are resolved - env.make_subtype(t_rptr10, t_rptr1); - }) -} - -#[test] -fn lub_bound_bound() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); - env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_bound2], env.t_int()), - env.t_fn(22, &[t_rptr_bound1], env.t_int())); - }) -} - -#[test] -fn lub_bound_free() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_free1 = env.t_rptr_free(0, 1); - env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_free1], env.t_int()), - env.t_fn(22, &[t_rptr_free1], env.t_int())); - }) -} - -#[test] -fn lub_bound_static() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_static = env.t_rptr_static(); - env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_static], env.t_int()), - env.t_fn(22, &[t_rptr_static], env.t_int())); - }) -} - -#[test] -fn lub_bound_bound_inverse_order() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); - env.check_lub(env.t_fn(22, &[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1), - env.t_fn(22, &[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1), - env.t_fn(22, &[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1)); - }) -} - -#[test] -fn lub_free_free() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_free1 = env.t_rptr_free(0, 1); - let t_rptr_free2 = env.t_rptr_free(0, 2); - let t_rptr_static = env.t_rptr_static(); - env.check_lub(env.t_fn(22, &[t_rptr_free1], env.t_int()), - env.t_fn(22, &[t_rptr_free2], env.t_int()), - env.t_fn(22, &[t_rptr_static], env.t_int())); - }) -} - -#[test] -fn lub_returning_scope() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, - errors(&["cannot infer an appropriate lifetime"]), |env| { - let t_rptr_scope10 = env.t_rptr_scope(10); - let t_rptr_scope11 = env.t_rptr_scope(11); - - // this should generate an error when regions are resolved - env.make_lub_ty(env.t_fn(22, &[], t_rptr_scope10), - env.t_fn(22, &[], t_rptr_scope11)); - }) -} - -#[test] -fn glb_free_free_with_common_scope() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_free1 = env.t_rptr_free(0, 1); - let t_rptr_free2 = env.t_rptr_free(0, 2); - let t_rptr_scope = env.t_rptr_scope(0); - env.check_glb(env.t_fn(22, &[t_rptr_free1], env.t_int()), - env.t_fn(22, &[t_rptr_free2], env.t_int()), - env.t_fn(22, &[t_rptr_scope], env.t_int())); - }) -} - -#[test] -fn glb_bound_bound() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); - env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_bound2], env.t_int()), - env.t_fn(22, &[t_rptr_bound1], env.t_int())); - }) -} - -#[test] -fn glb_bound_free() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_free1 = env.t_rptr_free(0, 1); - env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_free1], env.t_int()), - env.t_fn(22, &[t_rptr_bound1], env.t_int())); - }) -} - -#[test] -fn glb_bound_static() { - test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); - let t_rptr_static = env.t_rptr_static(); - env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), - env.t_fn(22, &[t_rptr_static], env.t_int()), - env.t_fn(22, &[t_rptr_bound1], env.t_int())); - }) -} diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 8561594212f..634b153a9ce 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -65,13 +65,12 @@ pub use self::ExprAdjustment::*; pub use self::vtable_origin::*; pub use self::MethodOrigin::*; -use driver::config; - use middle::def; use middle::resolve; use middle::subst; use middle::subst::VecPerParamSpace; use middle::ty; +use session::config; use util::common::time; use util::ppaux::Repr; use util::ppaux; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index e107ac73d79..e0fe87d6d06 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -10,8 +10,8 @@ //! Validity checking for weak lang items -use driver::config; -use driver::session::Session; +use session::config; +use session::Session; use metadata::csearch; use middle::lang_items; diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs index 921cd7a4107..1a270d0b1b7 100644 --- a/src/librustc/plugin/load.rs +++ b/src/librustc/plugin/load.rs @@ -10,7 +10,7 @@ //! Used by `rustc` when loading a plugin. -use driver::session::Session; +use session::Session; use metadata::creader::PluginMetadataReader; use plugin::registry::Registry; diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs new file mode 100644 index 00000000000..e10a1a4342c --- /dev/null +++ b/src/librustc/session/config.rs @@ -0,0 +1,945 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Contains infrastructure for configuring the compiler, including parsing +//! command line options. + +pub use self::EntryFnType::*; +pub use self::CrateType::*; +pub use self::Passes::*; +pub use self::OptLevel::*; +pub use self::OutputType::*; +pub use self::DebugInfoLevel::*; + +use session::{early_error, early_warn, Session}; + +use rustc_back::target::Target; +use lint; +use metadata::cstore; + +use syntax::ast; +use syntax::ast::{IntTy, UintTy}; +use syntax::attr; +use syntax::attr::AttrMetaMethods; +use syntax::diagnostic::{ColorConfig, Auto, Always, Never, SpanHandler}; +use syntax::parse; +use syntax::parse::token::InternedString; + +use std::collections::HashMap; +use std::collections::hash_map::{Occupied, Vacant}; +use getopts::{optopt, optmulti, optflag, optflagopt}; +use getopts; +use std::cell::{RefCell}; +use std::fmt; + +use llvm; + +pub struct Config { + pub target: Target, + pub int_type: IntTy, + pub uint_type: UintTy, +} + +#[deriving(Clone, PartialEq)] +pub enum OptLevel { + No, // -O0 + Less, // -O1 + Default, // -O2 + Aggressive // -O3 +} + +#[deriving(Clone, PartialEq)] +pub enum DebugInfoLevel { + NoDebugInfo, + LimitedDebugInfo, + FullDebugInfo, +} + +#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] +pub enum OutputType { + OutputTypeBitcode, + OutputTypeAssembly, + OutputTypeLlvmAssembly, + OutputTypeObject, + OutputTypeExe, +} + +#[deriving(Clone)] +pub struct Options { + // The crate config requested for the session, which may be combined + // with additional crate configurations during the compile process + pub crate_types: Vec, + + pub gc: bool, + pub optimize: OptLevel, + pub debuginfo: DebugInfoLevel, + pub lint_opts: Vec<(String, lint::Level)>, + pub describe_lints: bool, + pub output_types: Vec , + // This was mutable for rustpkg, which updates search paths based on the + // parsed code. It remains mutable in case its replacements wants to use + // this. + pub addl_lib_search_paths: RefCell>, + pub libs: Vec<(String, cstore::NativeLibaryKind)>, + pub maybe_sysroot: Option, + pub target_triple: String, + // User-specified cfg meta items. The compiler itself will add additional + // items to the crate config, and during parsing the entire crate config + // will be added to the crate AST node. This should not be used for + // anything except building the full crate config prior to parsing. + pub cfg: ast::CrateConfig, + pub test: bool, + pub parse_only: bool, + pub no_trans: bool, + pub no_analysis: bool, + pub debugging_opts: u64, + /// Whether to write dependency files. It's (enabled, optional filename). + pub write_dependency_info: (bool, Option), + /// Crate id-related things to maybe print. It's (crate_name, crate_file_name). + pub print_metas: (bool, bool), + pub cg: CodegenOptions, + pub color: ColorConfig, + pub externs: HashMap>, + pub crate_name: Option, + /// An optional name to use as the crate for std during std injection, + /// written `extern crate std = "name"`. Default to "std". Used by + /// out-of-tree drivers. + pub alt_std_name: Option +} + +pub fn host_triple() -> &'static str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // + // Instead of grabbing the host triple (for the current host), we grab (at + // compile time) the target triple that this rustc is built with and + // calling that (at runtime) the host triple. + (option_env!("CFG_COMPILER_HOST_TRIPLE")). + expect("CFG_COMPILER_HOST_TRIPLE") +} + +/// Some reasonable defaults +pub fn basic_options() -> Options { + Options { + crate_types: Vec::new(), + gc: false, + optimize: No, + debuginfo: NoDebugInfo, + lint_opts: Vec::new(), + describe_lints: false, + output_types: Vec::new(), + addl_lib_search_paths: RefCell::new(Vec::new()), + maybe_sysroot: None, + target_triple: host_triple().to_string(), + cfg: Vec::new(), + test: false, + parse_only: false, + no_trans: false, + no_analysis: false, + debugging_opts: 0, + write_dependency_info: (false, None), + print_metas: (false, false), + cg: basic_codegen_options(), + color: Auto, + externs: HashMap::new(), + crate_name: None, + alt_std_name: None, + libs: Vec::new(), + } +} + +// The type of entry function, so +// users can have their own entry +// functions that don't start a +// scheduler +#[deriving(PartialEq)] +pub enum EntryFnType { + EntryMain, + EntryStart, + EntryNone, +} + +#[deriving(PartialEq, PartialOrd, Clone, Ord, Eq, Hash)] +pub enum CrateType { + CrateTypeExecutable, + CrateTypeDylib, + CrateTypeRlib, + CrateTypeStaticlib, +} + +macro_rules! debugging_opts( + ([ $opt:ident ] $cnt:expr ) => ( + pub const $opt: u64 = 1 << $cnt; + ); + ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => ( + pub const $opt: u64 = 1 << $cnt; + debugging_opts!([ $($rest),* ] $cnt + 1) + ) +) + +debugging_opts!( + [ + VERBOSE, + TIME_PASSES, + COUNT_LLVM_INSNS, + TIME_LLVM_PASSES, + TRANS_STATS, + ASM_COMMENTS, + NO_VERIFY, + BORROWCK_STATS, + NO_LANDING_PADS, + DEBUG_LLVM, + SHOW_SPAN, + COUNT_TYPE_SIZES, + META_STATS, + GC, + PRINT_LINK_ARGS, + PRINT_LLVM_PASSES, + AST_JSON, + AST_JSON_NOEXPAND, + LS, + SAVE_ANALYSIS, + FLOWGRAPH_PRINT_LOANS, + FLOWGRAPH_PRINT_MOVES, + FLOWGRAPH_PRINT_ASSIGNS, + FLOWGRAPH_PRINT_ALL + ] + 0 +) + +pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { + vec!(("verbose", "in general, enable more debug printouts", VERBOSE), + ("time-passes", "measure time of each rustc pass", TIME_PASSES), + ("count-llvm-insns", "count where LLVM \ + instrs originate", COUNT_LLVM_INSNS), + ("time-llvm-passes", "measure time of each LLVM pass", + TIME_LLVM_PASSES), + ("trans-stats", "gather trans statistics", TRANS_STATS), + ("asm-comments", "generate comments into the assembly (may change behavior)", + ASM_COMMENTS), + ("no-verify", "skip LLVM verification", NO_VERIFY), + ("borrowck-stats", "gather borrowck statistics", BORROWCK_STATS), + ("no-landing-pads", "omit landing pads for unwinding", + NO_LANDING_PADS), + ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM), + ("show-span", "show spans for compiler debugging", SHOW_SPAN), + ("count-type-sizes", "count the sizes of aggregate types", + COUNT_TYPE_SIZES), + ("meta-stats", "gather metadata statistics", META_STATS), + ("print-link-args", "Print the arguments passed to the linker", + PRINT_LINK_ARGS), + ("gc", "Garbage collect shared data (experimental)", GC), + ("print-llvm-passes", + "Prints the llvm optimization passes being run", + PRINT_LLVM_PASSES), + ("ast-json", "Print the AST as JSON and halt", AST_JSON), + ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND), + ("ls", "List the symbols defined by a library crate", LS), + ("save-analysis", "Write syntax and type analysis information \ + in addition to normal output", SAVE_ANALYSIS), + ("flowgraph-print-loans", "Include loan analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS), + ("flowgraph-print-moves", "Include move analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES), + ("flowgraph-print-assigns", "Include assignment analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS), + ("flowgraph-print-all", "Include all dataflow analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ALL)) +} + +#[deriving(Clone)] +pub enum Passes { + SomePasses(Vec), + AllPasses, +} + +impl Passes { + pub fn is_empty(&self) -> bool { + match *self { + SomePasses(ref v) => v.is_empty(), + AllPasses => false, + } + } +} + +/// Declare a macro that will define all CodegenOptions fields and parsers all +/// at once. The goal of this macro is to define an interface that can be +/// programmatically used by the option parser in order to initialize the struct +/// without hardcoding field names all over the place. +/// +/// The goal is to invoke this macro once with the correct fields, and then this +/// macro generates all necessary code. The main gotcha of this macro is the +/// cgsetters module which is a bunch of generated code to parse an option into +/// its respective field in the struct. There are a few hand-written parsers for +/// parsing specific types of values in this module. +macro_rules! cgoptions( + ($($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) => +( + #[deriving(Clone)] + pub struct CodegenOptions { $(pub $opt: $t),* } + + pub fn basic_codegen_options() -> CodegenOptions { + CodegenOptions { $($opt: $init),* } + } + + pub type CodegenSetter = fn(&mut CodegenOptions, v: Option<&str>) -> bool; + pub const CG_OPTIONS: &'static [(&'static str, CodegenSetter, + Option<&'static str>, &'static str)] = + &[ $( (stringify!($opt), cgsetters::$opt, cg_type_descs::$parse, $desc) ),* ]; + + #[allow(non_upper_case_globals)] + mod cg_type_descs { + pub const parse_bool: Option<&'static str> = None; + pub const parse_opt_bool: Option<&'static str> = None; + pub const parse_string: Option<&'static str> = Some("a string"); + pub const parse_opt_string: Option<&'static str> = Some("a string"); + pub const parse_list: Option<&'static str> = Some("a space-separated list of strings"); + pub const parse_opt_list: Option<&'static str> = Some("a space-separated list of strings"); + pub const parse_uint: Option<&'static str> = Some("a number"); + pub const parse_passes: Option<&'static str> = + Some("a space-separated list of passes, or `all`"); + } + + mod cgsetters { + use super::{CodegenOptions, Passes, SomePasses, AllPasses}; + + $( + pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool { + $parse(&mut cg.$opt, v) + } + )* + + fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { + match v { + Some(..) => false, + None => { *slot = true; true } + } + } + + fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(..) => false, + None => { *slot = Some(true); true } + } + } + + fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = Some(s.to_string()); true }, + None => false, + } + } + + fn parse_string(slot: &mut String, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.to_string(); true }, + None => false, + } + } + + fn parse_list(slot: &mut Vec, v: Option<&str>) + -> bool { + match v { + Some(s) => { + for s in s.words() { + slot.push(s.to_string()); + } + true + }, + None => false, + } + } + + fn parse_opt_list(slot: &mut Option>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + let v = s.words().map(|s| s.to_string()).collect(); + *slot = Some(v); + true + }, + None => false, + } + } + + fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool { + match v.and_then(from_str) { + Some(i) => { *slot = i; true }, + None => false + } + } + + fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { + match v { + Some("all") => { + *slot = AllPasses; + true + } + v => { + let mut passes = vec!(); + if parse_list(&mut passes, v) { + *slot = SomePasses(passes); + true + } else { + false + } + } + } + } + } +) ) + +cgoptions!( + ar: Option = (None, parse_opt_string, + "tool to assemble archives with"), + linker: Option = (None, parse_opt_string, + "system linker to link outputs with"), + link_args: Option> = (None, parse_opt_list, + "extra arguments to pass to the linker (space separated)"), + lto: bool = (false, parse_bool, + "perform LLVM link-time optimizations"), + target_cpu: Option = (None, parse_opt_string, + "select target processor (llc -mcpu=help for details)"), + target_feature: String = ("".to_string(), parse_string, + "target specific attributes (llc -mattr=help for details)"), + passes: Vec = (Vec::new(), parse_list, + "a list of extra LLVM passes to run (space separated)"), + llvm_args: Vec = (Vec::new(), parse_list, + "a list of arguments to pass to llvm (space separated)"), + save_temps: bool = (false, parse_bool, + "save all temporary output files during compilation"), + rpath: bool = (false, parse_bool, + "set rpath values in libs/exes"), + no_prepopulate_passes: bool = (false, parse_bool, + "don't pre-populate the pass manager with a list of passes"), + no_vectorize_loops: bool = (false, parse_bool, + "don't run the loop vectorization optimization passes"), + no_vectorize_slp: bool = (false, parse_bool, + "don't run LLVM's SLP vectorization pass"), + soft_float: bool = (false, parse_bool, + "generate software floating point library calls"), + prefer_dynamic: bool = (false, parse_bool, + "prefer dynamic linking to static linking"), + no_integrated_as: bool = (false, parse_bool, + "use an external assembler rather than LLVM's integrated one"), + no_redzone: Option = (None, parse_opt_bool, + "disable the use of the redzone"), + relocation_model: Option = (None, parse_opt_string, + "choose the relocation model to use (llc -relocation-model for details)"), + code_model: Option = (None, parse_opt_string, + "choose the code model to use (llc -code-model for details)"), + metadata: Vec = (Vec::new(), parse_list, + "metadata to mangle symbol names with"), + extra_filename: String = ("".to_string(), parse_string, + "extra data to put in each output filename"), + codegen_units: uint = (1, parse_uint, + "divide crate into N units to optimize in parallel"), + remark: Passes = (SomePasses(Vec::new()), parse_passes, + "print remarks for these optimization passes (space separated, or \"all\")"), + no_stack_check: bool = (false, parse_bool, + "disable checks for stack exhaustion (a memory-safety hazard!)"), +) + +pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions +{ + let mut cg = basic_codegen_options(); + for option in matches.opt_strs("C").into_iter() { + let mut iter = option.as_slice().splitn(1, '='); + let key = iter.next().unwrap(); + let value = iter.next(); + let option_to_lookup = key.replace("-", "_"); + let mut found = false; + for &(candidate, setter, opt_type_desc, _) in CG_OPTIONS.iter() { + if option_to_lookup.as_slice() != candidate { continue } + if !setter(&mut cg, value) { + match (value, opt_type_desc) { + (Some(..), None) => { + early_error(format!("codegen option `{}` takes no \ + value", key).as_slice()) + } + (None, Some(type_desc)) => { + early_error(format!("codegen option `{0}` requires \ + {1} (-C {0}=)", + key, type_desc).as_slice()) + } + (Some(value), Some(type_desc)) => { + early_error(format!("incorrect value `{}` for codegen \ + option `{}` - {} was expected", + value, key, type_desc).as_slice()) + } + (None, None) => unreachable!() + } + } + found = true; + break; + } + if !found { + early_error(format!("unknown codegen option: `{}`", + key).as_slice()); + } + } + return cg; +} + +pub fn default_lib_output() -> CrateType { + CrateTypeRlib +} + +pub fn default_configuration(sess: &Session) -> ast::CrateConfig { + use syntax::parse::token::intern_and_get_ident as intern; + + let end = sess.target.target.target_endian.as_slice(); + let arch = sess.target.target.arch.as_slice(); + let wordsz = sess.target.target.target_word_size.as_slice(); + let os = sess.target.target.target_os.as_slice(); + + let fam = match sess.target.target.options.is_like_windows { + true => InternedString::new("windows"), + false => InternedString::new("unix") + }; + + let mk = attr::mk_name_value_item_str; + return vec!(// Target bindings. + attr::mk_word_item(fam.clone()), + mk(InternedString::new("target_os"), intern(os)), + mk(InternedString::new("target_family"), fam), + mk(InternedString::new("target_arch"), intern(arch)), + mk(InternedString::new("target_endian"), intern(end)), + mk(InternedString::new("target_word_size"), + intern(wordsz)) + ); +} + +pub fn append_configuration(cfg: &mut ast::CrateConfig, + name: InternedString) { + if !cfg.iter().any(|mi| mi.name() == name) { + cfg.push(attr::mk_word_item(name)) + } +} + +pub fn build_configuration(sess: &Session) -> ast::CrateConfig { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items + let default_cfg = default_configuration(sess); + let mut user_cfg = sess.opts.cfg.clone(); + // If the user wants a test runner, then add the test cfg + if sess.opts.test { + append_configuration(&mut user_cfg, InternedString::new("test")) + } + let mut v = user_cfg.into_iter().collect::>(); + v.push_all(default_cfg.as_slice()); + v +} + +pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config { + let target = match Target::search(opts.target_triple.as_slice()) { + Ok(t) => t, + Err(e) => { + sp.handler().fatal((format!("Error loading target specification: {}", e)).as_slice()); + } + }; + + let (int_type, uint_type) = match target.target_word_size.as_slice() { + "32" => (ast::TyI32, ast::TyU32), + "64" => (ast::TyI64, ast::TyU64), + w => sp.handler().fatal((format!("target specification was invalid: unrecognized \ + target-word-size {}", w)).as_slice()) + }; + + Config { + target: target, + int_type: int_type, + uint_type: uint_type, + } +} + +// rustc command line options +pub fn optgroups() -> Vec { + vec!( + optflag("h", "help", "Display this message"), + optmulti("", "cfg", "Configure the compilation environment", "SPEC"), + optmulti("L", "", "Add a directory to the library search path", "PATH"), + optmulti("l", "", "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of, + static, dylib, or framework. If omitted, dylib is + assumed.", "NAME[:KIND]"), + optmulti("", "crate-type", "Comma separated list of types of crates + for the compiler to emit", + "[bin|lib|rlib|dylib|staticlib]"), + optmulti("", "emit", "Comma separated list of types of output for the compiler to emit", + "[asm|bc|ir|obj|link]"), + optopt("", "crate-name", "Specify the name of the crate being built", + "NAME"), + optflag("", "print-crate-name", "Output the crate name and exit"), + optflag("", "print-file-name", "Output the file(s) that would be written if compilation \ + continued and exit"), + optflag("", "crate-file-name", "deprecated in favor of --print-file-name"), + optflag("g", "", "Equivalent to --debuginfo=2"), + optopt("", "debuginfo", "Emit DWARF debug info to the objects created: + 0 = no debug info, + 1 = line-tables only (for stacktraces and breakpoints), + 2 = full debug info with variable and type information (same as -g)", "LEVEL"), + optflag("", "no-trans", "Run all passes except translation; no output"), + optflag("", "no-analysis", + "Parse and expand the source, but run no analysis and produce no output"), + optflag("O", "", "Equivalent to --opt-level=2"), + optopt("o", "", "Write output to ", "FILENAME"), + optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"), + optopt( "", "out-dir", "Write output to compiler-chosen filename in ", "DIR"), + optflag("", "parse-only", "Parse only; do not compile, assemble, or link"), + optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"), + optflagopt("", "pretty", + "Pretty-print the input instead of compiling; + valid types are: `normal` (un-annotated source), + `expanded` (crates expanded), + `typed` (crates expanded, with type annotations), + `expanded,identified` (fully parenthesized, AST nodes with IDs), or + `flowgraph=` (graphviz formatted flowgraph for node)", + "TYPE"), + optflagopt("", "dep-info", + "Output dependency info to after compiling, \ + in a format suitable for use by Makefiles", "FILENAME"), + optopt("", "sysroot", "Override the system root", "PATH"), + optflag("", "test", "Build a test harness"), + optopt("", "target", "Target triple cpu-manufacturer-kernel[-os] + to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/ + for details)", "TRIPLE"), + optmulti("W", "warn", "Set lint warnings", "OPT"), + optmulti("A", "allow", "Set lint allowed", "OPT"), + optmulti("D", "deny", "Set lint denied", "OPT"), + optmulti("F", "forbid", "Set lint forbidden", "OPT"), + optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + optmulti("Z", "", "Set internal debugging options", "FLAG"), + optflagopt("v", "version", "Print version info and exit", "verbose"), + optopt("", "color", "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", "auto|always|never"), + optmulti("", "extern", "Specify where an external rust library is located", + "NAME=PATH"), + ) +} + + +// Convert strings provided as --cfg [cfgspec] into a crate_cfg +pub fn parse_cfgspecs(cfgspecs: Vec ) -> ast::CrateConfig { + cfgspecs.into_iter().map(|s| { + parse::parse_meta_from_source_str("cfgspec".to_string(), + s.to_string(), + Vec::new(), + &parse::new_parse_sess()) + }).collect::() +} + +pub fn build_session_options(matches: &getopts::Matches) -> Options { + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(e.as_slice())); + + let parse_only = matches.opt_present("parse-only"); + let no_trans = matches.opt_present("no-trans"); + let no_analysis = matches.opt_present("no-analysis"); + + let mut lint_opts = vec!(); + let mut describe_lints = false; + + for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() { + for lint_name in matches.opt_strs(level.as_str()).into_iter() { + if lint_name.as_slice() == "help" { + describe_lints = true; + } else { + lint_opts.push((lint_name.replace("-", "_").into_string(), level)); + } + } + } + + let mut debugging_opts = 0; + let debug_flags = matches.opt_strs("Z"); + let debug_map = debugging_opts_map(); + for debug_flag in debug_flags.iter() { + let mut this_bit = 0; + for tuple in debug_map.iter() { + let (name, bit) = match *tuple { (ref a, _, b) => (a, b) }; + if *name == debug_flag.as_slice() { + this_bit = bit; + break; + } + } + if this_bit == 0 { + early_error(format!("unknown debug flag: {}", + *debug_flag).as_slice()) + } + debugging_opts |= this_bit; + } + + if debugging_opts & DEBUG_LLVM != 0 { + unsafe { llvm::LLVMSetDebug(1); } + } + + let mut output_types = Vec::new(); + if !parse_only && !no_trans { + let unparsed_output_types = matches.opt_strs("emit"); + for unparsed_output_type in unparsed_output_types.iter() { + for part in unparsed_output_type.as_slice().split(',') { + let output_type = match part.as_slice() { + "asm" => OutputTypeAssembly, + "ir" => OutputTypeLlvmAssembly, + "bc" => OutputTypeBitcode, + "obj" => OutputTypeObject, + "link" => OutputTypeExe, + _ => { + early_error(format!("unknown emission type: `{}`", + part).as_slice()) + } + }; + output_types.push(output_type) + } + } + }; + output_types.as_mut_slice().sort(); + output_types.dedup(); + if output_types.len() == 0 { + output_types.push(OutputTypeExe); + } + + let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m)); + let target = matches.opt_str("target").unwrap_or( + host_triple().to_string()); + let opt_level = { + if matches.opt_present("O") { + if matches.opt_present("opt-level") { + early_error("-O and --opt-level both provided"); + } + Default + } else if matches.opt_present("opt-level") { + match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) { + None | + Some("0") => No, + Some("1") => Less, + Some("2") => Default, + Some("3") => Aggressive, + Some(arg) => { + early_error(format!("optimization level needs to be \ + between 0-3 (instead was `{}`)", + arg).as_slice()); + } + } + } else { + No + } + }; + let gc = debugging_opts & GC != 0; + let debuginfo = if matches.opt_present("g") { + if matches.opt_present("debuginfo") { + early_error("-g and --debuginfo both provided"); + } + FullDebugInfo + } else if matches.opt_present("debuginfo") { + match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) { + Some("0") => NoDebugInfo, + Some("1") => LimitedDebugInfo, + None | + Some("2") => FullDebugInfo, + Some(arg) => { + early_error(format!("debug info level needs to be between \ + 0-2 (instead was `{}`)", + arg).as_slice()); + } + } + } else { + NoDebugInfo + }; + + let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| { + Path::new(s.as_slice()) + }).collect(); + + let libs = matches.opt_strs("l").into_iter().map(|s| { + let mut parts = s.as_slice().rsplitn(1, ':'); + let kind = parts.next().unwrap(); + let (name, kind) = match (parts.next(), kind) { + (None, name) | + (Some(name), "dylib") => (name, cstore::NativeUnknown), + (Some(name), "framework") => (name, cstore::NativeFramework), + (Some(name), "static") => (name, cstore::NativeStatic), + (_, s) => { + early_error(format!("unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s).as_slice()); + } + }; + (name.to_string(), kind) + }).collect(); + + let cfg = parse_cfgspecs(matches.opt_strs("cfg")); + let test = matches.opt_present("test"); + let write_dependency_info = (matches.opt_present("dep-info"), + matches.opt_str("dep-info") + .map(|p| Path::new(p))); + + let print_metas = (matches.opt_present("print-crate-name"), + matches.opt_present("print-file-name") || + matches.opt_present("crate-file-name")); + if matches.opt_present("crate-file-name") { + early_warn("the --crate-file-name argument has been renamed to \ + --print-file-name"); + } + let cg = build_codegen_options(matches); + + if !cg.remark.is_empty() && debuginfo == NoDebugInfo { + early_warn("-C remark will not show source locations without --debuginfo"); + } + + let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) { + Some("auto") => Auto, + Some("always") => Always, + Some("never") => Never, + + None => Auto, + + Some(arg) => { + early_error(format!("argument for --color must be auto, always \ + or never (instead was `{}`)", + arg).as_slice()) + } + }; + + let mut externs = HashMap::new(); + for arg in matches.opt_strs("extern").iter() { + let mut parts = arg.as_slice().splitn(1, '='); + let name = match parts.next() { + Some(s) => s, + None => early_error("--extern value must not be empty"), + }; + let location = match parts.next() { + Some(s) => s, + None => early_error("--extern value must be of the format `foo=bar`"), + }; + + match externs.entry(name.to_string()) { + Vacant(entry) => { entry.set(vec![location.to_string()]); }, + Occupied(mut entry) => { entry.get_mut().push(location.to_string()); }, + } + } + + let crate_name = matches.opt_str("crate-name"); + + Options { + crate_types: crate_types, + gc: gc, + optimize: opt_level, + debuginfo: debuginfo, + lint_opts: lint_opts, + describe_lints: describe_lints, + output_types: output_types, + addl_lib_search_paths: RefCell::new(addl_lib_search_paths), + maybe_sysroot: sysroot_opt, + target_triple: target, + cfg: cfg, + test: test, + parse_only: parse_only, + no_trans: no_trans, + no_analysis: no_analysis, + debugging_opts: debugging_opts, + write_dependency_info: write_dependency_info, + print_metas: print_metas, + cg: cg, + color: color, + externs: externs, + crate_name: crate_name, + alt_std_name: None, + libs: libs, + } +} + +pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { + + let mut crate_types: Vec = Vec::new(); + for unparsed_crate_type in list_list.iter() { + for part in unparsed_crate_type.as_slice().split(',') { + let new_part = match part { + "lib" => default_lib_output(), + "rlib" => CrateTypeRlib, + "staticlib" => CrateTypeStaticlib, + "dylib" => CrateTypeDylib, + "bin" => CrateTypeExecutable, + _ => { + return Err(format!("unknown crate type: `{}`", + part)); + } + }; + crate_types.push(new_part) + } + } + + return Ok(crate_types); +} + +impl fmt::Show for CrateType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CrateTypeExecutable => "bin".fmt(f), + CrateTypeDylib => "dylib".fmt(f), + CrateTypeRlib => "rlib".fmt(f), + CrateTypeStaticlib => "staticlib".fmt(f) + } + } +} + +#[cfg(test)] +mod test { + + use session::config::{build_configuration, optgroups, build_session_options}; + use session::build_session; + + use getopts::getopts; + use syntax::attr; + use syntax::attr::AttrMetaMethods; + use syntax::diagnostics; + + // When the user supplies --test we should implicitly supply --cfg test + #[test] + fn test_switch_implies_cfg_test() { + let matches = + &match getopts(&["--test".to_string()], optgroups().as_slice()) { + Ok(m) => m, + Err(f) => panic!("test_switch_implies_cfg_test: {}", f) + }; + let registry = diagnostics::registry::Registry::new(&[]); + let sessopts = build_session_options(matches); + let sess = build_session(sessopts, None, registry); + let cfg = build_configuration(&sess); + assert!((attr::contains_name(cfg.as_slice(), "test"))); + } + + // When the user supplies --test and --cfg test, don't implicitly add + // another --cfg test + #[test] + fn test_switch_implies_cfg_test_unless_cfg_test() { + let matches = + &match getopts(&["--test".to_string(), "--cfg=test".to_string()], + optgroups().as_slice()) { + Ok(m) => m, + Err(f) => { + panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f) + } + }; + let registry = diagnostics::registry::Registry::new(&[]); + let sessopts = build_session_options(matches); + let sess = build_session(sessopts, None, registry); + let cfg = build_configuration(&sess); + let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test"))); + assert!(test_items.next().is_some()); + assert!(test_items.next().is_none()); + } +} diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs new file mode 100644 index 00000000000..d723db7706f --- /dev/null +++ b/src/librustc/session/mod.rs @@ -0,0 +1,277 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use metadata::cstore::CStore; +use metadata::filesearch; +use lint; +use util::nodemap::NodeMap; + +use syntax::ast::NodeId; +use syntax::codemap::Span; +use syntax::diagnostic::{mod, Emitter}; +use syntax::diagnostics; +use syntax::feature_gate; +use syntax::parse; +use syntax::parse::token; +use syntax::parse::ParseSess; +use syntax::{ast, codemap}; + +use std::os; +use std::cell::{Cell, RefCell}; + +pub mod config; + +// Represents the data associated with a compilation +// session for a single crate. +pub struct Session { + pub target: config::Config, + pub opts: config::Options, + pub cstore: CStore, + pub parse_sess: ParseSess, + // For a library crate, this is always none + pub entry_fn: RefCell>, + pub entry_type: Cell>, + pub plugin_registrar_fn: Cell>, + pub default_sysroot: Option, + // The name of the root source file of the crate, in the local file system. The path is always + // expected to be absolute. `None` means that there is no source file. + pub local_crate_source_file: Option, + pub working_dir: Path, + pub lint_store: RefCell, + pub lints: RefCell>>, + pub crate_types: RefCell>, + pub crate_metadata: RefCell>, + pub features: RefCell, + + /// The maximum recursion limit for potentially infinitely recursive + /// operations such as auto-dereference and monomorphization. + pub recursion_limit: Cell, +} + +impl Session { + pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { + self.diagnostic().span_fatal(sp, msg) + } + pub fn fatal(&self, msg: &str) -> ! { + self.diagnostic().handler().fatal(msg) + } + pub fn span_err(&self, sp: Span, msg: &str) { + self.diagnostic().span_err(sp, msg) + } + pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) { + self.diagnostic().span_err_with_code(sp, msg, code) + } + pub fn err(&self, msg: &str) { + self.diagnostic().handler().err(msg) + } + pub fn err_count(&self) -> uint { + self.diagnostic().handler().err_count() + } + pub fn has_errors(&self) -> bool { + self.diagnostic().handler().has_errors() + } + pub fn abort_if_errors(&self) { + self.diagnostic().handler().abort_if_errors() + } + pub fn span_warn(&self, sp: Span, msg: &str) { + self.diagnostic().span_warn(sp, msg) + } + pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { + self.diagnostic().span_warn_with_code(sp, msg, code) + } + pub fn warn(&self, msg: &str) { + self.diagnostic().handler().warn(msg) + } + pub fn span_note(&self, sp: Span, msg: &str) { + self.diagnostic().span_note(sp, msg) + } + pub fn span_end_note(&self, sp: Span, msg: &str) { + self.diagnostic().span_end_note(sp, msg) + } + pub fn span_help(&self, sp: Span, msg: &str) { + self.diagnostic().span_help(sp, msg) + } + pub fn fileline_note(&self, sp: Span, msg: &str) { + self.diagnostic().fileline_note(sp, msg) + } + pub fn note(&self, msg: &str) { + self.diagnostic().handler().note(msg) + } + pub fn help(&self, msg: &str) { + self.diagnostic().handler().note(msg) + } + pub fn span_bug(&self, sp: Span, msg: &str) -> ! { + self.diagnostic().span_bug(sp, msg) + } + pub fn bug(&self, msg: &str) -> ! { + self.diagnostic().handler().bug(msg) + } + pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { + self.diagnostic().span_unimpl(sp, msg) + } + pub fn unimpl(&self, msg: &str) -> ! { + self.diagnostic().handler().unimpl(msg) + } + pub fn add_lint(&self, + lint: &'static lint::Lint, + id: ast::NodeId, + sp: Span, + msg: String) { + let lint_id = lint::LintId::of(lint); + let mut lints = self.lints.borrow_mut(); + match lints.get_mut(&id) { + Some(arr) => { arr.push((lint_id, sp, msg)); return; } + None => {} + } + lints.insert(id, vec!((lint_id, sp, msg))); + } + pub fn next_node_id(&self) -> ast::NodeId { + self.parse_sess.next_node_id() + } + pub fn reserve_node_ids(&self, count: ast::NodeId) -> ast::NodeId { + self.parse_sess.reserve_node_ids(count) + } + pub fn diagnostic<'a>(&'a self) -> &'a diagnostic::SpanHandler { + &self.parse_sess.span_diagnostic + } + pub fn debugging_opt(&self, opt: u64) -> bool { + (self.opts.debugging_opts & opt) != 0 + } + pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap { + &self.parse_sess.span_diagnostic.cm + } + // This exists to help with refactoring to eliminate impossible + // cases later on + pub fn impossible_case(&self, sp: Span, msg: &str) -> ! { + self.span_bug(sp, + format!("impossible case reached: {}", msg).as_slice()); + } + pub fn verbose(&self) -> bool { self.debugging_opt(config::VERBOSE) } + pub fn time_passes(&self) -> bool { self.debugging_opt(config::TIME_PASSES) } + pub fn count_llvm_insns(&self) -> bool { + self.debugging_opt(config::COUNT_LLVM_INSNS) + } + pub fn count_type_sizes(&self) -> bool { + self.debugging_opt(config::COUNT_TYPE_SIZES) + } + pub fn time_llvm_passes(&self) -> bool { + self.debugging_opt(config::TIME_LLVM_PASSES) + } + pub fn trans_stats(&self) -> bool { self.debugging_opt(config::TRANS_STATS) } + pub fn meta_stats(&self) -> bool { self.debugging_opt(config::META_STATS) } + pub fn asm_comments(&self) -> bool { self.debugging_opt(config::ASM_COMMENTS) } + pub fn no_verify(&self) -> bool { self.debugging_opt(config::NO_VERIFY) } + pub fn borrowck_stats(&self) -> bool { self.debugging_opt(config::BORROWCK_STATS) } + pub fn print_llvm_passes(&self) -> bool { + self.debugging_opt(config::PRINT_LLVM_PASSES) + } + pub fn lto(&self) -> bool { + self.opts.cg.lto + } + pub fn no_landing_pads(&self) -> bool { + self.debugging_opt(config::NO_LANDING_PADS) + } + pub fn show_span(&self) -> bool { + self.debugging_opt(config::SHOW_SPAN) + } + pub fn sysroot<'a>(&'a self) -> &'a Path { + match self.opts.maybe_sysroot { + Some (ref sysroot) => sysroot, + None => self.default_sysroot.as_ref() + .expect("missing sysroot and default_sysroot in Session") + } + } + pub fn target_filesearch<'a>(&'a self) -> filesearch::FileSearch<'a> { + filesearch::FileSearch::new(self.sysroot(), + self.opts.target_triple.as_slice(), + &self.opts.addl_lib_search_paths) + } + pub fn host_filesearch<'a>(&'a self) -> filesearch::FileSearch<'a> { + filesearch::FileSearch::new( + self.sysroot(), + config::host_triple(), + &self.opts.addl_lib_search_paths) + } +} + +pub fn build_session(sopts: config::Options, + local_crate_source_file: Option, + registry: diagnostics::registry::Registry) + -> Session { + let codemap = codemap::CodeMap::new(); + let diagnostic_handler = + diagnostic::default_handler(sopts.color, Some(registry)); + let span_diagnostic_handler = + diagnostic::mk_span_handler(diagnostic_handler, codemap); + + build_session_(sopts, local_crate_source_file, span_diagnostic_handler) +} + +pub fn build_session_(sopts: config::Options, + local_crate_source_file: Option, + span_diagnostic: diagnostic::SpanHandler) + -> Session { + let target_cfg = config::build_target_config(&sopts, &span_diagnostic); + let p_s = parse::new_parse_sess_special_handler(span_diagnostic); + let default_sysroot = match sopts.maybe_sysroot { + Some(_) => None, + None => Some(filesearch::get_or_default_sysroot()) + }; + + // Make the path absolute, if necessary + let local_crate_source_file = local_crate_source_file.map(|path| + if path.is_absolute() { + path.clone() + } else { + os::getcwd().join(&path) + } + ); + + let sess = Session { + target: target_cfg, + opts: sopts, + cstore: CStore::new(token::get_ident_interner()), + parse_sess: p_s, + // For a library crate, this is always none + entry_fn: RefCell::new(None), + entry_type: Cell::new(None), + plugin_registrar_fn: Cell::new(None), + default_sysroot: default_sysroot, + local_crate_source_file: local_crate_source_file, + working_dir: os::getcwd(), + lint_store: RefCell::new(lint::LintStore::new()), + lints: RefCell::new(NodeMap::new()), + crate_types: RefCell::new(Vec::new()), + crate_metadata: RefCell::new(Vec::new()), + features: RefCell::new(feature_gate::Features::new()), + recursion_limit: Cell::new(64), + }; + + sess.lint_store.borrow_mut().register_builtin(Some(&sess)); + sess +} + +// Seems out of place, but it uses session, so I'm putting it here +pub fn expect(sess: &Session, opt: Option, msg: || -> String) -> T { + diagnostic::expect(sess.diagnostic(), opt, msg) +} + +pub fn early_error(msg: &str) -> ! { + let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); + emitter.emit(None, msg, None, diagnostic::Fatal); + panic!(diagnostic::FatalError); +} + +pub fn early_warn(msg: &str) { + let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); + emitter.emit(None, msg, None, diagnostic::Warning); +} + diff --git a/src/librustc_trans/README.txt b/src/librustc_trans/README.txt new file mode 100644 index 00000000000..3904db4433c --- /dev/null +++ b/src/librustc_trans/README.txt @@ -0,0 +1 @@ +See the README.txt in ../librustc. diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs new file mode 100644 index 00000000000..d5d488e8965 --- /dev/null +++ b/src/librustc_trans/back/link.rs @@ -0,0 +1,1313 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; +use super::archive; +use super::rpath; +use super::rpath::RPathConfig; +use super::svh::Svh; +use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput}; +use session::config; +use session::config::NoDebugInfo; +use session::config::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; +use session::Session; +use metadata::common::LinkMeta; +use metadata::{encoder, cstore, filesearch, csearch, creader}; +use trans::context::CrateContext; +use trans::common::gensym_name; +use middle::ty; +use util::common::time; +use util::ppaux; +use util::sha2::{Digest, Sha256}; + +use std::char; +use std::io::fs::PathExtensions; +use std::io::{fs, TempDir, Command}; +use std::io; +use std::mem; +use std::str; +use std::string::String; +use flate; +use serialize::hex::ToHex; +use syntax::ast; +use syntax::ast_map::{PathElem, PathElems, PathName}; +use syntax::ast_map; +use syntax::attr::AttrMetaMethods; +use syntax::codemap::Span; +use syntax::parse::token; + +// RLIB LLVM-BYTECODE OBJECT LAYOUT +// Version 1 +// Bytes Data +// 0..10 "RUST_OBJECT" encoded in ASCII +// 11..14 format version as little-endian u32 +// 15..22 size in bytes of deflate compressed LLVM bitcode as +// little-endian u64 +// 23.. compressed LLVM bitcode + +// This is the "magic number" expected at the beginning of a LLVM bytecode +// object in an rlib. +pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; + +// The version number this compiler will write to bytecode objects in rlibs +pub const RLIB_BYTECODE_OBJECT_VERSION: u32 = 1; + +// The offset in bytes the bytecode object format version number can be found at +pub const RLIB_BYTECODE_OBJECT_VERSION_OFFSET: uint = 11; + +// The offset in bytes the size of the compressed bytecode can be found at in +// format version 1 +pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: uint = + RLIB_BYTECODE_OBJECT_VERSION_OFFSET + 4; + +// The offset in bytes the compressed LLVM bytecode can be found at in format +// version 1 +pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint = + RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; + + +/* + * Name mangling and its relationship to metadata. This is complex. Read + * carefully. + * + * The semantic model of Rust linkage is, broadly, that "there's no global + * namespace" between crates. Our aim is to preserve the illusion of this + * model despite the fact that it's not *quite* possible to implement on + * modern linkers. We initially didn't use system linkers at all, but have + * been convinced of their utility. + * + * There are a few issues to handle: + * + * - Linkers operate on a flat namespace, so we have to flatten names. + * We do this using the C++ namespace-mangling technique. Foo::bar + * symbols and such. + * + * - Symbols with the same name but different types need to get different + * linkage-names. We do this by hashing a string-encoding of the type into + * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: + * we use SHA256) to "prevent collisions". This is not airtight but 16 hex + * digits on uniform probability means you're going to need 2**32 same-name + * symbols in the same process before you're even hitting birthday-paradox + * collision probability. + * + * - Symbols in different crates but with same names "within" the crate need + * to get different linkage-names. + * + * - The hash shown in the filename needs to be predictable and stable for + * build tooling integration. It also needs to be using a hash function + * which is easy to use from Python, make, etc. + * + * So here is what we do: + * + * - Consider the package id; every crate has one (specified with crate_id + * attribute). If a package id isn't provided explicitly, we infer a + * versionless one from the output name. The version will end up being 0.0 + * in this case. CNAME and CVERS are taken from this package id. For + * example, github.com/mozilla/CNAME#CVERS. + * + * - Define CMH as SHA256(crateid). + * + * - Define CMH8 as the first 8 characters of CMH. + * + * - Compile our crate to lib CNAME-CMH8-CVERS.so + * + * - Define STH(sym) as SHA256(CMH, type_str(sym)) + * + * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the + * name, non-name metadata, and type sense, and versioned in the way + * system linkers understand. + */ + +pub fn find_crate_name(sess: Option<&Session>, + attrs: &[ast::Attribute], + input: &Input) -> String { + let validate = |s: String, span: Option| { + creader::validate_crate_name(sess, s.as_slice(), span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = attrs.iter().find(|at| at.check_name("crate_name")) + .and_then(|at| at.value_str().map(|s| (at, s))); + + match sess { + Some(sess) => { + match sess.opts.crate_name { + Some(ref s) => { + match attr_crate_name { + Some((attr, ref name)) if s.as_slice() != name.get() => { + let msg = format!("--crate-name and #[crate_name] \ + are required to match, but `{}` \ + != `{}`", s, name); + sess.span_err(attr.span, msg.as_slice()); + } + _ => {}, + } + return validate(s.clone(), None); + } + None => {} + } + } + None => {} + } + + match attr_crate_name { + Some((attr, s)) => return validate(s.get().to_string(), Some(attr.span)), + None => {} + } + match *input { + FileInput(ref path) => { + match path.filestem_str() { + Some(s) => return validate(s.to_string(), None), + None => {} + } + } + _ => {} + } + + "rust-out".to_string() +} + +pub fn build_link_meta(sess: &Session, krate: &ast::Crate, + name: String) -> LinkMeta { + let r = LinkMeta { + crate_name: name, + crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate), + }; + info!("{}", r); + return r; +} + +fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String { + let output = symbol_hasher.result_bytes(); + // 64 bits should be enough to avoid collisions. + output.slice_to(8).to_hex().to_string() +} + + +// This calculates STH for a symbol, as defined above +fn symbol_hash(tcx: &ty::ctxt, + symbol_hasher: &mut Sha256, + t: ty::t, + link_meta: &LinkMeta) + -> String { + // NB: do *not* use abbrevs here as we want the symbol names + // to be independent of one another in the crate. + + symbol_hasher.reset(); + symbol_hasher.input_str(link_meta.crate_name.as_slice()); + symbol_hasher.input_str("-"); + symbol_hasher.input_str(link_meta.crate_hash.as_str()); + for meta in tcx.sess.crate_metadata.borrow().iter() { + symbol_hasher.input_str(meta.as_slice()); + } + symbol_hasher.input_str("-"); + symbol_hasher.input_str(encoder::encoded_ty(tcx, t).as_slice()); + // Prefix with 'h' so that it never blends into adjacent digits + let mut hash = String::from_str("h"); + hash.push_str(truncated_hash_result(symbol_hasher).as_slice()); + hash +} + +fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String { + match ccx.type_hashcodes().borrow().get(&t) { + Some(h) => return h.to_string(), + None => {} + } + + let mut symbol_hasher = ccx.symbol_hasher().borrow_mut(); + let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, ccx.link_meta()); + ccx.type_hashcodes().borrow_mut().insert(t, hash.clone()); + hash +} + + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ +pub fn sanitize(s: &str) -> String { + let mut result = String::new(); + for c in s.chars() { + match c { + // Escape these with $ sequences + '@' => result.push_str("$SP$"), + '~' => result.push_str("$UP$"), + '*' => result.push_str("$RP$"), + '&' => result.push_str("$BP$"), + '<' => result.push_str("$LT$"), + '>' => result.push_str("$GT$"), + '(' => result.push_str("$LP$"), + ')' => result.push_str("$RP$"), + ',' => result.push_str("$C$"), + + // '.' doesn't occur in types and functions, so reuse it + // for ':' and '-' + '-' | ':' => result.push('.'), + + // These are legal symbols + 'a' ... 'z' + | 'A' ... 'Z' + | '0' ... '9' + | '_' | '.' | '$' => result.push(c), + + _ => { + let mut tstr = String::new(); + char::escape_unicode(c, |c| tstr.push(c)); + result.push('$'); + result.push_str(tstr.as_slice().slice_from(1)); + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + if result.len() > 0u && + result.as_bytes()[0] != '_' as u8 && + ! char::is_XID_start(result.as_bytes()[0] as char) { + return format!("_{}", result.as_slice()); + } + + return result; +} + +pub fn mangle>(mut path: PI, + hash: Option<&str>) -> String { + // Follow C++ namespace-mangling style, see + // http://en.wikipedia.org/wiki/Name_mangling for more info. + // + // It turns out that on OSX you can actually have arbitrary symbols in + // function names (at least when given to LLVM), but this is not possible + // when using unix's linker. Perhaps one day when we just use a linker from LLVM + // we won't need to do this name mangling. The problem with name mangling is + // that it seriously limits the available characters. For example we can't + // have things like &T or ~[T] in symbol names when one would theoretically + // want them for things like impls of traits on that type. + // + // To be able to work on all platforms and get *some* reasonable output, we + // use C++ name-mangling. + + let mut n = String::from_str("_ZN"); // _Z == Begin name-sequence, N == nested + + fn push(n: &mut String, s: &str) { + let sani = sanitize(s); + n.push_str(format!("{}{}", sani.len(), sani).as_slice()); + } + + // First, connect each component with pairs. + for e in path { + push(&mut n, token::get_name(e.name()).get().as_slice()) + } + + match hash { + Some(s) => push(&mut n, s), + None => {} + } + + n.push('E'); // End name-sequence. + n +} + +pub fn exported_name(path: PathElems, hash: &str) -> String { + mangle(path, Some(hash)) +} + +pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems, + t: ty::t, id: ast::NodeId) -> String { + let mut hash = get_symbol_hash(ccx, t); + + // Paths can be completely identical for different nodes, + // e.g. `fn foo() { { fn a() {} } { fn a() {} } }`, so we + // generate unique characters from the node id. For now + // hopefully 3 characters is enough to avoid collisions. + static EXTRA_CHARS: &'static str = + "abcdefghijklmnopqrstuvwxyz\ + ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + 0123456789"; + let id = id as uint; + let extra1 = id % EXTRA_CHARS.len(); + let id = id / EXTRA_CHARS.len(); + let extra2 = id % EXTRA_CHARS.len(); + let id = id / EXTRA_CHARS.len(); + let extra3 = id % EXTRA_CHARS.len(); + hash.push(EXTRA_CHARS.as_bytes()[extra1] as char); + hash.push(EXTRA_CHARS.as_bytes()[extra2] as char); + hash.push(EXTRA_CHARS.as_bytes()[extra3] as char); + + exported_name(path, hash.as_slice()) +} + +pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext, + t: ty::t, + name: &str) -> String { + let s = ppaux::ty_to_string(ccx.tcx(), t); + let path = [PathName(token::intern(s.as_slice())), + gensym_name(name)]; + let hash = get_symbol_hash(ccx, t); + mangle(ast_map::Values(path.iter()), Some(hash.as_slice())) +} + +pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> String { + mangle(path.chain(Some(gensym_name(flav)).into_iter()), None) +} + +pub fn get_cc_prog(sess: &Session) -> String { + match sess.opts.cg.linker { + Some(ref linker) => return linker.to_string(), + None => sess.target.target.options.linker.clone(), + } +} + +pub fn remove(sess: &Session, path: &Path) { + match fs::unlink(path) { + Ok(..) => {} + Err(e) => { + sess.err(format!("failed to remove {}: {}", + path.display(), + e).as_slice()); + } + } +} + +/// Perform the linkage portion of the compilation phase. This will generate all +/// of the requested outputs for this compilation session. +pub fn link_binary(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames, + crate_name: &str) -> Vec { + let mut out_filenames = Vec::new(); + for &crate_type in sess.crate_types.borrow().iter() { + if invalid_output_for_target(sess, crate_type) { + sess.bug(format!("invalid output type `{}` for target os `{}`", + crate_type, sess.opts.target_triple).as_slice()); + } + let out_file = link_binary_output(sess, trans, crate_type, outputs, + crate_name); + out_filenames.push(out_file); + } + + // Remove the temporary object file and metadata if we aren't saving temps + if !sess.opts.cg.save_temps { + let obj_filename = outputs.temp_path(OutputTypeObject); + if !sess.opts.output_types.contains(&OutputTypeObject) { + remove(sess, &obj_filename); + } + remove(sess, &obj_filename.with_extension("metadata.o")); + } + + out_filenames +} + + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateTypeExecutable for all platforms but iOS as there is no +/// way to run iOS binaries anyway without jailbreaking and +/// interaction with Rust code through static library is the only +/// option for now +pub fn default_output_for_target(sess: &Session) -> config::CrateType { + if !sess.target.target.options.executables { + config::CrateTypeStaticlib + } else { + config::CrateTypeExecutable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, + crate_type: config::CrateType) -> bool { + match (sess.target.target.options.dynamic_linking, + sess.target.target.options.executables, crate_type) { + (false, _, config::CrateTypeDylib) => true, + (_, false, config::CrateTypeExecutable) => true, + _ => false + } +} + +fn is_writeable(p: &Path) -> bool { + match p.stat() { + Err(..) => true, + Ok(m) => m.perm & io::USER_WRITE == io::USER_WRITE + } +} + +pub fn filename_for_input(sess: &Session, + crate_type: config::CrateType, + name: &str, + out_filename: &Path) -> Path { + let libname = format!("{}{}", name, sess.opts.cg.extra_filename); + match crate_type { + config::CrateTypeRlib => { + out_filename.with_filename(format!("lib{}.rlib", libname)) + } + config::CrateTypeDylib => { + let (prefix, suffix) = (sess.target.target.options.dll_prefix.as_slice(), + sess.target.target.options.dll_suffix.as_slice()); + out_filename.with_filename(format!("{}{}{}", + prefix, + libname, + suffix)) + } + config::CrateTypeStaticlib => { + out_filename.with_filename(format!("lib{}.a", libname)) + } + config::CrateTypeExecutable => { + let suffix = sess.target.target.options.exe_suffix.as_slice(); + out_filename.with_filename(format!("{}{}", libname, suffix)) + } + } +} + +fn link_binary_output(sess: &Session, + trans: &CrateTranslation, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) -> Path { + let obj_filename = outputs.temp_path(OutputTypeObject); + let out_filename = match outputs.single_output_file { + Some(ref file) => file.clone(), + None => { + let out_filename = outputs.path(OutputTypeExe); + filename_for_input(sess, crate_type, crate_name, &out_filename) + } + }; + + // Make sure the output and obj_filename are both writeable. + // Mac, FreeBSD, and Windows system linkers check this already -- + // however, the Linux linker will happily overwrite a read-only file. + // We should be consistent. + let obj_is_writeable = is_writeable(&obj_filename); + let out_is_writeable = is_writeable(&out_filename); + if !out_is_writeable { + sess.fatal(format!("output file {} is not writeable -- check its \ + permissions.", + out_filename.display()).as_slice()); + } + else if !obj_is_writeable { + sess.fatal(format!("object file {} is not writeable -- check its \ + permissions.", + obj_filename.display()).as_slice()); + } + + match crate_type { + config::CrateTypeRlib => { + link_rlib(sess, Some(trans), &obj_filename, &out_filename).build(); + } + config::CrateTypeStaticlib => { + link_staticlib(sess, &obj_filename, &out_filename); + } + config::CrateTypeExecutable => { + link_natively(sess, trans, false, &obj_filename, &out_filename); + } + config::CrateTypeDylib => { + link_natively(sess, trans, true, &obj_filename, &out_filename); + } + } + + out_filename +} + +fn archive_search_paths(sess: &Session) -> Vec { + let mut rustpath = filesearch::rust_path(); + rustpath.push(sess.target_filesearch().get_lib_path()); + let mut search: Vec = sess.opts.addl_lib_search_paths.borrow().clone(); + search.push_all(rustpath.as_slice()); + return search; +} + +// Create an 'rlib' +// +// An rlib in its current incarnation is essentially a renamed .a file. The +// rlib primarily contains the object file of the crate, but it also contains +// all of the object files from native libraries. This is done by unzipping +// native libraries and inserting all of the contents into this archive. +fn link_rlib<'a>(sess: &'a Session, + trans: Option<&CrateTranslation>, // None == no metadata/bytecode + obj_filename: &Path, + out_filename: &Path) -> ArchiveBuilder<'a> { + let handler = &sess.diagnostic().handler; + let config = ArchiveConfig { + handler: handler, + dst: out_filename.clone(), + lib_search_paths: archive_search_paths(sess), + slib_prefix: sess.target.target.options.staticlib_prefix.clone(), + slib_suffix: sess.target.target.options.staticlib_suffix.clone(), + maybe_ar_prog: sess.opts.cg.ar.clone() + }; + let mut ab = ArchiveBuilder::create(config); + ab.add_file(obj_filename).unwrap(); + + for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() { + match kind { + cstore::NativeStatic => { + ab.add_native_library(l.as_slice()).unwrap(); + } + cstore::NativeFramework | cstore::NativeUnknown => {} + } + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. + ab.update_symbols(); + + let mut ab = match sess.target.target.options.is_like_osx { + // For OSX/iOS, we must be careful to update symbols only when adding + // object files. We're about to start adding non-object files, so run + // `ar` now to process the object files. + true => ab.build().extend(), + false => ab, + }; + + // Note that it is important that we add all of our non-object "magical + // files" *after* all of the object files in the archive. The reason for + // this is as follows: + // + // * When performing LTO, this archive will be modified to remove + // obj_filename from above. The reason for this is described below. + // + // * When the system linker looks at an archive, it will attempt to + // determine the architecture of the archive in order to see whether its + // linkable. + // + // The algorithm for this detection is: iterate over the files in the + // archive. Skip magical SYMDEF names. Interpret the first file as an + // object file. Read architecture from the object file. + // + // * As one can probably see, if "metadata" and "foo.bc" were placed + // before all of the objects, then the architecture of this archive would + // not be correctly inferred once 'foo.o' is removed. + // + // Basically, all this means is that this code should not move above the + // code above. + match trans { + Some(trans) => { + // Instead of putting the metadata in an object file section, rlibs + // contain the metadata in a separate file. We use a temp directory + // here so concurrent builds in the same directory don't try to use + // the same filename for metadata (stomping over one another) + let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); + let metadata = tmpdir.path().join(METADATA_FILENAME); + match fs::File::create(&metadata).write(trans.metadata + .as_slice()) { + Ok(..) => {} + Err(e) => { + sess.err(format!("failed to write {}: {}", + metadata.display(), + e).as_slice()); + sess.abort_if_errors(); + } + } + ab.add_file(&metadata).unwrap(); + remove(sess, &metadata); + + // For LTO purposes, the bytecode of this library is also inserted + // into the archive. If codegen_units > 1, we insert each of the + // bitcode files. + for i in range(0, sess.opts.cg.codegen_units) { + // Note that we make sure that the bytecode filename in the + // archive is never exactly 16 bytes long by adding a 16 byte + // extension to it. This is to work around a bug in LLDB that + // would cause it to crash if the name of a file in an archive + // was exactly 16 bytes. + let bc_filename = obj_filename.with_extension(format!("{}.bc", i).as_slice()); + let bc_deflated_filename = obj_filename.with_extension( + format!("{}.bytecode.deflate", i).as_slice()); + + let bc_data = match fs::File::open(&bc_filename).read_to_end() { + Ok(buffer) => buffer, + Err(e) => sess.fatal(format!("failed to read bytecode: {}", + e).as_slice()) + }; + + let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) { + Some(compressed) => compressed, + None => sess.fatal(format!("failed to compress bytecode from {}", + bc_filename.display()).as_slice()) + }; + + let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { + Ok(file) => file, + Err(e) => { + sess.fatal(format!("failed to create compressed bytecode \ + file: {}", e).as_slice()) + } + }; + + match write_rlib_bytecode_object_v1(&mut bc_file_deflated, + bc_data_deflated.as_slice()) { + Ok(()) => {} + Err(e) => { + sess.err(format!("failed to write compressed bytecode: \ + {}", e).as_slice()); + sess.abort_if_errors() + } + }; + + ab.add_file(&bc_deflated_filename).unwrap(); + remove(sess, &bc_deflated_filename); + + // See the bottom of back::write::run_passes for an explanation + // of when we do and don't keep .0.bc files around. + let user_wants_numbered_bitcode = + sess.opts.output_types.contains(&OutputTypeBitcode) && + sess.opts.cg.codegen_units > 1; + if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { + remove(sess, &bc_filename); + } + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. This currently dies on OSX (see + // #11162), and isn't necessary there anyway + if !sess.target.target.options.is_like_osx { + ab.update_symbols(); + } + } + + None => {} + } + + ab +} + +fn write_rlib_bytecode_object_v1(writer: &mut T, + bc_data_deflated: &[u8]) + -> ::std::io::IoResult<()> { + let bc_data_deflated_size: u64 = bc_data_deflated.as_slice().len() as u64; + + try! { writer.write(RLIB_BYTECODE_OBJECT_MAGIC) }; + try! { writer.write_le_u32(1) }; + try! { writer.write_le_u64(bc_data_deflated_size) }; + try! { writer.write(bc_data_deflated.as_slice()) }; + + let number_of_bytes_written_so_far = + RLIB_BYTECODE_OBJECT_MAGIC.len() + // magic id + mem::size_of_val(&RLIB_BYTECODE_OBJECT_VERSION) + // version + mem::size_of_val(&bc_data_deflated_size) + // data size field + bc_data_deflated_size as uint; // actual data + + // If the number of bytes written to the object so far is odd, add a + // padding byte to make it even. This works around a crash bug in LLDB + // (see issue #15950) + if number_of_bytes_written_so_far % 2 == 1 { + try! { writer.write_u8(0) }; + } + + return Ok(()); +} + +// Create a static archive +// +// This is essentially the same thing as an rlib, but it also involves adding +// all of the upstream crates' objects into the archive. This will slurp in +// all of the native libraries of upstream dependencies as well. +// +// Additionally, there's no way for us to link dynamic libraries, so we warn +// about all dynamic library dependencies that they're not linked in. +// +// There's no need to include metadata in a static archive, so ensure to not +// link in the metadata object file (and also don't prepare the archive with a +// metadata file). +fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) { + let ab = link_rlib(sess, None, obj_filename, out_filename); + let mut ab = match sess.target.target.options.is_like_osx { + true => ab.build().extend(), + false => ab, + }; + if sess.target.target.options.morestack { + ab.add_native_library("morestack").unwrap(); + } + if !sess.target.target.options.no_compiler_rt { + ab.add_native_library("compiler-rt").unwrap(); + } + + let crates = sess.cstore.get_used_crates(cstore::RequireStatic); + let mut all_native_libs = vec![]; + + for &(cnum, ref path) in crates.iter() { + let ref name = sess.cstore.get_crate_data(cnum).name; + let p = match *path { + Some(ref p) => p.clone(), None => { + sess.err(format!("could not find rlib for: `{}`", + name).as_slice()); + continue + } + }; + ab.add_rlib(&p, name.as_slice(), sess.lto()).unwrap(); + + let native_libs = csearch::get_native_libraries(&sess.cstore, cnum); + all_native_libs.extend(native_libs.into_iter()); + } + + ab.update_symbols(); + let _ = ab.build(); + + if !all_native_libs.is_empty() { + sess.warn("link against the following native artifacts when linking against \ + this static library"); + sess.note("the order and any duplication can be significant on some platforms, \ + and so may need to be preserved"); + } + + for &(kind, ref lib) in all_native_libs.iter() { + let name = match kind { + cstore::NativeStatic => "static library", + cstore::NativeUnknown => "library", + cstore::NativeFramework => "framework", + }; + sess.note(format!("{}: {}", name, *lib).as_slice()); + } +} + +// Create a dynamic library or executable +// +// This will invoke the system linker/cc to create the resulting file. This +// links to all upstream files as well. +fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, + obj_filename: &Path, out_filename: &Path) { + let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); + + // The invocations of cc share some flags across platforms + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.args(sess.target.target.options.pre_link_args.as_slice()); + link_args(&mut cmd, sess, dylib, tmpdir.path(), + trans, obj_filename, out_filename); + cmd.args(sess.target.target.options.post_link_args.as_slice()); + if !sess.target.target.options.no_compiler_rt { + cmd.arg("-lcompiler-rt"); + } + + if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { + println!("{}", &cmd); + } + + // May have not found libraries in the right formats. + sess.abort_if_errors(); + + // Invoke the system linker + debug!("{}", &cmd); + let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output()); + match prog { + Ok(prog) => { + if !prog.status.success() { + sess.err(format!("linking with `{}` failed: {}", + pname, + prog.status).as_slice()); + sess.note(format!("{}", &cmd).as_slice()); + let mut output = prog.error.clone(); + output.push_all(prog.output.as_slice()); + sess.note(str::from_utf8(output.as_slice()).unwrap()); + sess.abort_if_errors(); + } + debug!("linker stderr:\n{}", String::from_utf8(prog.error).unwrap()); + debug!("linker stdout:\n{}", String::from_utf8(prog.output).unwrap()); + }, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + } + } + + + // On OSX, debuggers need this utility to get run to do some munging of + // the symbols + if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo { + match Command::new("dsymutil").arg(out_filename).output() { + Ok(..) => {} + Err(e) => { + sess.err(format!("failed to run dsymutil: {}", e).as_slice()); + sess.abort_if_errors(); + } + } + } +} + +fn link_args(cmd: &mut Command, + sess: &Session, + dylib: bool, + tmpdir: &Path, + trans: &CrateTranslation, + obj_filename: &Path, + out_filename: &Path) { + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let lib_path = sess.target_filesearch().get_lib_path(); + + // target descriptor + let t = &sess.target.target; + + cmd.arg("-L").arg(&lib_path); + + cmd.arg("-o").arg(out_filename).arg(obj_filename); + + + // Stack growth requires statically linking a __morestack function. Note + // that this is listed *before* all other libraries. Due to the usage of the + // --as-needed flag below, the standard library may only be useful for its + // rust_stack_exhausted function. In this case, we must ensure that the + // libmorestack.a file appears *before* the standard library (so we put it + // at the very front). + // + // Most of the time this is sufficient, except for when LLVM gets super + // clever. If, for example, we have a main function `fn main() {}`, LLVM + // will optimize out calls to `__morestack` entirely because the function + // doesn't need any stack at all! + // + // To get around this snag, we specially tell the linker to always include + // all contents of this library. This way we're guaranteed that the linker + // will include the __morestack symbol 100% of the time, always resolving + // references to it even if the object above didn't use it. + if t.options.morestack { + if t.options.is_like_osx { + let morestack = lib_path.join("libmorestack.a"); + + let mut v = b"-Wl,-force_load,".to_vec(); + v.push_all(morestack.as_vec()); + cmd.arg(v.as_slice()); + } else { + cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]); + } + } + + // When linking a dynamic library, we put the metadata into a section of the + // executable. This metadata is in a separate object file from the main + // object file, so we link that in here. + if dylib { + cmd.arg(obj_filename.with_extension("metadata.o")); + } + + if t.options.is_like_osx { + // The dead_strip option to the linker specifies that functions and data + // unreachable by the entry point will be removed. This is quite useful + // with Rust's compilation model of compiling libraries at a time into + // one object file. For example, this brings hello world from 1.7MB to + // 458K. + // + // Note that this is done for both executables and dynamic libraries. We + // won't get much benefit from dylibs because LLVM will have already + // stripped away as much as it could. This has not been seen to impact + // link times negatively. + // + // -dead_strip can't be part of the pre_link_args because it's also used for partial + // linking when using multiple codegen units (-r). So we insert it here. + cmd.arg("-Wl,-dead_strip"); + } + + // If we're building a dylib, we don't use --gc-sections because LLVM has + // already done the best it can do, and we also don't want to eliminate the + // metadata. If we're building an executable, however, --gc-sections drops + // the size of hello world from 1.8MB to 597K, a 67% reduction. + if !dylib && !t.options.is_like_osx { + cmd.arg("-Wl,--gc-sections"); + } + + let used_link_args = sess.cstore.get_used_link_args().borrow(); + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let empty_str = String::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let mut args = args.iter().chain(used_link_args.iter()); + if !dylib + && (t.options.relocation_model.as_slice() == "pic" + || sess.opts.cg.relocation_model.as_ref() + .unwrap_or(&empty_str).as_slice() == "pic") + && !args.any(|x| x.as_slice() == "-static") { + cmd.arg("-pie"); + } + } + + if t.options.linker_is_gnu { + // GNU-style linkers support optimization with -O. GNU ld doesn't need a + // numeric argument, but other linkers do. + if sess.opts.optimize == config::Default || + sess.opts.optimize == config::Aggressive { + cmd.arg("-Wl,-O1"); + } + } + + // We want to prevent the compiler from accidentally leaking in any system + // libraries, so we explicitly ask gcc to not link to any libraries by + // default. Note that this does not happen for windows because windows pulls + // in some large number of libraries and I couldn't quite figure out which + // subset we wanted. + if !t.options.is_like_windows { + cmd.arg("-nodefaultlibs"); + } + + // Mark all dynamic libraries and executables as compatible with ASLR + // FIXME #17098: ASLR breaks gdb + if t.options.is_like_windows && sess.opts.debuginfo == NoDebugInfo { + // cmd.arg("-Wl,--dynamicbase"); + } + + // Take careful note of the ordering of the arguments we pass to the linker + // here. Linkers will assume that things on the left depend on things to the + // right. Things on the right cannot depend on things on the left. This is + // all formally implemented in terms of resolving symbols (libs on the right + // resolve unknown symbols of libs on the left, but not vice versa). + // + // For this reason, we have organized the arguments we pass to the linker as + // such: + // + // 1. The local object that LLVM just generated + // 2. Upstream rust libraries + // 3. Local native libraries + // 4. Upstream native libraries + // + // This is generally fairly natural, but some may expect 2 and 3 to be + // swapped. The reason that all native libraries are put last is that it's + // not recommended for a native library to depend on a symbol from a rust + // crate. If this is the case then a staticlib crate is recommended, solving + // the problem. + // + // Additionally, it is occasionally the case that upstream rust libraries + // depend on a local native library. In the case of libraries such as + // lua/glfw/etc the name of the library isn't the same across all platforms, + // so only the consumer crate of a library knows the actual name. This means + // that downstream crates will provide the #[link] attribute which upstream + // crates will depend on. Hence local native libraries are after out + // upstream rust crates. + // + // In theory this means that a symbol in an upstream native library will be + // shadowed by a local native library when it wouldn't have been before, but + // this kind of behavior is pretty platform specific and generally not + // recommended anyway, so I don't think we're shooting ourself in the foot + // much with that. + add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans); + add_local_native_libraries(cmd, sess); + add_upstream_native_libraries(cmd, sess); + + // # Telling the linker what we're doing + + if dylib { + // On mac we need to tell the linker to let this library be rpathed + if sess.target.target.options.is_like_osx { + cmd.args(&["-dynamiclib", "-Wl,-dylib"]); + + if sess.opts.cg.rpath { + let mut v = "-Wl,-install_name,@rpath/".as_bytes().to_vec(); + v.push_all(out_filename.filename().unwrap()); + cmd.arg(v.as_slice()); + } + } else { + cmd.arg("-shared"); + } + } + + // FIXME (#2397): At some point we want to rpath our guesses as to + // where extern libraries might live, based on the + // addl_lib_search_paths + if sess.opts.cg.rpath { + let sysroot = sess.sysroot(); + let target_triple = sess.opts.target_triple.as_slice(); + let get_install_prefix_lib_path = || { + let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); + let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); + let mut path = Path::new(install_prefix); + path.push(&tlib); + + path + }; + let rpath_config = RPathConfig { + used_crates: sess.cstore.get_used_crates(cstore::RequireDynamic), + out_filename: out_filename.clone(), + has_rpath: sess.target.target.options.has_rpath, + is_like_osx: sess.target.target.options.is_like_osx, + get_install_prefix_lib_path: get_install_prefix_lib_path, + realpath: ::util::fs::realpath + }; + cmd.args(rpath::get_rpath_flags(rpath_config).as_slice()); + } + + // Finally add all the linker arguments provided on the command line along + // with any #[link_args] attributes found inside the crate + let empty = Vec::new(); + cmd.args(sess.opts.cg.link_args.as_ref().unwrap_or(&empty).as_slice()); + cmd.args(used_link_args.as_slice()); +} + +// # Native library linking +// +// User-supplied library search paths (-L on the command line). These are +// the same paths used to find Rust crates, so some of them may have been +// added already by the previous crate linking code. This only allows them +// to be found at compile time so it is still entirely up to outside +// forces to make sure that library can be found at runtime. +// +// Also note that the native libraries linked here are only the ones located +// in the current crate. Upstream crates with native library dependencies +// may have their native library pulled in above. +fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { + for path in sess.opts.addl_lib_search_paths.borrow().iter() { + cmd.arg("-L").arg(path); + } + + let rustpath = filesearch::rust_path(); + for path in rustpath.iter() { + cmd.arg("-L").arg(path); + } + + // Some platforms take hints about whether a library is static or dynamic. + // For those that support this, we ensure we pass the option if the library + // was flagged "static" (most defaults are dynamic) to ensure that if + // libfoo.a and libfoo.so both exist that the right one is chosen. + let takes_hints = !sess.target.target.options.is_like_osx; + + let libs = sess.cstore.get_used_libraries(); + let libs = libs.borrow(); + + let mut staticlibs = libs.iter().filter_map(|&(ref l, kind)| { + if kind == cstore::NativeStatic {Some(l)} else {None} + }); + let mut others = libs.iter().filter(|&&(_, kind)| { + kind != cstore::NativeStatic + }); + + // Platforms that take hints generally also support the --whole-archive + // flag. We need to pass this flag when linking static native libraries to + // ensure the entire library is included. + // + // For more details see #15460, but the gist is that the linker will strip + // away any unused objects in the archive if we don't otherwise explicitly + // reference them. This can occur for libraries which are just providing + // bindings, libraries with generic functions, etc. + if takes_hints { + cmd.arg("-Wl,--whole-archive").arg("-Wl,-Bstatic"); + } + let search_path = archive_search_paths(sess); + for l in staticlibs { + if takes_hints { + cmd.arg(format!("-l{}", l)); + } else { + // -force_load is the OSX equivalent of --whole-archive, but it + // involves passing the full path to the library to link. + let lib = archive::find_library(l.as_slice(), + sess.target.target.options.staticlib_prefix.as_slice(), + sess.target.target.options.staticlib_suffix.as_slice(), + search_path.as_slice(), + &sess.diagnostic().handler); + let mut v = b"-Wl,-force_load,".to_vec(); + v.push_all(lib.as_vec()); + cmd.arg(v.as_slice()); + } + } + if takes_hints { + cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic"); + } + + for &(ref l, kind) in others { + match kind { + cstore::NativeUnknown => { + cmd.arg(format!("-l{}", l)); + } + cstore::NativeFramework => { + cmd.arg("-framework").arg(l.as_slice()); + } + cstore::NativeStatic => unreachable!(), + } + } +} + +// # Rust Crate linking +// +// Rust crates are not considered at all when creating an rlib output. All +// dependencies will be linked when producing the final output (instead of +// the intermediate rlib version) +fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, + dylib: bool, tmpdir: &Path, + trans: &CrateTranslation) { + // All of the heavy lifting has previously been accomplished by the + // dependency_format module of the compiler. This is just crawling the + // output of that module, adding crates as necessary. + // + // Linking to a rlib involves just passing it to the linker (the linker + // will slurp up the object files inside), and linking to a dynamic library + // involves just passing the right -l flag. + + let data = if dylib { + &trans.crate_formats[config::CrateTypeDylib] + } else { + &trans.crate_formats[config::CrateTypeExecutable] + }; + + // Invoke get_used_crates to ensure that we get a topological sorting of + // crates. + let deps = sess.cstore.get_used_crates(cstore::RequireDynamic); + + for &(cnum, _) in deps.iter() { + // We may not pass all crates through to the linker. Some crates may + // appear statically in an existing dylib, meaning we'll pick up all the + // symbols from the dylib. + let kind = match data[cnum as uint - 1] { + Some(t) => t, + None => continue + }; + let src = sess.cstore.get_used_crate_source(cnum).unwrap(); + match kind { + cstore::RequireDynamic => { + add_dynamic_crate(cmd, sess, src.dylib.unwrap()) + } + cstore::RequireStatic => { + add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap()) + } + } + + } + + // Converts a library file-stem into a cc -l argument + fn unlib<'a>(config: &config::Config, stem: &'a [u8]) -> &'a [u8] { + if stem.starts_with("lib".as_bytes()) && !config.target.options.is_like_windows { + stem[3..] + } else { + stem + } + } + + // Adds the static "rlib" versions of all crates to the command line. + fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path, + cratepath: Path) { + // When performing LTO on an executable output, all of the + // bytecode from the upstream libraries has already been + // included in our object file output. We need to modify all of + // the upstream archives to remove their corresponding object + // file to make sure we don't pull the same code in twice. + // + // We must continue to link to the upstream archives to be sure + // to pull in native static dependencies. As the final caveat, + // on Linux it is apparently illegal to link to a blank archive, + // so if an archive no longer has any object files in it after + // we remove `lib.o`, then don't link against it at all. + // + // If we're not doing LTO, then our job is simply to just link + // against the archive. + if sess.lto() { + let name = cratepath.filename_str().unwrap(); + let name = name.slice(3, name.len() - 5); // chop off lib/.rlib + time(sess.time_passes(), + format!("altering {}.rlib", name).as_slice(), + (), |()| { + let dst = tmpdir.join(cratepath.filename().unwrap()); + match fs::copy(&cratepath, &dst) { + Ok(..) => {} + Err(e) => { + sess.err(format!("failed to copy {} to {}: {}", + cratepath.display(), + dst.display(), + e).as_slice()); + sess.abort_if_errors(); + } + } + // Fix up permissions of the copy, as fs::copy() preserves + // permissions, but the original file may have been installed + // by a package manager and may be read-only. + match fs::chmod(&dst, io::USER_READ | io::USER_WRITE) { + Ok(..) => {} + Err(e) => { + sess.err(format!("failed to chmod {} when preparing \ + for LTO: {}", dst.display(), + e).as_slice()); + sess.abort_if_errors(); + } + } + let handler = &sess.diagnostic().handler; + let config = ArchiveConfig { + handler: handler, + dst: dst.clone(), + lib_search_paths: archive_search_paths(sess), + slib_prefix: sess.target.target.options.staticlib_prefix.clone(), + slib_suffix: sess.target.target.options.staticlib_suffix.clone(), + maybe_ar_prog: sess.opts.cg.ar.clone() + }; + let mut archive = Archive::open(config); + archive.remove_file(format!("{}.o", name).as_slice()); + let files = archive.files(); + if files.iter().any(|s| s.as_slice().ends_with(".o")) { + cmd.arg(dst); + } + }); + } else { + cmd.arg(cratepath); + } + } + + // Same thing as above, but for dynamic crates instead of static crates. + fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: Path) { + // If we're performing LTO, then it should have been previously required + // that all upstream rust dependencies were available in an rlib format. + assert!(!sess.lto()); + + // Just need to tell the linker about where the library lives and + // what its name is + let dir = cratepath.dirname(); + if !dir.is_empty() { cmd.arg("-L").arg(dir); } + + let mut v = "-l".as_bytes().to_vec(); + v.push_all(unlib(&sess.target, cratepath.filestem().unwrap())); + cmd.arg(v.as_slice()); + } +} + +// Link in all of our upstream crates' native dependencies. Remember that +// all of these upstream native dependencies are all non-static +// dependencies. We've got two cases then: +// +// 1. The upstream crate is an rlib. In this case we *must* link in the +// native dependency because the rlib is just an archive. +// +// 2. The upstream crate is a dylib. In order to use the dylib, we have to +// have the dependency present on the system somewhere. Thus, we don't +// gain a whole lot from not linking in the dynamic dependency to this +// crate as well. +// +// The use case for this is a little subtle. In theory the native +// dependencies of a crate are purely an implementation detail of the crate +// itself, but the problem arises with generic and inlined functions. If a +// generic function calls a native function, then the generic function must +// be instantiated in the target crate, meaning that the native symbol must +// also be resolved in the target crate. +fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { + // Be sure to use a topological sorting of crates because there may be + // interdependencies between native libraries. When passing -nodefaultlibs, + // for example, almost all native libraries depend on libc, so we have to + // make sure that's all the way at the right (liblibc is near the base of + // the dependency chain). + // + // This passes RequireStatic, but the actual requirement doesn't matter, + // we're just getting an ordering of crate numbers, we're not worried about + // the paths. + let crates = sess.cstore.get_used_crates(cstore::RequireStatic); + for (cnum, _) in crates.into_iter() { + let libs = csearch::get_native_libraries(&sess.cstore, cnum); + for &(kind, ref lib) in libs.iter() { + match kind { + cstore::NativeUnknown => { + cmd.arg(format!("-l{}", *lib)); + } + cstore::NativeFramework => { + cmd.arg("-framework"); + cmd.arg(lib.as_slice()); + } + cstore::NativeStatic => { + sess.bug("statics shouldn't be propagated"); + } + } + } + } +} diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs new file mode 100644 index 00000000000..407f632bee7 --- /dev/null +++ b/src/librustc_trans/back/lto.rs @@ -0,0 +1,211 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::link; +use super::write; +use rustc::session::{mod, config}; +use llvm; +use llvm::archive_ro::ArchiveRO; +use llvm::{ModuleRef, TargetMachineRef, True, False}; +use rustc::metadata::cstore; +use rustc::util::common::time; + +use libc; +use flate; + +use std::iter; +use std::mem; +use std::num::Int; + +pub fn run(sess: &session::Session, llmod: ModuleRef, + tm: TargetMachineRef, reachable: &[String]) { + if sess.opts.cg.prefer_dynamic { + sess.err("cannot prefer dynamic linking when performing LTO"); + sess.note("only 'staticlib' and 'bin' outputs are supported with LTO"); + sess.abort_if_errors(); + } + + // Make sure we actually can run LTO + for crate_type in sess.crate_types.borrow().iter() { + match *crate_type { + config::CrateTypeExecutable | config::CrateTypeStaticlib => {} + _ => { + sess.fatal("lto can only be run for executables and \ + static library outputs"); + } + } + } + + // For each of our upstream dependencies, find the corresponding rlib and + // load the bitcode from the archive. Then merge it into the current LLVM + // module that we've got. + let crates = sess.cstore.get_used_crates(cstore::RequireStatic); + for (cnum, path) in crates.into_iter() { + let name = sess.cstore.get_crate_data(cnum).name.clone(); + let path = match path { + Some(p) => p, + None => { + sess.fatal(format!("could not find rlib for: `{}`", + name).as_slice()); + } + }; + + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); + let file = path.filename_str().unwrap(); + let file = file.slice(3, file.len() - 5); // chop off lib/.rlib + debug!("reading {}", file); + for i in iter::count(0u, 1) { + let bc_encoded = time(sess.time_passes(), + format!("check for {}.{}.bytecode.deflate", name, i).as_slice(), + (), + |_| { + archive.read(format!("{}.{}.bytecode.deflate", + file, i).as_slice()) + }); + let bc_encoded = match bc_encoded { + Some(data) => data, + None => { + if i == 0 { + // No bitcode was found at all. + sess.fatal(format!("missing compressed bytecode in {}", + path.display()).as_slice()); + } + // No more bitcode files to read. + break; + }, + }; + let bc_extractor = if is_versioned_bytecode_format(bc_encoded) { + |_| { + // Read the version + let version = extract_bytecode_format_version(bc_encoded); + + if version == 1 { + // The only version existing so far + let data_size = extract_compressed_bytecode_size_v1(bc_encoded); + let compressed_data = bc_encoded[ + link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET.. + link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint]; + + match flate::inflate_bytes(compressed_data) { + Some(inflated) => inflated, + None => { + sess.fatal(format!("failed to decompress bc of `{}`", + name).as_slice()) + } + } + } else { + sess.fatal(format!("Unsupported bytecode format version {}", + version).as_slice()) + } + } + } else { + // the object must be in the old, pre-versioning format, so simply + // inflate everything and let LLVM decide if it can make sense of it + |_| { + match flate::inflate_bytes(bc_encoded) { + Some(bc) => bc, + None => { + sess.fatal(format!("failed to decompress bc of `{}`", + name).as_slice()) + } + } + } + }; + + let bc_decoded = time(sess.time_passes(), + format!("decode {}.{}.bc", file, i).as_slice(), + (), + bc_extractor); + + let ptr = bc_decoded.as_slice().as_ptr(); + debug!("linking {}, part {}", name, i); + time(sess.time_passes(), + format!("ll link {}.{}", name, i).as_slice(), + (), + |()| unsafe { + if !llvm::LLVMRustLinkInExternalBitcode(llmod, + ptr as *const libc::c_char, + bc_decoded.len() as libc::size_t) { + write::llvm_err(sess.diagnostic().handler(), + format!("failed to load bc of `{}`", + name.as_slice())); + } + }); + } + } + + // Internalize everything but the reachable symbols of the current module + let cstrs: Vec<::std::c_str::CString> = + reachable.iter().map(|s| s.as_slice().to_c_str()).collect(); + let arr: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect(); + let ptr = arr.as_ptr(); + unsafe { + llvm::LLVMRustRunRestrictionPass(llmod, + ptr as *const *const libc::c_char, + arr.len() as libc::size_t); + } + + if sess.no_landing_pads() { + unsafe { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + } + } + + // Now we have one massive module inside of llmod. Time to run the + // LTO-specific optimization passes that LLVM provides. + // + // This code is based off the code found in llvm's LTO code generator: + // tools/lto/LTOCodeGenerator.cpp + debug!("running the pass manager"); + unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); + "verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s)); + + let builder = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm, + /* Internalize = */ False, + /* RunInliner = */ True); + llvm::LLVMPassManagerBuilderDispose(builder); + + "verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s)); + + time(sess.time_passes(), "LTO passes", (), |()| + llvm::LLVMRunPassManager(pm, llmod)); + + llvm::LLVMDisposePassManager(pm); + } + debug!("lto done"); +} + +fn is_versioned_bytecode_format(bc: &[u8]) -> bool { + let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len(); + return bc.len() > magic_id_byte_count && + bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC; +} + +fn extract_bytecode_format_version(bc: &[u8]) -> u32 { + return read_from_le_bytes::(bc, link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET); +} + +fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 { + return read_from_le_bytes::(bc, link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET); +} + +fn read_from_le_bytes(bytes: &[u8], position_in_bytes: uint) -> T { + let byte_data = bytes[position_in_bytes.. + position_in_bytes + mem::size_of::()]; + let data = unsafe { + *(byte_data.as_ptr() as *const T) + }; + + Int::from_le(data) +} + diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs new file mode 100644 index 00000000000..abf29fe3a4c --- /dev/null +++ b/src/librustc_trans/back/write.rs @@ -0,0 +1,1048 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::lto; +use back::link::{get_cc_prog, remove}; +use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames}; +use session::config::{NoDebugInfo, Passes, SomePasses, AllPasses}; +use session::Session; +use session::config; +use llvm; +use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; +use llvm::SMDiagnosticRef; +use util::common::time; +use syntax::codemap; +use syntax::diagnostic; +use syntax::diagnostic::{Emitter, Handler, Level, mk_handler}; + +use std::c_str::{ToCStr, CString}; +use std::io::Command; +use std::io::fs; +use std::iter::Unfold; +use std::ptr; +use std::str; +use std::mem; +use std::sync::{Arc, Mutex}; +use std::task::TaskBuilder; +use libc::{c_uint, c_int, c_void}; + +pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! { + unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + handler.fatal(msg.as_slice()); + } else { + let err = CString::new(cstr, true); + let err = String::from_utf8_lossy(err.as_bytes()); + handler.fatal(format!("{}: {}", + msg.as_slice(), + err.as_slice()).as_slice()); + } + } +} + +pub fn write_output_file( + handler: &diagnostic::Handler, + target: llvm::TargetMachineRef, + pm: llvm::PassManagerRef, + m: ModuleRef, + output: &Path, + file_type: llvm::FileType) { + unsafe { + output.with_c_str(|output| { + let result = llvm::LLVMRustWriteOutputFile( + target, pm, m, output, file_type); + if !result { + llvm_err(handler, "could not write output".to_string()); + } + }) + } +} + + +struct Diagnostic { + msg: String, + code: Option, + lvl: Level, +} + +// We use an Arc instead of just returning a list of diagnostics from the +// child task because we need to make sure that the messages are seen even +// if the child task panics (for example, when `fatal` is called). +#[deriving(Clone)] +struct SharedEmitter { + buffer: Arc>>, +} + +impl SharedEmitter { + fn new() -> SharedEmitter { + SharedEmitter { + buffer: Arc::new(Mutex::new(Vec::new())), + } + } + + fn dump(&mut self, handler: &Handler) { + let mut buffer = self.buffer.lock(); + for diag in buffer.iter() { + match diag.code { + Some(ref code) => { + handler.emit_with_code(None, + diag.msg.as_slice(), + code.as_slice(), + diag.lvl); + }, + None => { + handler.emit(None, + diag.msg.as_slice(), + diag.lvl); + }, + } + } + buffer.clear(); + } +} + +impl Emitter for SharedEmitter { + fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>, + msg: &str, code: Option<&str>, lvl: Level) { + assert!(cmsp.is_none(), "SharedEmitter doesn't support spans"); + + self.buffer.lock().push(Diagnostic { + msg: msg.to_string(), + code: code.map(|s| s.to_string()), + lvl: lvl, + }); + } + + fn custom_emit(&mut self, _cm: &codemap::CodeMap, + _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) { + panic!("SharedEmitter doesn't support custom_emit"); + } +} + + +// On android, we by default compile for armv7 processors. This enables +// things like double word CAS instructions (rather than emulating them) +// which are *far* more efficient. This is obviously undesirable in some +// cases, so if any sort of target feature is specified we don't append v7 +// to the feature list. +// +// On iOS only armv7 and newer are supported. So it is useful to +// get all hardware potential via VFP3 (hardware floating point) +// and NEON (SIMD) instructions supported by LLVM. +// Note that without those flags various linking errors might +// arise as some of intrinsics are converted into function calls +// and nobody provides implementations those functions +fn target_feature(sess: &Session) -> String { + format!("{},{}", sess.target.target.options.features, sess.opts.cg.target_feature) +} + +fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { + match optimize { + config::No => llvm::CodeGenLevelNone, + config::Less => llvm::CodeGenLevelLess, + config::Default => llvm::CodeGenLevelDefault, + config::Aggressive => llvm::CodeGenLevelAggressive, + } +} + +fn create_target_machine(sess: &Session) -> TargetMachineRef { + let reloc_model_arg = match sess.opts.cg.relocation_model { + Some(ref s) => s.as_slice(), + None => sess.target.target.options.relocation_model.as_slice() + }; + let reloc_model = match reloc_model_arg { + "pic" => llvm::RelocPIC, + "static" => llvm::RelocStatic, + "default" => llvm::RelocDefault, + "dynamic-no-pic" => llvm::RelocDynamicNoPic, + _ => { + sess.err(format!("{} is not a valid relocation mode", + sess.opts + .cg + .relocation_model).as_slice()); + sess.abort_if_errors(); + unreachable!(); + } + }; + + let opt_level = get_llvm_opt_level(sess.opts.optimize); + let use_softfp = sess.opts.cg.soft_float; + + // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. + let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || + !sess.target.target.options.eliminate_frame_pointer; + + let any_library = sess.crate_types.borrow().iter().any(|ty| { + *ty != config::CrateTypeExecutable + }); + + let ffunction_sections = sess.target.target.options.function_sections; + let fdata_sections = ffunction_sections; + + let code_model_arg = match sess.opts.cg.code_model { + Some(ref s) => s.as_slice(), + None => sess.target.target.options.code_model.as_slice() + }; + + let code_model = match code_model_arg { + "default" => llvm::CodeModelDefault, + "small" => llvm::CodeModelSmall, + "kernel" => llvm::CodeModelKernel, + "medium" => llvm::CodeModelMedium, + "large" => llvm::CodeModelLarge, + _ => { + sess.err(format!("{} is not a valid code model", + sess.opts + .cg + .code_model).as_slice()); + sess.abort_if_errors(); + unreachable!(); + } + }; + + let triple = sess.target.target.llvm_target.as_slice(); + + let tm = unsafe { + triple.with_c_str(|t| { + let cpu = match sess.opts.cg.target_cpu { + Some(ref s) => s.as_slice(), + None => sess.target.target.options.cpu.as_slice() + }; + cpu.with_c_str(|cpu| { + target_feature(sess).with_c_str(|features| { + llvm::LLVMRustCreateTargetMachine( + t, cpu, features, + code_model, + reloc_model, + opt_level, + true /* EnableSegstk */, + use_softfp, + no_fp_elim, + !any_library && reloc_model == llvm::RelocPIC, + ffunction_sections, + fdata_sections, + ) + }) + }) + }) + }; + + if tm.is_null() { + llvm_err(sess.diagnostic().handler(), + format!("Could not create LLVM TargetMachine for triple: {}", + triple).to_string()); + } else { + return tm; + }; +} + + +/// Module-specific configuration for `optimize_and_codegen`. +#[deriving(Clone)] +struct ModuleConfig { + /// LLVM TargetMachine to use for codegen. + tm: TargetMachineRef, + /// Names of additional optimization passes to run. + passes: Vec, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + opt_level: Option, + + // Flags indicating which outputs to produce. + emit_no_opt_bc: bool, + emit_bc: bool, + emit_lto_bc: bool, + emit_ir: bool, + emit_asm: bool, + emit_obj: bool, + + // Miscellaneous flags. These are mostly copied from command-line + // options. + no_verify: bool, + no_prepopulate_passes: bool, + no_builtins: bool, + time_passes: bool, +} + +impl ModuleConfig { + fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { + ModuleConfig { + tm: tm, + passes: passes, + opt_level: None, + + emit_no_opt_bc: false, + emit_bc: false, + emit_lto_bc: false, + emit_ir: false, + emit_asm: false, + emit_obj: false, + + no_verify: false, + no_prepopulate_passes: false, + no_builtins: false, + time_passes: false, + } + } + + fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { + self.no_verify = sess.no_verify(); + self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; + self.no_builtins = trans.no_builtins; + self.time_passes = sess.time_passes(); + } +} + +/// Additional resources used by optimize_and_codegen (not module specific) +struct CodegenContext<'a> { + // Extra resources used for LTO: (sess, reachable). This will be `None` + // when running in a worker thread. + lto_ctxt: Option<(&'a Session, &'a [String])>, + // Handler to use for diagnostics produced during codegen. + handler: &'a Handler, + // LLVM optimizations for which we want to print remarks. + remark: Passes, +} + +impl<'a> CodegenContext<'a> { + fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> { + CodegenContext { + lto_ctxt: Some((sess, reachable)), + handler: sess.diagnostic().handler(), + remark: sess.opts.cg.remark.clone(), + } + } +} + +struct HandlerFreeVars<'a> { + llcx: ContextRef, + cgcx: &'a CodegenContext<'a>, +} + +unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, + user: *const c_void, + cookie: c_uint) { + use syntax::codemap::ExpnId; + + let HandlerFreeVars { cgcx, .. } + = *mem::transmute::<_, *const HandlerFreeVars>(user); + + let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s)) + .expect("non-UTF8 SMDiagnostic"); + + match cgcx.lto_ctxt { + Some((sess, _)) => { + sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info { + Some(ei) => sess.span_err(ei.call_site, msg.as_slice()), + None => sess.err(msg.as_slice()), + }); + } + + None => { + cgcx.handler.err(msg.as_slice()); + cgcx.handler.note("build without -C codegen-units for more exact errors"); + } + } +} + +unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { + let HandlerFreeVars { llcx, cgcx } + = *mem::transmute::<_, *const HandlerFreeVars>(user); + + match llvm::diagnostic::Diagnostic::unpack(info) { + llvm::diagnostic::Optimization(opt) => { + let pass_name = CString::new(opt.pass_name, false); + let pass_name = pass_name.as_str().expect("got a non-UTF8 pass name from LLVM"); + let enabled = match cgcx.remark { + AllPasses => true, + SomePasses(ref v) => v.iter().any(|s| s.as_slice() == pass_name), + }; + + if enabled { + let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc); + cgcx.handler.note(format!("optimization {:s} for {:s} at {:s}: {:s}", + opt.kind.describe(), + pass_name, + if loc.is_empty() { "[unknown]" } else { loc.as_slice() }, + llvm::twine_to_string(opt.message)).as_slice()); + } + } + + _ => (), + } +} + +// Unsafe due to LLVM calls. +unsafe fn optimize_and_codegen(cgcx: &CodegenContext, + mtrans: ModuleTranslation, + config: ModuleConfig, + name_extra: String, + output_names: OutputFilenames) { + let ModuleTranslation { llmod, llcx } = mtrans; + let tm = config.tm; + + // llcx doesn't outlive this function, so we can put this on the stack. + let fv = HandlerFreeVars { + llcx: llcx, + cgcx: cgcx, + }; + let fv = &fv as *const HandlerFreeVars as *mut c_void; + + llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); + + if !cgcx.remark.is_empty() { + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); + } + + if config.emit_no_opt_bc { + let ext = format!("{}.no-opt.bc", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + match config.opt_level { + Some(opt_level) => { + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass: &str| { + pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) + }; + if !config.no_verify { assert!(addpass("verify")); } + + if !config.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + populate_llvm_passes(fpm, mpm, llmod, opt_level, + config.no_builtins); + } + + for pass in config.passes.iter() { + pass.as_slice().with_c_str(|s| { + if !llvm::LLVMRustAddPass(mpm, s) { + cgcx.handler.warn(format!("unknown pass {}, ignoring", + *pass).as_slice()); + } + }) + } + + // Finally, run the actual optimization passes + time(config.time_passes, "llvm function passes", (), |()| + llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + time(config.time_passes, "llvm module passes", (), |()| + llvm::LLVMRunPassManager(mpm, llmod)); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + + match cgcx.lto_ctxt { + Some((sess, reachable)) if sess.lto() => { + time(sess.time_passes(), "all lto passes", (), |()| + lto::run(sess, llmod, tm, reachable)); + + if config.emit_lto_bc { + let name = format!("{}.lto.bc", name_extra); + output_names.with_extension(name.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + }, + _ => {}, + } + }, + None => {}, + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, + no_builtins: bool, f: |PassManagerRef|) { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm); + llvm::LLVMDisposePassManager(cpm); + } + + if config.emit_bc { + let ext = format!("{}.bc", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + time(config.time_passes, "codegen passes", (), |()| { + if config.emit_ir { + let ext = format!("{}.ll", name_extra); + output_names.with_extension(ext.as_slice()).with_c_str(|output| { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, output); + }) + }) + } + + if config.emit_asm { + let path = output_names.with_extension(format!("{}.s", name_extra).as_slice()); + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFileType); + }); + } + + if config.emit_obj { + let path = output_names.with_extension(format!("{}.o", name_extra).as_slice()); + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); + }); + } + }); + + llvm::LLVMDisposeModule(llmod); + llvm::LLVMContextDispose(llcx); + llvm::LLVMRustDisposeTargetMachine(tm); +} + +pub fn run_passes(sess: &Session, + trans: &CrateTranslation, + output_types: &[config::OutputType], + crate_output: &OutputFilenames) { + // It's possible that we have `codegen_units > 1` but only one item in + // `trans.modules`. We could theoretically proceed and do LTO in that + // case, but it would be confusing to have the validity of + // `-Z lto -C codegen-units=2` depend on details of the crate being + // compiled, so we complain regardless. + if sess.lto() && sess.opts.cg.codegen_units > 1 { + // This case is impossible to handle because LTO expects to be able + // to combine the entire crate and all its dependencies into a + // single compilation unit, but each codegen unit is in a separate + // LLVM context, so they can't easily be combined. + sess.fatal("can't perform LTO when using multiple codegen units"); + } + + // Sanity check + assert!(trans.modules.len() == sess.opts.cg.codegen_units); + + unsafe { + configure_llvm(sess); + } + + let tm = create_target_machine(sess); + + // Figure out what we actually need to build. + + let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(tm, vec!()); + + modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); + + // Save all versions of the bytecode if we're saving our temporaries. + if sess.opts.cg.save_temps { + modules_config.emit_no_opt_bc = true; + modules_config.emit_bc = true; + modules_config.emit_lto_bc = true; + metadata_config.emit_bc = true; + } + + // Emit bitcode files for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the + // archive in order to allow LTO against it. + let needs_crate_bitcode = + sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains(&config::OutputTypeExe); + if needs_crate_bitcode { + modules_config.emit_bc = true; + } + + for output_type in output_types.iter() { + match *output_type { + config::OutputTypeBitcode => { modules_config.emit_bc = true; }, + config::OutputTypeLlvmAssembly => { modules_config.emit_ir = true; }, + config::OutputTypeAssembly => { + modules_config.emit_asm = true; + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, so + // in this case we still want the metadata object file. + if !sess.opts.output_types.contains(&config::OutputTypeAssembly) { + metadata_config.emit_obj = true; + } + }, + config::OutputTypeObject => { modules_config.emit_obj = true; }, + config::OutputTypeExe => { + modules_config.emit_obj = true; + metadata_config.emit_obj = true; + }, + } + } + + modules_config.set_flags(sess, trans); + metadata_config.set_flags(sess, trans); + + + // Populate a buffer with a list of codegen tasks. Items are processed in + // LIFO order, just because it's a tiny bit simpler that way. (The order + // doesn't actually matter.) + let mut work_items = Vec::with_capacity(1 + trans.modules.len()); + + { + let work = build_work_item(sess, + trans.metadata_module, + metadata_config.clone(), + crate_output.clone(), + "metadata".to_string()); + work_items.push(work); + } + + for (index, mtrans) in trans.modules.iter().enumerate() { + let work = build_work_item(sess, + *mtrans, + modules_config.clone(), + crate_output.clone(), + format!("{}", index)); + work_items.push(work); + } + + // Process the work items, optionally using worker threads. + if sess.opts.cg.codegen_units == 1 { + run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items); + } else { + run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units); + } + + // All codegen is finished. + unsafe { + llvm::LLVMRustDisposeTargetMachine(tm); + } + + // Produce final compile outputs. + + let copy_if_one_unit = |ext: &str, output_type: config::OutputType, keep_numbered: bool| { + // Three cases: + if sess.opts.cg.codegen_units == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + fs::copy(&crate_output.with_extension(ext), + &crate_output.path(output_type)).unwrap(); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.0.x`. + remove(sess, &crate_output.with_extension(ext)); + } + } else { + if crate_output.single_output_file.is_some() { + // 2) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(format!("ignoring -o because multiple .{} files were produced", + ext).as_slice()); + } else { + // 3) Multiple codegen units, but no `-o some_name`. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } + } + }; + + let link_obj = |output_path: &Path| { + // Running `ld -r` on a single input is kind of pointless. + if sess.opts.cg.codegen_units == 1 { + fs::copy(&crate_output.with_extension("0.o"), + output_path).unwrap(); + // Leave the .0.o file around, to mimic the behavior of the normal + // code path. + return; + } + + // Some builds of MinGW GCC will pass --force-exe-suffix to ld, which + // will automatically add a .exe extension if the extension is not + // already .exe or .dll. To ensure consistent behavior on Windows, we + // add the .exe suffix explicitly and then rename the output file to + // the desired path. This will give the correct behavior whether or + // not GCC adds --force-exe-suffix. + let windows_output_path = + if sess.target.target.options.is_like_windows { + Some(output_path.with_extension("o.exe")) + } else { + None + }; + + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.args(sess.target.target.options.pre_link_args.as_slice()); + cmd.arg("-nostdlib"); + + for index in range(0, trans.modules.len()) { + cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice())); + } + + cmd.arg("-r") + .arg("-o") + .arg(windows_output_path.as_ref().unwrap_or(output_path)); + + cmd.args(sess.target.target.options.post_link_args.as_slice()); + + if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 { + println!("{}", &cmd); + } + + cmd.stdin(::std::io::process::Ignored) + .stdout(::std::io::process::InheritFd(1)) + .stderr(::std::io::process::InheritFd(2)); + match cmd.status() { + Ok(status) => { + if !status.success() { + sess.err(format!("linking of {} with `{}` failed", + output_path.display(), cmd).as_slice()); + sess.abort_if_errors(); + } + }, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + }, + } + + match windows_output_path { + Some(ref windows_path) => { + fs::rename(windows_path, output_path).unwrap(); + }, + None => { + // The file is already named according to `output_path`. + } + } + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + let mut user_wants_bitcode = false; + for output_type in output_types.iter() { + match *output_type { + config::OutputTypeBitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit("0.bc", config::OutputTypeBitcode, true); + } + config::OutputTypeLlvmAssembly => { + copy_if_one_unit("0.ll", config::OutputTypeLlvmAssembly, false); + } + config::OutputTypeAssembly => { + copy_if_one_unit("0.s", config::OutputTypeAssembly, false); + } + config::OutputTypeObject => { + link_obj(&crate_output.path(config::OutputTypeObject)); + } + config::OutputTypeExe => { + // If config::OutputTypeObject is already in the list, then + // `crate.o` will be handled by the config::OutputTypeObject case. + // Otherwise, we need to create the temporary object so we + // can run the linker. + if !sess.opts.output_types.contains(&config::OutputTypeObject) { + link_obj(&crate_output.temp_path(config::OutputTypeObject)); + } + } + } + } + let user_wants_bitcode = user_wants_bitcode; + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - crate.0.bc + // - crate.0.o + // - crate.metadata.bc + // - crate.metadata.o + // - crate.o (linked from crate.##.o) + // - crate.bc (copied from crate.0.bc) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .0.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), and the bitcode is not + // needed for building an rlib, then we must remove .0.bc as well. + + // Specific rules for keeping .0.bc: + // - If we're building an rlib (`needs_crate_bitcode`), then keep + // it. + // - If the user requested bitcode (`user_wants_bitcode`), and + // codegen_units > 1, then keep it. + // - If the user requested bitcode but codegen_units == 1, then we + // can toss .0.bc because we copied it to .bc earlier. + // - If we're not building an rlib and the user didn't request + // bitcode, then delete .0.bc. + // If you change how this works, also update back::link::link_rlib, + // where .0.bc files are (maybe) deleted after making an rlib. + let keep_numbered_bitcode = needs_crate_bitcode || + (user_wants_bitcode && sess.opts.cg.codegen_units > 1); + + for i in range(0, trans.modules.len()) { + if modules_config.emit_obj { + let ext = format!("{}.o", i); + remove(sess, &crate_output.with_extension(ext.as_slice())); + } + + if modules_config.emit_bc && !keep_numbered_bitcode { + let ext = format!("{}.bc", i); + remove(sess, &crate_output.with_extension(ext.as_slice())); + } + } + + if metadata_config.emit_bc && !user_wants_bitcode { + remove(sess, &crate_output.with_extension("metadata.bc")); + } + } + + // We leave the following files around by default: + // - crate.o + // - crate.metadata.o + // - crate.bc + // These are used in linking steps and will be cleaned up afterward. + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + //if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } +} + +type WorkItem = proc(&CodegenContext):Send; + +fn build_work_item(sess: &Session, + mtrans: ModuleTranslation, + config: ModuleConfig, + output_names: OutputFilenames, + name_extra: String) -> WorkItem { + let mut config = config; + config.tm = create_target_machine(sess); + + proc(cgcx) unsafe { + optimize_and_codegen(cgcx, mtrans, config, name_extra, output_names); + } +} + +fn run_work_singlethreaded(sess: &Session, + reachable: &[String], + work_items: Vec) { + let cgcx = CodegenContext::new_with_session(sess, reachable); + let mut work_items = work_items; + + // Since we're running single-threaded, we can pass the session to + // the proc, allowing `optimize_and_codegen` to perform LTO. + for work in Unfold::new((), |_| work_items.pop()) { + work(&cgcx); + } +} + +fn run_work_multithreaded(sess: &Session, + work_items: Vec, + num_workers: uint) { + // Run some workers to process the work items. + let work_items_arc = Arc::new(Mutex::new(work_items)); + let mut diag_emitter = SharedEmitter::new(); + let mut futures = Vec::with_capacity(num_workers); + + for i in range(0, num_workers) { + let work_items_arc = work_items_arc.clone(); + let diag_emitter = diag_emitter.clone(); + let remark = sess.opts.cg.remark.clone(); + + let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() { + let diag_handler = mk_handler(box diag_emitter); + + // Must construct cgcx inside the proc because it has non-Send + // fields. + let cgcx = CodegenContext { + lto_ctxt: None, + handler: &diag_handler, + remark: remark, + }; + + loop { + // Avoid holding the lock for the entire duration of the match. + let maybe_work = work_items_arc.lock().pop(); + match maybe_work { + Some(work) => { + work(&cgcx); + + // Make sure to fail the worker so the main thread can + // tell that there were errors. + cgcx.handler.abort_if_errors(); + } + None => break, + } + } + }); + futures.push(future); + } + + let mut panicked = false; + for future in futures.into_iter() { + match future.unwrap() { + Ok(()) => {}, + Err(_) => { + panicked = true; + }, + } + // Display any new diagnostics. + diag_emitter.dump(sess.diagnostic().handler()); + } + if panicked { + sess.fatal("aborting due to worker thread panic"); + } +} + +pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.arg("-c").arg("-o").arg(outputs.path(config::OutputTypeObject)) + .arg(outputs.temp_path(config::OutputTypeAssembly)); + debug!("{}", &cmd); + + match cmd.output() { + Ok(prog) => { + if !prog.status.success() { + sess.err(format!("linking with `{}` failed: {}", + pname, + prog.status).as_slice()); + sess.note(format!("{}", &cmd).as_slice()); + let mut note = prog.error.clone(); + note.push_all(prog.output.as_slice()); + sess.note(str::from_utf8(note.as_slice()).unwrap()); + sess.abort_if_errors(); + } + }, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + } + } +} + +unsafe fn configure_llvm(sess: &Session) { + use std::sync::{Once, ONCE_INIT}; + static INIT: Once = ONCE_INIT; + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3 + let vectorize_loop = !sess.opts.cg.no_vectorize_loops && + (sess.opts.optimize == config::Default || + sess.opts.optimize == config::Aggressive); + let vectorize_slp = !sess.opts.cg.no_vectorize_slp && + sess.opts.optimize == config::Aggressive; + + let mut llvm_c_strs = Vec::new(); + let mut llvm_args = Vec::new(); + { + let add = |arg: &str| { + let s = arg.to_c_str(); + llvm_args.push(s.as_ptr()); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + if vectorize_loop { add("-vectorize-loops"); } + if vectorize_slp { add("-vectorize-slp"); } + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + + for arg in sess.opts.cg.llvm_args.iter() { + add((*arg).as_slice()); + } + } + + INIT.doit(|| { + llvm::LLVMInitializePasses(); + + // Only initialize the platforms supported by Rust here, because + // using --llvm-root will have multiple platforms that rustllvm + // doesn't actually link to and it's pointless to put target info + // into the registry that Rust cannot generate machine code for. + llvm::LLVMInitializeX86TargetInfo(); + llvm::LLVMInitializeX86Target(); + llvm::LLVMInitializeX86TargetMC(); + llvm::LLVMInitializeX86AsmPrinter(); + llvm::LLVMInitializeX86AsmParser(); + + llvm::LLVMInitializeARMTargetInfo(); + llvm::LLVMInitializeARMTarget(); + llvm::LLVMInitializeARMTargetMC(); + llvm::LLVMInitializeARMAsmPrinter(); + llvm::LLVMInitializeARMAsmParser(); + + llvm::LLVMInitializeMipsTargetInfo(); + llvm::LLVMInitializeMipsTarget(); + llvm::LLVMInitializeMipsTargetMC(); + llvm::LLVMInitializeMipsAsmPrinter(); + llvm::LLVMInitializeMipsAsmParser(); + + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); + }); +} + +unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, + mpm: llvm::PassManagerRef, + llmod: ModuleRef, + opt: llvm::CodeGenOptLevel, + no_builtins: bool) { + // Create the PassManagerBuilder for LLVM. We configure it with + // reasonable defaults and prepare it to actually populate the pass + // manager. + let builder = llvm::LLVMPassManagerBuilderCreate(); + match opt { + llvm::CodeGenLevelNone => { + // Don't add lifetime intrinsics at O0 + llvm::LLVMRustAddAlwaysInlinePass(builder, false); + } + llvm::CodeGenLevelLess => { + llvm::LLVMRustAddAlwaysInlinePass(builder, true); + } + // numeric values copied from clang + llvm::CodeGenLevelDefault => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 225); + } + llvm::CodeGenLevelAggressive => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 275); + } + } + llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); + llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); + + // Use the builder to populate the function/module pass managers. + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); + llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); + llvm::LLVMPassManagerBuilderDispose(builder); + + match opt { + llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { + "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); + } + _ => {} + }; +} diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs new file mode 100644 index 00000000000..ca70a37bf3d --- /dev/null +++ b/src/librustc_trans/driver/driver.rs @@ -0,0 +1,840 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::Input::*; + +use back::link; +use back::write; +use session::Session; +use session::config; +use lint; +use llvm::{ContextRef, ModuleRef}; +use metadata::common::LinkMeta; +use metadata::creader; +use middle::{stability, ty, typeck, reachable}; +use middle::dependency_format; +use middle; +use plugin::load::Plugins; +use plugin::registry::Registry; +use plugin; +use trans; + +use util::common::time; +use util::nodemap::{NodeSet}; + +use serialize::{json, Encodable}; + +use std::io; +use std::io::fs; +use std::os; +use arena::TypedArena; +use save; +use syntax::ast; +use syntax::ast_map; +use syntax::attr; +use syntax::attr::{AttrMetaMethods}; +use syntax::diagnostics; +use syntax::parse; +use syntax::parse::token; +use syntax; + +pub fn compile_input(sess: Session, + cfg: ast::CrateConfig, + input: &Input, + outdir: &Option, + output: &Option, + addl_plugins: Option) { + // We need nested scopes here, because the intermediate results can keep + // large chunks of memory alive and we want to free them as soon as + // possible to keep the peak memory usage low + let (outputs, trans, sess) = { + let (outputs, expanded_crate, id) = { + let krate = phase_1_parse_input(&sess, cfg, input); + if stop_after_phase_1(&sess) { return; } + let outputs = build_output_filenames(input, + outdir, + output, + krate.attrs.as_slice(), + &sess); + let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), + input); + let expanded_crate + = match phase_2_configure_and_expand(&sess, krate, id.as_slice(), + addl_plugins) { + None => return, + Some(k) => k + }; + + (outputs, expanded_crate, id) + }; + + let mut forest = ast_map::Forest::new(expanded_crate); + let ast_map = assign_node_ids_and_map(&sess, &mut forest); + + write_out_deps(&sess, input, &outputs, id.as_slice()); + + if stop_after_phase_2(&sess) { return; } + + let type_arena = TypedArena::new(); + let analysis = phase_3_run_analysis_passes(sess, ast_map, &type_arena, id); + phase_save_analysis(&analysis.ty_cx.sess, analysis.ty_cx.map.krate(), &analysis, outdir); + if stop_after_phase_3(&analysis.ty_cx.sess) { return; } + let (tcx, trans) = phase_4_translate_to_llvm(analysis); + + // Discard interned strings as they are no longer required. + token::get_ident_interner().clear(); + + (outputs, trans, tcx.sess) + }; + phase_5_run_llvm_passes(&sess, &trans, &outputs); + if stop_after_phase_5(&sess) { return; } + phase_6_link_output(&sess, &trans, &outputs); +} + +/** + * The name used for source code that doesn't originate in a file + * (e.g. source from stdin or a string) + */ +pub fn anon_src() -> String { + "".to_string() +} + +pub fn source_name(input: &Input) -> String { + match *input { + // FIXME (#9639): This needs to handle non-utf8 paths + FileInput(ref ifile) => ifile.as_str().unwrap().to_string(), + StrInput(_) => anon_src() + } +} + +pub enum Input { + /// Load source from file + FileInput(Path), + /// The string is the source + StrInput(String) +} + +impl Input { + fn filestem(&self) -> String { + match *self { + FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(), + StrInput(_) => "rust_out".to_string(), + } + } +} + + +pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) + -> ast::Crate { + let krate = time(sess.time_passes(), "parsing", (), |_| { + match *input { + FileInput(ref file) => { + parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess) + } + StrInput(ref src) => { + parse::parse_crate_from_source_str(anon_src().to_string(), + src.to_string(), + cfg.clone(), + &sess.parse_sess) + } + } + }); + + if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 { + let mut stdout = io::BufferedWriter::new(io::stdout()); + let mut json = json::PrettyEncoder::new(&mut stdout); + // unwrapping so IoError isn't ignored + krate.encode(&mut json).unwrap(); + } + + if sess.show_span() { + syntax::show_span::run(sess.diagnostic(), &krate); + } + + krate +} + +// For continuing compilation after a parsed crate has been +// modified + +/// Run the "early phases" of the compiler: initial `cfg` processing, +/// loading compiler plugins (including those from `addl_plugins`), +/// syntax expansion, secondary `cfg` expansion, synthesis of a test +/// harness if one is to be provided and injection of a dependency on the +/// standard library and prelude. +/// +/// Returns `None` if we're aborting after handling -W help. +pub fn phase_2_configure_and_expand(sess: &Session, + mut krate: ast::Crate, + crate_name: &str, + addl_plugins: Option) + -> Option { + let time_passes = sess.time_passes(); + + *sess.crate_types.borrow_mut() = + collect_crate_types(sess, krate.attrs.as_slice()); + *sess.crate_metadata.borrow_mut() = + collect_crate_metadata(sess, krate.attrs.as_slice()); + + time(time_passes, "gated feature checking", (), |_| { + let (features, unknown_features) = + syntax::feature_gate::check_crate(&sess.parse_sess.span_diagnostic, &krate); + + for uf in unknown_features.iter() { + sess.add_lint(lint::builtin::UNKNOWN_FEATURES, + ast::CRATE_NODE_ID, + *uf, + "unknown feature".to_string()); + } + + sess.abort_if_errors(); + *sess.features.borrow_mut() = features; + }); + + let any_exe = sess.crate_types.borrow().iter().any(|ty| { + *ty == config::CrateTypeExecutable + }); + + // strip before expansion to allow macros to depend on + // configuration variables e.g/ in + // + // #[macro_escape] #[cfg(foo)] + // mod bar { macro_rules! baz!(() => {{}}) } + // + // baz! should not use this definition unless foo is enabled. + + krate = time(time_passes, "configuration 1", krate, |krate| + syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); + + krate = time(time_passes, "crate injection", krate, |krate| + syntax::std_inject::maybe_inject_crates_ref(krate, + sess.opts.alt_std_name.clone(), + any_exe)); + + let mut addl_plugins = Some(addl_plugins); + let Plugins { macros, registrars } + = time(time_passes, "plugin loading", (), |_| + plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap())); + + let mut registry = Registry::new(&krate); + + time(time_passes, "plugin registration", (), |_| { + if sess.features.borrow().rustc_diagnostic_macros { + registry.register_macro("__diagnostic_used", + diagnostics::plugin::expand_diagnostic_used); + registry.register_macro("__register_diagnostic", + diagnostics::plugin::expand_register_diagnostic); + registry.register_macro("__build_diagnostic_array", + diagnostics::plugin::expand_build_diagnostic_array); + } + + for ®istrar in registrars.iter() { + registrar(&mut registry); + } + }); + + let Registry { syntax_exts, lint_passes, lint_groups, .. } = registry; + + { + let mut ls = sess.lint_store.borrow_mut(); + for pass in lint_passes.into_iter() { + ls.register_pass(Some(sess), true, pass); + } + + for (name, to) in lint_groups.into_iter() { + ls.register_group(Some(sess), true, name, to); + } + } + + // Lint plugins are registered; now we can process command line flags. + if sess.opts.describe_lints { + super::describe_lints(&*sess.lint_store.borrow(), true); + return None; + } + sess.lint_store.borrow_mut().process_command_line(sess); + + // Abort if there are errors from lint processing or a plugin registrar. + sess.abort_if_errors(); + + krate = time(time_passes, "expansion", (krate, macros, syntax_exts), + |(krate, macros, syntax_exts)| { + // Windows dlls do not have rpaths, so they don't know how to find their + // dependencies. It's up to us to tell the system where to find all the + // dependent dlls. Note that this uses cfg!(windows) as opposed to + // targ_cfg because syntax extensions are always loaded for the host + // compiler, not for the target. + let mut _old_path = String::new(); + if cfg!(windows) { + _old_path = os::getenv("PATH").unwrap_or(_old_path); + let mut new_path = sess.host_filesearch().get_dylib_search_paths(); + new_path.extend(os::split_paths(_old_path.as_slice()).into_iter()); + os::setenv("PATH", os::join_paths(new_path.as_slice()).unwrap()); + } + let cfg = syntax::ext::expand::ExpansionConfig { + crate_name: crate_name.to_string(), + deriving_hash_type_parameter: sess.features.borrow().default_type_params, + enable_quotes: sess.features.borrow().quote, + recursion_limit: sess.recursion_limit.get(), + }; + let ret = syntax::ext::expand::expand_crate(&sess.parse_sess, + cfg, + macros, + syntax_exts, + krate); + if cfg!(windows) { + os::setenv("PATH", _old_path); + } + ret + } + ); + + // JBC: make CFG processing part of expansion to avoid this problem: + + // strip again, in case expansion added anything with a #[cfg]. + krate = time(time_passes, "configuration 2", krate, |krate| + syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); + + krate = time(time_passes, "maybe building test harness", krate, |krate| + syntax::test::modify_for_testing(&sess.parse_sess, + &sess.opts.cfg, + krate, + sess.diagnostic())); + + krate = time(time_passes, "prelude injection", krate, |krate| + syntax::std_inject::maybe_inject_prelude(krate)); + + time(time_passes, "checking that all macro invocations are gone", &krate, |krate| + syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); + + Some(krate) +} + +pub fn assign_node_ids_and_map<'ast>(sess: &Session, + forest: &'ast mut ast_map::Forest) + -> ast_map::Map<'ast> { + struct NodeIdAssigner<'a> { + sess: &'a Session + } + + impl<'a> ast_map::FoldOps for NodeIdAssigner<'a> { + fn new_id(&self, old_id: ast::NodeId) -> ast::NodeId { + assert_eq!(old_id, ast::DUMMY_NODE_ID); + self.sess.next_node_id() + } + } + + let map = time(sess.time_passes(), "assigning node ids and indexing ast", forest, |forest| + ast_map::map_crate(forest, NodeIdAssigner { sess: sess })); + + if sess.opts.debugging_opts & config::AST_JSON != 0 { + let mut stdout = io::BufferedWriter::new(io::stdout()); + let mut json = json::PrettyEncoder::new(&mut stdout); + // unwrapping so IoError isn't ignored + map.krate().encode(&mut json).unwrap(); + } + + map +} + +pub struct CrateAnalysis<'tcx> { + pub exp_map2: middle::resolve::ExportMap2, + pub exported_items: middle::privacy::ExportedItems, + pub public_items: middle::privacy::PublicItems, + pub ty_cx: ty::ctxt<'tcx>, + pub reachable: NodeSet, + pub name: String, +} + + +/// Run the resolution, typechecking, region checking and other +/// miscellaneous analysis passes on the crate. Return various +/// structures carrying the results of the analysis. +pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, + ast_map: ast_map::Map<'tcx>, + type_arena: &'tcx TypedArena, + name: String) -> CrateAnalysis<'tcx> { + let time_passes = sess.time_passes(); + let krate = ast_map.krate(); + + time(time_passes, "external crate/lib resolution", (), |_| + creader::read_crates(&sess, krate)); + + let lang_items = time(time_passes, "language item collection", (), |_| + middle::lang_items::collect_language_items(krate, &sess)); + + let middle::resolve::CrateMap { + def_map, + freevars, + capture_mode_map, + exp_map2, + trait_map, + external_exports, + last_private_map + } = + time(time_passes, "resolution", (), |_| + middle::resolve::resolve_crate(&sess, &lang_items, krate)); + + // Discard MTWT tables that aren't required past resolution. + syntax::ext::mtwt::clear_tables(); + + let named_region_map = time(time_passes, "lifetime resolution", (), + |_| middle::resolve_lifetime::krate(&sess, krate)); + + time(time_passes, "looking for entry point", (), + |_| middle::entry::find_entry_point(&sess, &ast_map)); + + sess.plugin_registrar_fn.set( + time(time_passes, "looking for plugin registrar", (), |_| + plugin::build::find_plugin_registrar( + sess.diagnostic(), krate))); + + let region_map = time(time_passes, "region resolution", (), |_| + middle::region::resolve_crate(&sess, krate)); + + time(time_passes, "loop checking", (), |_| + middle::check_loop::check_crate(&sess, krate)); + + let stability_index = time(time_passes, "stability index", (), |_| + stability::Index::build(krate)); + + time(time_passes, "static item recursion checking", (), |_| + middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map)); + + let ty_cx = ty::mk_ctxt(sess, + type_arena, + def_map, + named_region_map, + ast_map, + freevars, + capture_mode_map, + region_map, + lang_items, + stability_index); + + // passes are timed inside typeck + typeck::check_crate(&ty_cx, trait_map); + + time(time_passes, "check static items", (), |_| + middle::check_static::check_crate(&ty_cx)); + + // These next two const passes can probably be merged + time(time_passes, "const marking", (), |_| + middle::const_eval::process_crate(&ty_cx)); + + time(time_passes, "const checking", (), |_| + middle::check_const::check_crate(&ty_cx)); + + let maps = (external_exports, last_private_map); + let (exported_items, public_items) = + time(time_passes, "privacy checking", maps, |(a, b)| + middle::privacy::check_crate(&ty_cx, &exp_map2, a, b)); + + time(time_passes, "intrinsic checking", (), |_| + middle::intrinsicck::check_crate(&ty_cx)); + + time(time_passes, "effect checking", (), |_| + middle::effect::check_crate(&ty_cx)); + + time(time_passes, "match checking", (), |_| + middle::check_match::check_crate(&ty_cx)); + + time(time_passes, "liveness checking", (), |_| + middle::liveness::check_crate(&ty_cx)); + + time(time_passes, "borrow checking", (), |_| + middle::borrowck::check_crate(&ty_cx)); + + time(time_passes, "rvalue checking", (), |_| + middle::check_rvalues::check_crate(&ty_cx, krate)); + + // Avoid overwhelming user with errors if type checking failed. + // I'm not sure how helpful this is, to be honest, but it avoids a + // lot of annoying errors in the compile-fail tests (basically, + // lint warnings and so on -- kindck used to do this abort, but + // kindck is gone now). -nmatsakis + ty_cx.sess.abort_if_errors(); + + let reachable_map = + time(time_passes, "reachability checking", (), |_| + reachable::find_reachable(&ty_cx, &exported_items)); + + time(time_passes, "death checking", (), |_| { + middle::dead::check_crate(&ty_cx, + &exported_items, + &reachable_map) + }); + + time(time_passes, "lint checking", (), |_| + lint::check_crate(&ty_cx, &exported_items)); + + CrateAnalysis { + exp_map2: exp_map2, + ty_cx: ty_cx, + exported_items: exported_items, + public_items: public_items, + reachable: reachable_map, + name: name, + } +} + +pub fn phase_save_analysis(sess: &Session, + krate: &ast::Crate, + analysis: &CrateAnalysis, + odir: &Option) { + if (sess.opts.debugging_opts & config::SAVE_ANALYSIS) == 0 { + return; + } + time(sess.time_passes(), "save analysis", krate, |krate| + save::process_crate(sess, krate, analysis, odir)); +} + +pub struct ModuleTranslation { + pub llcx: ContextRef, + pub llmod: ModuleRef, +} + +pub struct CrateTranslation { + pub modules: Vec, + pub metadata_module: ModuleTranslation, + pub link: LinkMeta, + pub metadata: Vec, + pub reachable: Vec, + pub crate_formats: dependency_format::Dependencies, + pub no_builtins: bool, +} + +/// Run the translation phase to LLVM, after which the AST and analysis can +/// be discarded. +pub fn phase_4_translate_to_llvm<'tcx>(analysis: CrateAnalysis<'tcx>) + -> (ty::ctxt<'tcx>, CrateTranslation) { + let time_passes = analysis.ty_cx.sess.time_passes(); + + time(time_passes, "resolving dependency formats", (), |_| + dependency_format::calculate(&analysis.ty_cx)); + + // Option dance to work around the lack of stack once closures. + time(time_passes, "translation", analysis, |analysis| + trans::base::trans_crate(analysis)) +} + +/// Run LLVM itself, producing a bitcode file, assembly file or object file +/// as a side effect. +pub fn phase_5_run_llvm_passes(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames) { + if sess.opts.cg.no_integrated_as { + let output_type = config::OutputTypeAssembly; + + time(sess.time_passes(), "LLVM passes", (), |_| + write::run_passes(sess, trans, &[output_type], outputs)); + + write::run_assembler(sess, outputs); + + // Remove assembly source, unless --save-temps was specified + if !sess.opts.cg.save_temps { + fs::unlink(&outputs.temp_path(config::OutputTypeAssembly)).unwrap(); + } + } else { + time(sess.time_passes(), "LLVM passes", (), |_| + write::run_passes(sess, + trans, + sess.opts.output_types.as_slice(), + outputs)); + } + + sess.abort_if_errors(); +} + +/// Run the linker on any artifacts that resulted from the LLVM run. +/// This should produce either a finished executable or library. +pub fn phase_6_link_output(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames) { + let old_path = os::getenv("PATH").unwrap_or_else(||String::new()); + let mut new_path = sess.host_filesearch().get_tools_search_paths(); + new_path.extend(os::split_paths(old_path.as_slice()).into_iter()); + os::setenv("PATH", os::join_paths(new_path.as_slice()).unwrap()); + + time(sess.time_passes(), "linking", (), |_| + link::link_binary(sess, + trans, + outputs, + trans.link.crate_name.as_slice())); + + os::setenv("PATH", old_path); +} + +pub fn stop_after_phase_3(sess: &Session) -> bool { + if sess.opts.no_trans { + debug!("invoked with --no-trans, returning early from compile_input"); + return true; + } + return false; +} + +pub fn stop_after_phase_1(sess: &Session) -> bool { + if sess.opts.parse_only { + debug!("invoked with --parse-only, returning early from compile_input"); + return true; + } + if sess.show_span() { + return true; + } + return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0; +} + +pub fn stop_after_phase_2(sess: &Session) -> bool { + if sess.opts.no_analysis { + debug!("invoked with --no-analysis, returning early from compile_input"); + return true; + } + return sess.opts.debugging_opts & config::AST_JSON != 0; +} + +pub fn stop_after_phase_5(sess: &Session) -> bool { + if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) { + debug!("not building executable, returning early from compile_input"); + return true; + } + return false; +} + +fn escape_dep_filename(filename: &str) -> String { + // Apparently clang and gcc *only* escape spaces: + // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 + filename.replace(" ", "\\ ") +} + +fn write_out_deps(sess: &Session, + input: &Input, + outputs: &OutputFilenames, + id: &str) { + + let mut out_filenames = Vec::new(); + for output_type in sess.opts.output_types.iter() { + let file = outputs.path(*output_type); + match *output_type { + config::OutputTypeExe => { + for output in sess.crate_types.borrow().iter() { + let p = link::filename_for_input(sess, *output, + id, &file); + out_filenames.push(p); + } + } + _ => { out_filenames.push(file); } + } + } + + // Write out dependency rules to the dep-info file if requested with + // --dep-info + let deps_filename = match sess.opts.write_dependency_info { + // Use filename from --dep-file argument if given + (true, Some(ref filename)) => filename.clone(), + // Use default filename: crate source filename with extension replaced + // by ".d" + (true, None) => match *input { + FileInput(..) => outputs.with_extension("d"), + StrInput(..) => { + sess.warn("can not write --dep-info without a filename \ + when compiling stdin."); + return + }, + }, + _ => return, + }; + + let result = (|| -> io::IoResult<()> { + // Build a list of files used to compile the output and + // write Makefile-compatible dependency rules + let files: Vec = sess.codemap().files.borrow() + .iter().filter(|fmap| fmap.is_real_file()) + .map(|fmap| escape_dep_filename(fmap.name.as_slice())) + .collect(); + let mut file = try!(io::File::create(&deps_filename)); + for path in out_filenames.iter() { + try!(write!(&mut file as &mut Writer, + "{}: {}\n\n", path.display(), files.connect(" "))); + } + Ok(()) + })(); + + match result { + Ok(()) => {} + Err(e) => { + sess.fatal(format!("error writing dependencies to `{}`: {}", + deps_filename.display(), e).as_slice()); + } + } +} + +pub fn collect_crate_types(session: &Session, + attrs: &[ast::Attribute]) -> Vec { + // Unconditionally collect crate types from attributes to make them used + let attr_types: Vec = attrs.iter().filter_map(|a| { + if a.check_name("crate_type") { + match a.value_str() { + Some(ref n) if n.equiv(&("rlib")) => { + Some(config::CrateTypeRlib) + } + Some(ref n) if n.equiv(&("dylib")) => { + Some(config::CrateTypeDylib) + } + Some(ref n) if n.equiv(&("lib")) => { + Some(config::default_lib_output()) + } + Some(ref n) if n.equiv(&("staticlib")) => { + Some(config::CrateTypeStaticlib) + } + Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), + Some(_) => { + session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + a.span, + "invalid `crate_type` \ + value".to_string()); + None + } + _ => { + session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + a.span, + "`crate_type` requires a \ + value".to_string()); + None + } + } + } else { + None + } + }).collect(); + + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec!(config::CrateTypeExecutable) + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + let mut base = session.opts.crate_types.clone(); + if base.len() == 0 { + base.extend(attr_types.into_iter()); + if base.len() == 0 { + base.push(link::default_output_for_target(session)); + } + base.as_mut_slice().sort(); + base.dedup(); + } + + base.into_iter().filter(|crate_type| { + let res = !link::invalid_output_for_target(session, *crate_type); + + if !res { + session.warn(format!("dropping unsupported crate type `{}` \ + for target `{}`", + *crate_type, session.opts.target_triple).as_slice()); + } + + res + }).collect() +} + +pub fn collect_crate_metadata(session: &Session, + _attrs: &[ast::Attribute]) -> Vec { + session.opts.cg.metadata.clone() +} + +#[deriving(Clone)] +pub struct OutputFilenames { + pub out_directory: Path, + pub out_filestem: String, + pub single_output_file: Option, + extra: String, +} + +impl OutputFilenames { + pub fn path(&self, flavor: config::OutputType) -> Path { + match self.single_output_file { + Some(ref path) => return path.clone(), + None => {} + } + self.temp_path(flavor) + } + + pub fn temp_path(&self, flavor: config::OutputType) -> Path { + let base = self.out_directory.join(self.filestem()); + match flavor { + config::OutputTypeBitcode => base.with_extension("bc"), + config::OutputTypeAssembly => base.with_extension("s"), + config::OutputTypeLlvmAssembly => base.with_extension("ll"), + config::OutputTypeObject => base.with_extension("o"), + config::OutputTypeExe => base, + } + } + + pub fn with_extension(&self, extension: &str) -> Path { + self.out_directory.join(self.filestem()).with_extension(extension) + } + + fn filestem(&self) -> String { + format!("{}{}", self.out_filestem, self.extra) + } +} + +pub fn build_output_filenames(input: &Input, + odir: &Option, + ofile: &Option, + attrs: &[ast::Attribute], + sess: &Session) + -> OutputFilenames { + match *ofile { + None => { + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirpath = match *odir { + Some(ref d) => d.clone(), + None => Path::new(".") + }; + + // If a crate name is present, we use it as the link name + let stem = sess.opts.crate_name.clone().or_else(|| { + attr::find_crate_name(attrs).map(|n| n.get().to_string()) + }).unwrap_or(input.filestem()); + + OutputFilenames { + out_directory: dirpath, + out_filestem: stem, + single_output_file: None, + extra: sess.opts.cg.extra_filename.clone(), + } + } + + Some(ref out_file) => { + let ofile = if sess.opts.output_types.len() > 1 { + sess.warn("ignoring specified output filename because multiple \ + outputs were requested"); + None + } else { + Some(out_file.clone()) + }; + if *odir != None { + sess.warn("ignoring --out-dir flag due to -o flag."); + } + OutputFilenames { + out_directory: out_file.dir_path(), + out_filestem: out_file.filestem_str().unwrap().to_string(), + single_output_file: ofile, + extra: sess.opts.cg.extra_filename.clone(), + } + } + } +} diff --git a/src/librustc_trans/driver/mod.rs b/src/librustc_trans/driver/mod.rs new file mode 100644 index 00000000000..8985d05b6ca --- /dev/null +++ b/src/librustc_trans/driver/mod.rs @@ -0,0 +1,502 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use syntax::diagnostic; + +use back::link; +use driver::driver::{Input, FileInput, StrInput}; +use session::{config, Session, build_session}; +use lint::Lint; +use lint; +use metadata; + +use rustc::DIAGNOSTICS; + +use std::any::AnyRefExt; +use std::io; +use std::os; +use std::task::TaskBuilder; + +use session::early_error; + +use syntax::ast; +use syntax::parse; +use syntax::diagnostic::Emitter; +use syntax::diagnostics; + +use getopts; + +pub mod driver; +pub mod pretty; + +pub fn run(args: Vec) -> int { + monitor(proc() run_compiler(args.as_slice())); + 0 +} + +static BUG_REPORT_URL: &'static str = + "http://doc.rust-lang.org/complement-bugreport.html"; + +fn run_compiler(args: &[String]) { + let matches = match handle_options(args.to_vec()) { + Some(matches) => matches, + None => return + }; + + let descriptions = diagnostics::registry::Registry::new(&DIAGNOSTICS); + match matches.opt_str("explain") { + Some(ref code) => { + match descriptions.find_description(code.as_slice()) { + Some(ref description) => { + println!("{}", description); + } + None => { + early_error(format!("no extended information for {}", code).as_slice()); + } + } + return; + }, + None => () + } + + let sopts = config::build_session_options(&matches); + let (input, input_file_path) = match matches.free.len() { + 0u => { + if sopts.describe_lints { + let mut ls = lint::LintStore::new(); + ls.register_builtin(None); + describe_lints(&ls, false); + return; + } + early_error("no input filename given"); + } + 1u => { + let ifile = matches.free[0].as_slice(); + if ifile == "-" { + let contents = io::stdin().read_to_end().unwrap(); + let src = String::from_utf8(contents).unwrap(); + (StrInput(src), None) + } else { + (FileInput(Path::new(ifile)), Some(Path::new(ifile))) + } + } + _ => early_error("multiple input filenames provided") + }; + + let sess = build_session(sopts, input_file_path, descriptions); + let cfg = config::build_configuration(&sess); + let odir = matches.opt_str("out-dir").map(|o| Path::new(o)); + let ofile = matches.opt_str("o").map(|o| Path::new(o)); + + let pretty = matches.opt_default("pretty", "normal").map(|a| { + pretty::parse_pretty(&sess, a.as_slice()) + }); + match pretty { + Some((ppm, opt_uii)) => { + pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile); + return; + } + None => {/* continue */ } + } + + let r = matches.opt_strs("Z"); + if r.contains(&("ls".to_string())) { + match input { + FileInput(ref ifile) => { + let mut stdout = io::stdout(); + list_metadata(&sess, &(*ifile), &mut stdout).unwrap(); + } + StrInput(_) => { + early_error("can not list metadata for stdin"); + } + } + return; + } + + if print_crate_info(&sess, &input, &odir, &ofile) { + return; + } + + driver::compile_input(sess, cfg, &input, &odir, &ofile, None); +} + +/// Returns a version string such as "0.12.0-dev". +pub fn release_str() -> Option<&'static str> { + option_env!("CFG_RELEASE") +} + +/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built. +pub fn commit_hash_str() -> Option<&'static str> { + option_env!("CFG_VER_HASH") +} + +/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string. +pub fn commit_date_str() -> Option<&'static str> { + option_env!("CFG_VER_DATE") +} + +/// Prints version information and returns None on success or an error +/// message on failure. +pub fn version(binary: &str, matches: &getopts::Matches) -> Option { + let verbose = match matches.opt_str("version").as_ref().map(|s| s.as_slice()) { + None => false, + Some("verbose") => true, + Some(s) => return Some(format!("Unrecognized argument: {}", s)) + }; + + println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version")); + if verbose { + fn unw(x: Option<&str>) -> &str { x.unwrap_or("unknown") } + println!("binary: {}", binary); + println!("commit-hash: {}", unw(commit_hash_str())); + println!("commit-date: {}", unw(commit_date_str())); + println!("host: {}", config::host_triple()); + println!("release: {}", unw(release_str())); + } + None +} + +fn usage() { + let message = format!("Usage: rustc [OPTIONS] INPUT"); + println!("{}\n\ +Additional help: + -C help Print codegen options + -W help Print 'lint' options and default settings + -Z help Print internal options for debugging rustc\n", + getopts::usage(message.as_slice(), + config::optgroups().as_slice())); +} + +fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) { + println!(" +Available lint options: + -W Warn about + -A Allow + -D Deny + -F Forbid (deny, and deny all overrides) + +"); + + fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { + let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect(); + lints.sort_by(|x: &&Lint, y: &&Lint| { + match x.default_level.cmp(&y.default_level) { + // The sort doesn't case-fold but it's doubtful we care. + Equal => x.name.cmp(y.name), + r => r, + } + }); + lints + } + + fn sort_lint_groups(lints: Vec<(&'static str, Vec, bool)>) + -> Vec<(&'static str, Vec)> { + let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect(); + lints.sort_by(|&(x, _): &(&'static str, Vec), + &(y, _): &(&'static str, Vec)| { + x.cmp(y) + }); + lints + } + + let (plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p); + let plugin = sort_lints(plugin); + let builtin = sort_lints(builtin); + + let (plugin_groups, builtin_groups) = lint_store.get_lint_groups().partitioned(|&(_, _, p)| p); + let plugin_groups = sort_lint_groups(plugin_groups); + let builtin_groups = sort_lint_groups(builtin_groups); + + let max_name_len = plugin.iter().chain(builtin.iter()) + .map(|&s| s.name.width(true)) + .max().unwrap_or(0); + let padded = |x: &str| { + let mut s = " ".repeat(max_name_len - x.char_len()); + s.push_str(x); + s + }; + + println!("Lint checks provided by rustc:\n"); + println!(" {} {:7.7s} {}", padded("name"), "default", "meaning"); + println!(" {} {:7.7s} {}", padded("----"), "-------", "-------"); + + let print_lints = |lints: Vec<&Lint>| { + for lint in lints.into_iter() { + let name = lint.name_lower().replace("_", "-"); + println!(" {} {:7.7s} {}", + padded(name.as_slice()), lint.default_level.as_str(), lint.desc); + } + println!("\n"); + }; + + print_lints(builtin); + + + + let max_name_len = plugin_groups.iter().chain(builtin_groups.iter()) + .map(|&(s, _)| s.width(true)) + .max().unwrap_or(0); + let padded = |x: &str| { + let mut s = " ".repeat(max_name_len - x.char_len()); + s.push_str(x); + s + }; + + println!("Lint groups provided by rustc:\n"); + println!(" {} {}", padded("name"), "sub-lints"); + println!(" {} {}", padded("----"), "---------"); + + let print_lint_groups = |lints: Vec<(&'static str, Vec)>| { + for (name, to) in lints.into_iter() { + let name = name.chars().map(|x| x.to_lowercase()) + .collect::().replace("_", "-"); + let desc = to.into_iter().map(|x| x.as_str().replace("_", "-")) + .collect::>().connect(", "); + println!(" {} {}", + padded(name.as_slice()), desc); + } + println!("\n"); + }; + + print_lint_groups(builtin_groups); + + match (loaded_plugins, plugin.len(), plugin_groups.len()) { + (false, 0, _) | (false, _, 0) => { + println!("Compiler plugins can provide additional lints and lint groups. To see a \ + listing of these, re-run `rustc -W help` with a crate filename."); + } + (false, _, _) => panic!("didn't load lint plugins but got them anyway!"), + (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."), + (true, l, g) => { + if l > 0 { + println!("Lint checks provided by plugins loaded by this crate:\n"); + print_lints(plugin); + } + if g > 0 { + println!("Lint groups provided by plugins loaded by this crate:\n"); + print_lint_groups(plugin_groups); + } + } + } +} + +fn describe_debug_flags() { + println!("\nAvailable debug options:\n"); + let r = config::debugging_opts_map(); + for tuple in r.iter() { + match *tuple { + (ref name, ref desc, _) => { + println!(" -Z {:>20s} -- {}", *name, *desc); + } + } + } +} + +fn describe_codegen_flags() { + println!("\nAvailable codegen options:\n"); + for &(name, _, opt_type_desc, desc) in config::CG_OPTIONS.iter() { + let (width, extra) = match opt_type_desc { + Some(..) => (21, "=val"), + None => (25, "") + }; + println!(" -C {:>width$s}{} -- {}", name.replace("_", "-"), + extra, desc, width=width); + } +} + +/// Process command line options. Emits messages as appropriate. If compilation +/// should continue, returns a getopts::Matches object parsed from args, otherwise +/// returns None. +pub fn handle_options(mut args: Vec) -> Option { + // Throw away the first argument, the name of the binary + let _binary = args.remove(0).unwrap(); + + if args.is_empty() { + usage(); + return None; + } + + let matches = + match getopts::getopts(args.as_slice(), config::optgroups().as_slice()) { + Ok(m) => m, + Err(f) => { + early_error(f.to_string().as_slice()); + } + }; + + if matches.opt_present("h") || matches.opt_present("help") { + usage(); + return None; + } + + // Don't handle -W help here, because we might first load plugins. + + let r = matches.opt_strs("Z"); + if r.iter().any(|x| x.as_slice() == "help") { + describe_debug_flags(); + return None; + } + + let cg_flags = matches.opt_strs("C"); + if cg_flags.iter().any(|x| x.as_slice() == "help") { + describe_codegen_flags(); + return None; + } + + if cg_flags.contains(&"passes=list".to_string()) { + unsafe { ::llvm::LLVMRustPrintPasses(); } + return None; + } + + if matches.opt_present("version") { + match version("rustc", &matches) { + Some(err) => early_error(err.as_slice()), + None => return None + } + } + + Some(matches) +} + +fn print_crate_info(sess: &Session, + input: &Input, + odir: &Option, + ofile: &Option) + -> bool { + let (crate_name, crate_file_name) = sess.opts.print_metas; + // these nasty nested conditions are to avoid doing extra work + if crate_name || crate_file_name { + let attrs = parse_crate_attrs(sess, input); + let t_outputs = driver::build_output_filenames(input, + odir, + ofile, + attrs.as_slice(), + sess); + let id = link::find_crate_name(Some(sess), attrs.as_slice(), input); + + if crate_name { + println!("{}", id); + } + if crate_file_name { + let crate_types = driver::collect_crate_types(sess, attrs.as_slice()); + let metadata = driver::collect_crate_metadata(sess, attrs.as_slice()); + *sess.crate_metadata.borrow_mut() = metadata; + for &style in crate_types.iter() { + let fname = link::filename_for_input(sess, style, id.as_slice(), + &t_outputs.with_extension("")); + println!("{}", fname.filename_display()); + } + } + + true + } else { + false + } +} + +fn parse_crate_attrs(sess: &Session, input: &Input) -> + Vec { + let result = match *input { + FileInput(ref ifile) => { + parse::parse_crate_attrs_from_file(ifile, + Vec::new(), + &sess.parse_sess) + } + StrInput(ref src) => { + parse::parse_crate_attrs_from_source_str( + driver::anon_src().to_string(), + src.to_string(), + Vec::new(), + &sess.parse_sess) + } + }; + result.into_iter().collect() +} + +pub fn list_metadata(sess: &Session, path: &Path, + out: &mut io::Writer) -> io::IoResult<()> { + metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, path, out) +} + +/// Run a procedure which will detect failures in the compiler and print nicer +/// error messages rather than just failing the test. +/// +/// The diagnostic emitter yielded to the procedure should be used for reporting +/// errors of the compiler. +pub fn monitor(f: proc():Send) { + // FIXME: This is a hack for newsched since it doesn't support split stacks. + // rustc needs a lot of stack! When optimizations are disabled, it needs + // even *more* stack than usual as well. + #[cfg(rtopt)] + static STACK_SIZE: uint = 6000000; // 6MB + #[cfg(not(rtopt))] + static STACK_SIZE: uint = 20000000; // 20MB + + let (tx, rx) = channel(); + let w = io::ChanWriter::new(tx); + let mut r = io::ChanReader::new(rx); + + let mut task = TaskBuilder::new().named("rustc").stderr(box w); + + // FIXME: Hacks on hacks. If the env is trying to override the stack size + // then *don't* set it explicitly. + if os::getenv("RUST_MIN_STACK").is_none() { + task = task.stack_size(STACK_SIZE); + } + + match task.try(f) { + Ok(()) => { /* fallthrough */ } + Err(value) => { + // Task panicked without emitting a fatal diagnostic + if !value.is::() { + let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); + + // a .span_bug or .bug call has already printed what + // it wants to print. + if !value.is::() { + emitter.emit( + None, + "unexpected panic", + None, + diagnostic::Bug); + } + + let xs = [ + "the compiler unexpectedly panicked. this is a bug.".to_string(), + format!("we would appreciate a bug report: {}", + BUG_REPORT_URL), + "run with `RUST_BACKTRACE=1` for a backtrace".to_string(), + ]; + for note in xs.iter() { + emitter.emit(None, note.as_slice(), None, diagnostic::Note) + } + + match r.read_to_string() { + Ok(s) => println!("{}", s), + Err(e) => { + emitter.emit(None, + format!("failed to read internal \ + stderr: {}", + e).as_slice(), + None, + diagnostic::Error) + } + } + } + + // Panic so the process returns a failure code, but don't pollute the + // output with some unnecessary failure messages, we've already + // printed everything that we needed to. + io::stdio::set_stderr(box io::util::NullWriter); + panic!(); + } + } +} + diff --git a/src/librustc_trans/driver/pretty.rs b/src/librustc_trans/driver/pretty.rs new file mode 100644 index 00000000000..c20f2e097f3 --- /dev/null +++ b/src/librustc_trans/driver/pretty.rs @@ -0,0 +1,597 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The various pretty print routines. + +pub use self::UserIdentifiedItem::*; +pub use self::PpSourceMode::*; +pub use self::PpMode::*; +use self::NodesMatchingUII::*; + +use back::link; + +use session::{config, Session}; +use driver::driver::{mod, CrateAnalysis}; + +use middle::ty; +use middle::borrowck::{mod, FnPartsWithCFG}; +use middle::borrowck::graphviz as borrowck_dot; +use middle::cfg; +use middle::cfg::graphviz::LabelledCFG; + +use util::ppaux; + +use syntax::ast; +use syntax::ast_map::{mod, blocks, NodePrinter}; +use syntax::print::{pp, pprust}; + +use graphviz as dot; + +use std::io::{mod, MemReader}; +use std::option; +use std::str::FromStr; +use arena::TypedArena; + +#[deriving(PartialEq, Show)] +pub enum PpSourceMode { + PpmNormal, + PpmExpanded, + PpmTyped, + PpmIdentified, + PpmExpandedIdentified, + PpmExpandedHygiene, +} + +#[deriving(PartialEq, Show)] +pub enum PpMode { + PpmSource(PpSourceMode), + PpmFlowGraph, +} + +pub fn parse_pretty(sess: &Session, name: &str) -> (PpMode, Option) { + let mut split = name.splitn(1, '='); + let first = split.next().unwrap(); + let opt_second = split.next(); + let first = match first { + "normal" => PpmSource(PpmNormal), + "expanded" => PpmSource(PpmExpanded), + "typed" => PpmSource(PpmTyped), + "expanded,identified" => PpmSource(PpmExpandedIdentified), + "expanded,hygiene" => PpmSource(PpmExpandedHygiene), + "identified" => PpmSource(PpmIdentified), + "flowgraph" => PpmFlowGraph, + _ => { + sess.fatal(format!( + "argument to `pretty` must be one of `normal`, \ + `expanded`, `flowgraph=`, `typed`, `identified`, \ + or `expanded,identified`; got {}", name).as_slice()); + } + }; + let opt_second = opt_second.and_then::(from_str); + (first, opt_second) +} + + + +// This slightly awkward construction is to allow for each PpMode to +// choose whether it needs to do analyses (which can consume the +// Session) and then pass through the session (now attached to the +// analysis results) on to the chosen pretty-printer, along with the +// `&PpAnn` object. +// +// Note that since the `&PrinterSupport` is freshly constructed on each +// call, it would not make sense to try to attach the lifetime of `self` +// to the lifetime of the `&PrinterObject`. +// +// (The `use_once_payload` is working around the current lack of once +// functions in the compiler.) + +impl PpSourceMode { + /// Constructs a `PrinterSupport` object and passes it to `f`. + fn call_with_pp_support<'tcx, A, B>(&self, + sess: Session, + ast_map: Option>, + type_arena: &'tcx TypedArena, + id: String, + payload: B, + f: |&PrinterSupport, B| -> A) -> A { + match *self { + PpmNormal | PpmExpanded => { + let annotation = NoAnn { sess: sess, ast_map: ast_map }; + f(&annotation, payload) + } + + PpmIdentified | PpmExpandedIdentified => { + let annotation = IdentifiedAnnotation { sess: sess, ast_map: ast_map }; + f(&annotation, payload) + } + PpmExpandedHygiene => { + let annotation = HygieneAnnotation { sess: sess, ast_map: ast_map }; + f(&annotation, payload) + } + PpmTyped => { + let ast_map = ast_map.expect("--pretty=typed missing ast_map"); + let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, + type_arena, id); + let annotation = TypedAnnotation { analysis: analysis }; + f(&annotation, payload) + } + } + } +} + +trait PrinterSupport<'ast>: pprust::PpAnn { + /// Provides a uniform interface for re-extracting a reference to a + /// `Session` from a value that now owns it. + fn sess<'a>(&'a self) -> &'a Session; + + /// Provides a uniform interface for re-extracting a reference to an + /// `ast_map::Map` from a value that now owns it. + fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>>; + + /// Produces the pretty-print annotation object. + /// + /// (Rust does not yet support upcasting from a trait object to + /// an object for one of its super-traits.) + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self as &pprust::PpAnn } +} + +struct NoAnn<'ast> { + sess: Session, + ast_map: Option> +} + +impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> { + fn sess<'a>(&'a self) -> &'a Session { &self.sess } + + fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { + self.ast_map.as_ref() + } +} + +impl<'ast> pprust::PpAnn for NoAnn<'ast> {} + +struct IdentifiedAnnotation<'ast> { + sess: Session, + ast_map: Option>, +} + +impl<'ast> PrinterSupport<'ast> for IdentifiedAnnotation<'ast> { + fn sess<'a>(&'a self) -> &'a Session { &self.sess } + + fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { + self.ast_map.as_ref() + } +} + +impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> { + fn pre(&self, + s: &mut pprust::State, + node: pprust::AnnNode) -> io::IoResult<()> { + match node { + pprust::NodeExpr(_) => s.popen(), + _ => Ok(()) + } + } + fn post(&self, + s: &mut pprust::State, + node: pprust::AnnNode) -> io::IoResult<()> { + match node { + pprust::NodeIdent(_) | pprust::NodeName(_) => Ok(()), + + pprust::NodeItem(item) => { + try!(pp::space(&mut s.s)); + s.synth_comment(item.id.to_string()) + } + pprust::NodeBlock(blk) => { + try!(pp::space(&mut s.s)); + s.synth_comment(format!("block {}", blk.id)) + } + pprust::NodeExpr(expr) => { + try!(pp::space(&mut s.s)); + try!(s.synth_comment(expr.id.to_string())); + s.pclose() + } + pprust::NodePat(pat) => { + try!(pp::space(&mut s.s)); + s.synth_comment(format!("pat {}", pat.id)) + } + } + } +} + +struct HygieneAnnotation<'ast> { + sess: Session, + ast_map: Option>, +} + +impl<'ast> PrinterSupport<'ast> for HygieneAnnotation<'ast> { + fn sess<'a>(&'a self) -> &'a Session { &self.sess } + + fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { + self.ast_map.as_ref() + } +} + +impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { + fn post(&self, + s: &mut pprust::State, + node: pprust::AnnNode) -> io::IoResult<()> { + match node { + pprust::NodeIdent(&ast::Ident { name: ast::Name(nm), ctxt }) => { + try!(pp::space(&mut s.s)); + // FIXME #16420: this doesn't display the connections + // between syntax contexts + s.synth_comment(format!("{}#{}", nm, ctxt)) + } + pprust::NodeName(&ast::Name(nm)) => { + try!(pp::space(&mut s.s)); + s.synth_comment(nm.to_string()) + } + _ => Ok(()) + } + } +} + + +struct TypedAnnotation<'tcx> { + analysis: CrateAnalysis<'tcx>, +} + +impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> { + fn sess<'a>(&'a self) -> &'a Session { &self.analysis.ty_cx.sess } + + fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'tcx>> { + Some(&self.analysis.ty_cx.map) + } +} + +impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> { + fn pre(&self, + s: &mut pprust::State, + node: pprust::AnnNode) -> io::IoResult<()> { + match node { + pprust::NodeExpr(_) => s.popen(), + _ => Ok(()) + } + } + fn post(&self, + s: &mut pprust::State, + node: pprust::AnnNode) -> io::IoResult<()> { + let tcx = &self.analysis.ty_cx; + match node { + pprust::NodeExpr(expr) => { + try!(pp::space(&mut s.s)); + try!(pp::word(&mut s.s, "as")); + try!(pp::space(&mut s.s)); + try!(pp::word(&mut s.s, + ppaux::ty_to_string( + tcx, + ty::expr_ty(tcx, expr)).as_slice())); + s.pclose() + } + _ => Ok(()) + } + } +} + +fn gather_flowgraph_variants(sess: &Session) -> Vec { + let print_loans = config::FLOWGRAPH_PRINT_LOANS; + let print_moves = config::FLOWGRAPH_PRINT_MOVES; + let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS; + let print_all = config::FLOWGRAPH_PRINT_ALL; + let opt = |print_which| sess.debugging_opt(print_which); + let mut variants = Vec::new(); + if opt(print_all) || opt(print_loans) { + variants.push(borrowck_dot::Loans); + } + if opt(print_all) || opt(print_moves) { + variants.push(borrowck_dot::Moves); + } + if opt(print_all) || opt(print_assigns) { + variants.push(borrowck_dot::Assigns); + } + variants +} + +#[deriving(Clone, Show)] +pub enum UserIdentifiedItem { + ItemViaNode(ast::NodeId), + ItemViaPath(Vec), +} + +impl FromStr for UserIdentifiedItem { + fn from_str(s: &str) -> Option { + let extract_path_parts = || { + let v : Vec<_> = s.split_str("::") + .map(|x|x.to_string()) + .collect(); + Some(ItemViaPath(v)) + }; + + from_str(s).map(ItemViaNode).or_else(extract_path_parts) + } +} + +enum NodesMatchingUII<'a, 'ast: 'a> { + NodesMatchingDirect(option::Item), + NodesMatchingSuffix(ast_map::NodesMatchingSuffix<'a, 'ast, String>), +} + +impl<'a, 'ast> Iterator for NodesMatchingUII<'a, 'ast> { + fn next(&mut self) -> Option { + match self { + &NodesMatchingDirect(ref mut iter) => iter.next(), + &NodesMatchingSuffix(ref mut iter) => iter.next(), + } + } +} + +impl UserIdentifiedItem { + fn reconstructed_input(&self) -> String { + match *self { + ItemViaNode(node_id) => node_id.to_string(), + ItemViaPath(ref parts) => parts.connect("::"), + } + } + + fn all_matching_node_ids<'a, 'ast>(&'a self, map: &'a ast_map::Map<'ast>) + -> NodesMatchingUII<'a, 'ast> { + match *self { + ItemViaNode(node_id) => + NodesMatchingDirect(Some(node_id).into_iter()), + ItemViaPath(ref parts) => + NodesMatchingSuffix(map.nodes_matching_suffix(parts.as_slice())), + } + } + + fn to_one_node_id(self, user_option: &str, sess: &Session, map: &ast_map::Map) -> ast::NodeId { + let fail_because = |is_wrong_because| -> ast::NodeId { + let message = + format!("{:s} needs NodeId (int) or unique \ + path suffix (b::c::d); got {:s}, which {:s}", + user_option, + self.reconstructed_input(), + is_wrong_because); + sess.fatal(message.as_slice()) + }; + + let mut saw_node = ast::DUMMY_NODE_ID; + let mut seen = 0u; + for node in self.all_matching_node_ids(map) { + saw_node = node; + seen += 1; + if seen > 1 { + fail_because("does not resolve uniquely"); + } + } + if seen == 0 { + fail_because("does not resolve to any item"); + } + + assert!(seen == 1); + return saw_node; + } +} + +fn needs_ast_map(ppm: &PpMode, opt_uii: &Option) -> bool { + match *ppm { + PpmSource(PpmNormal) | + PpmSource(PpmIdentified) => opt_uii.is_some(), + + PpmSource(PpmExpanded) | + PpmSource(PpmExpandedIdentified) | + PpmSource(PpmExpandedHygiene) | + PpmSource(PpmTyped) | + PpmFlowGraph => true + } +} + +fn needs_expansion(ppm: &PpMode) -> bool { + match *ppm { + PpmSource(PpmNormal) | + PpmSource(PpmIdentified) => false, + + PpmSource(PpmExpanded) | + PpmSource(PpmExpandedIdentified) | + PpmSource(PpmExpandedHygiene) | + PpmSource(PpmTyped) | + PpmFlowGraph => true + } +} + +pub fn pretty_print_input(sess: Session, + cfg: ast::CrateConfig, + input: &driver::Input, + ppm: PpMode, + opt_uii: Option, + ofile: Option) { + let krate = driver::phase_1_parse_input(&sess, cfg, input); + let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), input); + + let is_expanded = needs_expansion(&ppm); + let compute_ast_map = needs_ast_map(&ppm, &opt_uii); + let krate = if compute_ast_map { + match driver::phase_2_configure_and_expand(&sess, krate, id.as_slice(), None) { + None => return, + Some(k) => k + } + } else { + krate + }; + + let mut forest = ast_map::Forest::new(krate); + let type_arena = TypedArena::new(); + + let (krate, ast_map) = if compute_ast_map { + let map = driver::assign_node_ids_and_map(&sess, &mut forest); + (map.krate(), Some(map)) + } else { + (forest.krate(), None) + }; + + let src_name = driver::source_name(input); + let src = sess.codemap().get_filemap(src_name.as_slice()) + .src.as_bytes().to_vec(); + let mut rdr = MemReader::new(src); + + let out = match ofile { + None => box io::stdout() as Box, + Some(p) => { + let r = io::File::create(&p); + match r { + Ok(w) => box w as Box, + Err(e) => panic!("print-print failed to open {} due to {}", + p.display(), e), + } + } + }; + + match (ppm, opt_uii) { + (PpmSource(s), None) => + s.call_with_pp_support( + sess, ast_map, &type_arena, id, out, |annotation, out| { + debug!("pretty printing source code {}", s); + let sess = annotation.sess(); + pprust::print_crate(sess.codemap(), + sess.diagnostic(), + krate, + src_name.to_string(), + &mut rdr, + out, + annotation.pp_ann(), + is_expanded) + }), + + (PpmSource(s), Some(uii)) => + s.call_with_pp_support( + sess, ast_map, &type_arena, id, (out,uii), |annotation, (out,uii)| { + debug!("pretty printing source code {}", s); + let sess = annotation.sess(); + let ast_map = annotation.ast_map() + .expect("--pretty missing ast_map"); + let mut pp_state = + pprust::State::new_from_input(sess.codemap(), + sess.diagnostic(), + src_name.to_string(), + &mut rdr, + out, + annotation.pp_ann(), + is_expanded); + for node_id in uii.all_matching_node_ids(ast_map) { + let node = ast_map.get(node_id); + try!(pp_state.print_node(&node)); + try!(pp::space(&mut pp_state.s)); + try!(pp_state.synth_comment(ast_map.path_to_string(node_id))); + try!(pp::hardbreak(&mut pp_state.s)); + } + pp::eof(&mut pp_state.s) + }), + + (PpmFlowGraph, opt_uii) => { + debug!("pretty printing flow graph for {}", opt_uii); + let uii = opt_uii.unwrap_or_else(|| { + sess.fatal(format!("`pretty flowgraph=..` needs NodeId (int) or + unique path suffix (b::c::d)").as_slice()) + + }); + let ast_map = ast_map.expect("--pretty flowgraph missing ast_map"); + let nodeid = uii.to_one_node_id("--pretty", &sess, &ast_map); + + let node = ast_map.find(nodeid).unwrap_or_else(|| { + sess.fatal(format!("--pretty flowgraph couldn't find id: {}", + nodeid).as_slice()) + }); + + let code = blocks::Code::from_node(node); + match code { + Some(code) => { + let variants = gather_flowgraph_variants(&sess); + let analysis = driver::phase_3_run_analysis_passes(sess, ast_map, + &type_arena, id); + print_flowgraph(variants, analysis, code, out) + } + None => { + let message = format!("--pretty=flowgraph needs \ + block, fn, or method; got {}", + node); + + // point to what was found, if there's an + // accessible span. + match ast_map.opt_span(nodeid) { + Some(sp) => sess.span_fatal(sp, message.as_slice()), + None => sess.fatal(message.as_slice()) + } + } + } + } + }.unwrap() +} + +fn print_flowgraph(variants: Vec, + analysis: CrateAnalysis, + code: blocks::Code, + mut out: W) -> io::IoResult<()> { + let ty_cx = &analysis.ty_cx; + let cfg = match code { + blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block), + blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, &*fn_like.body()), + }; + + match code { + _ if variants.len() == 0 => { + let lcfg = LabelledCFG { + ast_map: &ty_cx.map, + cfg: &cfg, + name: format!("node_{}", code.id()), + }; + let r = dot::render(&lcfg, &mut out); + return expand_err_details(r); + } + blocks::BlockCode(_) => { + ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \ + annotations requires fn-like node id."); + return Ok(()) + } + blocks::FnLikeCode(fn_like) => { + let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg); + let (bccx, analysis_data) = + borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts); + + let lcfg = LabelledCFG { + ast_map: &ty_cx.map, + cfg: &cfg, + name: format!("node_{}", code.id()), + }; + let lcfg = borrowck_dot::DataflowLabeller { + inner: lcfg, + variants: variants, + borrowck_ctxt: &bccx, + analysis_data: &analysis_data, + }; + let r = dot::render(&lcfg, &mut out); + return expand_err_details(r); + } + } + + fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> { + r.map_err(|ioerr| { + let orig_detail = ioerr.detail.clone(); + let m = "graphviz::render failed"; + io::IoError { + detail: Some(match orig_detail { + None => m.into_string(), + Some(d) => format!("{}: {}", m, d) + }), + ..ioerr + } + }) + } +} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs new file mode 100644 index 00000000000..ad9333e5794 --- /dev/null +++ b/src/librustc_trans/lib.rs @@ -0,0 +1,87 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +The Rust compiler. + +# Note + +This API is completely unstable and subject to change. + +*/ + +#![crate_name = "rustc_trans"] +#![experimental] +#![comment = "The Rust compiler back end and driver"] +#![license = "MIT/ASL2"] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/")] + +#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)] +#![feature(slicing_syntax, struct_variant, unsafe_destructor)] +#![feature(rustc_diagnostic_macros)] + +extern crate arena; +extern crate flate; +extern crate getopts; +extern crate graphviz; +extern crate libc; +extern crate rustc; +extern crate rustc_back; +#[phase(plugin, link)] extern crate log; +#[phase(plugin, link)] extern crate syntax; +extern crate serialize; +extern crate "rustc_llvm" as llvm; + +pub use rustc::session; +pub use rustc::metadata; +pub use rustc::middle; +pub use rustc::lint; +pub use rustc::plugin; +pub use rustc::util; + +pub mod back { + pub use rustc_back::abi; + pub use rustc_back::archive; + pub use rustc_back::arm; + pub use rustc_back::mips; + pub use rustc_back::mipsel; + pub use rustc_back::rpath; + pub use rustc_back::svh; + pub use rustc_back::target_strs; + pub use rustc_back::x86; + pub use rustc_back::x86_64; + + pub mod link; + pub mod lto; + pub mod write; + +} + +pub mod trans; +pub mod save; +pub mod driver; + +pub mod lib { + pub use llvm; +} + +pub fn main() { + let args = std::os::args(); + let result = driver::run(args); + std::os::set_exit_status(result); +} + +#[cfg(test)] +pub mod test; diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs new file mode 100644 index 00000000000..38bc8c99c0f --- /dev/null +++ b/src/librustc_trans/save/mod.rs @@ -0,0 +1,1558 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Output a CSV file containing the output from rustc's analysis. The data is +//! primarily designed to be used as input to the DXR tool, specifically its +//! Rust plugin. It could also be used by IDEs or other code browsing, search, or +//! cross-referencing tools. +//! +//! Dumping the analysis is implemented by walking the AST and getting a bunch of +//! info out from all over the place. We use Def IDs to identify objects. The +//! tricky part is getting syntactic (span, source text) and semantic (reference +//! Def IDs) information for parts of expressions which the compiler has discarded. +//! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole +//! path and a reference to `baz`, but we want spans and references for all three +//! idents. +//! +//! SpanUtils is used to manipulate spans. In particular, to extract sub-spans +//! from spans (e.g., the span for `bar` from the above example path). +//! Recorder is used for recording the output in csv format. FmtStrs separates +//! the format of the output away from extracting it from the compiler. +//! DxrVisitor walks the AST and processes it. + +use driver::driver::CrateAnalysis; +use session::Session; + +use middle::def; +use middle::ty; +use middle::typeck; + +use std::cell::Cell; +use std::io; +use std::io::File; +use std::io::fs; +use std::os; + +use syntax::ast; +use syntax::ast_util; +use syntax::ast_util::PostExpansionMethod; +use syntax::ast::{NodeId,DefId}; +use syntax::ast_map::NodeItem; +use syntax::attr; +use syntax::codemap::*; +use syntax::parse::token; +use syntax::parse::token::{get_ident,keywords}; +use syntax::owned_slice::OwnedSlice; +use syntax::visit; +use syntax::visit::Visitor; +use syntax::print::pprust::{path_to_string,ty_to_string}; +use syntax::ptr::P; + +use self::span_utils::SpanUtils; +use self::recorder::Recorder; +use self::recorder::FmtStrs; + +use util::ppaux; + +mod span_utils; +mod recorder; + +// Helper function to escape quotes in a string +fn escape(s: String) -> String { + s.replace("\"", "\"\"") +} + +// If the expression is a macro expansion or other generated code, run screaming and don't index. +fn generated_code(span: Span) -> bool { + span.expn_id != NO_EXPANSION || span == DUMMY_SP +} + +struct DxrVisitor<'l, 'tcx: 'l> { + sess: &'l Session, + analysis: &'l CrateAnalysis<'tcx>, + + collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, + collecting: bool, + + span: SpanUtils<'l>, + fmt: FmtStrs<'l>, + + cur_scope: NodeId +} + +impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { + fn nest(&mut self, scope_id: NodeId, f: |&mut DxrVisitor<'l, 'tcx>|) { + let parent_scope = self.cur_scope; + self.cur_scope = scope_id; + f(self); + self.cur_scope = parent_scope; + } + + fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { + // the current crate + self.fmt.crate_str(krate.span, name); + + // dump info about all the external crates referenced from this crate + self.sess.cstore.iter_crate_data(|n, cmd| { + self.fmt.external_crate_str(krate.span, cmd.name.as_slice(), n); + }); + self.fmt.recorder.record("end_external_crates\n"); + } + + // Return all non-empty prefixes of a path. + // For each prefix, we return the span for the last segment in the prefix and + // a str representation of the entire prefix. + fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> { + let spans = self.span.spans_for_path_segments(path); + + // Paths to enums seem to not match their spans - the span includes all the + // variants too. But they seem to always be at the end, so I hope we can cope with + // always using the first ones. So, only error out if we don't have enough spans. + // What could go wrong...? + if spans.len() < path.segments.len() { + error!("Mis-calculated spans for path '{}'. \ + Found {} spans, expected {}. Found spans:", + path_to_string(path), spans.len(), path.segments.len()); + for s in spans.iter() { + let loc = self.sess.codemap().lookup_char_pos(s.lo); + error!(" '{}' in {}, line {}", + self.span.snippet(*s), loc.file.name, loc.line); + } + return vec!(); + } + + let mut result: Vec<(Span, String)> = vec!(); + + + let mut segs = vec!(); + for (seg, span) in path.segments.iter().zip(spans.iter()) { + segs.push(seg.clone()); + let sub_path = ast::Path{span: *span, // span for the last segment + global: path.global, + segments: segs}; + let qualname = path_to_string(&sub_path); + result.push((*span, qualname)); + segs = sub_path.segments; + } + + result + } + + fn write_sub_paths(&mut self, path: &ast::Path) { + let sub_paths = self.process_path_prefixes(path); + for &(ref span, ref qualname) in sub_paths.iter() { + self.fmt.sub_mod_ref_str(path.span, + *span, + qualname.as_slice(), + self.cur_scope); + } + } + + // As write_sub_paths, but does not process the last ident in the path (assuming it + // will be processed elsewhere). + fn write_sub_paths_truncated(&mut self, path: &ast::Path) { + let sub_paths = self.process_path_prefixes(path); + let len = sub_paths.len(); + if len <= 1 { + return; + } + + let sub_paths = sub_paths.slice(0, len-1); + for &(ref span, ref qualname) in sub_paths.iter() { + self.fmt.sub_mod_ref_str(path.span, + *span, + qualname.as_slice(), + self.cur_scope); + } + } + + // As write_sub_paths, but expects a path of the form module_path::trait::method + // Where trait could actually be a struct too. + fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) { + let sub_paths = self.process_path_prefixes(path); + let len = sub_paths.len(); + if len <= 1 { + return; + } + let sub_paths = sub_paths.slice_to(len-1); + + // write the trait part of the sub-path + let (ref span, ref qualname) = sub_paths[len-2]; + self.fmt.sub_type_ref_str(path.span, + *span, + qualname.as_slice()); + + // write the other sub-paths + if len <= 2 { + return; + } + let sub_paths = sub_paths[..len-2]; + for &(ref span, ref qualname) in sub_paths.iter() { + self.fmt.sub_mod_ref_str(path.span, + *span, + qualname.as_slice(), + self.cur_scope); + } + } + + // looks up anything, not just a type + fn lookup_type_ref(&self, ref_id: NodeId) -> Option { + if !self.analysis.ty_cx.def_map.borrow().contains_key(&ref_id) { + self.sess.bug(format!("def_map has no key for {} in lookup_type_ref", + ref_id).as_slice()); + } + let def = (*self.analysis.ty_cx.def_map.borrow())[ref_id]; + match def { + def::DefPrimTy(_) => None, + _ => Some(def.def_id()), + } + } + + fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option { + let def_map = self.analysis.ty_cx.def_map.borrow(); + if !def_map.contains_key(&ref_id) { + self.sess.span_bug(span, format!("def_map has no key for {} in lookup_def_kind", + ref_id).as_slice()); + } + let def = (*def_map)[ref_id]; + match def { + def::DefMod(_) | + def::DefForeignMod(_) => Some(recorder::ModRef), + def::DefStruct(_) => Some(recorder::StructRef), + def::DefTy(..) | + def::DefAssociatedTy(..) | + def::DefTrait(_) => Some(recorder::TypeRef), + def::DefStatic(_, _) | + def::DefConst(_) | + def::DefLocal(_) | + def::DefVariant(_, _, _) | + def::DefUpvar(..) => Some(recorder::VarRef), + + def::DefFn(..) => Some(recorder::FnRef), + + def::DefSelfTy(_) | + def::DefRegion(_) | + def::DefTyParamBinder(_) | + def::DefLabel(_) | + def::DefStaticMethod(..) | + def::DefTyParam(..) | + def::DefUse(_) | + def::DefMethod(..) | + def::DefPrimTy(_) => { + self.sess.span_bug(span, format!("lookup_def_kind for unexpected item: {}", + def).as_slice()); + }, + } + } + + fn process_formals(&mut self, formals: &Vec, qualname: &str) { + for arg in formals.iter() { + assert!(self.collected_paths.len() == 0 && !self.collecting); + self.collecting = true; + self.visit_pat(&*arg.pat); + self.collecting = false; + let span_utils = self.span; + for &(id, ref p, _, _) in self.collected_paths.iter() { + let typ = ppaux::ty_to_string(&self.analysis.ty_cx, + (*self.analysis.ty_cx.node_types.borrow())[id]); + // get the span only for the name of the variable (I hope the path is only ever a + // variable name, but who knows?) + self.fmt.formal_str(p.span, + span_utils.span_for_last_ident(p.span), + id, + qualname, + path_to_string(p).as_slice(), + typ.as_slice()); + } + self.collected_paths.clear(); + } + } + + fn process_method(&mut self, method: &ast::Method) { + if generated_code(method.span) { + return; + } + + let mut scope_id; + // The qualname for a method is the trait name or name of the struct in an impl in + // which the method is declared in followed by the method's name. + let mut qualname = match ty::impl_of_method(&self.analysis.ty_cx, + ast_util::local_def(method.id)) { + Some(impl_id) => match self.analysis.ty_cx.map.get(impl_id.node) { + NodeItem(item) => { + scope_id = item.id; + match item.node { + ast::ItemImpl(_, _, ref ty, _) => { + let mut result = String::from_str("<"); + result.push_str(ty_to_string(&**ty).as_slice()); + + match ty::trait_of_item(&self.analysis.ty_cx, + ast_util::local_def(method.id)) { + Some(def_id) => { + result.push_str(" as "); + result.push_str( + ty::item_path_str(&self.analysis.ty_cx, def_id).as_slice()); + }, + None => {} + } + result.push_str(">::"); + result + } + _ => { + self.sess.span_bug(method.span, + format!("Container {} for method {} not an impl?", + impl_id.node, method.id).as_slice()); + }, + } + }, + _ => { + self.sess.span_bug(method.span, + format!("Container {} for method {} is not a node item {}", + impl_id.node, + method.id, + self.analysis.ty_cx.map.get(impl_id.node) + ).as_slice()); + }, + }, + None => match ty::trait_of_item(&self.analysis.ty_cx, + ast_util::local_def(method.id)) { + Some(def_id) => { + scope_id = def_id.node; + match self.analysis.ty_cx.map.get(def_id.node) { + NodeItem(_) => { + let mut result = ty::item_path_str(&self.analysis.ty_cx, def_id); + result.push_str("::"); + result + } + _ => { + self.sess.span_bug(method.span, + format!("Could not find container {} for method {}", + def_id.node, method.id).as_slice()); + } + } + }, + None => { + self.sess.span_bug(method.span, + format!("Could not find container for method {}", + method.id).as_slice()); + }, + }, + }; + + qualname.push_str(get_ident(method.pe_ident()).get()); + let qualname = qualname.as_slice(); + + // record the decl for this def (if it has one) + let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx, + ast_util::local_def(method.id)) + .and_then(|def_id| { + if match def_id { + ty::MethodTraitItemId(def_id) => { + method.id != 0 && def_id.node == 0 + } + ty::TypeTraitItemId(_) => false, + } { + Some(def_id) + } else { + None + } + }); + let decl_id = match decl_id { + None => None, + Some(id) => Some(id.def_id()), + }; + + let sub_span = self.span.sub_span_after_keyword(method.span, keywords::Fn); + self.fmt.method_str(method.span, + sub_span, + method.id, + qualname, + decl_id, + scope_id); + + self.process_formals(&method.pe_fn_decl().inputs, qualname); + + // walk arg and return types + for arg in method.pe_fn_decl().inputs.iter() { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = method.pe_fn_decl().output { + self.visit_ty(&**ret_ty); + } + + // walk the fn body + self.nest(method.id, |v| v.visit_block(&*method.pe_body())); + + self.process_generic_params(method.pe_generics(), + method.span, + qualname, + method.id); + } + + fn process_trait_ref(&mut self, + trait_ref: &ast::TraitRef, + impl_id: Option) { + match self.lookup_type_ref(trait_ref.ref_id) { + Some(id) => { + let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span); + self.fmt.ref_str(recorder::TypeRef, + trait_ref.path.span, + sub_span, + id, + self.cur_scope); + match impl_id { + Some(impl_id) => self.fmt.impl_str(trait_ref.path.span, + sub_span, + impl_id, + id, + self.cur_scope), + None => (), + } + visit::walk_path(self, &trait_ref.path); + }, + None => () + } + } + + fn process_struct_field_def(&mut self, + field: &ast::StructField, + qualname: &str, + scope_id: NodeId) { + match field.node.kind { + ast::NamedField(ident, _) => { + let name = get_ident(ident); + let qualname = format!("{}::{}", qualname, name); + let typ = ppaux::ty_to_string(&self.analysis.ty_cx, + (*self.analysis.ty_cx.node_types.borrow())[field.node.id]); + match self.span.sub_span_before_token(field.span, token::Colon) { + Some(sub_span) => self.fmt.field_str(field.span, + Some(sub_span), + field.node.id, + name.get().as_slice(), + qualname.as_slice(), + typ.as_slice(), + scope_id), + None => self.sess.span_bug(field.span, + format!("Could not find sub-span for field {}", + qualname).as_slice()), + } + }, + _ => (), + } + } + + // Dump generic params bindings, then visit_generics + fn process_generic_params(&mut self, generics:&ast::Generics, + full_span: Span, + prefix: &str, + id: NodeId) { + // We can't only use visit_generics since we don't have spans for param + // bindings, so we reparse the full_span to get those sub spans. + // However full span is the entire enum/fn/struct block, so we only want + // the first few to match the number of generics we're looking for. + let param_sub_spans = self.span.spans_for_ty_params(full_span, + (generics.ty_params.len() as int)); + for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans.iter()) { + // Append $id to name to make sure each one is unique + let name = format!("{}::{}${}", + prefix, + escape(self.span.snippet(*param_ss)), + id); + self.fmt.typedef_str(full_span, + Some(*param_ss), + param.id, + name.as_slice(), + ""); + } + self.visit_generics(generics); + } + + fn process_fn(&mut self, + item: &ast::Item, + decl: &ast::FnDecl, + ty_params: &ast::Generics, + body: &ast::Block) { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn); + self.fmt.fn_str(item.span, + sub_span, + item.id, + qualname.as_slice(), + self.cur_scope); + + self.process_formals(&decl.inputs, qualname.as_slice()); + + // walk arg and return types + for arg in decl.inputs.iter() { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = decl.output { + self.visit_ty(&**ret_ty); + } + + // walk the body + self.nest(item.id, |v| v.visit_block(&*body)); + + self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); + } + + fn process_static(&mut self, + item: &ast::Item, + typ: &ast::Ty, + mt: ast::Mutability, + expr: &ast::Expr) + { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + // If the variable is immutable, save the initialising expression. + let value = match mt { + ast::MutMutable => String::from_str(""), + ast::MutImmutable => self.span.snippet(expr.span), + }; + + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Static); + self.fmt.static_str(item.span, + sub_span, + item.id, + get_ident(item.ident).get(), + qualname.as_slice(), + value.as_slice(), + ty_to_string(&*typ).as_slice(), + self.cur_scope); + + // walk type and init value + self.visit_ty(&*typ); + self.visit_expr(expr); + } + + fn process_const(&mut self, + item: &ast::Item, + typ: &ast::Ty, + expr: &ast::Expr) + { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + let sub_span = self.span.sub_span_after_keyword(item.span, + keywords::Const); + self.fmt.static_str(item.span, + sub_span, + item.id, + get_ident(item.ident).get(), + qualname.as_slice(), + "", + ty_to_string(&*typ).as_slice(), + self.cur_scope); + + // walk type and init value + self.visit_ty(&*typ); + self.visit_expr(expr); + } + + fn process_struct(&mut self, + item: &ast::Item, + def: &ast::StructDef, + ty_params: &ast::Generics) { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + let ctor_id = match def.ctor_id { + Some(node_id) => node_id, + None => -1, + }; + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct); + self.fmt.struct_str(item.span, + sub_span, + item.id, + ctor_id, + qualname.as_slice(), + self.cur_scope); + + // fields + for field in def.fields.iter() { + self.process_struct_field_def(field, qualname.as_slice(), item.id); + self.visit_ty(&*field.node.ty); + } + + self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); + } + + fn process_enum(&mut self, + item: &ast::Item, + enum_definition: &ast::EnumDef, + ty_params: &ast::Generics) { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + match self.span.sub_span_after_keyword(item.span, keywords::Enum) { + Some(sub_span) => self.fmt.enum_str(item.span, + Some(sub_span), + item.id, + qualname.as_slice(), + self.cur_scope), + None => self.sess.span_bug(item.span, + format!("Could not find subspan for enum {}", + qualname).as_slice()), + } + for variant in enum_definition.variants.iter() { + let name = get_ident(variant.node.name); + let name = name.get(); + let mut qualname = qualname.clone(); + qualname.push_str("::"); + qualname.push_str(name); + let val = self.span.snippet(variant.span); + match variant.node.kind { + ast::TupleVariantKind(ref args) => { + // first ident in span is the variant's name + self.fmt.tuple_variant_str(variant.span, + self.span.span_for_first_ident(variant.span), + variant.node.id, + name, + qualname.as_slice(), + val.as_slice(), + item.id); + for arg in args.iter() { + self.visit_ty(&*arg.ty); + } + } + ast::StructVariantKind(ref struct_def) => { + let ctor_id = match struct_def.ctor_id { + Some(node_id) => node_id, + None => -1, + }; + self.fmt.struct_variant_str( + variant.span, + self.span.span_for_first_ident(variant.span), + variant.node.id, + ctor_id, + qualname.as_slice(), + val.as_slice(), + item.id); + + for field in struct_def.fields.iter() { + self.process_struct_field_def(field, qualname.as_slice(), variant.node.id); + self.visit_ty(&*field.node.ty); + } + } + } + } + + self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); + } + + fn process_impl(&mut self, + item: &ast::Item, + type_parameters: &ast::Generics, + trait_ref: &Option, + typ: &ast::Ty, + impl_items: &Vec) { + match typ.node { + ast::TyPath(ref path, _, id) => { + match self.lookup_type_ref(id) { + Some(id) => { + let sub_span = self.span.sub_span_for_type_name(path.span); + self.fmt.ref_str(recorder::TypeRef, + path.span, + sub_span, + id, + self.cur_scope); + self.fmt.impl_str(path.span, + sub_span, + item.id, + id, + self.cur_scope); + }, + None => () + } + }, + _ => self.visit_ty(&*typ), + } + + match *trait_ref { + Some(ref trait_ref) => self.process_trait_ref(trait_ref, Some(item.id)), + None => (), + } + + self.process_generic_params(type_parameters, item.span, "", item.id); + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + visit::walk_method_helper(self, &**method) + } + ast::TypeImplItem(ref typedef) => { + visit::walk_ty(self, &*typedef.typ) + } + } + } + } + + fn process_trait(&mut self, + item: &ast::Item, + generics: &ast::Generics, + trait_refs: &OwnedSlice, + methods: &Vec) { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait); + self.fmt.trait_str(item.span, + sub_span, + item.id, + qualname.as_slice(), + self.cur_scope); + + // super-traits + for super_bound in trait_refs.iter() { + let trait_ref = match *super_bound { + ast::TraitTyParamBound(ref trait_ref) => { + trait_ref + } + ast::RegionTyParamBound(..) => { + continue; + } + }; + + let trait_ref = &trait_ref.trait_ref; + match self.lookup_type_ref(trait_ref.ref_id) { + Some(id) => { + let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span); + self.fmt.ref_str(recorder::TypeRef, + trait_ref.path.span, + sub_span, + id, + self.cur_scope); + self.fmt.inherit_str(trait_ref.path.span, + sub_span, + id, + item.id); + }, + None => () + } + } + + // walk generics and methods + self.process_generic_params(generics, item.span, qualname.as_slice(), item.id); + for method in methods.iter() { + self.visit_trait_item(method) + } + } + + fn process_mod(&mut self, + item: &ast::Item, // The module in question, represented as an item. + m: &ast::Mod) { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + + let cm = self.sess.codemap(); + let filename = cm.span_to_filename(m.inner); + + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Mod); + self.fmt.mod_str(item.span, + sub_span, + item.id, + qualname.as_slice(), + self.cur_scope, + filename.as_slice()); + + self.nest(item.id, |v| visit::walk_mod(v, m)); + } + + fn process_path(&mut self, + ex: &ast::Expr, + path: &ast::Path) { + if generated_code(path.span) { + return + } + + let def_map = self.analysis.ty_cx.def_map.borrow(); + if !def_map.contains_key(&ex.id) { + self.sess.span_bug(ex.span, + format!("def_map has no key for {} in visit_expr", + ex.id).as_slice()); + } + let def = &(*def_map)[ex.id]; + let sub_span = self.span.span_for_last_ident(ex.span); + match *def { + def::DefUpvar(..) | + def::DefLocal(..) | + def::DefStatic(..) | + def::DefConst(..) | + def::DefVariant(..) => self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + def.def_id(), + self.cur_scope), + def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef, + ex.span, + sub_span, + def_id, + self.cur_scope), + def::DefStaticMethod(declid, provenence) => { + let sub_span = self.span.sub_span_for_meth_name(ex.span); + let defid = if declid.krate == ast::LOCAL_CRATE { + let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, + declid); + match provenence { + def::FromTrait(def_id) => { + Some(ty::trait_items(&self.analysis.ty_cx, + def_id) + .iter() + .find(|mr| { + mr.name() == ti.name() + }) + .unwrap() + .def_id()) + } + def::FromImpl(def_id) => { + let impl_items = self.analysis + .ty_cx + .impl_items + .borrow(); + Some((*impl_items)[def_id] + .iter() + .find(|mr| { + ty::impl_or_trait_item( + &self.analysis.ty_cx, + mr.def_id() + ).name() == ti.name() + }) + .unwrap() + .def_id()) + } + } + } else { + None + }; + self.fmt.meth_call_str(ex.span, + sub_span, + defid, + Some(declid), + self.cur_scope); + }, + def::DefFn(def_id, _) => self.fmt.fn_call_str(ex.span, + sub_span, + def_id, + self.cur_scope), + _ => self.sess.span_bug(ex.span, + format!("Unexpected def kind while looking up path in '{}'", + self.span.snippet(ex.span)).as_slice()), + } + // modules or types in the path prefix + match *def { + def::DefStaticMethod(..) => { + self.write_sub_path_trait_truncated(path); + }, + def::DefLocal(_) | + def::DefStatic(_,_) | + def::DefConst(..) | + def::DefStruct(_) | + def::DefFn(..) => self.write_sub_paths_truncated(path), + _ => {}, + } + + visit::walk_path(self, path); + } + + fn process_struct_lit(&mut self, + ex: &ast::Expr, + path: &ast::Path, + fields: &Vec, + base: &Option>) { + if generated_code(path.span) { + return + } + + let mut struct_def: Option = None; + match self.lookup_type_ref(ex.id) { + Some(id) => { + struct_def = Some(id); + let sub_span = self.span.span_for_last_ident(path.span); + self.fmt.ref_str(recorder::StructRef, + path.span, + sub_span, + id, + self.cur_scope); + }, + None => () + } + + self.write_sub_paths_truncated(path); + + for field in fields.iter() { + match struct_def { + Some(struct_def) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); + for f in fields.iter() { + if generated_code(field.ident.span) { + continue; + } + if f.name == field.ident.node.name { + // We don't really need a sub-span here, but no harm done + let sub_span = self.span.span_for_last_ident(field.ident.span); + self.fmt.ref_str(recorder::VarRef, + field.ident.span, + sub_span, + f.id, + self.cur_scope); + } + } + } + None => {} + } + + self.visit_expr(&*field.expr) + } + visit::walk_expr_opt(self, base) + } + + fn process_method_call(&mut self, + ex: &ast::Expr, + args: &Vec>) { + let method_map = self.analysis.ty_cx.method_map.borrow(); + let method_callee = &(*method_map)[typeck::MethodCall::expr(ex.id)]; + let (def_id, decl_id) = match method_callee.origin { + typeck::MethodStatic(def_id) | + typeck::MethodStaticUnboxedClosure(def_id) => { + // method invoked on an object with a concrete type (not a static method) + let decl_id = + match ty::trait_item_of_item(&self.analysis.ty_cx, + def_id) { + None => None, + Some(decl_id) => Some(decl_id.def_id()), + }; + + // This incantation is required if the method referenced is a + // trait's default implementation. + let def_id = match ty::impl_or_trait_item(&self.analysis + .ty_cx, + def_id) { + ty::MethodTraitItem(method) => { + method.provided_source.unwrap_or(def_id) + } + ty::TypeTraitItem(_) => def_id, + }; + (Some(def_id), decl_id) + } + typeck::MethodTypeParam(ref mp) => { + // method invoked on a type parameter + let trait_item = ty::trait_item(&self.analysis.ty_cx, + mp.trait_ref.def_id, + mp.method_num); + (None, Some(trait_item.def_id())) + } + typeck::MethodTraitObject(ref mo) => { + // method invoked on a trait instance + let trait_item = ty::trait_item(&self.analysis.ty_cx, + mo.trait_ref.def_id, + mo.method_num); + (None, Some(trait_item.def_id())) + } + }; + let sub_span = self.span.sub_span_for_meth_name(ex.span); + self.fmt.meth_call_str(ex.span, + sub_span, + def_id, + decl_id, + self.cur_scope); + + // walk receiver and args + visit::walk_exprs(self, args.as_slice()); + } + + fn process_pat(&mut self, p:&ast::Pat) { + if generated_code(p.span) { + return + } + + match p.node { + ast::PatStruct(ref path, ref fields, _) => { + self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef)); + visit::walk_path(self, path); + let struct_def = match self.lookup_type_ref(p.id) { + Some(sd) => sd, + None => { + self.sess.span_bug(p.span, + format!("Could not find struct_def for `{}`", + self.span.snippet(p.span)).as_slice()); + } + }; + for &Spanned { node: ref field, span } in fields.iter() { + self.visit_pat(&*field.pat); + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); + for f in fields.iter() { + if f.name == field.ident.name { + self.fmt.ref_str(recorder::VarRef, + p.span, + Some(span), + f.id, + self.cur_scope); + break; + } + } + } + } + ast::PatEnum(ref path, _) => { + self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); + visit::walk_pat(self, p); + } + ast::PatIdent(bm, ref path1, ref optional_subpattern) => { + let immut = match bm { + // Even if the ref is mut, you can't change the ref, only + // the data pointed at, so showing the initialising expression + // is still worthwhile. + ast::BindByRef(_) => true, + ast::BindByValue(mt) => { + match mt { + ast::MutMutable => false, + ast::MutImmutable => true, + } + } + }; + // collect path for either visit_local or visit_arm + let path = ast_util::ident_to_path(path1.span,path1.node); + self.collected_paths.push((p.id, path, immut, recorder::VarRef)); + match *optional_subpattern { + None => {} + Some(ref subpattern) => self.visit_pat(&**subpattern) + } + } + _ => visit::walk_pat(self, p) + } + } +} + +impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + if generated_code(item.span) { + return + } + + match item.node { + ast::ItemFn(ref decl, _, _, ref ty_params, ref body) => + self.process_fn(item, &**decl, ty_params, &**body), + ast::ItemStatic(ref typ, mt, ref expr) => + self.process_static(item, &**typ, mt, &**expr), + ast::ItemConst(ref typ, ref expr) => + self.process_const(item, &**typ, &**expr), + ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), + ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), + ast::ItemImpl(ref ty_params, + ref trait_ref, + ref typ, + ref impl_items) => { + self.process_impl(item, + ty_params, + trait_ref, + &**typ, + impl_items) + } + ast::ItemTrait(ref generics, _, ref trait_refs, ref methods) => + self.process_trait(item, generics, trait_refs, methods), + ast::ItemMod(ref m) => self.process_mod(item, m), + ast::ItemTy(ref ty, ref ty_params) => { + let qualname = self.analysis.ty_cx.map.path_to_string(item.id); + let value = ty_to_string(&**ty); + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type); + self.fmt.typedef_str(item.span, + sub_span, + item.id, + qualname.as_slice(), + value.as_slice()); + + self.visit_ty(&**ty); + self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id); + }, + ast::ItemMac(_) => (), + _ => visit::walk_item(self, item), + } + } + + fn visit_generics(&mut self, generics: &ast::Generics) { + for param in generics.ty_params.iter() { + for bound in param.bounds.iter() { + match *bound { + ast::TraitTyParamBound(ref trait_ref) => { + self.process_trait_ref(&trait_ref.trait_ref, None); + } + _ => {} + } + } + match param.default { + Some(ref ty) => self.visit_ty(&**ty), + None => {} + } + } + } + + // We don't actually index functions here, that is done in visit_item/ItemFn. + // Here we just visit methods. + fn visit_fn(&mut self, + fk: visit::FnKind<'v>, + fd: &'v ast::FnDecl, + b: &'v ast::Block, + s: Span, + _: NodeId) { + if generated_code(s) { + return; + } + + match fk { + visit::FkMethod(_, _, method) => self.process_method(method), + _ => visit::walk_fn(self, fk, fd, b, s), + } + } + + fn visit_trait_item(&mut self, tm: &ast::TraitItem) { + match *tm { + ast::RequiredMethod(ref method_type) => { + if generated_code(method_type.span) { + return; + } + + let mut scope_id; + let mut qualname = match ty::trait_of_item(&self.analysis.ty_cx, + ast_util::local_def(method_type.id)) { + Some(def_id) => { + scope_id = def_id.node; + let mut s = ty::item_path_str(&self.analysis.ty_cx, def_id); + s.push_str("::"); + s + }, + None => { + self.sess.span_bug(method_type.span, + format!("Could not find trait for method {}", + method_type.id).as_slice()); + }, + }; + + qualname.push_str(get_ident(method_type.ident).get()); + let qualname = qualname.as_slice(); + + let sub_span = self.span.sub_span_after_keyword(method_type.span, keywords::Fn); + self.fmt.method_decl_str(method_type.span, + sub_span, + method_type.id, + qualname, + scope_id); + + // walk arg and return types + for arg in method_type.decl.inputs.iter() { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = method_type.decl.output { + self.visit_ty(&**ret_ty); + } + + self.process_generic_params(&method_type.generics, + method_type.span, + qualname, + method_type.id); + } + ast::ProvidedMethod(ref method) => self.process_method(&**method), + ast::TypeTraitItem(_) => {} + } + } + + fn visit_view_item(&mut self, i: &ast::ViewItem) { + if generated_code(i.span) { + return + } + + match i.node { + ast::ViewItemUse(ref path) => { + match path.node { + ast::ViewPathSimple(ident, ref path, id) => { + let sub_span = self.span.span_for_last_ident(path.span); + let mod_id = match self.lookup_type_ref(id) { + Some(def_id) => { + match self.lookup_def_kind(id, path.span) { + Some(kind) => self.fmt.ref_str(kind, + path.span, + sub_span, + def_id, + self.cur_scope), + None => {}, + } + Some(def_id) + }, + None => None, + }; + + // 'use' always introduces an alias, if there is not an explicit + // one, there is an implicit one. + let sub_span = + match self.span.sub_span_before_token(path.span, token::Eq) { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; + + self.fmt.use_alias_str(path.span, + sub_span, + id, + mod_id, + get_ident(ident).get(), + self.cur_scope); + self.write_sub_paths_truncated(path); + } + ast::ViewPathGlob(ref path, _) => { + self.write_sub_paths(path); + } + ast::ViewPathList(ref path, ref list, _) => { + for plid in list.iter() { + match plid.node { + ast::PathListIdent { id, .. } => { + match self.lookup_type_ref(id) { + Some(def_id) => + match self.lookup_def_kind(id, plid.span) { + Some(kind) => { + self.fmt.ref_str( + kind, plid.span, + Some(plid.span), + def_id, self.cur_scope); + } + None => () + }, + None => () + } + }, + ast::PathListMod { .. } => () + } + } + + self.write_sub_paths(path); + } + } + }, + ast::ViewItemExternCrate(ident, ref s, id) => { + let name = get_ident(ident); + let name = name.get(); + let s = match *s { + Some((ref s, _)) => s.get().to_string(), + None => name.to_string(), + }; + let sub_span = self.span.sub_span_after_keyword(i.span, keywords::Crate); + let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(id) { + Some(cnum) => cnum, + None => 0, + }; + self.fmt.extern_crate_str(i.span, + sub_span, + id, + cnum, + name, + s.as_slice(), + self.cur_scope); + }, + } + } + + fn visit_ty(&mut self, t: &ast::Ty) { + if generated_code(t.span) { + return + } + + match t.node { + ast::TyPath(ref path, _, id) => { + match self.lookup_type_ref(id) { + Some(id) => { + let sub_span = self.span.sub_span_for_type_name(t.span); + self.fmt.ref_str(recorder::TypeRef, + t.span, + sub_span, + id, + self.cur_scope); + }, + None => () + } + + self.write_sub_paths_truncated(path); + + visit::walk_path(self, path); + }, + _ => visit::walk_ty(self, t), + } + } + + fn visit_expr(&mut self, ex: &ast::Expr) { + if generated_code(ex.span) { + return + } + + match ex.node { + ast::ExprCall(ref _f, ref _args) => { + // Don't need to do anything for function calls, + // because just walking the callee path does what we want. + visit::walk_expr(self, ex); + }, + ast::ExprPath(ref path) => self.process_path(ex, path), + ast::ExprStruct(ref path, ref fields, ref base) => + self.process_struct_lit(ex, path, fields, base), + ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args), + ast::ExprField(ref sub_ex, ident, _) => { + if generated_code(sub_ex.span) { + return + } + + self.visit_expr(&**sub_ex); + + let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex); + let t_box = ty::get(t); + match t_box.sty { + ty::ty_struct(def_id, _) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); + for f in fields.iter() { + if f.name == ident.node.name { + let sub_span = self.span.span_for_last_ident(ex.span); + self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + f.id, + self.cur_scope); + break; + } + } + }, + _ => self.sess.span_bug(ex.span, + "Expected struct type, but not ty_struct"), + } + }, + ast::ExprTupField(ref sub_ex, idx, _) => { + if generated_code(sub_ex.span) { + return + } + + self.visit_expr(&**sub_ex); + + let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex); + let t_box = ty::get(t); + match t_box.sty { + ty::ty_struct(def_id, _) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); + for (i, f) in fields.iter().enumerate() { + if i == idx.node { + let sub_span = self.span.span_for_last_ident(ex.span); + self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + f.id, + self.cur_scope); + break; + } + } + }, + _ => self.sess.span_bug(ex.span, + "Expected struct type, but not ty_struct"), + } + }, + ast::ExprFnBlock(_, ref decl, ref body) => { + if generated_code(body.span) { + return + } + + let mut id = String::from_str("$"); + id.push_str(ex.id.to_string().as_slice()); + self.process_formals(&decl.inputs, id.as_slice()); + + // walk arg and return types + for arg in decl.inputs.iter() { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = decl.output { + self.visit_ty(&**ret_ty); + } + + // walk the body + self.nest(ex.id, |v| v.visit_block(&**body)); + }, + _ => { + visit::walk_expr(self, ex) + }, + } + } + + fn visit_mac(&mut self, _: &ast::Mac) { + // Just stop, macros are poison to us. + } + + fn visit_pat(&mut self, p: &ast::Pat) { + self.process_pat(p); + if !self.collecting { + self.collected_paths.clear(); + } + } + + fn visit_arm(&mut self, arm: &ast::Arm) { + assert!(self.collected_paths.len() == 0 && !self.collecting); + self.collecting = true; + + for pattern in arm.pats.iter() { + // collect paths from the arm's patterns + self.visit_pat(&**pattern); + } + self.collecting = false; + // process collected paths + for &(id, ref p, ref immut, ref_kind) in self.collected_paths.iter() { + let value = if *immut { + self.span.snippet(p.span).into_string() + } else { + "".to_string() + }; + let sub_span = self.span.span_for_first_ident(p.span); + let def_map = self.analysis.ty_cx.def_map.borrow(); + if !def_map.contains_key(&id) { + self.sess.span_bug(p.span, + format!("def_map has no key for {} in visit_arm", + id).as_slice()); + } + let def = &(*def_map)[id]; + match *def { + def::DefLocal(id) => self.fmt.variable_str(p.span, + sub_span, + id, + path_to_string(p).as_slice(), + value.as_slice(), + ""), + def::DefVariant(_,id,_) => self.fmt.ref_str(ref_kind, + p.span, + sub_span, + id, + self.cur_scope), + // FIXME(nrc) what is this doing here? + def::DefStatic(_, _) => {} + def::DefConst(..) => {} + _ => error!("unexpected definition kind when processing collected paths: {}", + *def) + } + } + self.collected_paths.clear(); + visit::walk_expr_opt(self, &arm.guard); + self.visit_expr(&*arm.body); + } + + fn visit_stmt(&mut self, s: &ast::Stmt) { + if generated_code(s.span) { + return + } + + visit::walk_stmt(self, s) + } + + fn visit_local(&mut self, l: &ast::Local) { + if generated_code(l.span) { + return + } + + // The local could declare multiple new vars, we must walk the + // pattern and collect them all. + assert!(self.collected_paths.len() == 0 && !self.collecting); + self.collecting = true; + self.visit_pat(&*l.pat); + self.collecting = false; + + let value = self.span.snippet(l.span); + + for &(id, ref p, ref immut, _) in self.collected_paths.iter() { + let value = if *immut { value.to_string() } else { "".to_string() }; + let types = self.analysis.ty_cx.node_types.borrow(); + let typ = ppaux::ty_to_string(&self.analysis.ty_cx, (*types)[id]); + // Get the span only for the name of the variable (I hope the path + // is only ever a variable name, but who knows?). + let sub_span = self.span.span_for_last_ident(p.span); + // Rust uses the id of the pattern for var lookups, so we'll use it too. + self.fmt.variable_str(p.span, + sub_span, + id, + path_to_string(p).as_slice(), + value.as_slice(), + typ.as_slice()); + } + self.collected_paths.clear(); + + // Just walk the initialiser and type (don't want to walk the pattern again). + self.visit_ty(&*l.ty); + visit::walk_expr_opt(self, &l.init); + } +} + +pub fn process_crate(sess: &Session, + krate: &ast::Crate, + analysis: &CrateAnalysis, + odir: &Option) { + if generated_code(krate.span) { + return; + } + + let cratename = match attr::find_crate_name(krate.attrs.as_slice()) { + Some(name) => name.get().to_string(), + None => { + info!("Could not find crate name, using 'unknown_crate'"); + String::from_str("unknown_crate") + }, + }; + + info!("Dumping crate {}", cratename); + + // find a path to dump our data to + let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") { + Some(val) => Path::new(val), + None => match *odir { + Some(ref val) => val.join("dxr"), + None => Path::new("dxr-temp"), + }, + }; + + match fs::mkdir_recursive(&root_path, io::USER_RWX) { + Err(e) => sess.err(format!("Could not create directory {}: {}", + root_path.display(), e).as_slice()), + _ => (), + } + + { + let disp = root_path.display(); + info!("Writing output to {}", disp); + } + + // Create output file. + let mut out_name = cratename.clone(); + out_name.push_str(".csv"); + root_path.push(out_name); + let output_file = match File::create(&root_path) { + Ok(f) => box f, + Err(e) => { + let disp = root_path.display(); + sess.fatal(format!("Could not open {}: {}", disp, e).as_slice()); + } + }; + root_path.pop(); + + let mut visitor = DxrVisitor { + sess: sess, + analysis: analysis, + collected_paths: vec!(), + collecting: false, + fmt: FmtStrs::new(box Recorder { + out: output_file as Box, + dump_spans: false, + }, + SpanUtils { + sess: sess, + err_count: Cell::new(0) + }, + cratename.clone()), + span: SpanUtils { + sess: sess, + err_count: Cell::new(0) + }, + cur_scope: 0 + }; + + visitor.dump_crate_info(cratename.as_slice(), krate); + + visit::walk_crate(&mut visitor, krate); +} diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs new file mode 100644 index 00000000000..120e4b203e0 --- /dev/null +++ b/src/librustc_trans/save/recorder.rs @@ -0,0 +1,592 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::Row::*; + +use super::escape; +use super::span_utils::SpanUtils; + +use std::vec::Vec; + +use syntax::ast; +use syntax::ast::{NodeId,DefId}; +use syntax::codemap::*; + +pub struct Recorder { + // output file + pub out: Box, + pub dump_spans: bool, +} + +impl Recorder { + pub fn record(&mut self, info: &str) { + match write!(self.out, "{}", info) { + Err(_) => error!("Error writing output '{}'", info), + _ => (), + } + } + + pub fn dump_span(&mut self, + su: SpanUtils, + kind: &str, + span: Span, + _sub_span: Option) { + assert!(self.dump_spans); + let result = format!("span,kind,{},{},text,\"{}\"\n", + kind, su.extent_str(span), escape(su.snippet(span))); + self.record(result.as_slice()); + } +} + +pub struct FmtStrs<'a> { + pub recorder: Box, + span: SpanUtils<'a>, + krate: String, +} + +macro_rules! s { ($e:expr) => { format!("{}", $e) }} +macro_rules! svec { + ($($e:expr),*) => ({ + // leading _ to allow empty construction without a warning. + let mut _temp = ::std::vec::Vec::new(); + $(_temp.push(s!($e));)* + _temp + }) +} + +pub enum Row { + Variable, + Enum, + Variant, + VariantStruct, + Function, + MethodDecl, + Struct, + Trait, + Impl, + Module, + UseAlias, + ExternCrate, + Inheritance, + MethodCall, + Typedef, + ExternalCrate, + Crate, + FnCall, + ModRef, + VarRef, + TypeRef, + StructRef, + FnRef, +} + +impl<'a> FmtStrs<'a> { + pub fn new(rec: Box, span: SpanUtils<'a>, krate: String) -> FmtStrs<'a> { + FmtStrs { + recorder: rec, + span: span, + krate: krate, + } + } + + // A map from kind of item to a tuple of + // a string representation of the name + // a vector of field names + // whether this kind requires a span + // whether dump_spans should dump for this kind + fn lookup_row(r: Row) -> (&'static str, Vec<&'static str>, bool, bool) { + match r { + Variable => ("variable", + vec!("id","name","qualname","value","type","scopeid"), + true, true), + Enum => ("enum", vec!("id","qualname","scopeid"), true, true), + Variant => ("variant", vec!("id","name","qualname","value","scopeid"), true, true), + VariantStruct => ("variant_struct", + vec!("id","ctor_id","qualname","value","scopeid"), true, true), + Function => ("function", vec!("id","qualname","declid","declidcrate","scopeid"), + true, true), + MethodDecl => ("method_decl", vec!("id","qualname","scopeid"), true, true), + Struct => ("struct", vec!("id","ctor_id","qualname","scopeid"), true, true), + Trait => ("trait", vec!("id","qualname","scopeid"), true, true), + Impl => ("impl", vec!("id","refid","refidcrate","scopeid"), true, true), + Module => ("module", vec!("id","qualname","scopeid","def_file"), true, false), + UseAlias => ("use_alias", + vec!("id","refid","refidcrate","name","scopeid"), + true, true), + ExternCrate => ("extern_crate", + vec!("id","name","location","crate","scopeid"), + true, true), + Inheritance => ("inheritance", + vec!("base","basecrate","derived","derivedcrate"), + true, false), + MethodCall => ("method_call", + vec!("refid","refidcrate","declid","declidcrate","scopeid"), + true, true), + Typedef => ("typedef", vec!("id","qualname","value"), true, true), + ExternalCrate => ("external_crate", vec!("name","crate","file_name"), false, false), + Crate => ("crate", vec!("name"), true, false), + FnCall => ("fn_call", vec!("refid","refidcrate","qualname","scopeid"), true, true), + ModRef => ("mod_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true), + VarRef => ("var_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true), + TypeRef => ("type_ref", + vec!("refid","refidcrate","qualname","scopeid"), + true, true), + StructRef => ("struct_ref", + vec!("refid","refidcrate","qualname","scopeid"), + true, true), + FnRef => ("fn_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true) + } + } + + pub fn make_values_str(&self, + kind: &'static str, + fields: &Vec<&'static str>, + values: Vec, + span: Span) -> Option { + if values.len() != fields.len() { + self.span.sess.span_bug(span, format!( + "Mismatch between length of fields for '{}', expected '{}', found '{}'", + kind, fields.len(), values.len()).as_slice()); + } + + let values = values.iter().map(|s| { + if s.len() > 1020 { + s.as_slice().slice_to(1020) + } else { + s.as_slice() + } + }); + + let pairs = fields.iter().zip(values); + let mut strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape( + if *f == "qualname" { + let mut n = self.krate.clone(); + n.push_str("::"); + n.push_str(v); + n + } else { + String::from_str(v) + } + ))); + Some(strs.fold(String::new(), |mut s, ss| { + s.push_str(ss.as_slice()); + s + })) + } + + pub fn record_without_span(&mut self, + kind: Row, + values: Vec, + span: Span) { + let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind); + + if needs_span { + self.span.sess.span_bug(span, format!( + "Called record_without_span for '{}' which does requires a span", + label).as_slice()); + } + assert!(!dump_spans); + + if self.recorder.dump_spans { + return; + } + + let values_str = match self.make_values_str(label, fields, values, span) { + Some(vs) => vs, + None => return, + }; + + let mut result = String::from_str(label); + result.push_str(values_str.as_slice()); + result.push_str("\n"); + self.recorder.record(result.as_slice()); + } + + pub fn record_with_span(&mut self, + kind: Row, + span: Span, + sub_span: Span, + values: Vec) { + let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind); + + if self.recorder.dump_spans { + if dump_spans { + self.recorder.dump_span(self.span, label, span, Some(sub_span)); + } + return; + } + + if !needs_span { + self.span.sess.span_bug(span, + format!("Called record_with_span for '{}' \ + which does not require a span", label).as_slice()); + } + + let values_str = match self.make_values_str(label, fields, values, span) { + Some(vs) => vs, + None => return, + }; + let result = format!("{},{}{}\n", label, self.span.extent_str(sub_span), values_str); + self.recorder.record(result.as_slice()); + } + + pub fn check_and_record(&mut self, + kind: Row, + span: Span, + sub_span: Option, + values: Vec) { + match sub_span { + Some(sub_span) => self.record_with_span(kind, span, sub_span, values), + None => { + let (label, _, _, _) = FmtStrs::lookup_row(kind); + self.span.report_span_err(label, span); + } + } + } + + pub fn variable_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + value: &str, + typ: &str) { + // Getting a fully qualified name for a variable is hard because in + // the local case they can be overridden in one block and there is no nice way + // to refer to such a scope in english, so we just hack it by appending the + // variable def's node id + let mut qualname = String::from_str(name); + qualname.push_str("$"); + qualname.push_str(id.to_string().as_slice()); + self.check_and_record(Variable, + span, + sub_span, + svec!(id, name, qualname, value, typ, 0u)); + } + + // formal parameters + pub fn formal_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + fn_name: &str, + name: &str, + typ: &str) { + let mut qualname = String::from_str(fn_name); + qualname.push_str("::"); + qualname.push_str(name); + self.check_and_record(Variable, + span, + sub_span, + svec!(id, name, qualname, "", typ, 0u)); + } + + // value is the initialising expression of the static if it is not mut, otherwise "". + pub fn static_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + qualname: &str, + value: &str, + typ: &str, + scope_id: NodeId) { + self.check_and_record(Variable, + span, + sub_span, + svec!(id, name, qualname, value, typ, scope_id)); + } + + pub fn field_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + qualname: &str, + typ: &str, + scope_id: NodeId) { + self.check_and_record(Variable, + span, + sub_span, + svec!(id, name, qualname, "", typ, scope_id)); + } + + pub fn enum_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + scope_id: NodeId) { + self.check_and_record(Enum, + span, + sub_span, + svec!(id, name, scope_id)); + } + + pub fn tuple_variant_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + qualname: &str, + val: &str, + scope_id: NodeId) { + self.check_and_record(Variant, + span, + sub_span, + svec!(id, name, qualname, val, scope_id)); + } + + pub fn struct_variant_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + ctor_id: NodeId, + name: &str, + val: &str, + scope_id: NodeId) { + self.check_and_record(VariantStruct, + span, + sub_span, + svec!(id, ctor_id, name, val, scope_id)); + } + + pub fn fn_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + scope_id: NodeId) { + self.check_and_record(Function, + span, + sub_span, + svec!(id, name, "", "", scope_id)); + } + + pub fn method_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + decl_id: Option, + scope_id: NodeId) { + let values = match decl_id { + Some(decl_id) => svec!(id, name, decl_id.node, decl_id.krate, scope_id), + None => svec!(id, name, "", "", scope_id) + }; + self.check_and_record(Function, + span, + sub_span, + values); + } + + pub fn method_decl_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + scope_id: NodeId) { + self.check_and_record(MethodDecl, + span, + sub_span, + svec!(id, name, scope_id)); + } + + pub fn struct_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + ctor_id: NodeId, + name: &str, + scope_id: NodeId) { + self.check_and_record(Struct, + span, + sub_span, + svec!(id, ctor_id, name, scope_id)); + } + + pub fn trait_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + scope_id: NodeId) { + self.check_and_record(Trait, + span, + sub_span, + svec!(id, name, scope_id)); + } + + pub fn impl_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + ref_id: DefId, + scope_id: NodeId) { + self.check_and_record(Impl, + span, + sub_span, + svec!(id, ref_id.node, ref_id.krate, scope_id)); + } + + pub fn mod_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + name: &str, + parent: NodeId, + filename: &str) { + self.check_and_record(Module, + span, + sub_span, + svec!(id, name, parent, filename)); + } + + pub fn use_alias_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + mod_id: Option, + name: &str, + parent: NodeId) { + let (mod_node, mod_crate) = match mod_id { + Some(mod_id) => (mod_id.node, mod_id.krate), + None => (0, 0) + }; + self.check_and_record(UseAlias, + span, + sub_span, + svec!(id, mod_node, mod_crate, name, parent)); + } + + pub fn extern_crate_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + cnum: ast::CrateNum, + name: &str, + loc: &str, + parent: NodeId) { + self.check_and_record(ExternCrate, + span, + sub_span, + svec!(id, name, loc, cnum, parent)); + } + + pub fn inherit_str(&mut self, + span: Span, + sub_span: Option, + base_id: DefId, + deriv_id: NodeId) { + self.check_and_record(Inheritance, + span, + sub_span, + svec!(base_id.node, + base_id.krate, + deriv_id, + 0u)); + } + + pub fn fn_call_str(&mut self, + span: Span, + sub_span: Option, + id: DefId, + scope_id:NodeId) { + self.check_and_record(FnCall, + span, + sub_span, + svec!(id.node, id.krate, "", scope_id)); + } + + pub fn meth_call_str(&mut self, + span: Span, + sub_span: Option, + defid: Option, + declid: Option, + scope_id: NodeId) { + let (dfn, dfk) = match defid { + Some(defid) => (defid.node, defid.krate), + None => (0, 0) + }; + let (dcn, dck) = match declid { + Some(declid) => (s!(declid.node), s!(declid.krate)), + None => ("".to_string(), "".to_string()) + }; + self.check_and_record(MethodCall, + span, + sub_span, + svec!(dfn, dfk, dcn, dck, scope_id)); + } + + pub fn sub_mod_ref_str(&mut self, + span: Span, + sub_span: Span, + qualname: &str, + parent:NodeId) { + self.record_with_span(ModRef, + span, + sub_span, + svec!(0u, 0u, qualname, parent)); + } + + pub fn typedef_str(&mut self, + span: Span, + sub_span: Option, + id: NodeId, + qualname: &str, + value: &str) { + self.check_and_record(Typedef, + span, + sub_span, + svec!(id, qualname, value)); + } + + pub fn crate_str(&mut self, + span: Span, + name: &str) { + self.record_with_span(Crate, + span, + span, + svec!(name)); + } + + pub fn external_crate_str(&mut self, + span: Span, + name: &str, + num: ast::CrateNum) { + let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo); + self.record_without_span(ExternalCrate, + svec!(name, num, lo_loc.file.name), + span); + } + + pub fn sub_type_ref_str(&mut self, + span: Span, + sub_span: Span, + qualname: &str) { + self.record_with_span(TypeRef, + span, + sub_span, + svec!(0u, 0u, qualname, 0u)); + } + + // A slightly generic function for a reference to an item of any kind. + pub fn ref_str(&mut self, + kind: Row, + span: Span, + sub_span: Option, + id: DefId, + scope_id: NodeId) { + self.check_and_record(kind, + span, + sub_span, + svec!(id.node, id.krate, "", scope_id)); + } +} diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs new file mode 100644 index 00000000000..e01081d8422 --- /dev/null +++ b/src/librustc_trans/save/span_utils.rs @@ -0,0 +1,333 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::session::Session; + +use save::generated_code; + +use std::cell::Cell; + +use syntax::ast; +use syntax::codemap::*; +use syntax::parse::lexer; +use syntax::parse::lexer::{Reader,StringReader}; +use syntax::parse::token; +use syntax::parse::token::{keywords, Token}; + +pub struct SpanUtils<'a> { + pub sess: &'a Session, + pub err_count: Cell, +} + +impl<'a> SpanUtils<'a> { + // Standard string for extents/location. + pub fn extent_str(&self, span: Span) -> String { + let lo_loc = self.sess.codemap().lookup_char_pos(span.lo); + let hi_loc = self.sess.codemap().lookup_char_pos(span.hi); + let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo); + let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi); + let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos; + let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos; + + format!("file_name,{},file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\ + file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}", + lo_loc.file.name, + lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(), lo_pos_byte.to_uint(), + hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint(), hi_pos_byte.to_uint()) + } + + // sub_span starts at span.lo, so we need to adjust the positions etc. + // If sub_span is None, we don't need to adjust. + pub fn make_sub_span(&self, span: Span, sub_span: Option) -> Option { + let loc = self.sess.codemap().lookup_char_pos(span.lo); + assert!(!generated_code(span), + "generated code; we should not be processing this `{}` in {}, line {}", + self.snippet(span), loc.file.name, loc.line); + + match sub_span { + None => None, + Some(sub) => { + let FileMapAndBytePos {fm, pos} = + self.sess.codemap().lookup_byte_offset(span.lo); + let base = pos + fm.start_pos; + Some(Span { + lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos, + hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos, + expn_id: NO_EXPANSION, + }) + } + } + } + + pub fn snippet(&self, span: Span) -> String { + match self.sess.codemap().span_to_snippet(span) { + Some(s) => s, + None => String::new(), + } + } + + pub fn retokenise_span(&self, span: Span) -> StringReader<'a> { + // sadness - we don't have spans for sub-expressions nor access to the tokens + // so in order to get extents for the function name itself (which dxr expects) + // we need to re-tokenise the fn definition + + // Note: this is a bit awful - it adds the contents of span to the end of + // the codemap as a new filemap. This is mostly OK, but means we should + // not iterate over the codemap. Also, any spans over the new filemap + // are incompatible with spans over other filemaps. + let filemap = self.sess.codemap().new_filemap(String::from_str(""), + self.snippet(span)); + let s = self.sess; + lexer::StringReader::new(s.diagnostic(), filemap) + } + + // Re-parses a path and returns the span for the last identifier in the path + pub fn span_for_last_ident(&self, span: Span) -> Option { + let mut result = None; + + let mut toks = self.retokenise_span(span); + let mut bracket_count = 0u; + loop { + let ts = toks.real_token(); + if ts.tok == token::Eof { + return self.make_sub_span(span, result) + } + if bracket_count == 0 && + (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) { + result = Some(ts.sp); + } + + bracket_count += match ts.tok { + token::Lt => 1, + token::Gt => -1, + token::BinOp(token::Shr) => -2, + _ => 0 + } + } + } + + // Return the span for the first identifier in the path. + pub fn span_for_first_ident(&self, span: Span) -> Option { + let mut toks = self.retokenise_span(span); + let mut bracket_count = 0u; + loop { + let ts = toks.real_token(); + if ts.tok == token::Eof { + return None; + } + if bracket_count == 0 && + (ts.tok.is_ident() || ts.tok.is_keyword(keywords::Self)) { + return self.make_sub_span(span, Some(ts.sp)); + } + + bracket_count += match ts.tok { + token::Lt => 1, + token::Gt => -1, + token::BinOp(token::Shr) => -2, + _ => 0 + } + } + } + + // Return the span for the last ident before a `(` or `<` or '::<' and outside any + // any brackets, or the last span. + pub fn sub_span_for_meth_name(&self, span: Span) -> Option { + let mut toks = self.retokenise_span(span); + let mut prev = toks.real_token(); + let mut result = None; + let mut bracket_count = 0u; + let mut last_span = None; + while prev.tok != token::Eof { + last_span = None; + let mut next = toks.real_token(); + + if (next.tok == token::OpenDelim(token::Paren) || + next.tok == token::Lt) && + bracket_count == 0 && + prev.tok.is_ident() { + result = Some(prev.sp); + } + + if bracket_count == 0 && + next.tok == token::ModSep { + let old = prev; + prev = next; + next = toks.real_token(); + if next.tok == token::Lt && + old.tok.is_ident() { + result = Some(old.sp); + } + } + + bracket_count += match prev.tok { + token::OpenDelim(token::Paren) | token::Lt => 1, + token::CloseDelim(token::Paren) | token::Gt => -1, + token::BinOp(token::Shr) => -2, + _ => 0 + }; + + if prev.tok.is_ident() && bracket_count == 0 { + last_span = Some(prev.sp); + } + prev = next; + } + if result.is_none() && last_span.is_some() { + return self.make_sub_span(span, last_span); + } + return self.make_sub_span(span, result); + } + + // Return the span for the last ident before a `<` and outside any + // brackets, or the last span. + pub fn sub_span_for_type_name(&self, span: Span) -> Option { + let mut toks = self.retokenise_span(span); + let mut prev = toks.real_token(); + let mut result = None; + let mut bracket_count = 0u; + loop { + let next = toks.real_token(); + + if (next.tok == token::Lt || + next.tok == token::Colon) && + bracket_count == 0 && + prev.tok.is_ident() { + result = Some(prev.sp); + } + + bracket_count += match prev.tok { + token::Lt => 1, + token::Gt => -1, + token::BinOp(token::Shr) => -2, + _ => 0 + }; + + if next.tok == token::Eof { + break; + } + prev = next; + } + if bracket_count != 0 { + let loc = self.sess.codemap().lookup_char_pos(span.lo); + self.sess.span_bug(span, + format!("Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}", + self.snippet(span), loc.file.name, loc.line).as_slice()); + } + if result.is_none() && prev.tok.is_ident() && bracket_count == 0 { + return self.make_sub_span(span, Some(prev.sp)); + } + self.make_sub_span(span, result) + } + + // Reparse span and return an owned vector of sub spans of the first limit + // identifier tokens in the given nesting level. + // example with Foo, Bar> + // Nesting = 0: all idents outside of brackets: ~[Foo] + // Nesting = 1: idents within one level of brackets: ~[Bar, Bar] + pub fn spans_with_brackets(&self, span: Span, nesting: int, limit: int) -> Vec { + let mut result: Vec = vec!(); + + let mut toks = self.retokenise_span(span); + // We keep track of how many brackets we're nested in + let mut bracket_count = 0i; + loop { + let ts = toks.real_token(); + if ts.tok == token::Eof { + if bracket_count != 0 { + let loc = self.sess.codemap().lookup_char_pos(span.lo); + self.sess.span_bug(span, format!( + "Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}", + self.snippet(span), loc.file.name, loc.line).as_slice()); + } + return result + } + if (result.len() as int) == limit { + return result; + } + bracket_count += match ts.tok { + token::Lt => 1, + token::Gt => -1, + token::BinOp(token::Shl) => 2, + token::BinOp(token::Shr) => -2, + _ => 0 + }; + if ts.tok.is_ident() && + bracket_count == nesting { + result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); + } + } + } + + pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option { + let mut toks = self.retokenise_span(span); + let mut prev = toks.real_token(); + loop { + if prev.tok == token::Eof { + return None; + } + let next = toks.real_token(); + if next.tok == tok { + return self.make_sub_span(span, Some(prev.sp)); + } + prev = next; + } + } + + pub fn sub_span_after_keyword(&self, + span: Span, + keyword: keywords::Keyword) -> Option { + let mut toks = self.retokenise_span(span); + loop { + let ts = toks.real_token(); + if ts.tok == token::Eof { + return None; + } + if ts.tok.is_keyword(keyword) { + let ts = toks.real_token(); + if ts.tok == token::Eof { + return None + } else { + println!("found keyword: {} at {}", ts, ts.sp); + return self.make_sub_span(span, Some(ts.sp)); + } + } + } + } + + // Returns a list of the spans of idents in a patch. + // E.g., For foo::bar::baz, we return [foo, bar, baz] (well, their spans) + pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec { + if generated_code(path.span) { + return vec!(); + } + + self.spans_with_brackets(path.span, 0, -1) + } + + // Return an owned vector of the subspans of the param identifier + // tokens found in span. + pub fn spans_for_ty_params(&self, span: Span, number: int) -> Vec { + if generated_code(span) { + return vec!(); + } + // Type params are nested within one level of brackets: + // i.e. we want ~[A, B] from Foo> + self.spans_with_brackets(span, 1, number) + } + + pub fn report_span_err(&self, kind: &str, span: Span) { + let loc = self.sess.codemap().lookup_char_pos(span.lo); + info!("({}) Could not find sub_span in `{}` in {}, line {}", + kind, self.snippet(span), loc.file.name, loc.line); + self.err_count.set(self.err_count.get()+1); + if self.err_count.get() > 1000 { + self.sess.bug("span errors reached 1000, giving up"); + } + } +} diff --git a/src/librustc_trans/test.rs b/src/librustc_trans/test.rs new file mode 100644 index 00000000000..4648021cc5f --- /dev/null +++ b/src/librustc_trans/test.rs @@ -0,0 +1,511 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +# Standalone Tests for the Inference Module + +*/ + +// This is only used by tests, hence allow dead code. +#![allow(dead_code)] + +use driver::diagnostic; +use driver::diagnostic::Emitter; +use driver::driver; +use middle::lang_items; +use middle::region; +use middle::resolve; +use middle::resolve_lifetime; +use middle::stability; +use middle::ty; +use middle::typeck::infer::combine::Combine; +use middle::typeck::infer; +use middle::typeck::infer::lub::Lub; +use middle::typeck::infer::glb::Glb; +use session::{mod, config}; +use syntax::codemap; +use syntax::codemap::{Span, CodeMap, DUMMY_SP}; +use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help}; +use syntax::{ast, ast_map}; +use util::ppaux::{ty_to_string, UserString}; + +use arena::TypedArena; + +struct Env<'a, 'tcx: 'a> { + infcx: &'a infer::InferCtxt<'a, 'tcx>, +} + +struct RH<'a> { + id: ast::NodeId, + sub: &'a [RH<'a>] +} + +static EMPTY_SOURCE_STR: &'static str = "#![no_std]"; + +struct ExpectErrorEmitter { + messages: Vec +} + +fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { + match lvl { + Bug | Fatal | Error => { } + Warning | Note | Help => { return; } + } + + debug!("Error: {}", msg); + match e.messages.iter().position(|m| msg.contains(m.as_slice())) { + Some(i) => { + e.messages.remove(i); + } + None => { + panic!("Unexpected error: {} Expected: {}", + msg, e.messages); + } + } +} + +impl Emitter for ExpectErrorEmitter { + fn emit(&mut self, + _cmsp: Option<(&codemap::CodeMap, Span)>, + msg: &str, + _: Option<&str>, + lvl: Level) + { + remove_message(self, msg, lvl); + } + + fn custom_emit(&mut self, + _cm: &codemap::CodeMap, + _sp: RenderSpan, + msg: &str, + lvl: Level) + { + remove_message(self, msg, lvl); + } +} + +fn errors(msgs: &[&str]) -> (Box, uint) { + let v = msgs.iter().map(|m| m.to_string()).collect(); + (box ExpectErrorEmitter { messages: v } as Box, msgs.len()) +} + +fn test_env(_test_name: &str, + source_string: &str, + (emitter, expected_err_count): (Box, uint), + body: |Env|) { + let options = + config::basic_options(); + let codemap = + CodeMap::new(); + let diagnostic_handler = + diagnostic::mk_handler(emitter); + let span_diagnostic_handler = + diagnostic::mk_span_handler(diagnostic_handler, codemap); + + let sess = session::build_session_(options, None, span_diagnostic_handler); + let krate_config = Vec::new(); + let input = driver::StrInput(source_string.to_string()); + let krate = driver::phase_1_parse_input(&sess, krate_config, &input); + let krate = driver::phase_2_configure_and_expand(&sess, krate, "test", None) + .expect("phase 2 aborted"); + + let mut forest = ast_map::Forest::new(krate); + let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest); + let krate = ast_map.krate(); + + // run just enough stuff to build a tcx: + let lang_items = lang_items::collect_language_items(krate, &sess); + let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } = + resolve::resolve_crate(&sess, &lang_items, krate); + let named_region_map = resolve_lifetime::krate(&sess, krate); + let region_map = region::resolve_crate(&sess, krate); + let stability_index = stability::Index::build(krate); + let type_arena = TypedArena::new(); + let tcx = ty::mk_ctxt(sess, + &type_arena, + def_map, + named_region_map, + ast_map, + freevars, + capture_mode_map, + region_map, + lang_items, + stability_index); + let infcx = infer::new_infer_ctxt(&tcx); + body(Env { infcx: &infcx }); + infcx.resolve_regions_and_report_errors(); + assert_eq!(tcx.sess.err_count(), expected_err_count); +} + +impl<'a, 'tcx> Env<'a, 'tcx> { + pub fn create_region_hierarchy(&self, rh: &RH) { + for child_rh in rh.sub.iter() { + self.create_region_hierarchy(child_rh); + self.infcx.tcx.region_maps.record_encl_scope(child_rh.id, rh.id); + } + } + + pub fn create_simple_region_hierarchy(&self) { + // creates a region hierarchy where 1 is root, 10 and 11 are + // children of 1, etc + self.create_region_hierarchy( + &RH {id: 1, + sub: &[RH {id: 10, + sub: &[]}, + RH {id: 11, + sub: &[]}]}); + } + + pub fn lookup_item(&self, names: &[String]) -> ast::NodeId { + return match search_mod(self, &self.infcx.tcx.map.krate().module, 0, names) { + Some(id) => id, + None => { + panic!("no item found: `{}`", names.connect("::")); + } + }; + + fn search_mod(this: &Env, + m: &ast::Mod, + idx: uint, + names: &[String]) + -> Option { + assert!(idx < names.len()); + for item in m.items.iter() { + if item.ident.user_string(this.infcx.tcx) == names[idx] { + return search(this, &**item, idx+1, names); + } + } + return None; + } + + fn search(this: &Env, + it: &ast::Item, + idx: uint, + names: &[String]) + -> Option { + if idx == names.len() { + return Some(it.id); + } + + return match it.node { + ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemFn(..) | + ast::ItemForeignMod(..) | ast::ItemTy(..) => { + None + } + + ast::ItemEnum(..) | ast::ItemStruct(..) | + ast::ItemTrait(..) | ast::ItemImpl(..) | + ast::ItemMac(..) => { + None + } + + ast::ItemMod(ref m) => { + search_mod(this, m, idx, names) + } + }; + } + } + + pub fn make_subtype(&self, a: ty::t, b: ty::t) -> bool { + match infer::mk_subty(self.infcx, true, infer::Misc(DUMMY_SP), a, b) { + Ok(_) => true, + Err(ref e) => panic!("Encountered error: {}", + ty::type_err_to_str(self.infcx.tcx, e)) + } + } + + pub fn is_subtype(&self, a: ty::t, b: ty::t) -> bool { + match infer::can_mk_subty(self.infcx, a, b) { + Ok(_) => true, + Err(_) => false + } + } + + pub fn assert_subtype(&self, a: ty::t, b: ty::t) { + if !self.is_subtype(a, b) { + panic!("{} is not a subtype of {}, but it should be", + self.ty_to_string(a), + self.ty_to_string(b)); + } + } + + pub fn assert_not_subtype(&self, a: ty::t, b: ty::t) { + if self.is_subtype(a, b) { + panic!("{} is a subtype of {}, but it shouldn't be", + self.ty_to_string(a), + self.ty_to_string(b)); + } + } + + pub fn assert_eq(&self, a: ty::t, b: ty::t) { + self.assert_subtype(a, b); + self.assert_subtype(b, a); + } + + pub fn ty_to_string(&self, a: ty::t) -> String { + ty_to_string(self.infcx.tcx, a) + } + + pub fn t_fn(&self, + binder_id: ast::NodeId, + input_tys: &[ty::t], + output_ty: ty::t) + -> ty::t + { + ty::mk_ctor_fn(self.infcx.tcx, binder_id, input_tys, output_ty) + } + + pub fn t_int(&self) -> ty::t { + ty::mk_int() + } + + pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)), + self.t_int()) + } + + pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), self.t_int()) + } + + pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, + ty::ReFree(ty::FreeRegion {scope_id: nid, + bound_region: ty::BrAnon(id)}), + self.t_int()) + } + + pub fn t_rptr_static(&self) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, self.t_int()) + } + + pub fn dummy_type_trace(&self) -> infer::TypeTrace { + infer::TypeTrace::dummy() + } + + pub fn lub(&self) -> Lub<'a, 'tcx> { + let trace = self.dummy_type_trace(); + Lub(self.infcx.combine_fields(true, trace)) + } + + pub fn glb(&self) -> Glb<'a, 'tcx> { + let trace = self.dummy_type_trace(); + Glb(self.infcx.combine_fields(true, trace)) + } + + pub fn resolve_regions(&self) { + self.infcx.resolve_regions_and_report_errors(); + } + + pub fn make_lub_ty(&self, t1: ty::t, t2: ty::t) -> ty::t { + match self.lub().tys(t1, t2) { + Ok(t) => t, + Err(ref e) => panic!("unexpected error computing LUB: {}", + ty::type_err_to_str(self.infcx.tcx, e)) + } + } + + /// Checks that `LUB(t1,t2) == t_lub` + pub fn check_lub(&self, t1: ty::t, t2: ty::t, t_lub: ty::t) { + match self.lub().tys(t1, t2) { + Ok(t) => { + self.assert_eq(t, t_lub); + } + Err(ref e) => { + panic!("unexpected error in LUB: {}", + ty::type_err_to_str(self.infcx.tcx, e)) + } + } + } + + /// Checks that `GLB(t1,t2) == t_glb` + pub fn check_glb(&self, t1: ty::t, t2: ty::t, t_glb: ty::t) { + debug!("check_glb(t1={}, t2={}, t_glb={})", + self.ty_to_string(t1), + self.ty_to_string(t2), + self.ty_to_string(t_glb)); + match self.glb().tys(t1, t2) { + Err(e) => { + panic!("unexpected error computing LUB: {}", e) + } + Ok(t) => { + self.assert_eq(t, t_glb); + + // sanity check for good measure: + self.assert_subtype(t, t1); + self.assert_subtype(t, t2); + } + } + } + + /// Checks that `LUB(t1,t2)` is undefined + pub fn check_no_lub(&self, t1: ty::t, t2: ty::t) { + match self.lub().tys(t1, t2) { + Err(_) => {} + Ok(t) => { + panic!("unexpected success computing LUB: {}", self.ty_to_string(t)) + } + } + } + + /// Checks that `GLB(t1,t2)` is undefined + pub fn check_no_glb(&self, t1: ty::t, t2: ty::t) { + match self.glb().tys(t1, t2) { + Err(_) => {} + Ok(t) => { + panic!("unexpected success computing GLB: {}", self.ty_to_string(t)) + } + } + } +} + +#[test] +fn contravariant_region_ptr_ok() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + env.create_simple_region_hierarchy(); + let t_rptr1 = env.t_rptr_scope(1); + let t_rptr10 = env.t_rptr_scope(10); + env.assert_eq(t_rptr1, t_rptr1); + env.assert_eq(t_rptr10, t_rptr10); + env.make_subtype(t_rptr1, t_rptr10); + }) +} + +#[test] +fn contravariant_region_ptr_err() { + test_env("contravariant_region_ptr", + EMPTY_SOURCE_STR, + errors(&["lifetime mismatch"]), + |env| { + env.create_simple_region_hierarchy(); + let t_rptr1 = env.t_rptr_scope(1); + let t_rptr10 = env.t_rptr_scope(10); + env.assert_eq(t_rptr1, t_rptr1); + env.assert_eq(t_rptr10, t_rptr10); + + // will cause an error when regions are resolved + env.make_subtype(t_rptr10, t_rptr1); + }) +} + +#[test] +fn lub_bound_bound() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); + env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_bound2], env.t_int()), + env.t_fn(22, &[t_rptr_bound1], env.t_int())); + }) +} + +#[test] +fn lub_bound_free() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_free1 = env.t_rptr_free(0, 1); + env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_free1], env.t_int()), + env.t_fn(22, &[t_rptr_free1], env.t_int())); + }) +} + +#[test] +fn lub_bound_static() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_static = env.t_rptr_static(); + env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_static], env.t_int()), + env.t_fn(22, &[t_rptr_static], env.t_int())); + }) +} + +#[test] +fn lub_bound_bound_inverse_order() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); + env.check_lub(env.t_fn(22, &[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1), + env.t_fn(22, &[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1), + env.t_fn(22, &[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1)); + }) +} + +#[test] +fn lub_free_free() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_free1 = env.t_rptr_free(0, 1); + let t_rptr_free2 = env.t_rptr_free(0, 2); + let t_rptr_static = env.t_rptr_static(); + env.check_lub(env.t_fn(22, &[t_rptr_free1], env.t_int()), + env.t_fn(22, &[t_rptr_free2], env.t_int()), + env.t_fn(22, &[t_rptr_static], env.t_int())); + }) +} + +#[test] +fn lub_returning_scope() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, + errors(&["cannot infer an appropriate lifetime"]), |env| { + let t_rptr_scope10 = env.t_rptr_scope(10); + let t_rptr_scope11 = env.t_rptr_scope(11); + + // this should generate an error when regions are resolved + env.make_lub_ty(env.t_fn(22, &[], t_rptr_scope10), + env.t_fn(22, &[], t_rptr_scope11)); + }) +} + +#[test] +fn glb_free_free_with_common_scope() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_free1 = env.t_rptr_free(0, 1); + let t_rptr_free2 = env.t_rptr_free(0, 2); + let t_rptr_scope = env.t_rptr_scope(0); + env.check_glb(env.t_fn(22, &[t_rptr_free1], env.t_int()), + env.t_fn(22, &[t_rptr_free2], env.t_int()), + env.t_fn(22, &[t_rptr_scope], env.t_int())); + }) +} + +#[test] +fn glb_bound_bound() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_bound2 = env.t_rptr_late_bound(22, 2); + env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_bound2], env.t_int()), + env.t_fn(22, &[t_rptr_bound1], env.t_int())); + }) +} + +#[test] +fn glb_bound_free() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_free1 = env.t_rptr_free(0, 1); + env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_free1], env.t_int()), + env.t_fn(22, &[t_rptr_bound1], env.t_int())); + }) +} + +#[test] +fn glb_bound_static() { + test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| { + let t_rptr_bound1 = env.t_rptr_late_bound(22, 1); + let t_rptr_static = env.t_rptr_static(); + env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()), + env.t_fn(22, &[t_rptr_static], env.t_int()), + env.t_fn(22, &[t_rptr_bound1], env.t_int())); + }) +} diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs new file mode 100644 index 00000000000..001a3a4dca0 --- /dev/null +++ b/src/librustc_trans/trans/_match.rs @@ -0,0 +1,1746 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * + * # Compilation of match statements + * + * I will endeavor to explain the code as best I can. I have only a loose + * understanding of some parts of it. + * + * ## Matching + * + * The basic state of the code is maintained in an array `m` of `Match` + * objects. Each `Match` describes some list of patterns, all of which must + * match against the current list of values. If those patterns match, then + * the arm listed in the match is the correct arm. A given arm may have + * multiple corresponding match entries, one for each alternative that + * remains. As we proceed these sets of matches are adjusted by the various + * `enter_XXX()` functions, each of which adjusts the set of options given + * some information about the value which has been matched. + * + * So, initially, there is one value and N matches, each of which have one + * constituent pattern. N here is usually the number of arms but may be + * greater, if some arms have multiple alternatives. For example, here: + * + * enum Foo { A, B(int), C(uint, uint) } + * match foo { + * A => ..., + * B(x) => ..., + * C(1u, 2) => ..., + * C(_) => ... + * } + * + * The value would be `foo`. There would be four matches, each of which + * contains one pattern (and, in one case, a guard). We could collect the + * various options and then compile the code for the case where `foo` is an + * `A`, a `B`, and a `C`. When we generate the code for `C`, we would (1) + * drop the two matches that do not match a `C` and (2) expand the other two + * into two patterns each. In the first case, the two patterns would be `1u` + * and `2`, and the in the second case the _ pattern would be expanded into + * `_` and `_`. The two values are of course the arguments to `C`. + * + * Here is a quick guide to the various functions: + * + * - `compile_submatch()`: The main workhouse. It takes a list of values and + * a list of matches and finds the various possibilities that could occur. + * + * - `enter_XXX()`: modifies the list of matches based on some information + * about the value that has been matched. For example, + * `enter_rec_or_struct()` adjusts the values given that a record or struct + * has been matched. This is an infallible pattern, so *all* of the matches + * must be either wildcards or record/struct patterns. `enter_opt()` + * handles the fallible cases, and it is correspondingly more complex. + * + * ## Bindings + * + * We store information about the bound variables for each arm as part of the + * per-arm `ArmData` struct. There is a mapping from identifiers to + * `BindingInfo` structs. These structs contain the mode/id/type of the + * binding, but they also contain an LLVM value which points at an alloca + * called `llmatch`. For by value bindings that are Copy, we also create + * an extra alloca that we copy the matched value to so that any changes + * we do to our copy is not reflected in the original and vice-versa. + * We don't do this if it's a move since the original value can't be used + * and thus allowing us to cheat in not creating an extra alloca. + * + * The `llmatch` binding always stores a pointer into the value being matched + * which points at the data for the binding. If the value being matched has + * type `T`, then, `llmatch` will point at an alloca of type `T*` (and hence + * `llmatch` has type `T**`). So, if you have a pattern like: + * + * let a: A = ...; + * let b: B = ...; + * match (a, b) { (ref c, d) => { ... } } + * + * For `c` and `d`, we would generate allocas of type `C*` and `D*` + * respectively. These are called the `llmatch`. As we match, when we come + * up against an identifier, we store the current pointer into the + * corresponding alloca. + * + * Once a pattern is completely matched, and assuming that there is no guard + * pattern, we will branch to a block that leads to the body itself. For any + * by-value bindings, this block will first load the ptr from `llmatch` (the + * one of type `D*`) and then load a second time to get the actual value (the + * one of type `D`). For by ref bindings, the value of the local variable is + * simply the first alloca. + * + * So, for the example above, we would generate a setup kind of like this: + * + * +-------+ + * | Entry | + * +-------+ + * | + * +--------------------------------------------+ + * | llmatch_c = (addr of first half of tuple) | + * | llmatch_d = (addr of second half of tuple) | + * +--------------------------------------------+ + * | + * +--------------------------------------+ + * | *llbinding_d = **llmatch_d | + * +--------------------------------------+ + * + * If there is a guard, the situation is slightly different, because we must + * execute the guard code. Moreover, we need to do so once for each of the + * alternatives that lead to the arm, because if the guard fails, they may + * have different points from which to continue the search. Therefore, in that + * case, we generate code that looks more like: + * + * +-------+ + * | Entry | + * +-------+ + * | + * +-------------------------------------------+ + * | llmatch_c = (addr of first half of tuple) | + * | llmatch_d = (addr of first half of tuple) | + * +-------------------------------------------+ + * | + * +-------------------------------------------------+ + * | *llbinding_d = **llmatch_d | + * | check condition | + * | if false { goto next case } | + * | if true { goto body } | + * +-------------------------------------------------+ + * + * The handling for the cleanups is a bit... sensitive. Basically, the body + * is the one that invokes `add_clean()` for each binding. During the guard + * evaluation, we add temporary cleanups and revoke them after the guard is + * evaluated (it could fail, after all). Note that guards and moves are + * just plain incompatible. + * + * Some relevant helper functions that manage bindings: + * - `create_bindings_map()` + * - `insert_lllocals()` + * + * + * ## Notes on vector pattern matching. + * + * Vector pattern matching is surprisingly tricky. The problem is that + * the structure of the vector isn't fully known, and slice matches + * can be done on subparts of it. + * + * The way that vector pattern matches are dealt with, then, is as + * follows. First, we make the actual condition associated with a + * vector pattern simply a vector length comparison. So the pattern + * [1, .. x] gets the condition "vec len >= 1", and the pattern + * [.. x] gets the condition "vec len >= 0". The problem here is that + * having the condition "vec len >= 1" hold clearly does not mean that + * only a pattern that has exactly that condition will match. This + * means that it may well be the case that a condition holds, but none + * of the patterns matching that condition match; to deal with this, + * when doing vector length matches, we have match failures proceed to + * the next condition to check. + * + * There are a couple more subtleties to deal with. While the "actual" + * condition associated with vector length tests is simply a test on + * the vector length, the actual vec_len Opt entry contains more + * information used to restrict which matches are associated with it. + * So that all matches in a submatch are matching against the same + * values from inside the vector, they are split up by how many + * elements they match at the front and at the back of the vector. In + * order to make sure that arms are properly checked in order, even + * with the overmatching conditions, each vec_len Opt entry is + * associated with a range of matches. + * Consider the following: + * + * match &[1, 2, 3] { + * [1, 1, .. _] => 0, + * [1, 2, 2, .. _] => 1, + * [1, 2, 3, .. _] => 2, + * [1, 2, .. _] => 3, + * _ => 4 + * } + * The proper arm to match is arm 2, but arms 0 and 3 both have the + * condition "len >= 2". If arm 3 was lumped in with arm 0, then the + * wrong branch would be taken. Instead, vec_len Opts are associated + * with a contiguous range of matches that have the same "shape". + * This is sort of ugly and requires a bunch of special handling of + * vec_len options. + * + */ + +pub use self::BranchKind::*; +pub use self::OptResult::*; +pub use self::TransBindingMode::*; +use self::Opt::*; +use self::FailureHandler::*; + +use back::abi; +use llvm::{ValueRef, BasicBlockRef}; +use middle::check_match::StaticInliner; +use middle::check_match; +use middle::const_eval; +use middle::def; +use middle::expr_use_visitor as euv; +use middle::lang_items::StrEqFnLangItem; +use middle::mem_categorization as mc; +use middle::pat_util::*; +use middle::resolve::DefMap; +use trans::adt; +use trans::base::*; +use trans::build::{AddCase, And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load}; +use trans::build::{Mul, Not, Store, Sub, add_comment}; +use trans::build; +use trans::callee; +use trans::cleanup::{mod, CleanupMethods}; +use trans::common::*; +use trans::consts; +use trans::datum::*; +use trans::expr::{mod, Dest}; +use trans::tvec; +use trans::type_of; +use trans::debuginfo; +use middle::ty; +use session::config::FullDebugInfo; +use util::common::indenter; +use util::nodemap::FnvHashMap; +use util::ppaux::{Repr, vec_map_to_string}; + +use std; +use std::iter::AdditiveIterator; +use std::rc::Rc; +use syntax::ast; +use syntax::ast::{DUMMY_NODE_ID, Ident}; +use syntax::codemap::Span; +use syntax::fold::Folder; +use syntax::ptr::P; + +#[deriving(Show)] +struct ConstantExpr<'a>(&'a ast::Expr); + +impl<'a> ConstantExpr<'a> { + fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool { + let ConstantExpr(expr) = self; + let ConstantExpr(other_expr) = other; + match const_eval::compare_lit_exprs(tcx, expr, other_expr) { + Some(val1) => val1 == 0, + None => panic!("compare_list_exprs: type mismatch"), + } + } +} + +// An option identifying a branch (either a literal, an enum variant or a range) +#[deriving(Show)] +enum Opt<'a> { + ConstantValue(ConstantExpr<'a>), + ConstantRange(ConstantExpr<'a>, ConstantExpr<'a>), + Variant(ty::Disr, Rc, ast::DefId), + SliceLengthEqual(uint), + SliceLengthGreaterOrEqual(/* prefix length */ uint, /* suffix length */ uint), +} + +impl<'a> Opt<'a> { + fn eq(&self, other: &Opt<'a>, tcx: &ty::ctxt) -> bool { + match (self, other) { + (&ConstantValue(a), &ConstantValue(b)) => a.eq(b, tcx), + (&ConstantRange(a1, a2), &ConstantRange(b1, b2)) => { + a1.eq(b1, tcx) && a2.eq(b2, tcx) + } + (&Variant(a_disr, ref a_repr, a_def), &Variant(b_disr, ref b_repr, b_def)) => { + a_disr == b_disr && *a_repr == *b_repr && a_def == b_def + } + (&SliceLengthEqual(a), &SliceLengthEqual(b)) => a == b, + (&SliceLengthGreaterOrEqual(a1, a2), &SliceLengthGreaterOrEqual(b1, b2)) => { + a1 == b1 && a2 == b2 + } + _ => false + } + } + + fn trans<'blk, 'tcx>(&self, mut bcx: Block<'blk, 'tcx>) -> OptResult<'blk, 'tcx> { + let _icx = push_ctxt("match::trans_opt"); + let ccx = bcx.ccx(); + match *self { + ConstantValue(ConstantExpr(lit_expr)) => { + let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id); + let (llval, _) = consts::const_expr(ccx, &*lit_expr); + let lit_datum = immediate_rvalue(llval, lit_ty); + let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); + SingleResult(Result::new(bcx, lit_datum.val)) + } + ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2)) => { + let (l1, _) = consts::const_expr(ccx, &**l1); + let (l2, _) = consts::const_expr(ccx, &**l2); + RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) + } + Variant(disr_val, ref repr, _) => { + adt::trans_case(bcx, &**repr, disr_val) + } + SliceLengthEqual(length) => { + SingleResult(Result::new(bcx, C_uint(ccx, length))) + } + SliceLengthGreaterOrEqual(prefix, suffix) => { + LowerBound(Result::new(bcx, C_uint(ccx, prefix + suffix))) + } + } + } +} + +#[deriving(PartialEq)] +pub enum BranchKind { + NoBranch, + Single, + Switch, + Compare, + CompareSliceLength +} + +pub enum OptResult<'blk, 'tcx: 'blk> { + SingleResult(Result<'blk, 'tcx>), + RangeResult(Result<'blk, 'tcx>, Result<'blk, 'tcx>), + LowerBound(Result<'blk, 'tcx>) +} + +#[deriving(Clone)] +pub enum TransBindingMode { + TrByCopy(/* llbinding */ ValueRef), + TrByMove, + TrByRef, +} + +/** + * Information about a pattern binding: + * - `llmatch` is a pointer to a stack slot. The stack slot contains a + * pointer into the value being matched. Hence, llmatch has type `T**` + * where `T` is the value being matched. + * - `trmode` is the trans binding mode + * - `id` is the node id of the binding + * - `ty` is the Rust type of the binding */ + #[deriving(Clone)] +pub struct BindingInfo { + pub llmatch: ValueRef, + pub trmode: TransBindingMode, + pub id: ast::NodeId, + pub span: Span, + pub ty: ty::t, +} + +type BindingsMap = FnvHashMap; + +struct ArmData<'p, 'blk, 'tcx: 'blk> { + bodycx: Block<'blk, 'tcx>, + arm: &'p ast::Arm, + bindings_map: BindingsMap +} + +/** + * Info about Match. + * If all `pats` are matched then arm `data` will be executed. + * As we proceed `bound_ptrs` are filled with pointers to values to be bound, + * these pointers are stored in llmatch variables just before executing `data` arm. + */ +struct Match<'a, 'p: 'a, 'blk: 'a, 'tcx: 'blk> { + pats: Vec<&'p ast::Pat>, + data: &'a ArmData<'p, 'blk, 'tcx>, + bound_ptrs: Vec<(Ident, ValueRef)>, +} + +impl<'a, 'p, 'blk, 'tcx> Repr for Match<'a, 'p, 'blk, 'tcx> { + fn repr(&self, tcx: &ty::ctxt) -> String { + if tcx.sess.verbose() { + // for many programs, this just take too long to serialize + self.pats.repr(tcx) + } else { + format!("{} pats", self.pats.len()) + } + } +} + +fn has_nested_bindings(m: &[Match], col: uint) -> bool { + for br in m.iter() { + match br.pats[col].node { + ast::PatIdent(_, _, Some(_)) => return true, + _ => () + } + } + return false; +} + +fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + m: &[Match<'a, 'p, 'blk, 'tcx>], + col: uint, + val: ValueRef) + -> Vec> { + debug!("expand_nested_bindings(bcx={}, m={}, col={}, val={})", + bcx.to_str(), + m.repr(bcx.tcx()), + col, + bcx.val_to_string(val)); + let _indenter = indenter(); + + m.iter().map(|br| { + let mut bound_ptrs = br.bound_ptrs.clone(); + let mut pat = br.pats[col]; + loop { + pat = match pat.node { + ast::PatIdent(_, ref path, Some(ref inner)) => { + bound_ptrs.push((path.node, val)); + &**inner + }, + _ => break + } + } + + let mut pats = br.pats.clone(); + pats[col] = pat; + Match { + pats: pats, + data: &*br.data, + bound_ptrs: bound_ptrs + } + }).collect() +} + +type EnterPatterns<'a, 'p> = |&[&'p ast::Pat]|: 'a -> Option>; + +fn enter_match<'a, 'b, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + dm: &DefMap, + m: &[Match<'a, 'p, 'blk, 'tcx>], + col: uint, + val: ValueRef, + e: EnterPatterns<'b, 'p>) + -> Vec> { + debug!("enter_match(bcx={}, m={}, col={}, val={})", + bcx.to_str(), + m.repr(bcx.tcx()), + col, + bcx.val_to_string(val)); + let _indenter = indenter(); + + m.iter().filter_map(|br| { + e(br.pats.as_slice()).map(|pats| { + let this = br.pats[col]; + let mut bound_ptrs = br.bound_ptrs.clone(); + match this.node { + ast::PatIdent(_, ref path, None) => { + if pat_is_binding(dm, &*this) { + bound_ptrs.push((path.node, val)); + } + } + ast::PatVec(ref before, Some(ref slice), ref after) => { + match slice.node { + ast::PatIdent(_, ref path, None) => { + let subslice_val = bind_subslice_pat( + bcx, this.id, val, + before.len(), after.len()); + bound_ptrs.push((path.node, subslice_val)); + } + _ => {} + } + } + _ => {} + } + Match { + pats: pats, + data: br.data, + bound_ptrs: bound_ptrs + } + }) + }).collect() +} + +fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + dm: &DefMap, + m: &[Match<'a, 'p, 'blk, 'tcx>], + col: uint, + val: ValueRef) + -> Vec> { + debug!("enter_default(bcx={}, m={}, col={}, val={})", + bcx.to_str(), + m.repr(bcx.tcx()), + col, + bcx.val_to_string(val)); + let _indenter = indenter(); + + // Collect all of the matches that can match against anything. + enter_match(bcx, dm, m, col, val, |pats| { + if pat_is_binding_or_wild(dm, &*pats[col]) { + let mut r = pats[..col].to_vec(); + r.push_all(pats[col + 1..]); + Some(r) + } else { + None + } + }) +} + +// nmatsakis: what does enter_opt do? +// in trans/match +// trans/match.rs is like stumbling around in a dark cave +// pcwalton: the enter family of functions adjust the set of +// patterns as needed +// yeah, at some point I kind of achieved some level of +// understanding +// anyhow, they adjust the patterns given that something of that +// kind has been found +// pcwalton: ok, right, so enter_XXX() adjusts the patterns, as I +// said +// enter_match() kind of embodies the generic code +// it is provided with a function that tests each pattern to see +// if it might possibly apply and so forth +// so, if you have a pattern like {a: _, b: _, _} and one like _ +// then _ would be expanded to (_, _) +// one spot for each of the sub-patterns +// enter_opt() is one of the more complex; it covers the fallible +// cases +// enter_rec_or_struct() or enter_tuple() are simpler, since they +// are infallible patterns +// so all patterns must either be records (resp. tuples) or +// wildcards + +/// The above is now outdated in that enter_match() now takes a function that +/// takes the complete row of patterns rather than just the first one. +/// Also, most of the enter_() family functions have been unified with +/// the check_match specialization step. +fn enter_opt<'a, 'p, 'blk, 'tcx>( + bcx: Block<'blk, 'tcx>, + _: ast::NodeId, + dm: &DefMap, + m: &[Match<'a, 'p, 'blk, 'tcx>], + opt: &Opt, + col: uint, + variant_size: uint, + val: ValueRef) + -> Vec> { + debug!("enter_opt(bcx={}, m={}, opt={}, col={}, val={})", + bcx.to_str(), + m.repr(bcx.tcx()), + *opt, + col, + bcx.val_to_string(val)); + let _indenter = indenter(); + + let ctor = match opt { + &ConstantValue(ConstantExpr(expr)) => check_match::ConstantValue( + const_eval::eval_const_expr(bcx.tcx(), &*expr) + ), + &ConstantRange(ConstantExpr(lo), ConstantExpr(hi)) => check_match::ConstantRange( + const_eval::eval_const_expr(bcx.tcx(), &*lo), + const_eval::eval_const_expr(bcx.tcx(), &*hi) + ), + &SliceLengthEqual(n) => + check_match::Slice(n), + &SliceLengthGreaterOrEqual(before, after) => + check_match::SliceWithSubslice(before, after), + &Variant(_, _, def_id) => + check_match::Constructor::Variant(def_id) + }; + + let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; + enter_match(bcx, dm, m, col, val, |pats| + check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size) + ) +} + +// Returns the options in one column of matches. An option is something that +// needs to be conditionally matched at runtime; for example, the discriminant +// on a set of enum variants or a literal. +fn get_branches<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + m: &[Match<'a, 'p, 'blk, 'tcx>], col: uint) + -> Vec> { + let tcx = bcx.tcx(); + + let mut found: Vec = vec![]; + for br in m.iter() { + let cur = br.pats[col]; + let opt = match cur.node { + ast::PatLit(ref l) => ConstantValue(ConstantExpr(&**l)), + ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => { + // This is either an enum variant or a variable binding. + let opt_def = tcx.def_map.borrow().get(&cur.id).cloned(); + match opt_def { + Some(def::DefVariant(enum_id, var_id, _)) => { + let variant = ty::enum_variant_with_id(tcx, enum_id, var_id); + Variant(variant.disr_val, adt::represent_node(bcx, cur.id), var_id) + } + _ => continue + } + } + ast::PatRange(ref l1, ref l2) => { + ConstantRange(ConstantExpr(&**l1), ConstantExpr(&**l2)) + } + ast::PatVec(ref before, None, ref after) => { + SliceLengthEqual(before.len() + after.len()) + } + ast::PatVec(ref before, Some(_), ref after) => { + SliceLengthGreaterOrEqual(before.len(), after.len()) + } + _ => continue + }; + + if !found.iter().any(|x| x.eq(&opt, tcx)) { + found.push(opt); + } + } + found +} + +struct ExtractedBlock<'blk, 'tcx: 'blk> { + vals: Vec, + bcx: Block<'blk, 'tcx>, +} + +fn extract_variant_args<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + repr: &adt::Repr, + disr_val: ty::Disr, + val: ValueRef) + -> ExtractedBlock<'blk, 'tcx> { + let _icx = push_ctxt("match::extract_variant_args"); + let args = Vec::from_fn(adt::num_args(repr, disr_val), |i| { + adt::trans_field_ptr(bcx, repr, val, disr_val, i) + }); + + ExtractedBlock { vals: args, bcx: bcx } +} + +fn match_datum(val: ValueRef, left_ty: ty::t) -> Datum { + /*! + * Helper for converting from the ValueRef that we pass around in + * the match code, which is always an lvalue, into a Datum. Eventually + * we should just pass around a Datum and be done with it. + */ + Datum::new(val, left_ty, Lvalue) +} + +fn bind_subslice_pat(bcx: Block, + pat_id: ast::NodeId, + val: ValueRef, + offset_left: uint, + offset_right: uint) -> ValueRef { + let _icx = push_ctxt("match::bind_subslice_pat"); + let vec_ty = node_id_type(bcx, pat_id); + let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty))); + let vec_datum = match_datum(val, vec_ty); + let (base, len) = vec_datum.get_vec_base_and_len(bcx); + + let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), offset_left)); + let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset); + let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right); + let slice_len = Sub(bcx, len, slice_len_offset); + let slice_ty = ty::mk_slice(bcx.tcx(), + ty::ReStatic, + ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}); + let scratch = rvalue_scratch_datum(bcx, slice_ty, ""); + Store(bcx, slice_begin, + GEPi(bcx, scratch.val, &[0u, abi::slice_elt_base])); + Store(bcx, slice_len, GEPi(bcx, scratch.val, &[0u, abi::slice_elt_len])); + scratch.val +} + +fn extract_vec_elems<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + left_ty: ty::t, + before: uint, + after: uint, + val: ValueRef) + -> ExtractedBlock<'blk, 'tcx> { + let _icx = push_ctxt("match::extract_vec_elems"); + let vec_datum = match_datum(val, left_ty); + let (base, len) = vec_datum.get_vec_base_and_len(bcx); + let mut elems = vec![]; + elems.extend(range(0, before).map(|i| GEPi(bcx, base, &[i]))); + elems.extend(range(0, after).rev().map(|i| { + InBoundsGEP(bcx, base, &[ + Sub(bcx, len, C_uint(bcx.ccx(), i + 1)) + ]) + })); + ExtractedBlock { vals: elems, bcx: bcx } +} + +// Macro for deciding whether any of the remaining matches fit a given kind of +// pattern. Note that, because the macro is well-typed, either ALL of the +// matches should fit that sort of pattern or NONE (however, some of the +// matches may be wildcards like _ or identifiers). +macro_rules! any_pat ( + ($m:expr, $col:expr, $pattern:pat) => ( + ($m).iter().any(|br| { + match br.pats[$col].node { + $pattern => true, + _ => false + } + }) + ) +) + +fn any_uniq_pat(m: &[Match], col: uint) -> bool { + any_pat!(m, col, ast::PatBox(_)) +} + +fn any_region_pat(m: &[Match], col: uint) -> bool { + any_pat!(m, col, ast::PatRegion(_)) +} + +fn any_irrefutable_adt_pat(tcx: &ty::ctxt, m: &[Match], col: uint) -> bool { + m.iter().any(|br| { + let pat = br.pats[col]; + match pat.node { + ast::PatTup(_) => true, + ast::PatStruct(..) => { + match tcx.def_map.borrow().get(&pat.id) { + Some(&def::DefVariant(..)) => false, + _ => true, + } + } + ast::PatEnum(..) | ast::PatIdent(_, _, None) => { + match tcx.def_map.borrow().get(&pat.id) { + Some(&def::DefStruct(..)) => true, + _ => false + } + } + _ => false + } + }) +} + +/// What to do when the pattern match fails. +enum FailureHandler { + Infallible, + JumpToBasicBlock(BasicBlockRef), + Unreachable +} + +impl FailureHandler { + fn is_fallible(&self) -> bool { + match *self { + Infallible => false, + _ => true + } + } + + fn is_infallible(&self) -> bool { + !self.is_fallible() + } + + fn handle_fail(&self, bcx: Block) { + match *self { + Infallible => + panic!("attempted to panic in a non-panicking panic handler!"), + JumpToBasicBlock(basic_block) => + Br(bcx, basic_block), + Unreachable => + build::Unreachable(bcx) + } + } +} + +fn pick_column_to_specialize(def_map: &DefMap, m: &[Match]) -> Option { + fn pat_score(def_map: &DefMap, pat: &ast::Pat) -> uint { + match pat.node { + ast::PatIdent(_, _, Some(ref inner)) => pat_score(def_map, &**inner), + _ if pat_is_refutable(def_map, pat) => 1u, + _ => 0u + } + } + + let column_score: |&[Match], uint| -> uint = |m, col| { + let total_score = m.iter() + .map(|row| row.pats[col]) + .map(|pat| pat_score(def_map, pat)) + .sum(); + + // Irrefutable columns always go first, they'd only be duplicated in the branches. + if total_score == 0 { + std::uint::MAX + } else { + total_score + } + }; + + let column_contains_any_nonwild_patterns: |&uint| -> bool = |&col| { + m.iter().any(|row| match row.pats[col].node { + ast::PatWild(_) => false, + _ => true + }) + }; + + range(0, m[0].pats.len()) + .filter(column_contains_any_nonwild_patterns) + .map(|col| (col, column_score(m, col))) + .max_by(|&(_, score)| score) + .map(|(col, _)| col) +} + +// Compiles a comparison between two things. +fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + rhs_t: ty::t) + -> Result<'blk, 'tcx> { + fn compare_str<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + rhs_t: ty::t) + -> Result<'blk, 'tcx> { + let did = langcall(cx, + None, + format!("comparison of `{}`", + cx.ty_to_string(rhs_t)).as_slice(), + StrEqFnLangItem); + callee::trans_lang_call(cx, did, &[lhs, rhs], None) + } + + let _icx = push_ctxt("compare_values"); + if ty::type_is_scalar(rhs_t) { + let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, ast::BiEq); + return Result::new(rs.bcx, rs.val); + } + + match ty::get(rhs_t).sty { + ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { + ty::ty_str => compare_str(cx, lhs, rhs, rhs_t), + ty::ty_vec(ty, _) => match ty::get(ty).sty { + ty::ty_uint(ast::TyU8) => { + // NOTE: cast &[u8] to &str and abuse the str_eq lang item, + // which calls memcmp(). + let t = ty::mk_str_slice(cx.tcx(), ty::ReStatic, ast::MutImmutable); + let lhs = BitCast(cx, lhs, type_of::type_of(cx.ccx(), t).ptr_to()); + let rhs = BitCast(cx, rhs, type_of::type_of(cx.ccx(), t).ptr_to()); + compare_str(cx, lhs, rhs, rhs_t) + }, + _ => cx.sess().bug("only byte strings supported in compare_values"), + }, + _ => cx.sess().bug("only string and byte strings supported in compare_values"), + }, + _ => cx.sess().bug("only scalars, byte strings, and strings supported in compare_values"), + } +} + +fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + bindings_map: &BindingsMap, + cs: Option) + -> Block<'blk, 'tcx> { + /*! + * For each binding in `data.bindings_map`, adds an appropriate entry into + * the `fcx.lllocals` map + */ + + for (&ident, &binding_info) in bindings_map.iter() { + let llval = match binding_info.trmode { + // By value mut binding for a copy type: load from the ptr + // into the matched value and copy to our alloca + TrByCopy(llbinding) => { + let llval = Load(bcx, binding_info.llmatch); + let datum = Datum::new(llval, binding_info.ty, Lvalue); + call_lifetime_start(bcx, llbinding); + bcx = datum.store_to(bcx, llbinding); + match cs { + Some(cs) => bcx.fcx.schedule_lifetime_end(cs, llbinding), + _ => {} + } + + llbinding + }, + + // By value move bindings: load from the ptr into the matched value + TrByMove => Load(bcx, binding_info.llmatch), + + // By ref binding: use the ptr into the matched value + TrByRef => binding_info.llmatch + }; + + let datum = Datum::new(llval, binding_info.ty, Lvalue); + match cs { + Some(cs) => { + bcx.fcx.schedule_drop_and_zero_mem(cs, llval, binding_info.ty); + bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch); + } + _ => {} + } + + debug!("binding {} to {}", + binding_info.id, + bcx.val_to_string(llval)); + bcx.fcx.lllocals.borrow_mut().insert(binding_info.id, datum); + + if bcx.sess().opts.debuginfo == FullDebugInfo { + debuginfo::create_match_binding_metadata(bcx, + ident, + binding_info); + } + } + bcx +} + +fn compile_guard<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + guard_expr: &ast::Expr, + data: &ArmData, + m: &[Match<'a, 'p, 'blk, 'tcx>], + vals: &[ValueRef], + chk: &FailureHandler, + has_genuine_default: bool) + -> Block<'blk, 'tcx> { + debug!("compile_guard(bcx={}, guard_expr={}, m={}, vals={})", + bcx.to_str(), + bcx.expr_to_string(guard_expr), + m.repr(bcx.tcx()), + vec_map_to_string(vals, |v| bcx.val_to_string(*v))); + let _indenter = indenter(); + + let mut bcx = insert_lllocals(bcx, &data.bindings_map, None); + + let val = unpack_datum!(bcx, expr::trans(bcx, guard_expr)); + let val = val.to_llbool(bcx); + + for (_, &binding_info) in data.bindings_map.iter() { + match binding_info.trmode { + TrByCopy(llbinding) => call_lifetime_end(bcx, llbinding), + _ => {} + } + } + + with_cond(bcx, Not(bcx, val), |bcx| { + // Guard does not match: remove all bindings from the lllocals table + for (_, &binding_info) in data.bindings_map.iter() { + call_lifetime_end(bcx, binding_info.llmatch); + bcx.fcx.lllocals.borrow_mut().remove(&binding_info.id); + } + match chk { + // If the default arm is the only one left, move on to the next + // condition explicitly rather than (possibly) falling back to + // the default arm. + &JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => { + chk.handle_fail(bcx); + } + _ => { + compile_submatch(bcx, m, vals, chk, has_genuine_default); + } + }; + bcx + }) +} + +fn compile_submatch<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + m: &[Match<'a, 'p, 'blk, 'tcx>], + vals: &[ValueRef], + chk: &FailureHandler, + has_genuine_default: bool) { + debug!("compile_submatch(bcx={}, m={}, vals={})", + bcx.to_str(), + m.repr(bcx.tcx()), + vec_map_to_string(vals, |v| bcx.val_to_string(*v))); + let _indenter = indenter(); + let _icx = push_ctxt("match::compile_submatch"); + let mut bcx = bcx; + if m.len() == 0u { + if chk.is_fallible() { + chk.handle_fail(bcx); + } + return; + } + + let tcx = bcx.tcx(); + let def_map = &tcx.def_map; + match pick_column_to_specialize(def_map, m) { + Some(col) => { + let val = vals[col]; + if has_nested_bindings(m, col) { + let expanded = expand_nested_bindings(bcx, m, col, val); + compile_submatch_continue(bcx, + expanded.as_slice(), + vals, + chk, + col, + val, + has_genuine_default) + } else { + compile_submatch_continue(bcx, m, vals, chk, col, val, has_genuine_default) + } + } + None => { + let data = &m[0].data; + for &(ref ident, ref value_ptr) in m[0].bound_ptrs.iter() { + let llmatch = data.bindings_map[*ident].llmatch; + call_lifetime_start(bcx, llmatch); + Store(bcx, *value_ptr, llmatch); + } + match data.arm.guard { + Some(ref guard_expr) => { + bcx = compile_guard(bcx, + &**guard_expr, + m[0].data, + m[1..m.len()], + vals, + chk, + has_genuine_default); + } + _ => () + } + Br(bcx, data.bodycx.llbb); + } + } +} + +fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + m: &[Match<'a, 'p, 'blk, 'tcx>], + vals: &[ValueRef], + chk: &FailureHandler, + col: uint, + val: ValueRef, + has_genuine_default: bool) { + let fcx = bcx.fcx; + let tcx = bcx.tcx(); + let dm = &tcx.def_map; + + let mut vals_left = vals[0u..col].to_vec(); + vals_left.push_all(vals[col + 1u..]); + let ccx = bcx.fcx.ccx; + + // Find a real id (we're adding placeholder wildcard patterns, but + // each column is guaranteed to have at least one real pattern) + let pat_id = m.iter().map(|br| br.pats[col].id) + .find(|&id| id != DUMMY_NODE_ID) + .unwrap_or(DUMMY_NODE_ID); + + let left_ty = if pat_id == DUMMY_NODE_ID { + ty::mk_nil(tcx) + } else { + node_id_type(bcx, pat_id) + }; + + let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; + let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) { + let repr = adt::represent_type(bcx.ccx(), left_ty); + let arg_count = adt::num_args(&*repr, 0); + let field_vals: Vec = std::iter::range(0, arg_count).map(|ix| + adt::trans_field_ptr(bcx, &*repr, val, 0, ix) + ).collect(); + Some(field_vals) + } else if any_uniq_pat(m, col) || any_region_pat(m, col) { + Some(vec!(Load(bcx, val))) + } else { + match ty::get(left_ty).sty { + ty::ty_vec(_, Some(n)) => { + let args = extract_vec_elems(bcx, left_ty, n, 0, val); + Some(args.vals) + } + _ => None + } + }; + + match adt_vals { + Some(field_vals) => { + let pats = enter_match(bcx, dm, m, col, val, |pats| + check_match::specialize(&mcx, pats, + &check_match::Single, col, + field_vals.len()) + ); + let mut vals = field_vals; + vals.push_all(vals_left.as_slice()); + compile_submatch(bcx, pats.as_slice(), vals.as_slice(), chk, has_genuine_default); + return; + } + _ => () + } + + // Decide what kind of branch we need + let opts = get_branches(bcx, m, col); + debug!("options={}", opts); + let mut kind = NoBranch; + let mut test_val = val; + debug!("test_val={}", bcx.val_to_string(test_val)); + if opts.len() > 0u { + match opts[0] { + ConstantValue(_) | ConstantRange(_, _) => { + test_val = load_if_immediate(bcx, val, left_ty); + kind = if ty::type_is_integral(left_ty) { + Switch + } else { + Compare + }; + } + Variant(_, ref repr, _) => { + let (the_kind, val_opt) = adt::trans_switch(bcx, &**repr, val); + kind = the_kind; + for &tval in val_opt.iter() { test_val = tval; } + } + SliceLengthEqual(_) | SliceLengthGreaterOrEqual(_, _) => { + let (_, len) = tvec::get_base_and_len(bcx, val, left_ty); + test_val = len; + kind = Switch; + } + } + } + for o in opts.iter() { + match *o { + ConstantRange(_, _) => { kind = Compare; break }, + SliceLengthGreaterOrEqual(_, _) => { kind = CompareSliceLength; break }, + _ => () + } + } + let else_cx = match kind { + NoBranch | Single => bcx, + _ => bcx.fcx.new_temp_block("match_else") + }; + let sw = if kind == Switch { + build::Switch(bcx, test_val, else_cx.llbb, opts.len()) + } else { + C_int(ccx, 0i) // Placeholder for when not using a switch + }; + + let defaults = enter_default(else_cx, dm, m, col, val); + let exhaustive = chk.is_infallible() && defaults.len() == 0u; + let len = opts.len(); + + // Compile subtrees for each option + for (i, opt) in opts.iter().enumerate() { + // In some cases of range and vector pattern matching, we need to + // override the failure case so that instead of failing, it proceeds + // to try more matching. branch_chk, then, is the proper failure case + // for the current conditional branch. + let mut branch_chk = None; + let mut opt_cx = else_cx; + if !exhaustive || i + 1 < len { + opt_cx = bcx.fcx.new_temp_block("match_case"); + match kind { + Single => Br(bcx, opt_cx.llbb), + Switch => { + match opt.trans(bcx) { + SingleResult(r) => { + AddCase(sw, r.val, opt_cx.llbb); + bcx = r.bcx; + } + _ => { + bcx.sess().bug( + "in compile_submatch, expected \ + opt.trans() to return a SingleResult") + } + } + } + Compare | CompareSliceLength => { + let t = if kind == Compare { + left_ty + } else { + ty::mk_uint() // vector length + }; + let Result { bcx: after_cx, val: matches } = { + match opt.trans(bcx) { + SingleResult(Result { bcx, val }) => { + compare_values(bcx, test_val, val, t) + } + RangeResult(Result { val: vbegin, .. }, + Result { bcx, val: vend }) => { + let Result { bcx, val: llge } = + compare_scalar_types( + bcx, test_val, + vbegin, t, ast::BiGe); + let Result { bcx, val: llle } = + compare_scalar_types( + bcx, test_val, vend, + t, ast::BiLe); + Result::new(bcx, And(bcx, llge, llle)) + } + LowerBound(Result { bcx, val }) => { + compare_scalar_types(bcx, test_val, val, t, ast::BiGe) + } + } + }; + bcx = fcx.new_temp_block("compare_next"); + + // If none of the sub-cases match, and the current condition + // is guarded or has multiple patterns, move on to the next + // condition, if there is any, rather than falling back to + // the default. + let guarded = m[i].data.arm.guard.is_some(); + let multi_pats = m[i].pats.len() > 1; + if i + 1 < len && (guarded || multi_pats || kind == CompareSliceLength) { + branch_chk = Some(JumpToBasicBlock(bcx.llbb)); + } + CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); + } + _ => () + } + } else if kind == Compare || kind == CompareSliceLength { + Br(bcx, else_cx.llbb); + } + + let mut size = 0u; + let mut unpacked = Vec::new(); + match *opt { + Variant(disr_val, ref repr, _) => { + let ExtractedBlock {vals: argvals, bcx: new_bcx} = + extract_variant_args(opt_cx, &**repr, disr_val, val); + size = argvals.len(); + unpacked = argvals; + opt_cx = new_bcx; + } + SliceLengthEqual(len) => { + let args = extract_vec_elems(opt_cx, left_ty, len, 0, val); + size = args.vals.len(); + unpacked = args.vals.clone(); + opt_cx = args.bcx; + } + SliceLengthGreaterOrEqual(before, after) => { + let args = extract_vec_elems(opt_cx, left_ty, before, after, val); + size = args.vals.len(); + unpacked = args.vals.clone(); + opt_cx = args.bcx; + } + ConstantValue(_) | ConstantRange(_, _) => () + } + let opt_ms = enter_opt(opt_cx, pat_id, dm, m, opt, col, size, val); + let mut opt_vals = unpacked; + opt_vals.push_all(vals_left.as_slice()); + compile_submatch(opt_cx, + opt_ms.as_slice(), + opt_vals.as_slice(), + branch_chk.as_ref().unwrap_or(chk), + has_genuine_default); + } + + // Compile the fall-through case, if any + if !exhaustive && kind != Single { + if kind == Compare || kind == CompareSliceLength { + Br(bcx, else_cx.llbb); + } + match chk { + // If there is only one default arm left, move on to the next + // condition explicitly rather than (eventually) falling back to + // the last default arm. + &JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => { + chk.handle_fail(else_cx); + } + _ => { + compile_submatch(else_cx, + defaults.as_slice(), + vals_left.as_slice(), + chk, + has_genuine_default); + } + } + } +} + +pub fn trans_match<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + match_expr: &ast::Expr, + discr_expr: &ast::Expr, + arms: &[ast::Arm], + dest: Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("match::trans_match"); + trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest) +} + +/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body` +fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool { + match discr.node { + ast::ExprPath(..) => match bcx.def(discr.id) { + def::DefLocal(vid) | def::DefUpvar(vid, _, _) => { + let mut rc = ReassignmentChecker { + node: vid, + reassigned: false + }; + { + let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx); + visitor.walk_expr(body); + } + rc.reassigned + } + _ => false + }, + _ => false + } +} + +struct ReassignmentChecker { + node: ast::NodeId, + reassigned: bool +} + +impl euv::Delegate for ReassignmentChecker { + fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {} + fn consume_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::ConsumeMode) {} + fn borrow(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: ty::Region, + _: ty::BorrowKind, _: euv::LoanCause) {} + fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {} + + fn mutate(&mut self, _: ast::NodeId, _: Span, cmt: mc::cmt, _: euv::MutateMode) { + match cmt.cat { + mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: vid, .. }, .. }) | + mc::cat_local(vid) => self.reassigned = self.node == vid, + _ => {} + } + } +} + +fn create_bindings_map(bcx: Block, pat: &ast::Pat, + discr: &ast::Expr, body: &ast::Expr) -> BindingsMap { + // Create the bindings map, which is a mapping from each binding name + // to an alloca() that will be the value for that local variable. + // Note that we use the names because each binding will have many ids + // from the various alternatives. + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + let reassigned = is_discr_reassigned(bcx, discr, body); + let mut bindings_map = FnvHashMap::new(); + pat_bindings(&tcx.def_map, &*pat, |bm, p_id, span, path1| { + let ident = path1.node; + let variable_ty = node_id_type(bcx, p_id); + let llvariable_ty = type_of::type_of(ccx, variable_ty); + let tcx = bcx.tcx(); + + let llmatch; + let trmode; + match bm { + ast::BindByValue(_) + if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => { + llmatch = alloca_no_lifetime(bcx, + llvariable_ty.ptr_to(), + "__llmatch"); + trmode = TrByCopy(alloca_no_lifetime(bcx, + llvariable_ty, + bcx.ident(ident).as_slice())); + } + ast::BindByValue(_) => { + // in this case, the final type of the variable will be T, + // but during matching we need to store a *T as explained + // above + llmatch = alloca_no_lifetime(bcx, + llvariable_ty.ptr_to(), + bcx.ident(ident).as_slice()); + trmode = TrByMove; + } + ast::BindByRef(_) => { + llmatch = alloca_no_lifetime(bcx, + llvariable_ty, + bcx.ident(ident).as_slice()); + trmode = TrByRef; + } + }; + bindings_map.insert(ident, BindingInfo { + llmatch: llmatch, + trmode: trmode, + id: p_id, + span: span, + ty: variable_ty + }); + }); + return bindings_map; +} + +fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>, + match_id: ast::NodeId, + discr_expr: &ast::Expr, + arms: &[ast::Arm], + dest: Dest) -> Block<'blk, 'tcx> { + let _icx = push_ctxt("match::trans_match_inner"); + let fcx = scope_cx.fcx; + let mut bcx = scope_cx; + let tcx = bcx.tcx(); + + let discr_datum = unpack_datum!(bcx, expr::trans_to_lvalue(bcx, discr_expr, + "match")); + if bcx.unreachable.get() { + return bcx; + } + + let t = node_id_type(bcx, discr_expr.id); + let chk = if ty::type_is_empty(tcx, t) { + Unreachable + } else { + Infallible + }; + + let arm_datas: Vec = arms.iter().map(|arm| ArmData { + bodycx: fcx.new_id_block("case_body", arm.body.id), + arm: arm, + bindings_map: create_bindings_map(bcx, &*arm.pats[0], discr_expr, &*arm.body) + }).collect(); + + let mut static_inliner = StaticInliner::new(scope_cx.tcx()); + let arm_pats: Vec>> = arm_datas.iter().map(|arm_data| { + arm_data.arm.pats.iter().map(|p| static_inliner.fold_pat((*p).clone())).collect() + }).collect(); + let mut matches = Vec::new(); + for (arm_data, pats) in arm_datas.iter().zip(arm_pats.iter()) { + matches.extend(pats.iter().map(|p| Match { + pats: vec![&**p], + data: arm_data, + bound_ptrs: Vec::new(), + })); + } + + // `compile_submatch` works one column of arm patterns a time and + // then peels that column off. So as we progress, it may become + // impossible to tell whether we have a genuine default arm, i.e. + // `_ => foo` or not. Sometimes it is important to know that in order + // to decide whether moving on to the next condition or falling back + // to the default arm. + let has_default = arms.last().map_or(false, |arm| { + arm.pats.len() == 1 + && arm.pats.last().unwrap().node == ast::PatWild(ast::PatWildSingle) + }); + + compile_submatch(bcx, matches.as_slice(), &[discr_datum.val], &chk, has_default); + + let mut arm_cxs = Vec::new(); + for arm_data in arm_datas.iter() { + let mut bcx = arm_data.bodycx; + + // insert bindings into the lllocals map and add cleanups + let cs = fcx.push_custom_cleanup_scope(); + bcx = insert_lllocals(bcx, &arm_data.bindings_map, Some(cleanup::CustomScope(cs))); + bcx = expr::trans_into(bcx, &*arm_data.arm.body, dest); + bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, cs); + arm_cxs.push(bcx); + } + + bcx = scope_cx.fcx.join_blocks(match_id, arm_cxs.as_slice()); + return bcx; +} + +pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + local: &ast::Local) + -> Block<'blk, 'tcx> { + /*! + * Generates code for a local variable declaration like + * `let ;` or `let = `. + */ + let _icx = push_ctxt("match::store_local"); + let mut bcx = bcx; + let tcx = bcx.tcx(); + let pat = &*local.pat; + + fn create_dummy_locals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + pat: &ast::Pat) + -> Block<'blk, 'tcx> { + // create dummy memory for the variables if we have no + // value to store into them immediately + let tcx = bcx.tcx(); + pat_bindings(&tcx.def_map, pat, |_, p_id, _, path1| { + let scope = cleanup::var_scope(tcx, p_id); + bcx = mk_binding_alloca( + bcx, p_id, &path1.node, scope, (), + |(), bcx, llval, ty| { zero_mem(bcx, llval, ty); bcx }); + }); + bcx + } + + match local.init { + Some(ref init_expr) => { + // Optimize the "let x = expr" case. This just writes + // the result of evaluating `expr` directly into the alloca + // for `x`. Often the general path results in similar or the + // same code post-optimization, but not always. In particular, + // in unsafe code, you can have expressions like + // + // let x = intrinsics::uninit(); + // + // In such cases, the more general path is unsafe, because + // it assumes it is matching against a valid value. + match simple_identifier(&*pat) { + Some(ident) => { + let var_scope = cleanup::var_scope(tcx, local.id); + return mk_binding_alloca( + bcx, pat.id, ident, var_scope, (), + |(), bcx, v, _| expr::trans_into(bcx, &**init_expr, + expr::SaveIn(v))); + } + + None => {} + } + + // General path. + let init_datum = + unpack_datum!(bcx, expr::trans_to_lvalue(bcx, &**init_expr, "let")); + if bcx.sess().asm_comments() { + add_comment(bcx, "creating zeroable ref llval"); + } + let var_scope = cleanup::var_scope(tcx, local.id); + bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope) + } + None => { + create_dummy_locals(bcx, pat) + } + } +} + +pub fn store_arg<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + pat: &ast::Pat, + arg: Datum, + arg_scope: cleanup::ScopeId) + -> Block<'blk, 'tcx> { + /*! + * Generates code for argument patterns like `fn foo(: T)`. + * Creates entries in the `lllocals` map for each of the bindings + * in `pat`. + * + * # Arguments + * + * - `pat` is the argument pattern + * - `llval` is a pointer to the argument value (in other words, + * if the argument type is `T`, then `llval` is a `T*`). In some + * cases, this code may zero out the memory `llval` points at. + */ + + let _icx = push_ctxt("match::store_arg"); + + match simple_identifier(&*pat) { + Some(ident) => { + // Generate nicer LLVM for the common case of fn a pattern + // like `x: T` + let arg_ty = node_id_type(bcx, pat.id); + if type_of::arg_is_indirect(bcx.ccx(), arg_ty) + && bcx.sess().opts.debuginfo != FullDebugInfo { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + let arg_val = arg.add_clean(bcx.fcx, arg_scope); + bcx.fcx.lllocals.borrow_mut() + .insert(pat.id, Datum::new(arg_val, arg_ty, Lvalue)); + bcx + } else { + mk_binding_alloca( + bcx, pat.id, ident, arg_scope, arg, + |arg, bcx, llval, _| arg.store_to(bcx, llval)) + } + } + + None => { + // General path. Copy out the values that are used in the + // pattern. + let arg = unpack_datum!( + bcx, arg.to_lvalue_datum_in_scope(bcx, "__arg", arg_scope)); + bind_irrefutable_pat(bcx, pat, arg.val, arg_scope) + } + } +} + +/// Generates code for the pattern binding in a `for` loop like +/// `for in { ... }`. +pub fn store_for_loop_binding<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + pat: &ast::Pat, + llvalue: ValueRef, + body_scope: cleanup::ScopeId) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("match::store_for_loop_binding"); + + if simple_identifier(&*pat).is_some() { + // Generate nicer LLVM for the common case of a `for` loop pattern + // like `for x in blahblah { ... }`. + let binding_type = node_id_type(bcx, pat.id); + bcx.fcx.lllocals.borrow_mut().insert(pat.id, + Datum::new(llvalue, + binding_type, + Lvalue)); + return bcx + } + + // General path. Copy out the values that are used in the pattern. + bind_irrefutable_pat(bcx, pat, llvalue, body_scope) +} + +fn mk_binding_alloca<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>, + p_id: ast::NodeId, + ident: &ast::Ident, + cleanup_scope: cleanup::ScopeId, + arg: A, + populate: |A, Block<'blk, 'tcx>, ValueRef, ty::t| + -> Block<'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let var_ty = node_id_type(bcx, p_id); + + // Allocate memory on stack for the binding. + let llval = alloc_ty(bcx, var_ty, bcx.ident(*ident).as_slice()); + + // Subtle: be sure that we *populate* the memory *before* + // we schedule the cleanup. + let bcx = populate(arg, bcx, llval, var_ty); + bcx.fcx.schedule_lifetime_end(cleanup_scope, llval); + bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty); + + // Now that memory is initialized and has cleanup scheduled, + // create the datum and insert into the local variable map. + let datum = Datum::new(llval, var_ty, Lvalue); + bcx.fcx.lllocals.borrow_mut().insert(p_id, datum); + bcx +} + +fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + pat: &ast::Pat, + val: ValueRef, + cleanup_scope: cleanup::ScopeId) + -> Block<'blk, 'tcx> { + /*! + * A simple version of the pattern matching code that only handles + * irrefutable patterns. This is used in let/argument patterns, + * not in match statements. Unifying this code with the code above + * sounds nice, but in practice it produces very inefficient code, + * since the match code is so much more general. In most cases, + * LLVM is able to optimize the code, but it causes longer compile + * times and makes the generated code nigh impossible to read. + * + * # Arguments + * - bcx: starting basic block context + * - pat: the irrefutable pattern being matched. + * - val: the value being matched -- must be an lvalue (by ref, with cleanup) + */ + + debug!("bind_irrefutable_pat(bcx={}, pat={})", + bcx.to_str(), + pat.repr(bcx.tcx())); + + if bcx.sess().asm_comments() { + add_comment(bcx, format!("bind_irrefutable_pat(pat={})", + pat.repr(bcx.tcx())).as_slice()); + } + + let _indenter = indenter(); + + let _icx = push_ctxt("match::bind_irrefutable_pat"); + let mut bcx = bcx; + let tcx = bcx.tcx(); + let ccx = bcx.ccx(); + match pat.node { + ast::PatIdent(pat_binding_mode, ref path1, ref inner) => { + if pat_is_binding(&tcx.def_map, &*pat) { + // Allocate the stack slot where the value of this + // binding will live and place it into the appropriate + // map. + bcx = mk_binding_alloca( + bcx, pat.id, &path1.node, cleanup_scope, (), + |(), bcx, llval, ty| { + match pat_binding_mode { + ast::BindByValue(_) => { + // By value binding: move the value that `val` + // points at into the binding's stack slot. + let d = Datum::new(val, ty, Lvalue); + d.store_to(bcx, llval) + } + + ast::BindByRef(_) => { + // By ref binding: the value of the variable + // is the pointer `val` itself. + Store(bcx, val, llval); + bcx + } + } + }); + } + + for inner_pat in inner.iter() { + bcx = bind_irrefutable_pat(bcx, &**inner_pat, val, cleanup_scope); + } + } + ast::PatEnum(_, ref sub_pats) => { + let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).cloned(); + match opt_def { + Some(def::DefVariant(enum_id, var_id, _)) => { + let repr = adt::represent_node(bcx, pat.id); + let vinfo = ty::enum_variant_with_id(ccx.tcx(), + enum_id, + var_id); + let args = extract_variant_args(bcx, + &*repr, + vinfo.disr_val, + val); + for sub_pat in sub_pats.iter() { + for (i, &argval) in args.vals.iter().enumerate() { + bcx = bind_irrefutable_pat(bcx, &*sub_pat[i], + argval, cleanup_scope); + } + } + } + Some(def::DefStruct(..)) => { + match *sub_pats { + None => { + // This is a unit-like struct. Nothing to do here. + } + Some(ref elems) => { + // This is the tuple struct case. + let repr = adt::represent_node(bcx, pat.id); + for (i, elem) in elems.iter().enumerate() { + let fldptr = adt::trans_field_ptr(bcx, &*repr, + val, 0, i); + bcx = bind_irrefutable_pat(bcx, &**elem, + fldptr, cleanup_scope); + } + } + } + } + _ => { + // Nothing to do here. + } + } + } + ast::PatStruct(_, ref fields, _) => { + let tcx = bcx.tcx(); + let pat_ty = node_id_type(bcx, pat.id); + let pat_repr = adt::represent_type(bcx.ccx(), pat_ty); + expr::with_field_tys(tcx, pat_ty, Some(pat.id), |discr, field_tys| { + for f in fields.iter() { + let ix = ty::field_idx_strict(tcx, f.node.ident.name, field_tys); + let fldptr = adt::trans_field_ptr(bcx, &*pat_repr, val, + discr, ix); + bcx = bind_irrefutable_pat(bcx, &*f.node.pat, fldptr, cleanup_scope); + } + }) + } + ast::PatTup(ref elems) => { + let repr = adt::represent_node(bcx, pat.id); + for (i, elem) in elems.iter().enumerate() { + let fldptr = adt::trans_field_ptr(bcx, &*repr, val, 0, i); + bcx = bind_irrefutable_pat(bcx, &**elem, fldptr, cleanup_scope); + } + } + ast::PatBox(ref inner) => { + let llbox = Load(bcx, val); + bcx = bind_irrefutable_pat(bcx, &**inner, llbox, cleanup_scope); + } + ast::PatRegion(ref inner) => { + let loaded_val = Load(bcx, val); + bcx = bind_irrefutable_pat(bcx, &**inner, loaded_val, cleanup_scope); + } + ast::PatVec(ref before, ref slice, ref after) => { + let pat_ty = node_id_type(bcx, pat.id); + let mut extracted = extract_vec_elems(bcx, pat_ty, before.len(), after.len(), val); + match slice { + &Some(_) => { + extracted.vals.insert( + before.len(), + bind_subslice_pat(bcx, pat.id, val, before.len(), after.len()) + ); + } + &None => () + } + bcx = before + .iter() + .chain(slice.iter()) + .chain(after.iter()) + .zip(extracted.vals.into_iter()) + .fold(bcx, |bcx, (inner, elem)| + bind_irrefutable_pat(bcx, &**inner, elem, cleanup_scope) + ); + } + ast::PatMac(..) => { + bcx.sess().span_bug(pat.span, "unexpanded macro"); + } + ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () + } + return bcx; +} diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs new file mode 100644 index 00000000000..806c4a68ba2 --- /dev/null +++ b/src/librustc_trans/trans/adt.rs @@ -0,0 +1,1158 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * # Representation of Algebraic Data Types + * + * This module determines how to represent enums, structs, and tuples + * based on their monomorphized types; it is responsible both for + * choosing a representation and translating basic operations on + * values of those types. (Note: exporting the representations for + * debuggers is handled in debuginfo.rs, not here.) + * + * Note that the interface treats everything as a general case of an + * enum, so structs/tuples/etc. have one pseudo-variant with + * discriminant 0; i.e., as if they were a univariant enum. + * + * Having everything in one place will enable improvements to data + * structure representation; possibilities include: + * + * - User-specified alignment (e.g., cacheline-aligning parts of + * concurrently accessed data structures); LLVM can't represent this + * directly, so we'd have to insert padding fields in any structure + * that might contain one and adjust GEP indices accordingly. See + * issue #4578. + * + * - Store nested enums' discriminants in the same word. Rather, if + * some variants start with enums, and those enums representations + * have unused alignment padding between discriminant and body, the + * outer enum's discriminant can be stored there and those variants + * can start at offset 0. Kind of fancy, and might need work to + * make copies of the inner enum type cooperate, but it could help + * with `Option` or `Result` wrapped around another enum. + * + * - Tagged pointers would be neat, but given that any type can be + * used unboxed and any field can have pointers (including mutable) + * taken to it, implementing them for Rust seems difficult. + */ + +#![allow(unsigned_negation)] + +pub use self::PointerField::*; +pub use self::Repr::*; + +use std::num::Int; +use std::rc::Rc; + +use llvm::{ValueRef, True, IntEQ, IntNE}; +use back::abi::slice_elt_base; +use middle::subst; +use middle::subst::Subst; +use trans::_match; +use trans::build::*; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::common::*; +use trans::datum; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use middle::ty; +use middle::ty::Disr; +use syntax::ast; +use syntax::attr; +use syntax::attr::IntType; +use util::ppaux::ty_to_string; + +type Hint = attr::ReprAttr; + + +/// Representations. +#[deriving(Eq, PartialEq, Show)] +pub enum Repr { + /// C-like enums; basically an int. + CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType) + /** + * Single-case variants, and structs/tuples/records. + * + * Structs with destructors need a dynamic destroyedness flag to + * avoid running the destructor too many times; this is included + * in the `Struct` if present. + */ + Univariant(Struct, bool), + /** + * General-case enums: for each case there is a struct, and they + * all start with a field for the discriminant. + * + * Types with destructors need a dynamic destroyedness flag to + * avoid running the destructor too many times; the last argument + * indicates whether such a flag is present. + */ + General(IntType, Vec, bool), + /** + * Two cases distinguished by a nullable pointer: the case with discriminant + * `nndiscr` must have single field which is known to be nonnull due to its type. + * The other case is known to be zero sized. Hence we represent the enum + * as simply a nullable pointer: if not null it indicates the `nndiscr` variant, + * otherwise it indicates the other case. + */ + RawNullablePointer { + nndiscr: Disr, + nnty: ty::t, + nullfields: Vec + }, + /** + * Two cases distinguished by a nullable pointer: the case with discriminant + * `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th + * field is known to be nonnull due to its type; if that field is null, then + * it represents the other case, which is inhabited by at most one value + * (and all other fields are undefined/unused). + * + * For example, `std::option::Option` instantiated at a safe pointer type + * is represented such that `None` is a null pointer and `Some` is the + * identity function. + */ + StructWrappedNullablePointer { + nonnull: Struct, + nndiscr: Disr, + ptrfield: PointerField, + nullfields: Vec, + } +} + +/// For structs, and struct-like parts of anything fancier. +#[deriving(Eq, PartialEq, Show)] +pub struct Struct { + // If the struct is DST, then the size and alignment do not take into + // account the unsized fields of the struct. + pub size: u64, + pub align: u32, + pub sized: bool, + pub packed: bool, + pub fields: Vec +} + +/** + * Convenience for `represent_type`. There should probably be more or + * these, for places in trans where the `ty::t` isn't directly + * available. + */ +pub fn represent_node(bcx: Block, node: ast::NodeId) -> Rc { + represent_type(bcx.ccx(), node_id_type(bcx, node)) +} + +/// Decides how to represent a given type. +pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc { + debug!("Representing: {}", ty_to_string(cx.tcx(), t)); + match cx.adt_reprs().borrow().get(&t) { + Some(repr) => return repr.clone(), + None => {} + } + + let repr = Rc::new(represent_type_uncached(cx, t)); + debug!("Represented as: {}", repr) + cx.adt_reprs().borrow_mut().insert(t, repr.clone()); + repr +} + +fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr { + match ty::get(t).sty { + ty::ty_tup(ref elems) => { + return Univariant(mk_struct(cx, elems.as_slice(), false, t), false) + } + ty::ty_struct(def_id, ref substs) => { + let fields = ty::lookup_struct_fields(cx.tcx(), def_id); + let mut ftys = fields.iter().map(|field| { + ty::lookup_field_type(cx.tcx(), def_id, field.id, substs) + }).collect::>(); + let packed = ty::lookup_packed(cx.tcx(), def_id); + let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); + if dtor { ftys.push(ty::mk_bool()); } + + return Univariant(mk_struct(cx, ftys.as_slice(), packed, t), dtor) + } + ty::ty_unboxed_closure(def_id, _, ref substs) => { + let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs); + let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); + return Univariant(mk_struct(cx, upvar_types.as_slice(), false, t), + false) + } + ty::ty_enum(def_id, ref substs) => { + let cases = get_cases(cx.tcx(), def_id, substs); + let hint = *ty::lookup_repr_hints(cx.tcx(), def_id).as_slice().get(0) + .unwrap_or(&attr::ReprAny); + + let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); + + if cases.len() == 0 { + // Uninhabitable; represent as unit + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); + let ftys = if dtor { vec!(ty::mk_bool()) } else { vec!() }; + return Univariant(mk_struct(cx, ftys.as_slice(), false, t), + dtor); + } + + if !dtor && cases.iter().all(|c| c.tys.len() == 0) { + // All bodies empty -> intlike + let discrs: Vec = cases.iter().map(|c| c.discr).collect(); + let bounds = IntBounds { + ulo: *discrs.iter().min().unwrap(), + uhi: *discrs.iter().max().unwrap(), + slo: discrs.iter().map(|n| *n as i64).min().unwrap(), + shi: discrs.iter().map(|n| *n as i64).max().unwrap() + }; + return mk_cenum(cx, hint, &bounds); + } + + // Since there's at least one + // non-empty body, explicit discriminants should have + // been rejected by a checker before this point. + if !cases.iter().enumerate().all(|(i,c)| c.discr == (i as Disr)) { + cx.sess().bug(format!("non-C-like enum {} with specified \ + discriminants", + ty::item_path_str(cx.tcx(), + def_id)).as_slice()); + } + + if cases.len() == 1 { + // Equivalent to a struct/tuple/newtype. + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); + let mut ftys = cases[0].tys.clone(); + if dtor { ftys.push(ty::mk_bool()); } + return Univariant(mk_struct(cx, ftys.as_slice(), false, t), + dtor); + } + + if !dtor && cases.len() == 2 && hint == attr::ReprAny { + // Nullable pointer optimization + let mut discr = 0; + while discr < 2 { + if cases[1 - discr].is_zerolen(cx, t) { + let st = mk_struct(cx, cases[discr].tys.as_slice(), + false, t); + match cases[discr].find_ptr(cx) { + Some(ThinPointer(_)) if st.fields.len() == 1 => { + return RawNullablePointer { + nndiscr: discr as Disr, + nnty: st.fields[0], + nullfields: cases[1 - discr].tys.clone() + }; + } + Some(ptrfield) => { + return StructWrappedNullablePointer { + nndiscr: discr as Disr, + nonnull: st, + ptrfield: ptrfield, + nullfields: cases[1 - discr].tys.clone() + }; + } + None => { } + } + } + discr += 1; + } + } + + // The general case. + assert!((cases.len() - 1) as i64 >= 0); + let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64, + slo: 0, shi: (cases.len() - 1) as i64 }; + let ity = range_to_inttype(cx, hint, &bounds); + + let fields : Vec<_> = cases.iter().map(|c| { + let mut ftys = vec!(ty_of_inttype(ity)); + ftys.push_all(c.tys.as_slice()); + if dtor { ftys.push(ty::mk_bool()); } + mk_struct(cx, ftys.as_slice(), false, t) + }).collect(); + + ensure_enum_fits_in_address_space(cx, ity, fields.as_slice(), t); + + General(ity, fields, dtor) + } + _ => cx.sess().bug(format!("adt::represent_type called on non-ADT type: {}", + ty_to_string(cx.tcx(), t)).as_slice()) + } +} + +// this should probably all be in ty +struct Case { + discr: Disr, + tys: Vec +} + + +#[deriving(Eq, PartialEq, Show)] +pub enum PointerField { + ThinPointer(uint), + FatPointer(uint) +} + +impl Case { + fn is_zerolen(&self, cx: &CrateContext, scapegoat: ty::t) -> bool { + mk_struct(cx, self.tys.as_slice(), false, scapegoat).size == 0 + } + + fn find_ptr(&self, cx: &CrateContext) -> Option { + for (i, &ty) in self.tys.iter().enumerate() { + match ty::get(ty).sty { + // &T/&mut T/Box could either be a thin or fat pointer depending on T + ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty::get(ty).sty { + // &[T] and &str are a pointer and length pair + ty::ty_vec(_, None) | ty::ty_str => return Some(FatPointer(i)), + + // &Trait is a pair of pointers: the actual object and a vtable + ty::ty_trait(..) => return Some(FatPointer(i)), + + ty::ty_struct(..) if !ty::type_is_sized(cx.tcx(), ty) => { + return Some(FatPointer(i)) + } + + // Any other &T is just a pointer + _ => return Some(ThinPointer(i)) + }, + + // Functions are just pointers + ty::ty_bare_fn(..) => return Some(ThinPointer(i)), + + // Closures are a pair of pointers: the code and environment + ty::ty_closure(..) => return Some(FatPointer(i)), + + // Anything else is not a pointer + _ => continue + } + } + + None + } +} + +fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec { + ty::enum_variants(tcx, def_id).iter().map(|vi| { + let arg_tys = vi.args.iter().map(|&raw_ty| { + raw_ty.subst(tcx, substs) + }).collect(); + Case { discr: vi.disr_val, tys: arg_tys } + }).collect() +} + +fn mk_struct(cx: &CrateContext, tys: &[ty::t], packed: bool, scapegoat: ty::t) -> Struct { + let sized = tys.iter().all(|&ty| ty::type_is_sized(cx.tcx(), ty)); + let lltys : Vec = if sized { + tys.iter() + .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() + } else { + tys.iter().filter(|&ty| ty::type_is_sized(cx.tcx(), *ty)) + .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() + }; + + ensure_struct_fits_in_address_space(cx, lltys.as_slice(), packed, scapegoat); + + let llty_rec = Type::struct_(cx, lltys.as_slice(), packed); + Struct { + size: machine::llsize_of_alloc(cx, llty_rec), + align: machine::llalign_of_min(cx, llty_rec), + sized: sized, + packed: packed, + fields: tys.to_vec(), + } +} + +#[deriving(Show)] +struct IntBounds { + slo: i64, + shi: i64, + ulo: u64, + uhi: u64 +} + +fn mk_cenum(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> Repr { + let it = range_to_inttype(cx, hint, bounds); + match it { + attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr), + attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi) + } +} + +fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntType { + debug!("range_to_inttype: {} {}", hint, bounds); + // Lists of sizes to try. u64 is always allowed as a fallback. + #[allow(non_upper_case_globals)] + static choose_shortest: &'static[IntType] = &[ + attr::UnsignedInt(ast::TyU8), attr::SignedInt(ast::TyI8), + attr::UnsignedInt(ast::TyU16), attr::SignedInt(ast::TyI16), + attr::UnsignedInt(ast::TyU32), attr::SignedInt(ast::TyI32)]; + #[allow(non_upper_case_globals)] + static at_least_32: &'static[IntType] = &[ + attr::UnsignedInt(ast::TyU32), attr::SignedInt(ast::TyI32)]; + + let attempts; + match hint { + attr::ReprInt(span, ity) => { + if !bounds_usable(cx, ity, bounds) { + cx.sess().span_bug(span, "representation hint insufficient for discriminant range") + } + return ity; + } + attr::ReprExtern => { + attempts = match cx.sess().target.target.arch.as_slice() { + // WARNING: the ARM EABI has two variants; the one corresponding to `at_least_32` + // appears to be used on Linux and NetBSD, but some systems may use the variant + // corresponding to `choose_shortest`. However, we don't run on those yet...? + "arm" => at_least_32, + _ => at_least_32, + } + } + attr::ReprAny => { + attempts = choose_shortest; + }, + attr::ReprPacked => { + cx.tcx().sess.bug("range_to_inttype: found ReprPacked on an enum"); + } + } + for &ity in attempts.iter() { + if bounds_usable(cx, ity, bounds) { + return ity; + } + } + return attr::UnsignedInt(ast::TyU64); +} + +pub fn ll_inttype(cx: &CrateContext, ity: IntType) -> Type { + match ity { + attr::SignedInt(t) => Type::int_from_ty(cx, t), + attr::UnsignedInt(t) => Type::uint_from_ty(cx, t) + } +} + +fn bounds_usable(cx: &CrateContext, ity: IntType, bounds: &IntBounds) -> bool { + debug!("bounds_usable: {} {}", ity, bounds); + match ity { + attr::SignedInt(_) => { + let lllo = C_integral(ll_inttype(cx, ity), bounds.slo as u64, true); + let llhi = C_integral(ll_inttype(cx, ity), bounds.shi as u64, true); + bounds.slo == const_to_int(lllo) as i64 && bounds.shi == const_to_int(llhi) as i64 + } + attr::UnsignedInt(_) => { + let lllo = C_integral(ll_inttype(cx, ity), bounds.ulo, false); + let llhi = C_integral(ll_inttype(cx, ity), bounds.uhi, false); + bounds.ulo == const_to_uint(lllo) as u64 && bounds.uhi == const_to_uint(llhi) as u64 + } + } +} + +pub fn ty_of_inttype(ity: IntType) -> ty::t { + match ity { + attr::SignedInt(t) => ty::mk_mach_int(t), + attr::UnsignedInt(t) => ty::mk_mach_uint(t) + } +} + +// LLVM doesn't like types that don't fit in the address space +fn ensure_struct_fits_in_address_space(ccx: &CrateContext, + fields: &[Type], + packed: bool, + scapegoat: ty::t) { + let mut offset = 0; + for &llty in fields.iter() { + // Invariant: offset < ccx.max_obj_size() <= 1<<61 + if !packed { + let type_align = machine::llalign_of_min(ccx, llty); + offset = roundup(offset, type_align); + } + // type_align is a power-of-2, so still offset < ccx.max_obj_size() + // llsize_of_alloc(ccx, llty) is also less than ccx.max_obj_size() + // so the sum is less than 1<<62 (and therefore can't overflow). + offset += machine::llsize_of_alloc(ccx, llty); + + if offset >= ccx.max_obj_size() { + ccx.report_overbig_object(scapegoat); + } + } +} + +fn union_size_and_align(sts: &[Struct]) -> (machine::llsize, machine::llalign) { + let size = sts.iter().map(|st| st.size).max().unwrap(); + let most_aligned = sts.iter().max_by(|st| st.align).unwrap(); + (size, most_aligned.align) +} + +fn ensure_enum_fits_in_address_space(ccx: &CrateContext, + discr: IntType, + fields: &[Struct], + scapegoat: ty::t) { + let discr_size = machine::llsize_of_alloc(ccx, ll_inttype(ccx, discr)); + let (field_size, field_align) = union_size_and_align(fields); + + // field_align < 1<<32, discr_size <= 8, field_size < MAX_OBJ_SIZE <= 1<<61 + // so the sum is less than 1<<62 (and can't overflow). + let total_size = roundup(discr_size, field_align) + field_size; + + if total_size >= ccx.max_obj_size() { + ccx.report_overbig_object(scapegoat); + } +} + + +/** + * LLVM-level types are a little complicated. + * + * C-like enums need to be actual ints, not wrapped in a struct, + * because that changes the ABI on some platforms (see issue #10308). + * + * For nominal types, in some cases, we need to use LLVM named structs + * and fill in the actual contents in a second pass to prevent + * unbounded recursion; see also the comments in `trans::type_of`. + */ +pub fn type_of(cx: &CrateContext, r: &Repr) -> Type { + generic_type_of(cx, r, None, false, false) +} +// Pass dst=true if the type you are passing is a DST. Yes, we could figure +// this out, but if you call this on an unsized type without realising it, you +// are going to get the wrong type (it will not include the unsized parts of it). +pub fn sizing_type_of(cx: &CrateContext, r: &Repr, dst: bool) -> Type { + generic_type_of(cx, r, None, true, dst) +} +pub fn incomplete_type_of(cx: &CrateContext, r: &Repr, name: &str) -> Type { + generic_type_of(cx, r, Some(name), false, false) +} +pub fn finish_type_of(cx: &CrateContext, r: &Repr, llty: &mut Type) { + match *r { + CEnum(..) | General(..) | RawNullablePointer { .. } => { } + Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => + llty.set_struct_body(struct_llfields(cx, st, false, false).as_slice(), + st.packed) + } +} + +fn generic_type_of(cx: &CrateContext, + r: &Repr, + name: Option<&str>, + sizing: bool, + dst: bool) -> Type { + match *r { + CEnum(ity, _, _) => ll_inttype(cx, ity), + RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty), + Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => { + match name { + None => { + Type::struct_(cx, struct_llfields(cx, st, sizing, dst).as_slice(), + st.packed) + } + Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) } + } + } + General(ity, ref sts, _) => { + // We need a representation that has: + // * The alignment of the most-aligned field + // * The size of the largest variant (rounded up to that alignment) + // * No alignment padding anywhere any variant has actual data + // (currently matters only for enums small enough to be immediate) + // * The discriminant in an obvious place. + // + // So we start with the discriminant, pad it up to the alignment with + // more of its own type, then use alignment-sized ints to get the rest + // of the size. + // + // FIXME #10604: this breaks when vector types are present. + let (size, align) = union_size_and_align(sts.as_slice()); + let align_s = align as u64; + let discr_ty = ll_inttype(cx, ity); + let discr_size = machine::llsize_of_alloc(cx, discr_ty); + let align_units = (size + align_s - 1) / align_s - 1; + let pad_ty = match align_s { + 1 => Type::array(&Type::i8(cx), align_units), + 2 => Type::array(&Type::i16(cx), align_units), + 4 => Type::array(&Type::i32(cx), align_units), + 8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 => + Type::array(&Type::i64(cx), align_units), + a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4), + align_units), + _ => panic!("unsupported enum alignment: {}", align) + }; + assert_eq!(machine::llalign_of_min(cx, pad_ty), align); + assert_eq!(align_s % discr_size, 0); + let fields = vec!(discr_ty, + Type::array(&discr_ty, align_s / discr_size - 1), + pad_ty); + match name { + None => Type::struct_(cx, fields.as_slice(), false), + Some(name) => { + let mut llty = Type::named_struct(cx, name); + llty.set_struct_body(fields.as_slice(), false); + llty + } + } + } + } +} + +fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool, dst: bool) -> Vec { + if sizing { + st.fields.iter().filter(|&ty| !dst || ty::type_is_sized(cx.tcx(), *ty)) + .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() + } else { + st.fields.iter().map(|&ty| type_of::type_of(cx, ty)).collect() + } +} + +/** + * Obtain a representation of the discriminant sufficient to translate + * destructuring; this may or may not involve the actual discriminant. + * + * This should ideally be less tightly tied to `_match`. + */ +pub fn trans_switch(bcx: Block, r: &Repr, scrutinee: ValueRef) + -> (_match::BranchKind, Option) { + match *r { + CEnum(..) | General(..) | + RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { + (_match::Switch, Some(trans_get_discr(bcx, r, scrutinee, None))) + } + Univariant(..) => { + (_match::Single, None) + } + } +} + + + +/// Obtain the actual discriminant of a value. +pub fn trans_get_discr(bcx: Block, r: &Repr, scrutinee: ValueRef, cast_to: Option) + -> ValueRef { + let signed; + let val; + debug!("trans_get_discr r: {}", r); + match *r { + CEnum(ity, min, max) => { + val = load_discr(bcx, ity, scrutinee, min, max); + signed = ity.is_signed(); + } + General(ity, ref cases, _) => { + let ptr = GEPi(bcx, scrutinee, &[0, 0]); + val = load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr); + signed = ity.is_signed(); + } + Univariant(..) => { + val = C_u8(bcx.ccx(), 0); + signed = false; + } + RawNullablePointer { nndiscr, nnty, .. } => { + let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; + let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); + val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty)); + signed = false; + } + StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { + val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, ptrfield, scrutinee); + signed = false; + } + } + match cast_to { + None => val, + Some(llty) => if signed { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) } + } +} + +fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, ptrfield: PointerField, + scrutinee: ValueRef) -> ValueRef { + let llptrptr = match ptrfield { + ThinPointer(field) => GEPi(bcx, scrutinee, &[0, field]), + FatPointer(field) => GEPi(bcx, scrutinee, &[0, field, slice_elt_base]) + }; + let llptr = Load(bcx, llptrptr); + let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; + ICmp(bcx, cmp, llptr, C_null(val_ty(llptr))) +} + +/// Helper for cases where the discriminant is simply loaded. +fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr) + -> ValueRef { + let llty = ll_inttype(bcx.ccx(), ity); + assert_eq!(val_ty(ptr), llty.ptr_to()); + let bits = machine::llbitsize_of_real(bcx.ccx(), llty); + assert!(bits <= 64); + let bits = bits as uint; + let mask = (-1u64 >> (64 - bits)) as Disr; + if (max + 1) & mask == min & mask { + // i.e., if the range is everything. The lo==hi case would be + // rejected by the LLVM verifier (it would mean either an + // empty set, which is impossible, or the entire range of the + // type, which is pointless). + Load(bcx, ptr) + } else { + // llvm::ConstantRange can deal with ranges that wrap around, + // so an overflow on (max + 1) is fine. + LoadRangeAssert(bcx, ptr, min, (max+1), /* signed: */ True) + } +} + +/** + * Yield information about how to dispatch a case of the + * discriminant-like value returned by `trans_switch`. + * + * This should ideally be less tightly tied to `_match`. + */ +pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) + -> _match::OptResult<'blk, 'tcx> { + match *r { + CEnum(ity, _, _) => { + _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), + discr as u64, true))) + } + General(ity, _, _) => { + _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), + discr as u64, true))) + } + Univariant(..) => { + bcx.ccx().sess().bug("no cases for univariants or structs") + } + RawNullablePointer { .. } | + StructWrappedNullablePointer { .. } => { + assert!(discr == 0 || discr == 1); + _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) + } + } +} + +/** + * Set the discriminant for a new value of the given case of the given + * representation. + */ +pub fn trans_set_discr(bcx: Block, r: &Repr, val: ValueRef, discr: Disr) { + match *r { + CEnum(ity, min, max) => { + assert_discr_in_range(ity, min, max, discr); + Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true), + val) + } + General(ity, ref cases, dtor) => { + if dtor { + let ptr = trans_field_ptr(bcx, r, val, discr, + cases[discr as uint].fields.len() - 2); + Store(bcx, C_u8(bcx.ccx(), 1), ptr); + } + Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true), + GEPi(bcx, val, &[0, 0])) + } + Univariant(ref st, dtor) => { + assert_eq!(discr, 0); + if dtor { + Store(bcx, C_u8(bcx.ccx(), 1), + GEPi(bcx, val, &[0, st.fields.len() - 1])); + } + } + RawNullablePointer { nndiscr, nnty, ..} => { + if discr != nndiscr { + let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); + Store(bcx, C_null(llptrty), val) + } + } + StructWrappedNullablePointer { ref nonnull, nndiscr, ptrfield, .. } => { + if discr != nndiscr { + let (llptrptr, llptrty) = match ptrfield { + ThinPointer(field) => + (GEPi(bcx, val, &[0, field]), + type_of::type_of(bcx.ccx(), nonnull.fields[field])), + FatPointer(field) => { + let v = GEPi(bcx, val, &[0, field, slice_elt_base]); + (v, val_ty(v).element_type()) + } + }; + Store(bcx, C_null(llptrty), llptrptr) + } + } + } +} + +fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) { + match ity { + attr::UnsignedInt(_) => assert!(min <= discr && discr <= max), + attr::SignedInt(_) => assert!(min as i64 <= discr as i64 && discr as i64 <= max as i64) + } +} + +/** + * The number of fields in a given case; for use when obtaining this + * information from the type or definition is less convenient. + */ +pub fn num_args(r: &Repr, discr: Disr) -> uint { + match *r { + CEnum(..) => 0, + Univariant(ref st, dtor) => { + assert_eq!(discr, 0); + st.fields.len() - (if dtor { 1 } else { 0 }) + } + General(_, ref cases, dtor) => { + cases[discr as uint].fields.len() - 1 - (if dtor { 1 } else { 0 }) + } + RawNullablePointer { nndiscr, ref nullfields, .. } => { + if discr == nndiscr { 1 } else { nullfields.len() } + } + StructWrappedNullablePointer { ref nonnull, nndiscr, + ref nullfields, .. } => { + if discr == nndiscr { nonnull.fields.len() } else { nullfields.len() } + } + } +} + +/// Access a field, at a point when the value's case is known. +pub fn trans_field_ptr(bcx: Block, r: &Repr, val: ValueRef, discr: Disr, + ix: uint) -> ValueRef { + // Note: if this ever needs to generate conditionals (e.g., if we + // decide to do some kind of cdr-coding-like non-unique repr + // someday), it will need to return a possibly-new bcx as well. + match *r { + CEnum(..) => { + bcx.ccx().sess().bug("element access in C-like enum") + } + Univariant(ref st, _dtor) => { + assert_eq!(discr, 0); + struct_field_ptr(bcx, st, val, ix, false) + } + General(_, ref cases, _) => { + struct_field_ptr(bcx, &cases[discr as uint], val, ix + 1, true) + } + RawNullablePointer { nndiscr, ref nullfields, .. } | + StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => { + // The unit-like case might have a nonzero number of unit-like fields. + // (e.d., Result of Either with (), as one side.) + let ty = type_of::type_of(bcx.ccx(), nullfields[ix]); + assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0); + // The contents of memory at this pointer can't matter, but use + // the value that's "reasonable" in case of pointer comparison. + PointerCast(bcx, val, ty.ptr_to()) + } + RawNullablePointer { nndiscr, nnty, .. } => { + assert_eq!(ix, 0); + assert_eq!(discr, nndiscr); + let ty = type_of::type_of(bcx.ccx(), nnty); + PointerCast(bcx, val, ty.ptr_to()) + } + StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { + assert_eq!(discr, nndiscr); + struct_field_ptr(bcx, nonnull, val, ix, false) + } + } +} + +pub fn struct_field_ptr(bcx: Block, st: &Struct, val: ValueRef, + ix: uint, needs_cast: bool) -> ValueRef { + let val = if needs_cast { + let ccx = bcx.ccx(); + let fields = st.fields.iter().map(|&ty| type_of::type_of(ccx, ty)).collect::>(); + let real_ty = Type::struct_(ccx, fields.as_slice(), st.packed); + PointerCast(bcx, val, real_ty.ptr_to()) + } else { + val + }; + + GEPi(bcx, val, &[0, ix]) +} + +pub fn fold_variants<'blk, 'tcx>( + bcx: Block<'blk, 'tcx>, r: &Repr, value: ValueRef, + f: |Block<'blk, 'tcx>, &Struct, ValueRef| -> Block<'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let fcx = bcx.fcx; + match *r { + Univariant(ref st, _) => { + f(bcx, st, value) + } + General(ity, ref cases, _) => { + let ccx = bcx.ccx(); + let unr_cx = fcx.new_temp_block("enum-variant-iter-unr"); + Unreachable(unr_cx); + + let discr_val = trans_get_discr(bcx, r, value, None); + let llswitch = Switch(bcx, discr_val, unr_cx.llbb, cases.len()); + let bcx_next = fcx.new_temp_block("enum-variant-iter-next"); + + for (discr, case) in cases.iter().enumerate() { + let mut variant_cx = fcx.new_temp_block( + format!("enum-variant-iter-{}", discr.to_string()).as_slice() + ); + let rhs_val = C_integral(ll_inttype(ccx, ity), discr as u64, true); + AddCase(llswitch, rhs_val, variant_cx.llbb); + + let fields = case.fields.iter().map(|&ty| + type_of::type_of(bcx.ccx(), ty)).collect::>(); + let real_ty = Type::struct_(ccx, fields.as_slice(), case.packed); + let variant_value = PointerCast(variant_cx, value, real_ty.ptr_to()); + + variant_cx = f(variant_cx, case, variant_value); + Br(variant_cx, bcx_next.llbb); + } + + bcx_next + } + _ => unreachable!() + } +} + +/// Access the struct drop flag, if present. +pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, r: &Repr, val: ValueRef) + -> datum::DatumBlock<'blk, 'tcx, datum::Expr> { + let ptr_ty = ty::mk_imm_ptr(bcx.tcx(), ty::mk_bool()); + match *r { + Univariant(ref st, true) => { + let flag_ptr = GEPi(bcx, val, &[0, st.fields.len() - 1]); + datum::immediate_rvalue_bcx(bcx, flag_ptr, ptr_ty).to_expr_datumblock() + } + General(_, _, true) => { + let fcx = bcx.fcx; + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum( + bcx, ty::mk_bool(), "drop_flag", false, + cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx + )); + bcx = fold_variants(bcx, r, val, |variant_cx, st, value| { + let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false); + datum::Datum::new(ptr, ptr_ty, datum::Rvalue::new(datum::ByRef)) + .store_to(variant_cx, scratch.val) + }); + let expr_datum = scratch.to_expr_datum(); + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + datum::DatumBlock::new(bcx, expr_datum) + } + _ => bcx.ccx().sess().bug("tried to get drop flag of non-droppable type") + } +} + +/** + * Construct a constant value, suitable for initializing a + * GlobalVariable, given a case and constant values for its fields. + * Note that this may have a different LLVM type (and different + * alignment!) from the representation's `type_of`, so it needs a + * pointer cast before use. + * + * The LLVM type system does not directly support unions, and only + * pointers can be bitcast, so a constant (and, by extension, the + * GlobalVariable initialized by it) will have a type that can vary + * depending on which case of an enum it is. + * + * To understand the alignment situation, consider `enum E { V64(u64), + * V32(u32, u32) }` on Windows. The type has 8-byte alignment to + * accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, + * i32, i32}`, which is 4-byte aligned. + * + * Currently the returned value has the same size as the type, but + * this could be changed in the future to avoid allocating unnecessary + * space after values of shorter-than-maximum cases. + */ +pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr, + vals: &[ValueRef]) -> ValueRef { + match *r { + CEnum(ity, min, max) => { + assert_eq!(vals.len(), 0); + assert_discr_in_range(ity, min, max, discr); + C_integral(ll_inttype(ccx, ity), discr as u64, true) + } + General(ity, ref cases, _) => { + let case = &cases[discr as uint]; + let max_sz = cases.iter().map(|x| x.size).max().unwrap(); + let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true); + let mut f = vec![lldiscr]; + f.push_all(vals); + let mut contents = build_const_struct(ccx, case, f.as_slice()); + contents.push_all(&[padding(ccx, max_sz - case.size)]); + C_struct(ccx, contents.as_slice(), false) + } + Univariant(ref st, _dro) => { + assert!(discr == 0); + let contents = build_const_struct(ccx, st, vals); + C_struct(ccx, contents.as_slice(), st.packed) + } + RawNullablePointer { nndiscr, nnty, .. } => { + if discr == nndiscr { + assert_eq!(vals.len(), 1); + vals[0] + } else { + C_null(type_of::sizing_type_of(ccx, nnty)) + } + } + StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { + if discr == nndiscr { + C_struct(ccx, build_const_struct(ccx, + nonnull, + vals).as_slice(), + false) + } else { + let vals = nonnull.fields.iter().map(|&ty| { + // Always use null even if it's not the `ptrfield`th + // field; see #8506. + C_null(type_of::sizing_type_of(ccx, ty)) + }).collect::>(); + C_struct(ccx, build_const_struct(ccx, + nonnull, + vals.as_slice()).as_slice(), + false) + } + } + } +} + +/** + * Compute struct field offsets relative to struct begin. + */ +fn compute_struct_field_offsets(ccx: &CrateContext, st: &Struct) -> Vec { + let mut offsets = vec!(); + + let mut offset = 0; + for &ty in st.fields.iter() { + let llty = type_of::sizing_type_of(ccx, ty); + if !st.packed { + let type_align = type_of::align_of(ccx, ty); + offset = roundup(offset, type_align); + } + offsets.push(offset); + offset += machine::llsize_of_alloc(ccx, llty); + } + assert_eq!(st.fields.len(), offsets.len()); + offsets +} + +/** + * Building structs is a little complicated, because we might need to + * insert padding if a field's value is less aligned than its type. + * + * Continuing the example from `trans_const`, a value of type `(u32, + * E)` should have the `E` at offset 8, but if that field's + * initializer is 4-byte aligned then simply translating the tuple as + * a two-element struct will locate it at offset 4, and accesses to it + * will read the wrong memory. + */ +fn build_const_struct(ccx: &CrateContext, st: &Struct, vals: &[ValueRef]) + -> Vec { + assert_eq!(vals.len(), st.fields.len()); + + let target_offsets = compute_struct_field_offsets(ccx, st); + + // offset of current value + let mut offset = 0; + let mut cfields = Vec::new(); + for (&val, &target_offset) in vals.iter().zip(target_offsets.iter()) { + if !st.packed { + let val_align = machine::llalign_of_min(ccx, val_ty(val)); + offset = roundup(offset, val_align); + } + if offset != target_offset { + cfields.push(padding(ccx, target_offset - offset)); + offset = target_offset; + } + assert!(!is_undef(val)); + cfields.push(val); + offset += machine::llsize_of_alloc(ccx, val_ty(val)); + } + + assert!(st.sized && offset <= st.size); + if offset != st.size { + cfields.push(padding(ccx, st.size - offset)); + } + + cfields +} + +fn padding(ccx: &CrateContext, size: u64) -> ValueRef { + C_undef(Type::array(&Type::i8(ccx), size)) +} + +// FIXME this utility routine should be somewhere more general +#[inline] +fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a } + +/// Get the discriminant of a constant value. (Not currently used.) +pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) + -> Disr { + match *r { + CEnum(ity, _, _) => { + match ity { + attr::SignedInt(..) => const_to_int(val) as Disr, + attr::UnsignedInt(..) => const_to_uint(val) as Disr + } + } + General(ity, _, _) => { + match ity { + attr::SignedInt(..) => const_to_int(const_get_elt(ccx, val, &[0])) as Disr, + attr::UnsignedInt(..) => const_to_uint(const_get_elt(ccx, val, &[0])) as Disr + } + } + Univariant(..) => 0, + RawNullablePointer { nndiscr, .. } => { + if is_null(val) { + /* subtraction as uint is ok because nndiscr is either 0 or 1 */ + (1 - nndiscr) as Disr + } else { + nndiscr + } + } + StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { + let (idx, sub_idx) = match ptrfield { + ThinPointer(field) => (field, None), + FatPointer(field) => (field, Some(slice_elt_base)) + }; + if is_null(const_struct_field(ccx, val, idx, sub_idx)) { + /* subtraction as uint is ok because nndiscr is either 0 or 1 */ + (1 - nndiscr) as Disr + } else { + nndiscr + } + } + } +} + +/** + * Extract a field of a constant value, as appropriate for its + * representation. + * + * (Not to be confused with `common::const_get_elt`, which operates on + * raw LLVM-level structs and arrays.) + */ +pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef, + _discr: Disr, ix: uint) -> ValueRef { + match *r { + CEnum(..) => ccx.sess().bug("element access in C-like enum const"), + Univariant(..) => const_struct_field(ccx, val, ix, None), + General(..) => const_struct_field(ccx, val, ix + 1, None), + RawNullablePointer { .. } => { + assert_eq!(ix, 0); + val + } + StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix, None) + } +} + +/// Extract field of struct-like const, skipping our alignment padding. +fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint, sub_idx: Option) + -> ValueRef { + // Get the ix-th non-undef element of the struct. + let mut real_ix = 0; // actual position in the struct + let mut ix = ix; // logical index relative to real_ix + let mut field; + loop { + loop { + field = match sub_idx { + Some(si) => const_get_elt(ccx, val, &[real_ix, si as u32]), + None => const_get_elt(ccx, val, &[real_ix]) + }; + if !is_undef(field) { + break; + } + real_ix = real_ix + 1; + } + if ix == 0 { + return field; + } + ix = ix - 1; + real_ix = real_ix + 1; + } +} diff --git a/src/librustc_trans/trans/asm.rs b/src/librustc_trans/trans/asm.rs new file mode 100644 index 00000000000..9b499b6d1a1 --- /dev/null +++ b/src/librustc_trans/trans/asm.rs @@ -0,0 +1,176 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! +# Translation of inline assembly. +*/ + +use llvm; +use trans::build::*; +use trans::callee; +use trans::common::*; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::expr; +use trans::type_of; +use trans::type_::Type; + +use std::c_str::ToCStr; +use std::string::String; +use syntax::ast; +use libc::{c_uint, c_char}; + +// Take an inline assembly expression and splat it out via LLVM +pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm) + -> Block<'blk, 'tcx> { + let fcx = bcx.fcx; + let mut bcx = bcx; + let mut constraints = Vec::new(); + let mut output_types = Vec::new(); + + let temp_scope = fcx.push_custom_cleanup_scope(); + + let mut ext_inputs = Vec::new(); + let mut ext_constraints = Vec::new(); + + // Prepare the output operands + let outputs = ia.outputs.iter().enumerate().map(|(i, &(ref c, ref out, is_rw))| { + constraints.push((*c).clone()); + + let out_datum = unpack_datum!(bcx, expr::trans(bcx, &**out)); + output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty)); + let val = out_datum.val; + if is_rw { + ext_inputs.push(unpack_result!(bcx, { + callee::trans_arg_datum(bcx, + expr_ty(bcx, &**out), + out_datum, + cleanup::CustomScope(temp_scope), + callee::DontAutorefArg) + })); + ext_constraints.push(i.to_string()); + } + val + + }).collect::>(); + + // Now the input operands + let mut inputs = ia.inputs.iter().map(|&(ref c, ref input)| { + constraints.push((*c).clone()); + + let in_datum = unpack_datum!(bcx, expr::trans(bcx, &**input)); + unpack_result!(bcx, { + callee::trans_arg_datum(bcx, + expr_ty(bcx, &**input), + in_datum, + cleanup::CustomScope(temp_scope), + callee::DontAutorefArg) + }) + }).collect::>(); + inputs.push_all(ext_inputs.as_slice()); + + // no failure occurred preparing operands, no need to cleanup + fcx.pop_custom_cleanup_scope(temp_scope); + + let mut constraints = + String::from_str(constraints.iter() + .map(|s| s.get().to_string()) + .chain(ext_constraints.into_iter()) + .collect::>() + .connect(",") + .as_slice()); + + let mut clobbers = get_clobbers(); + if !ia.clobbers.get().is_empty() && !clobbers.is_empty() { + clobbers = format!("{},{}", ia.clobbers.get(), clobbers); + } else { + clobbers.push_str(ia.clobbers.get()); + } + + // Add the clobbers to our constraints list + if clobbers.len() != 0 && constraints.len() != 0 { + constraints.push(','); + constraints.push_str(clobbers.as_slice()); + } else { + constraints.push_str(clobbers.as_slice()); + } + + debug!("Asm Constraints: {}", constraints.as_slice()); + + let num_outputs = outputs.len(); + + // Depending on how many outputs we have, the return type is different + let output_type = if num_outputs == 0 { + Type::void(bcx.ccx()) + } else if num_outputs == 1 { + output_types[0] + } else { + Type::struct_(bcx.ccx(), output_types.as_slice(), false) + }; + + let dialect = match ia.dialect { + ast::AsmAtt => llvm::AD_ATT, + ast::AsmIntel => llvm::AD_Intel + }; + + let r = ia.asm.get().with_c_str(|a| { + constraints.as_slice().with_c_str(|c| { + InlineAsmCall(bcx, + a, + c, + inputs.as_slice(), + output_type, + ia.volatile, + ia.alignstack, + dialect) + }) + }); + + // Again, based on how many outputs we have + if num_outputs == 1 { + Store(bcx, r, outputs[0]); + } else { + for (i, o) in outputs.iter().enumerate() { + let v = ExtractValue(bcx, r, i); + Store(bcx, v, *o); + } + } + + // Store expn_id in a metadata node so we can map LLVM errors + // back to source locations. See #17552. + unsafe { + let key = "srcloc"; + let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx().llcx(), + key.as_ptr() as *const c_char, key.len() as c_uint); + + let val: llvm::ValueRef = C_i32(bcx.ccx(), ia.expn_id.to_llvm_cookie()); + + llvm::LLVMSetMetadata(r, kind, + llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1)); + } + + return bcx; + +} + +// Default per-arch clobbers +// Basically what clang does + +#[cfg(any(target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] +fn get_clobbers() -> String { + "".to_string() +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn get_clobbers() -> String { + "~{dirflag},~{fpsr},~{flags}".to_string() +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs new file mode 100644 index 00000000000..39474a99f6d --- /dev/null +++ b/src/librustc_trans/trans/base.rs @@ -0,0 +1,3195 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// trans.rs: Translate the completed AST to the LLVM IR. +// +// Some functions here, such as trans_block and trans_expr, return a value -- +// the result of the translation to LLVM -- while others, such as trans_fn, +// trans_impl, and trans_item, are called only for the side effect of adding a +// particular definition to the LLVM IR output we're producing. +// +// Hopefully useful general knowledge about trans: +// +// * There's no way to find out the ty::t type of a ValueRef. Doing so +// would be "trying to get the eggs out of an omelette" (credit: +// pcwalton). You can, instead, find out its TypeRef by calling val_ty, +// but one TypeRef corresponds to many `ty::t`s; for instance, tup(int, int, +// int) and rec(x=int, y=int, z=int) will have the same TypeRef. + +#![allow(non_camel_case_types)] + +pub use self::IsUnboxedClosureFlag::*; +pub use self::ValueOrigin::*; +pub use self::scalar_type::*; + +use back::link::{mangle_exported_name}; +use back::{link, abi}; +use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation}; +use lint; +use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param}; +use llvm; +use metadata::{csearch, encoder, loader}; +use middle::astencode; +use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; +use middle::subst; +use middle::weak_lang_items; +use middle::subst::Subst; +use middle::ty; +use session::config::{mod, NoDebugInfo, FullDebugInfo}; +use session::Session; +use trans::_match; +use trans::adt; +use trans::build::*; +use trans::builder::{Builder, noname}; +use trans::callee; +use trans::cleanup::{CleanupMethods, ScopeId}; +use trans::cleanup; +use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral}; +use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_uint, C_undef}; +use trans::common::{CrateContext, ExternMap, FunctionContext}; +use trans::common::{NodeInfo, Result, SubstP}; +use trans::common::{node_id_type, param_substs, return_type_is_void}; +use trans::common::{tydesc_info, type_is_immediate}; +use trans::common::{type_is_zero_size, val_ty}; +use trans::common; +use trans::consts; +use trans::context::SharedCrateContext; +use trans::controlflow; +use trans::datum; +use trans::debuginfo; +use trans::expr; +use trans::foreign; +use trans::glue; +use trans::inline; +use trans::intrinsic; +use trans::machine; +use trans::machine::{llsize_of, llsize_of_real, llalign_of_min}; +use trans::meth; +use trans::monomorphize; +use trans::tvec; +use trans::type_::Type; +use trans::type_of; +use trans::type_of::*; +use trans::value::Value; +use util::common::indenter; +use util::ppaux::{Repr, ty_to_string}; +use util::sha2::Sha256; +use util::nodemap::NodeMap; + +use arena::TypedArena; +use libc::{c_uint, uint64_t}; +use std::c_str::ToCStr; +use std::cell::{Cell, RefCell}; +use std::collections::HashSet; +use std::rc::Rc; +use std::{i8, i16, i32, i64}; +use syntax::abi::{Rust, RustCall, RustIntrinsic, Abi}; +use syntax::ast_util::local_def; +use syntax::attr::AttrMetaMethods; +use syntax::attr; +use syntax::codemap::Span; +use syntax::parse::token::InternedString; +use syntax::visit::Visitor; +use syntax::visit; +use syntax::{ast, ast_util, ast_map}; + +local_data_key!(task_local_insn_key: RefCell>) + +pub fn with_insn_ctxt(blk: |&[&'static str]|) { + match task_local_insn_key.get() { + Some(ctx) => blk(ctx.borrow().as_slice()), + None => () + } +} + +pub fn init_insn_ctxt() { + task_local_insn_key.replace(Some(RefCell::new(Vec::new()))); +} + +pub struct _InsnCtxt { + _cannot_construct_outside_of_this_module: () +} + +#[unsafe_destructor] +impl Drop for _InsnCtxt { + fn drop(&mut self) { + match task_local_insn_key.get() { + Some(ctx) => { ctx.borrow_mut().pop(); } + None => {} + } + } +} + +pub fn push_ctxt(s: &'static str) -> _InsnCtxt { + debug!("new InsnCtxt: {}", s); + match task_local_insn_key.get() { + Some(ctx) => ctx.borrow_mut().push(s), + None => {} + } + _InsnCtxt { _cannot_construct_outside_of_this_module: () } +} + +pub struct StatRecorder<'a, 'tcx: 'a> { + ccx: &'a CrateContext<'a, 'tcx>, + name: Option, + istart: uint, +} + +impl<'a, 'tcx> StatRecorder<'a, 'tcx> { + pub fn new(ccx: &'a CrateContext<'a, 'tcx>, name: String) + -> StatRecorder<'a, 'tcx> { + let istart = ccx.stats().n_llvm_insns.get(); + StatRecorder { + ccx: ccx, + name: Some(name), + istart: istart, + } + } +} + +#[unsafe_destructor] +impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { + fn drop(&mut self) { + if self.ccx.sess().trans_stats() { + let iend = self.ccx.stats().n_llvm_insns.get(); + self.ccx.stats().fn_stats.borrow_mut().push((self.name.take().unwrap(), + iend - self.istart)); + self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1); + // Reset LLVM insn count to avoid compound costs. + self.ccx.stats().n_llvm_insns.set(self.istart); + } + } +} + +// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions +pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, + ty: Type, output: ty::FnOutput) -> ValueRef { + + let llfn: ValueRef = name.with_c_str(|buf| { + unsafe { + llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf, ty.to_ref()) + } + }); + + // diverging functions may unwind, but can never return normally + if output == ty::FnDiverging { + llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute); + } + + if ccx.tcx().sess.opts.cg.no_redzone + .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) { + llvm::SetFunctionAttribute(llfn, llvm::NoRedZoneAttribute) + } + + llvm::SetFunctionCallConv(llfn, cc); + // Function addresses in Rust are never significant, allowing functions to be merged. + llvm::SetUnnamedAddr(llfn, true); + + if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check { + set_split_stack(llfn); + } + + llfn +} + +// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions +pub fn decl_cdecl_fn(ccx: &CrateContext, + name: &str, + ty: Type, + output: ty::t) -> ValueRef { + decl_fn(ccx, name, llvm::CCallConv, ty, ty::FnConverging(output)) +} + +// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions +pub fn get_extern_fn(ccx: &CrateContext, + externs: &mut ExternMap, + name: &str, + cc: llvm::CallConv, + ty: Type, + output: ty::t) + -> ValueRef { + match externs.get(name) { + Some(n) => return *n, + None => {} + } + let f = decl_fn(ccx, name, cc, ty, ty::FnConverging(output)); + externs.insert(name.to_string(), f); + f +} + +fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef { + match ccx.externs().borrow().get(name) { + Some(n) => return *n, + None => () + } + + let f = decl_rust_fn(ccx, fn_ty, name); + + csearch::get_item_attrs(&ccx.sess().cstore, did, |attrs| { + set_llvm_fn_attrs(ccx, attrs.as_slice(), f) + }); + + ccx.externs().borrow_mut().insert(name.to_string(), f); + f +} + +pub fn self_type_for_unboxed_closure(ccx: &CrateContext, + closure_id: ast::DefId, + fn_ty: ty::t) + -> ty::t { + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); + let unboxed_closure = &(*unboxed_closures)[closure_id]; + match unboxed_closure.kind { + ty::FnUnboxedClosureKind => { + ty::mk_imm_rptr(ccx.tcx(), ty::ReStatic, fn_ty) + } + ty::FnMutUnboxedClosureKind => { + ty::mk_mut_rptr(ccx.tcx(), ty::ReStatic, fn_ty) + } + ty::FnOnceUnboxedClosureKind => fn_ty + } +} + +pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) + -> ty::UnboxedClosureKind { + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); + (*unboxed_closures)[closure_id].kind +} + +pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { + let (inputs, output, abi, env) = match ty::get(fn_ty).sty { + ty::ty_bare_fn(ref f) => { + (f.sig.inputs.clone(), f.sig.output, f.abi, None) + } + ty::ty_closure(ref f) => { + (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx))) + } + ty::ty_unboxed_closure(closure_did, _, ref substs) => { + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); + let unboxed_closure = &(*unboxed_closures)[closure_did]; + let function_type = unboxed_closure.closure_type.clone(); + let self_type = self_type_for_unboxed_closure(ccx, closure_did, fn_ty); + let llenvironment_type = type_of_explicit_arg(ccx, self_type); + (function_type.sig.inputs.iter().map(|t| t.subst(ccx.tcx(), substs)).collect(), + function_type.sig.output.subst(ccx.tcx(), substs), + RustCall, + Some(llenvironment_type)) + } + _ => panic!("expected closure or fn") + }; + + let llfty = type_of_rust_fn(ccx, env, inputs.as_slice(), output, abi); + debug!("decl_rust_fn(input count={},type={})", + inputs.len(), + ccx.tn().type_to_string(llfty)); + + let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output); + let attrs = get_fn_llvm_attributes(ccx, fn_ty); + attrs.apply_llfn(llfn); + + llfn +} + +pub fn decl_internal_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { + let llfn = decl_rust_fn(ccx, fn_ty, name); + llvm::SetLinkage(llfn, llvm::InternalLinkage); + llfn +} + +pub fn get_extern_const(ccx: &CrateContext, did: ast::DefId, + t: ty::t) -> ValueRef { + let name = csearch::get_symbol(&ccx.sess().cstore, did); + let ty = type_of(ccx, t); + match ccx.externs().borrow_mut().get(&name) { + Some(n) => return *n, + None => () + } + unsafe { + let c = name.with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), ty.to_ref(), buf) + }); + // Thread-local statics in some other crate need to *always* be linked + // against in a thread-local fashion, so we need to be sure to apply the + // thread-local attribute locally if it was present remotely. If we + // don't do this then linker errors can be generated where the linker + // complains that one object files has a thread local version of the + // symbol and another one doesn't. + ty::each_attr(ccx.tcx(), did, |attr| { + if attr.check_name("thread_local") { + llvm::set_thread_local(c, true); + } + true + }); + ccx.externs().borrow_mut().insert(name.to_string(), c); + return c; + } +} + +// Returns a pointer to the body for the box. The box may be an opaque +// box. The result will be casted to the type of body_t, if it is statically +// known. +pub fn at_box_body(bcx: Block, body_t: ty::t, boxptr: ValueRef) -> ValueRef { + let _icx = push_ctxt("at_box_body"); + let ccx = bcx.ccx(); + let ty = Type::at_box(ccx, type_of(ccx, body_t)); + let boxptr = PointerCast(bcx, boxptr, ty.ptr_to()); + GEPi(bcx, boxptr, &[0u, abi::box_field_body]) +} + +fn require_alloc_fn(bcx: Block, info_ty: ty::t, it: LangItem) -> ast::DefId { + match bcx.tcx().lang_items.require(it) { + Ok(id) => id, + Err(s) => { + bcx.sess().fatal(format!("allocation of `{}` {}", + bcx.ty_to_string(info_ty), + s).as_slice()); + } + } +} + +// The following malloc_raw_dyn* functions allocate a box to contain +// a given type, but with a potentially dynamic size. + +pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + llty_ptr: Type, + info_ty: ty::t, + size: ValueRef, + align: ValueRef) + -> Result<'blk, 'tcx> { + let _icx = push_ctxt("malloc_raw_exchange"); + + // Allocate space: + let r = callee::trans_lang_call(bcx, + require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem), + &[size, align], + None); + + Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr)) +} + +pub fn malloc_raw_dyn_proc<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: ty::t) -> Result<'blk, 'tcx> { + let _icx = push_ctxt("malloc_raw_dyn_proc"); + let ccx = bcx.ccx(); + + // Grab the TypeRef type of ptr_ty. + let ptr_ty = ty::mk_uniq(bcx.tcx(), t); + let ptr_llty = type_of(ccx, ptr_ty); + + let llty = type_of(bcx.ccx(), t); + let size = llsize_of(bcx.ccx(), llty); + let llalign = C_uint(ccx, llalign_of_min(bcx.ccx(), llty)); + + // Allocate space and store the destructor pointer: + let Result {bcx, val: llbox} = malloc_raw_dyn(bcx, ptr_llty, t, size, llalign); + let dtor_ptr = GEPi(bcx, llbox, &[0u, abi::box_field_drop_glue]); + let drop_glue_field_ty = type_of(ccx, ty::mk_nil_ptr(bcx.tcx())); + let drop_glue = PointerCast(bcx, glue::get_drop_glue(ccx, ty::mk_uniq(bcx.tcx(), t)), + drop_glue_field_ty); + Store(bcx, drop_glue, dtor_ptr); + + Result::new(bcx, llbox) +} + +// Type descriptor and type glue stuff + +pub fn get_tydesc(ccx: &CrateContext, t: ty::t) -> Rc { + match ccx.tydescs().borrow().get(&t) { + Some(inf) => return inf.clone(), + _ => { } + } + + ccx.stats().n_static_tydescs.set(ccx.stats().n_static_tydescs.get() + 1u); + let inf = Rc::new(glue::declare_tydesc(ccx, t)); + + ccx.tydescs().borrow_mut().insert(t, inf.clone()); + inf +} + +#[allow(dead_code)] // useful +pub fn set_optimize_for_size(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::OptimizeForSizeAttribute) +} + +pub fn set_no_inline(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::NoInlineAttribute) +} + +#[allow(dead_code)] // useful +pub fn set_no_unwind(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::NoUnwindAttribute) +} + +// Tell LLVM to emit the information necessary to unwind the stack for the +// function f. +pub fn set_uwtable(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::UWTableAttribute) +} + +pub fn set_inline_hint(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::InlineHintAttribute) +} + +pub fn set_llvm_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) { + use syntax::attr::*; + // Set the inline hint if there is one + match find_inline_attr(attrs) { + InlineHint => set_inline_hint(llfn), + InlineAlways => set_always_inline(llfn), + InlineNever => set_no_inline(llfn), + InlineNone => { /* fallthrough */ } + } + + for attr in attrs.iter() { + let mut used = true; + match attr.name().get() { + "no_stack_check" => unset_split_stack(llfn), + "no_split_stack" => { + unset_split_stack(llfn); + ccx.sess().span_warn(attr.span, + "no_split_stack is a deprecated synonym for no_stack_check"); + } + "cold" => unsafe { + llvm::LLVMAddFunctionAttribute(llfn, + llvm::FunctionIndex as c_uint, + llvm::ColdAttribute as uint64_t) + }, + _ => used = false, + } + if used { + attr::mark_used(attr); + } + } +} + +pub fn set_always_inline(f: ValueRef) { + llvm::SetFunctionAttribute(f, llvm::AlwaysInlineAttribute) +} + +pub fn set_split_stack(f: ValueRef) { + "split-stack".with_c_str(|buf| { + unsafe { llvm::LLVMAddFunctionAttrString(f, llvm::FunctionIndex as c_uint, buf); } + }) +} + +pub fn unset_split_stack(f: ValueRef) { + "split-stack".with_c_str(|buf| { + unsafe { llvm::LLVMRemoveFunctionAttrString(f, llvm::FunctionIndex as c_uint, buf); } + }) +} + +// Double-check that we never ask LLVM to declare the same symbol twice. It +// silently mangles such symbols, breaking our linkage model. +pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) { + if ccx.all_llvm_symbols().borrow().contains(&sym) { + ccx.sess().bug(format!("duplicate LLVM symbol: {}", sym).as_slice()); + } + ccx.all_llvm_symbols().borrow_mut().insert(sym); +} + + +pub fn get_res_dtor(ccx: &CrateContext, + did: ast::DefId, + t: ty::t, + parent_id: ast::DefId, + substs: &subst::Substs) + -> ValueRef { + let _icx = push_ctxt("trans_res_dtor"); + let did = inline::maybe_instantiate_inline(ccx, did); + + if !substs.types.is_empty() { + assert_eq!(did.krate, ast::LOCAL_CRATE); + + // Since we're in trans we don't care for any region parameters + let ref substs = subst::Substs::erased(substs.types.clone()); + + let (val, _) = monomorphize::monomorphic_fn(ccx, did, substs, None); + + val + } else if did.krate == ast::LOCAL_CRATE { + get_item_val(ccx, did.node) + } else { + let tcx = ccx.tcx(); + let name = csearch::get_symbol(&ccx.sess().cstore, did); + let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs); + let llty = type_of_dtor(ccx, class_ty); + let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID, + &[glue::get_drop_glue_type(ccx, t)], ty::mk_nil(ccx.tcx())); + get_extern_fn(ccx, + &mut *ccx.externs().borrow_mut(), + name.as_slice(), + llvm::CCallConv, + llty, + dtor_ty) + } +} + +// Structural comparison: a rather involved form of glue. +pub fn maybe_name_value(cx: &CrateContext, v: ValueRef, s: &str) { + if cx.sess().opts.cg.save_temps { + s.with_c_str(|buf| { + unsafe { + llvm::LLVMSetValueName(v, buf) + } + }) + } +} + + +// Used only for creating scalar comparison glue. +pub enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } + +pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + t: ty::t, + op: ast::BinOp) + -> Result<'blk, 'tcx> { + let f = |a| Result::new(cx, compare_scalar_values(cx, lhs, rhs, a, op)); + + match ty::get(t).sty { + ty::ty_tup(ref tys) if tys.is_empty() => f(nil_type), + ty::ty_bool | ty::ty_uint(_) | ty::ty_char => f(unsigned_int), + ty::ty_ptr(mt) if ty::type_is_sized(cx.tcx(), mt.ty) => f(unsigned_int), + ty::ty_int(_) => f(signed_int), + ty::ty_float(_) => f(floating_point), + // Should never get here, because t is scalar. + _ => cx.sess().bug("non-scalar type passed to compare_scalar_types") + } +} + + +// A helper function to do the actual comparison of scalar values. +pub fn compare_scalar_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + nt: scalar_type, + op: ast::BinOp) + -> ValueRef { + let _icx = push_ctxt("compare_scalar_values"); + fn die(cx: Block) -> ! { + cx.sess().bug("compare_scalar_values: must be a comparison operator"); + } + match nt { + nil_type => { + // We don't need to do actual comparisons for nil. + // () == () holds but () < () does not. + match op { + ast::BiEq | ast::BiLe | ast::BiGe => return C_bool(cx.ccx(), true), + ast::BiNe | ast::BiLt | ast::BiGt => return C_bool(cx.ccx(), false), + // refinements would be nice + _ => die(cx) + } + } + floating_point => { + let cmp = match op { + ast::BiEq => llvm::RealOEQ, + ast::BiNe => llvm::RealUNE, + ast::BiLt => llvm::RealOLT, + ast::BiLe => llvm::RealOLE, + ast::BiGt => llvm::RealOGT, + ast::BiGe => llvm::RealOGE, + _ => die(cx) + }; + return FCmp(cx, cmp, lhs, rhs); + } + signed_int => { + let cmp = match op { + ast::BiEq => llvm::IntEQ, + ast::BiNe => llvm::IntNE, + ast::BiLt => llvm::IntSLT, + ast::BiLe => llvm::IntSLE, + ast::BiGt => llvm::IntSGT, + ast::BiGe => llvm::IntSGE, + _ => die(cx) + }; + return ICmp(cx, cmp, lhs, rhs); + } + unsigned_int => { + let cmp = match op { + ast::BiEq => llvm::IntEQ, + ast::BiNe => llvm::IntNE, + ast::BiLt => llvm::IntULT, + ast::BiLe => llvm::IntULE, + ast::BiGt => llvm::IntUGT, + ast::BiGe => llvm::IntUGE, + _ => die(cx) + }; + return ICmp(cx, cmp, lhs, rhs); + } + } +} + +pub fn compare_simd_types( + cx: Block, + lhs: ValueRef, + rhs: ValueRef, + t: ty::t, + size: uint, + op: ast::BinOp) + -> ValueRef { + match ty::get(t).sty { + ty::ty_float(_) => { + // The comparison operators for floating point vectors are challenging. + // LLVM outputs a `< size x i1 >`, but if we perform a sign extension + // then bitcast to a floating point vector, the result will be `-NaN` + // for each truth value. Because of this they are unsupported. + cx.sess().bug("compare_simd_types: comparison operators \ + not supported for floating point SIMD types") + }, + ty::ty_uint(_) | ty::ty_int(_) => { + let cmp = match op { + ast::BiEq => llvm::IntEQ, + ast::BiNe => llvm::IntNE, + ast::BiLt => llvm::IntSLT, + ast::BiLe => llvm::IntSLE, + ast::BiGt => llvm::IntSGT, + ast::BiGe => llvm::IntSGE, + _ => cx.sess().bug("compare_simd_types: must be a comparison operator"), + }; + let return_ty = Type::vector(&type_of(cx.ccx(), t), size as u64); + // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension + // to get the correctly sized type. This will compile to a single instruction + // once the IR is converted to assembly if the SIMD instruction is supported + // by the target architecture. + SExt(cx, ICmp(cx, cmp, lhs, rhs), return_ty) + }, + _ => cx.sess().bug("compare_simd_types: invalid SIMD type"), + } +} + +pub type val_and_ty_fn<'a, 'blk, 'tcx> = + |Block<'blk, 'tcx>, ValueRef, ty::t|: 'a -> Block<'blk, 'tcx>; + +// Iterates through the elements of a structural type. +pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, + av: ValueRef, + t: ty::t, + f: val_and_ty_fn<'a, 'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("iter_structural_ty"); + + fn iter_variant<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, + repr: &adt::Repr, + av: ValueRef, + variant: &ty::VariantInfo, + substs: &subst::Substs, + f: val_and_ty_fn<'a, 'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("iter_variant"); + let tcx = cx.tcx(); + let mut cx = cx; + + for (i, &arg) in variant.args.iter().enumerate() { + cx = f(cx, + adt::trans_field_ptr(cx, repr, av, variant.disr_val, i), + arg.subst(tcx, substs)); + } + return cx; + } + + let (data_ptr, info) = if ty::type_is_sized(cx.tcx(), t) { + (av, None) + } else { + let data = GEPi(cx, av, &[0, abi::slice_elt_base]); + let info = GEPi(cx, av, &[0, abi::slice_elt_len]); + (Load(cx, data), Some(Load(cx, info))) + }; + + let mut cx = cx; + match ty::get(t).sty { + ty::ty_struct(..) => { + let repr = adt::represent_type(cx.ccx(), t); + expr::with_field_tys(cx.tcx(), t, None, |discr, field_tys| { + for (i, field_ty) in field_tys.iter().enumerate() { + let field_ty = field_ty.mt.ty; + let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i); + + let val = if ty::type_is_sized(cx.tcx(), field_ty) { + llfld_a + } else { + let boxed_ty = ty::mk_open(cx.tcx(), field_ty); + let scratch = datum::rvalue_scratch_datum(cx, boxed_ty, "__fat_ptr_iter"); + Store(cx, llfld_a, GEPi(cx, scratch.val, &[0, abi::slice_elt_base])); + Store(cx, info.unwrap(), GEPi(cx, scratch.val, &[0, abi::slice_elt_len])); + scratch.val + }; + cx = f(cx, val, field_ty); + } + }) + } + ty::ty_unboxed_closure(def_id, _, ref substs) => { + let repr = adt::represent_type(cx.ccx(), t); + let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs); + for (i, upvar) in upvars.iter().enumerate() { + let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); + cx = f(cx, llupvar, upvar.ty); + } + } + ty::ty_vec(_, Some(n)) => { + let (base, len) = tvec::get_fixed_base_and_len(cx, data_ptr, n); + let unit_ty = ty::sequence_element_type(cx.tcx(), t); + cx = tvec::iter_vec_raw(cx, base, unit_ty, len, f); + } + ty::ty_tup(ref args) => { + let repr = adt::represent_type(cx.ccx(), t); + for (i, arg) in args.iter().enumerate() { + let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); + cx = f(cx, llfld_a, *arg); + } + } + ty::ty_enum(tid, ref substs) => { + let fcx = cx.fcx; + let ccx = fcx.ccx; + + let repr = adt::represent_type(ccx, t); + let variants = ty::enum_variants(ccx.tcx(), tid); + let n_variants = (*variants).len(); + + // NB: we must hit the discriminant first so that structural + // comparison know not to proceed when the discriminants differ. + + match adt::trans_switch(cx, &*repr, av) { + (_match::Single, None) => { + cx = iter_variant(cx, &*repr, av, &*(*variants)[0], + substs, f); + } + (_match::Switch, Some(lldiscrim_a)) => { + cx = f(cx, lldiscrim_a, ty::mk_int()); + let unr_cx = fcx.new_temp_block("enum-iter-unr"); + Unreachable(unr_cx); + let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, + n_variants); + let next_cx = fcx.new_temp_block("enum-iter-next"); + + for variant in (*variants).iter() { + let variant_cx = + fcx.new_temp_block( + format!("enum-iter-variant-{}", + variant.disr_val.to_string().as_slice()) + .as_slice()); + match adt::trans_case(cx, &*repr, variant.disr_val) { + _match::SingleResult(r) => { + AddCase(llswitch, r.val, variant_cx.llbb) + } + _ => ccx.sess().unimpl("value from adt::trans_case \ + in iter_structural_ty") + } + let variant_cx = + iter_variant(variant_cx, + &*repr, + data_ptr, + &**variant, + substs, + |x,y,z| f(x,y,z)); + Br(variant_cx, next_cx.llbb); + } + cx = next_cx; + } + _ => ccx.sess().unimpl("value from adt::trans_switch \ + in iter_structural_ty") + } + } + _ => cx.sess().unimpl("type in iter_structural_ty") + } + return cx; +} + +pub fn cast_shift_expr_rhs(cx: Block, + op: ast::BinOp, + lhs: ValueRef, + rhs: ValueRef) + -> ValueRef { + cast_shift_rhs(op, lhs, rhs, + |a,b| Trunc(cx, a, b), + |a,b| ZExt(cx, a, b)) +} + +pub fn cast_shift_const_rhs(op: ast::BinOp, + lhs: ValueRef, rhs: ValueRef) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, + |a, b| unsafe { llvm::LLVMConstTrunc(a, b.to_ref()) }, + |a, b| unsafe { llvm::LLVMConstZExt(a, b.to_ref()) }) +} + +pub fn cast_shift_rhs(op: ast::BinOp, + lhs: ValueRef, + rhs: ValueRef, + trunc: |ValueRef, Type| -> ValueRef, + zext: |ValueRef, Type| -> ValueRef) + -> ValueRef { + // Shifts may have any size int on the rhs + unsafe { + if ast_util::is_shift_binop(op) { + let mut rhs_llty = val_ty(rhs); + let mut lhs_llty = val_ty(lhs); + if rhs_llty.kind() == Vector { rhs_llty = rhs_llty.element_type() } + if lhs_llty.kind() == Vector { lhs_llty = lhs_llty.element_type() } + let rhs_sz = llvm::LLVMGetIntTypeWidth(rhs_llty.to_ref()); + let lhs_sz = llvm::LLVMGetIntTypeWidth(lhs_llty.to_ref()); + if lhs_sz < rhs_sz { + trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME (#1877: If shifting by negative + // values becomes not undefined then this is wrong. + zext(rhs, lhs_llty) + } else { + rhs + } + } else { + rhs + } + } +} + +pub fn fail_if_zero_or_overflows<'blk, 'tcx>( + cx: Block<'blk, 'tcx>, + span: Span, + divrem: ast::BinOp, + lhs: ValueRef, + rhs: ValueRef, + rhs_t: ty::t) + -> Block<'blk, 'tcx> { + let (zero_text, overflow_text) = if divrem == ast::BiDiv { + ("attempted to divide by zero", + "attempted to divide with overflow") + } else { + ("attempted remainder with a divisor of zero", + "attempted remainder with overflow") + }; + let (is_zero, is_signed) = match ty::get(rhs_t).sty { + ty::ty_int(t) => { + let zero = C_integral(Type::int_from_ty(cx.ccx(), t), 0u64, false); + (ICmp(cx, llvm::IntEQ, rhs, zero), true) + } + ty::ty_uint(t) => { + let zero = C_integral(Type::uint_from_ty(cx.ccx(), t), 0u64, false); + (ICmp(cx, llvm::IntEQ, rhs, zero), false) + } + _ => { + cx.sess().bug(format!("fail-if-zero on unexpected type: {}", + ty_to_string(cx.tcx(), rhs_t)).as_slice()); + } + }; + let bcx = with_cond(cx, is_zero, |bcx| { + controlflow::trans_fail(bcx, span, InternedString::new(zero_text)) + }); + + // To quote LLVM's documentation for the sdiv instruction: + // + // Division by zero leads to undefined behavior. Overflow also leads + // to undefined behavior; this is a rare case, but can occur, for + // example, by doing a 32-bit division of -2147483648 by -1. + // + // In order to avoid undefined behavior, we perform runtime checks for + // signed division/remainder which would trigger overflow. For unsigned + // integers, no action beyond checking for zero need be taken. + if is_signed { + let (llty, min) = match ty::get(rhs_t).sty { + ty::ty_int(t) => { + let llty = Type::int_from_ty(cx.ccx(), t); + let min = match t { + ast::TyI if llty == Type::i32(cx.ccx()) => i32::MIN as u64, + ast::TyI => i64::MIN as u64, + ast::TyI8 => i8::MIN as u64, + ast::TyI16 => i16::MIN as u64, + ast::TyI32 => i32::MIN as u64, + ast::TyI64 => i64::MIN as u64, + }; + (llty, min) + } + _ => unreachable!(), + }; + let minus_one = ICmp(bcx, llvm::IntEQ, rhs, + C_integral(llty, -1, false)); + with_cond(bcx, minus_one, |bcx| { + let is_min = ICmp(bcx, llvm::IntEQ, lhs, + C_integral(llty, min, true)); + with_cond(bcx, is_min, |bcx| { + controlflow::trans_fail(bcx, span, + InternedString::new(overflow_text)) + }) + }) + } else { + bcx + } +} + +pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> ValueRef { + let name = csearch::get_symbol(&ccx.sess().cstore, did); + match ty::get(t).sty { + ty::ty_bare_fn(ref fn_ty) => { + match ccx.sess().target.target.adjust_abi(fn_ty.abi) { + Rust | RustCall => { + get_extern_rust_fn(ccx, t, name.as_slice(), did) + } + RustIntrinsic => { + ccx.sess().bug("unexpected intrinsic in trans_external_path") + } + _ => { + foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, + name.as_slice()) + } + } + } + ty::ty_closure(_) => { + get_extern_rust_fn(ccx, t, name.as_slice(), did) + } + _ => { + get_extern_const(ccx, did, t) + } + } +} + +pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + llfn: ValueRef, + llargs: Vec , + fn_ty: ty::t, + call_info: Option, + // FIXME(15064) is_lang_item is a horrible hack, please remove it + // at the soonest opportunity. + is_lang_item: bool) + -> (ValueRef, Block<'blk, 'tcx>) { + let _icx = push_ctxt("invoke_"); + if bcx.unreachable.get() { + return (C_null(Type::i8(bcx.ccx())), bcx); + } + + // FIXME(15064) Lang item methods may (in the reflect case) not have proper + // types, so doing an attribute lookup will fail. + let attributes = if is_lang_item { + llvm::AttrBuilder::new() + } else { + get_fn_llvm_attributes(bcx.ccx(), fn_ty) + }; + + match bcx.opt_node_id { + None => { + debug!("invoke at ???"); + } + Some(id) => { + debug!("invoke at {}", bcx.tcx().map.node_to_string(id)); + } + } + + if need_invoke(bcx) { + debug!("invoking {} at {}", llfn, bcx.llbb); + for &llarg in llargs.iter() { + debug!("arg: {}", llarg); + } + let normal_bcx = bcx.fcx.new_temp_block("normal-return"); + let landing_pad = bcx.fcx.get_landing_pad(); + + match call_info { + Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), + None => debuginfo::clear_source_location(bcx.fcx) + }; + + let llresult = Invoke(bcx, + llfn, + llargs.as_slice(), + normal_bcx.llbb, + landing_pad, + Some(attributes)); + return (llresult, normal_bcx); + } else { + debug!("calling {} at {}", llfn, bcx.llbb); + for &llarg in llargs.iter() { + debug!("arg: {}", llarg); + } + + match call_info { + Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), + None => debuginfo::clear_source_location(bcx.fcx) + }; + + let llresult = Call(bcx, llfn, llargs.as_slice(), Some(attributes)); + return (llresult, bcx); + } +} + +pub fn need_invoke(bcx: Block) -> bool { + if bcx.sess().no_landing_pads() { + return false; + } + + // Avoid using invoke if we are already inside a landing pad. + if bcx.is_lpad { + return false; + } + + bcx.fcx.needs_invoke() +} + +pub fn load_if_immediate(cx: Block, v: ValueRef, t: ty::t) -> ValueRef { + let _icx = push_ctxt("load_if_immediate"); + if type_is_immediate(cx.ccx(), t) { return load_ty(cx, v, t); } + return v; +} + +pub fn load_ty(cx: Block, ptr: ValueRef, t: ty::t) -> ValueRef { + /*! + * Helper for loading values from memory. Does the necessary conversion if + * the in-memory type differs from the type used for SSA values. Also + * handles various special cases where the type gives us better information + * about what we are loading. + */ + if type_is_zero_size(cx.ccx(), t) { + C_undef(type_of::type_of(cx.ccx(), t)) + } else if ty::type_is_bool(t) { + Trunc(cx, LoadRangeAssert(cx, ptr, 0, 2, llvm::False), Type::i1(cx.ccx())) + } else if ty::type_is_char(t) { + // a char is a Unicode codepoint, and so takes values from 0 + // to 0x10FFFF inclusive only. + LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False) + } else { + Load(cx, ptr) + } +} + +pub fn store_ty(cx: Block, v: ValueRef, dst: ValueRef, t: ty::t) { + /*! + * Helper for storing values in memory. Does the necessary conversion if + * the in-memory type differs from the type used for SSA values. + */ + if ty::type_is_bool(t) { + Store(cx, ZExt(cx, v, Type::i8(cx.ccx())), dst); + } else { + Store(cx, v, dst); + }; +} + +pub fn ignore_lhs(_bcx: Block, local: &ast::Local) -> bool { + match local.pat.node { + ast::PatWild(ast::PatWildSingle) => true, _ => false + } +} + +pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &ast::Local) + -> Block<'blk, 'tcx> { + debug!("init_local(bcx={}, local.id={})", bcx.to_str(), local.id); + let _indenter = indenter(); + let _icx = push_ctxt("init_local"); + _match::store_local(bcx, local) +} + +pub fn raw_block<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, + is_lpad: bool, + llbb: BasicBlockRef) + -> Block<'blk, 'tcx> { + common::BlockS::new(llbb, is_lpad, None, fcx) +} + +pub fn with_cond<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + val: ValueRef, + f: |Block<'blk, 'tcx>| -> Block<'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("with_cond"); + let fcx = bcx.fcx; + let next_cx = fcx.new_temp_block("next"); + let cond_cx = fcx.new_temp_block("cond"); + CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); + let after_cx = f(cond_cx); + if !after_cx.terminated.get() { + Br(after_cx, next_cx.llbb); + } + next_cx +} + +pub fn call_lifetime_start(cx: Block, ptr: ValueRef) { + if cx.sess().opts.optimize == config::No { + return; + } + + let _icx = push_ctxt("lifetime_start"); + let ccx = cx.ccx(); + + let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type())); + let ptr = PointerCast(cx, ptr, Type::i8p(ccx)); + let lifetime_start = ccx.get_intrinsic(&"llvm.lifetime.start"); + Call(cx, lifetime_start, &[llsize, ptr], None); +} + +pub fn call_lifetime_end(cx: Block, ptr: ValueRef) { + if cx.sess().opts.optimize == config::No { + return; + } + + let _icx = push_ctxt("lifetime_end"); + let ccx = cx.ccx(); + + let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type())); + let ptr = PointerCast(cx, ptr, Type::i8p(ccx)); + let lifetime_end = ccx.get_intrinsic(&"llvm.lifetime.end"); + Call(cx, lifetime_end, &[llsize, ptr], None); +} + +pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { + let _icx = push_ctxt("call_memcpy"); + let ccx = cx.ccx(); + let key = match ccx.sess().target.target.target_word_size.as_slice() { + "32" => "llvm.memcpy.p0i8.p0i8.i32", + "64" => "llvm.memcpy.p0i8.p0i8.i64", + tws => panic!("Unsupported target word size for memcpy: {}", tws), + }; + let memcpy = ccx.get_intrinsic(&key); + let src_ptr = PointerCast(cx, src, Type::i8p(ccx)); + let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx)); + let size = IntCast(cx, n_bytes, ccx.int_type()); + let align = C_i32(ccx, align as i32); + let volatile = C_bool(ccx, false); + Call(cx, memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); +} + +pub fn memcpy_ty(bcx: Block, dst: ValueRef, src: ValueRef, t: ty::t) { + let _icx = push_ctxt("memcpy_ty"); + let ccx = bcx.ccx(); + if ty::type_is_structural(t) { + let llty = type_of::type_of(ccx, t); + let llsz = llsize_of(ccx, llty); + let llalign = type_of::align_of(ccx, t); + call_memcpy(bcx, dst, src, llsz, llalign as u32); + } else { + store_ty(bcx, Load(bcx, src), dst, t); + } +} + +pub fn zero_mem(cx: Block, llptr: ValueRef, t: ty::t) { + if cx.unreachable.get() { return; } + let _icx = push_ctxt("zero_mem"); + let bcx = cx; + memzero(&B(bcx), llptr, t); +} + +// Always use this function instead of storing a zero constant to the memory +// in question. If you store a zero constant, LLVM will drown in vreg +// allocation for large data structures, and the generated code will be +// awful. (A telltale sign of this is large quantities of +// `mov [byte ptr foo],0` in the generated code.) +fn memzero(b: &Builder, llptr: ValueRef, ty: ty::t) { + let _icx = push_ctxt("memzero"); + let ccx = b.ccx; + + let llty = type_of::type_of(ccx, ty); + + let intrinsic_key = match ccx.sess().target.target.target_word_size.as_slice() { + "32" => "llvm.memset.p0i8.i32", + "64" => "llvm.memset.p0i8.i64", + tws => panic!("Unsupported target word size for memset: {}", tws), + }; + + let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); + let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to()); + let llzeroval = C_u8(ccx, 0); + let size = machine::llsize_of(ccx, llty); + let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); + let volatile = C_bool(ccx, false); + b.call(llintrinsicfn, &[llptr, llzeroval, size, align, volatile], None); +} + +pub fn alloc_ty(bcx: Block, t: ty::t, name: &str) -> ValueRef { + let _icx = push_ctxt("alloc_ty"); + let ccx = bcx.ccx(); + let ty = type_of::type_of(ccx, t); + assert!(!ty::type_has_params(t)); + let val = alloca(bcx, ty, name); + return val; +} + +pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef { + let p = alloca_no_lifetime(cx, ty, name); + call_lifetime_start(cx, p); + p +} + +pub fn alloca_no_lifetime(cx: Block, ty: Type, name: &str) -> ValueRef { + let _icx = push_ctxt("alloca"); + if cx.unreachable.get() { + unsafe { + return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); + } + } + debuginfo::clear_source_location(cx.fcx); + Alloca(cx, ty, name) +} + +pub fn alloca_zeroed(cx: Block, ty: ty::t, name: &str) -> ValueRef { + let llty = type_of::type_of(cx.ccx(), ty); + if cx.unreachable.get() { + unsafe { + return llvm::LLVMGetUndef(llty.ptr_to().to_ref()); + } + } + let p = alloca_no_lifetime(cx, llty, name); + let b = cx.fcx.ccx.builder(); + b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); + memzero(&b, p, ty); + p +} + +pub fn arrayalloca(cx: Block, ty: Type, v: ValueRef) -> ValueRef { + let _icx = push_ctxt("arrayalloca"); + if cx.unreachable.get() { + unsafe { + return llvm::LLVMGetUndef(ty.to_ref()); + } + } + debuginfo::clear_source_location(cx.fcx); + let p = ArrayAlloca(cx, ty, v); + call_lifetime_start(cx, p); + p +} + +// Creates the alloca slot which holds the pointer to the slot for the final return value +pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef { + let lloutputtype = type_of::type_of(fcx.ccx, output_type); + + // We create an alloca to hold a pointer of type `output_type` + // which will hold the pointer to the right alloca which has the + // final ret value + if fcx.needs_ret_allocas { + // Let's create the stack slot + let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr"); + + // and if we're using an out pointer, then store that in our newly made slot + if type_of::return_uses_outptr(fcx.ccx, output_type) { + let outptr = get_param(fcx.llfn, 0); + + let b = fcx.ccx.builder(); + b.position_before(fcx.alloca_insert_pt.get().unwrap()); + b.store(outptr, slot); + } + + slot + + // But if there are no nested returns, we skip the indirection and have a single + // retslot + } else { + if type_of::return_uses_outptr(fcx.ccx, output_type) { + get_param(fcx.llfn, 0) + } else { + AllocaFcx(fcx, lloutputtype, "sret_slot") + } + } +} + +struct CheckForNestedReturnsVisitor { + found: bool, + in_return: bool +} + +impl CheckForNestedReturnsVisitor { + fn explicit() -> CheckForNestedReturnsVisitor { + CheckForNestedReturnsVisitor { found: false, in_return: false } + } + fn implicit() -> CheckForNestedReturnsVisitor { + CheckForNestedReturnsVisitor { found: false, in_return: true } + } +} + +impl<'v> Visitor<'v> for CheckForNestedReturnsVisitor { + fn visit_expr(&mut self, e: &ast::Expr) { + match e.node { + ast::ExprRet(..) => { + if self.in_return { + self.found = true; + } else { + self.in_return = true; + visit::walk_expr(self, e); + self.in_return = false; + } + } + _ => visit::walk_expr(self, e) + } + } +} + +fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool { + match tcx.map.find(id) { + Some(ast_map::NodeItem(i)) => { + match i.node { + ast::ItemFn(_, _, _, _, ref blk) => { + let mut explicit = CheckForNestedReturnsVisitor::explicit(); + let mut implicit = CheckForNestedReturnsVisitor::implicit(); + visit::walk_item(&mut explicit, &*i); + visit::walk_expr_opt(&mut implicit, &blk.expr); + explicit.found || implicit.found + } + _ => tcx.sess.bug("unexpected item variant in has_nested_returns") + } + } + Some(ast_map::NodeTraitItem(trait_method)) => { + match *trait_method { + ast::ProvidedMethod(ref m) => { + match m.node { + ast::MethDecl(_, _, _, _, _, _, ref blk, _) => { + let mut explicit = CheckForNestedReturnsVisitor::explicit(); + let mut implicit = CheckForNestedReturnsVisitor::implicit(); + visit::walk_method_helper(&mut explicit, &**m); + visit::walk_expr_opt(&mut implicit, &blk.expr); + explicit.found || implicit.found + } + ast::MethMac(_) => tcx.sess.bug("unexpanded macro") + } + } + ast::RequiredMethod(_) => { + tcx.sess.bug("unexpected variant: required trait method \ + in has_nested_returns") + } + ast::TypeTraitItem(_) => { + tcx.sess.bug("unexpected variant: type trait item in \ + has_nested_returns") + } + } + } + Some(ast_map::NodeImplItem(ii)) => { + match *ii { + ast::MethodImplItem(ref m) => { + match m.node { + ast::MethDecl(_, _, _, _, _, _, ref blk, _) => { + let mut explicit = CheckForNestedReturnsVisitor::explicit(); + let mut implicit = CheckForNestedReturnsVisitor::implicit(); + visit::walk_method_helper(&mut explicit, &**m); + visit::walk_expr_opt(&mut implicit, &blk.expr); + explicit.found || implicit.found + } + ast::MethMac(_) => tcx.sess.bug("unexpanded macro") + } + } + ast::TypeImplItem(_) => { + tcx.sess.bug("unexpected variant: type impl item in \ + has_nested_returns") + } + } + } + Some(ast_map::NodeExpr(e)) => { + match e.node { + ast::ExprFnBlock(_, _, ref blk) | + ast::ExprProc(_, ref blk) | + ast::ExprUnboxedFn(_, _, _, ref blk) => { + let mut explicit = CheckForNestedReturnsVisitor::explicit(); + let mut implicit = CheckForNestedReturnsVisitor::implicit(); + visit::walk_expr(&mut explicit, e); + visit::walk_expr_opt(&mut implicit, &blk.expr); + explicit.found || implicit.found + } + _ => tcx.sess.bug("unexpected expr variant in has_nested_returns") + } + } + + Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false, + + // glue, shims, etc + None if id == ast::DUMMY_NODE_ID => false, + + _ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}", + tcx.map.path_to_string(id)).as_slice()) + } +} + +// NB: must keep 4 fns in sync: +// +// - type_of_fn +// - create_datums_for_fn_args. +// - new_fn_ctxt +// - trans_args +// +// Be warned! You must call `init_function` before doing anything with the +// returned function context. +pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, + llfndecl: ValueRef, + id: ast::NodeId, + has_env: bool, + output_type: ty::FnOutput, + param_substs: &'a param_substs, + sp: Option, + block_arena: &'a TypedArena>) + -> FunctionContext<'a, 'tcx> { + param_substs.validate(); + + debug!("new_fn_ctxt(path={}, id={}, param_substs={})", + if id == -1 { + "".to_string() + } else { + ccx.tcx().map.path_to_string(id).to_string() + }, + id, param_substs.repr(ccx.tcx())); + + let uses_outptr = match output_type { + ty::FnConverging(output_type) => { + let substd_output_type = output_type.substp(ccx.tcx(), param_substs); + type_of::return_uses_outptr(ccx, substd_output_type) + } + ty::FnDiverging => false + }; + let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl); + let nested_returns = has_nested_returns(ccx.tcx(), id); + + let mut fcx = FunctionContext { + llfn: llfndecl, + llenv: None, + llretslotptr: Cell::new(None), + alloca_insert_pt: Cell::new(None), + llreturn: Cell::new(None), + needs_ret_allocas: nested_returns, + personality: Cell::new(None), + caller_expects_out_pointer: uses_outptr, + lllocals: RefCell::new(NodeMap::new()), + llupvars: RefCell::new(NodeMap::new()), + id: id, + param_substs: param_substs, + span: sp, + block_arena: block_arena, + ccx: ccx, + debug_context: debug_context, + scopes: RefCell::new(Vec::new()) + }; + + if has_env { + fcx.llenv = Some(get_param(fcx.llfn, fcx.env_arg_pos() as c_uint)) + } + + fcx +} + +/// Performs setup on a newly created function, creating the entry scope block +/// and allocating space for the return pointer. +pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, + skip_retptr: bool, + output: ty::FnOutput) -> Block<'a, 'tcx> { + let entry_bcx = fcx.new_temp_block("entry-block"); + + // Use a dummy instruction as the insertion point for all allocas. + // This is later removed in FunctionContext::cleanup. + fcx.alloca_insert_pt.set(Some(unsafe { + Load(entry_bcx, C_null(Type::i8p(fcx.ccx))); + llvm::LLVMGetFirstInstruction(entry_bcx.llbb) + })); + + if let ty::FnConverging(output_type) = output { + // This shouldn't need to recompute the return type, + // as new_fn_ctxt did it already. + let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs); + if !return_type_is_void(fcx.ccx, substd_output_type) { + // If the function returns nil/bot, there is no real return + // value, so do not set `llretslotptr`. + if !skip_retptr || fcx.caller_expects_out_pointer { + // Otherwise, we normally allocate the llretslotptr, unless we + // have been instructed to skip it for immediate return + // values. + fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); + } + } + } + + entry_bcx +} + +// NB: must keep 4 fns in sync: +// +// - type_of_fn +// - create_datums_for_fn_args. +// - new_fn_ctxt +// - trans_args + +pub fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue { + use trans::datum::{ByRef, ByValue}; + + datum::Rvalue { + mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue } + } +} + +// work around bizarre resolve errors +pub type RvalueDatum = datum::Datum; +pub type LvalueDatum = datum::Datum; + +// create_datums_for_fn_args: creates rvalue datums for each of the +// incoming function arguments. These will later be stored into +// appropriate lvalue datums. +pub fn create_datums_for_fn_args(fcx: &FunctionContext, + arg_tys: &[ty::t]) + -> Vec { + let _icx = push_ctxt("create_datums_for_fn_args"); + + // Return an array wrapping the ValueRefs that we get from `get_param` for + // each argument into datums. + arg_tys.iter().enumerate().map(|(i, &arg_ty)| { + let llarg = get_param(fcx.llfn, fcx.arg_pos(i) as c_uint); + datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty)) + }).collect() +} + +/// Creates rvalue datums for each of the incoming function arguments and +/// tuples the arguments. These will later be stored into appropriate lvalue +/// datums. +/// +/// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for. +fn create_datums_for_fn_args_under_call_abi( + mut bcx: Block, + arg_scope: cleanup::CustomScopeIndex, + arg_tys: &[ty::t]) + -> Vec { + let mut result = Vec::new(); + for (i, &arg_ty) in arg_tys.iter().enumerate() { + if i < arg_tys.len() - 1 { + // Regular argument. + let llarg = get_param(bcx.fcx.llfn, bcx.fcx.arg_pos(i) as c_uint); + result.push(datum::Datum::new(llarg, arg_ty, arg_kind(bcx.fcx, + arg_ty))); + continue + } + + // This is the last argument. Tuple it. + match ty::get(arg_ty).sty { + ty::ty_tup(ref tupled_arg_tys) => { + let tuple_args_scope_id = cleanup::CustomScope(arg_scope); + let tuple = + unpack_datum!(bcx, + datum::lvalue_scratch_datum(bcx, + arg_ty, + "tupled_args", + false, + tuple_args_scope_id, + (), + |(), + mut bcx, + llval| { + for (j, &tupled_arg_ty) in + tupled_arg_tys.iter().enumerate() { + let llarg = + get_param(bcx.fcx.llfn, + bcx.fcx.arg_pos(i + j) as c_uint); + let lldest = GEPi(bcx, llval, &[0, j]); + let datum = datum::Datum::new( + llarg, + tupled_arg_ty, + arg_kind(bcx.fcx, tupled_arg_ty)); + bcx = datum.store_to(bcx, lldest); + } + bcx + })); + let tuple = unpack_datum!(bcx, + tuple.to_expr_datum() + .to_rvalue_datum(bcx, + "argtuple")); + result.push(tuple); + } + _ => { + bcx.tcx().sess.bug("last argument of a function with \ + `rust-call` ABI isn't a tuple?!") + } + }; + + } + + result +} + +fn copy_args_to_allocas<'blk, 'tcx>(fcx: &FunctionContext<'blk, 'tcx>, + arg_scope: cleanup::CustomScopeIndex, + bcx: Block<'blk, 'tcx>, + args: &[ast::Arg], + arg_datums: Vec ) + -> Block<'blk, 'tcx> { + debug!("copy_args_to_allocas"); + + let _icx = push_ctxt("copy_args_to_allocas"); + let mut bcx = bcx; + + let arg_scope_id = cleanup::CustomScope(arg_scope); + + for (i, arg_datum) in arg_datums.into_iter().enumerate() { + // For certain mode/type combinations, the raw llarg values are passed + // by value. However, within the fn body itself, we want to always + // have all locals and arguments be by-ref so that we can cancel the + // cleanup and for better interaction with LLVM's debug info. So, if + // the argument would be passed by value, we store it into an alloca. + // This alloca should be optimized away by LLVM's mem-to-reg pass in + // the event it's not truly needed. + + bcx = _match::store_arg(bcx, &*args[i].pat, arg_datum, arg_scope_id); + + if fcx.ccx.sess().opts.debuginfo == FullDebugInfo { + debuginfo::create_argument_metadata(bcx, &args[i]); + } + } + + bcx +} + +fn copy_unboxed_closure_args_to_allocas<'blk, 'tcx>( + mut bcx: Block<'blk, 'tcx>, + arg_scope: cleanup::CustomScopeIndex, + args: &[ast::Arg], + arg_datums: Vec, + monomorphized_arg_types: &[ty::t]) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("copy_unboxed_closure_args_to_allocas"); + let arg_scope_id = cleanup::CustomScope(arg_scope); + + assert_eq!(arg_datums.len(), 1); + + let arg_datum = arg_datums.into_iter().next().unwrap(); + + // Untuple the rest of the arguments. + let tuple_datum = + unpack_datum!(bcx, + arg_datum.to_lvalue_datum_in_scope(bcx, + "argtuple", + arg_scope_id)); + let untupled_arg_types = match ty::get(monomorphized_arg_types[0]).sty { + ty::ty_tup(ref types) => types.as_slice(), + _ => { + bcx.tcx().sess.span_bug(args[0].pat.span, + "first arg to `rust-call` ABI function \ + wasn't a tuple?!") + } + }; + for j in range(0, args.len()) { + let tuple_element_type = untupled_arg_types[j]; + let tuple_element_datum = + tuple_datum.get_element(bcx, + tuple_element_type, + |llval| GEPi(bcx, llval, &[0, j])); + let tuple_element_datum = tuple_element_datum.to_expr_datum(); + let tuple_element_datum = + unpack_datum!(bcx, + tuple_element_datum.to_rvalue_datum(bcx, + "arg")); + bcx = _match::store_arg(bcx, + &*args[j].pat, + tuple_element_datum, + arg_scope_id); + + if bcx.fcx.ccx.sess().opts.debuginfo == FullDebugInfo { + debuginfo::create_argument_metadata(bcx, &args[j]); + } + } + + bcx +} + +// Ties up the llstaticallocas -> llloadenv -> lltop edges, +// and builds the return block. +pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, + last_bcx: Block<'blk, 'tcx>, + retty: ty::FnOutput) { + let _icx = push_ctxt("finish_fn"); + + let ret_cx = match fcx.llreturn.get() { + Some(llreturn) => { + if !last_bcx.terminated.get() { + Br(last_bcx, llreturn); + } + raw_block(fcx, false, llreturn) + } + None => last_bcx + }; + + // This shouldn't need to recompute the return type, + // as new_fn_ctxt did it already. + let substd_retty = retty.substp(fcx.ccx.tcx(), fcx.param_substs); + build_return_block(fcx, ret_cx, substd_retty); + + debuginfo::clear_source_location(fcx); + fcx.cleanup(); +} + +// Builds the return block for a function. +pub fn build_return_block(fcx: &FunctionContext, ret_cx: Block, retty: ty::FnOutput) { + if fcx.llretslotptr.get().is_none() || + (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) { + return RetVoid(ret_cx); + } + + let retslot = if fcx.needs_ret_allocas { + Load(ret_cx, fcx.llretslotptr.get().unwrap()) + } else { + fcx.llretslotptr.get().unwrap() + }; + let retptr = Value(retslot); + match retptr.get_dominating_store(ret_cx) { + // If there's only a single store to the ret slot, we can directly return + // the value that was stored and omit the store and the alloca + Some(s) => { + let retval = s.get_operand(0).unwrap().get(); + s.erase_from_parent(); + + if retptr.has_no_uses() { + retptr.erase_from_parent(); + } + + let retval = if retty == ty::FnConverging(ty::mk_bool()) { + Trunc(ret_cx, retval, Type::i1(fcx.ccx)) + } else { + retval + }; + + if fcx.caller_expects_out_pointer { + if let ty::FnConverging(retty) = retty { + store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); + } + RetVoid(ret_cx) + } else { + Ret(ret_cx, retval) + } + } + // Otherwise, copy the return value to the ret slot + None => match retty { + ty::FnConverging(retty) => { + if fcx.caller_expects_out_pointer { + memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); + RetVoid(ret_cx) + } else { + Ret(ret_cx, load_ty(ret_cx, retslot, retty)) + } + } + ty::FnDiverging => { + if fcx.caller_expects_out_pointer { + RetVoid(ret_cx) + } else { + Ret(ret_cx, C_undef(Type::nil(fcx.ccx))) + } + } + } + } +} + +#[deriving(Clone, Eq, PartialEq)] +pub enum IsUnboxedClosureFlag { + NotUnboxedClosure, + IsUnboxedClosure, +} + +// trans_closure: Builds an LLVM function out of a source function. +// If the function closes over its environment a closure will be +// returned. +pub fn trans_closure(ccx: &CrateContext, + decl: &ast::FnDecl, + body: &ast::Block, + llfndecl: ValueRef, + param_substs: ¶m_substs, + fn_ast_id: ast::NodeId, + _attributes: &[ast::Attribute], + output_type: ty::FnOutput, + abi: Abi, + has_env: bool, + is_unboxed_closure: IsUnboxedClosureFlag, + maybe_load_env: for<'blk, 'tcx> |Block<'blk, 'tcx>, ScopeId| + -> Block<'blk, 'tcx>) { + ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); + + let _icx = push_ctxt("trans_closure"); + set_uwtable(llfndecl); + + debug!("trans_closure(..., param_substs={})", + param_substs.repr(ccx.tcx())); + + let arena = TypedArena::new(); + let fcx = new_fn_ctxt(ccx, + llfndecl, + fn_ast_id, + has_env, + output_type, + param_substs, + Some(body.span), + &arena); + let mut bcx = init_function(&fcx, false, output_type); + + // cleanup scope for the incoming arguments + let fn_cleanup_debug_loc = + debuginfo::get_cleanup_debug_loc_for_ast_node(fn_ast_id, body.span, true); + let arg_scope = fcx.push_custom_cleanup_scope_with_debug_loc(fn_cleanup_debug_loc); + + let block_ty = node_id_type(bcx, body.id); + + // Set up arguments to the function. + let monomorphized_arg_types = + decl.inputs.iter() + .map(|arg| node_id_type(bcx, arg.id)) + .collect::>(); + let monomorphized_arg_types = match is_unboxed_closure { + NotUnboxedClosure => monomorphized_arg_types, + + // Tuple up closure argument types for the "rust-call" ABI. + IsUnboxedClosure => vec![ty::mk_tup(ccx.tcx(), monomorphized_arg_types)] + }; + for monomorphized_arg_type in monomorphized_arg_types.iter() { + debug!("trans_closure: monomorphized_arg_type: {}", + ty_to_string(ccx.tcx(), *monomorphized_arg_type)); + } + debug!("trans_closure: function lltype: {}", + bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn)); + + let arg_datums = if abi != RustCall { + create_datums_for_fn_args(&fcx, + monomorphized_arg_types.as_slice()) + } else { + create_datums_for_fn_args_under_call_abi( + bcx, + arg_scope, + monomorphized_arg_types.as_slice()) + }; + + bcx = match is_unboxed_closure { + NotUnboxedClosure => { + copy_args_to_allocas(&fcx, + arg_scope, + bcx, + decl.inputs.as_slice(), + arg_datums) + } + IsUnboxedClosure => { + copy_unboxed_closure_args_to_allocas( + bcx, + arg_scope, + decl.inputs.as_slice(), + arg_datums, + monomorphized_arg_types.as_slice()) + } + }; + + bcx = maybe_load_env(bcx, cleanup::CustomScope(arg_scope)); + + // Up until here, IR instructions for this function have explicitly not been annotated with + // source code location, so we don't step into call setup code. From here on, source location + // emitting should be enabled. + debuginfo::start_emitting_source_locations(&fcx); + + let dest = match fcx.llretslotptr.get() { + Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(block_ty), "iret_slot")), + None => { + assert!(type_is_zero_size(bcx.ccx(), block_ty)); + expr::Ignore + } + }; + + // This call to trans_block is the place where we bridge between + // translation calls that don't have a return value (trans_crate, + // trans_mod, trans_item, et cetera) and those that do + // (trans_block, trans_expr, et cetera). + bcx = controlflow::trans_block(bcx, body, dest); + + match dest { + expr::SaveIn(slot) if fcx.needs_ret_allocas => { + Store(bcx, slot, fcx.llretslotptr.get().unwrap()); + } + _ => {} + } + + match fcx.llreturn.get() { + Some(_) => { + Br(bcx, fcx.return_exit_block()); + fcx.pop_custom_cleanup_scope(arg_scope); + } + None => { + // Microoptimization writ large: avoid creating a separate + // llreturn basic block + bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope); + } + }; + + // Put return block after all other blocks. + // This somewhat improves single-stepping experience in debugger. + unsafe { + let llreturn = fcx.llreturn.get(); + for &llreturn in llreturn.iter() { + llvm::LLVMMoveBasicBlockAfter(llreturn, bcx.llbb); + } + } + + // Insert the mandatory first few basic blocks before lltop. + finish_fn(&fcx, bcx, output_type); +} + +// trans_fn: creates an LLVM function corresponding to a source language +// function. +pub fn trans_fn(ccx: &CrateContext, + decl: &ast::FnDecl, + body: &ast::Block, + llfndecl: ValueRef, + param_substs: ¶m_substs, + id: ast::NodeId, + attrs: &[ast::Attribute]) { + let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string()); + debug!("trans_fn(param_substs={})", param_substs.repr(ccx.tcx())); + let _icx = push_ctxt("trans_fn"); + let fn_ty = ty::node_id_to_type(ccx.tcx(), id); + let output_type = ty::ty_fn_ret(fn_ty); + let abi = ty::ty_fn_abi(fn_ty); + trans_closure(ccx, + decl, + body, + llfndecl, + param_substs, + id, + attrs, + output_type, + abi, + false, + NotUnboxedClosure, + |bcx, _| bcx); +} + +pub fn trans_enum_variant(ccx: &CrateContext, + _enum_id: ast::NodeId, + variant: &ast::Variant, + _args: &[ast::VariantArg], + disr: ty::Disr, + param_substs: ¶m_substs, + llfndecl: ValueRef) { + let _icx = push_ctxt("trans_enum_variant"); + + trans_enum_variant_or_tuple_like_struct( + ccx, + variant.node.id, + disr, + param_substs, + llfndecl); +} + +pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + ctor_ty: ty::t, + disr: ty::Disr, + args: callee::CallArgs, + dest: expr::Dest, + call_info: Option) + -> Result<'blk, 'tcx> { + + let ccx = bcx.fcx.ccx; + let tcx = ccx.tcx(); + + let result_ty = match ty::get(ctor_ty).sty { + ty::ty_bare_fn(ref bft) => bft.sig.output.unwrap(), + _ => ccx.sess().bug( + format!("trans_enum_variant_constructor: \ + unexpected ctor return type {}", + ctor_ty.repr(tcx)).as_slice()) + }; + + // Get location to store the result. If the user does not care about + // the result, just make a stack slot + let llresult = match dest { + expr::SaveIn(d) => d, + expr::Ignore => { + if !type_is_zero_size(ccx, result_ty) { + alloc_ty(bcx, result_ty, "constructor_result") + } else { + C_undef(type_of::type_of(ccx, result_ty)) + } + } + }; + + if !type_is_zero_size(ccx, result_ty) { + match args { + callee::ArgExprs(exprs) => { + let fields = exprs.iter().map(|x| &**x).enumerate().collect::>(); + bcx = expr::trans_adt(bcx, + result_ty, + disr, + fields.as_slice(), + None, + expr::SaveIn(llresult), + call_info); + } + _ => ccx.sess().bug("expected expr as arguments for variant/struct tuple constructor") + } + } + + // If the caller doesn't care about the result + // drop the temporary we made + let bcx = match dest { + expr::SaveIn(_) => bcx, + expr::Ignore => { + glue::drop_ty(bcx, llresult, result_ty, call_info) + } + }; + + Result::new(bcx, llresult) +} + +pub fn trans_tuple_struct(ccx: &CrateContext, + _fields: &[ast::StructField], + ctor_id: ast::NodeId, + param_substs: ¶m_substs, + llfndecl: ValueRef) { + let _icx = push_ctxt("trans_tuple_struct"); + + trans_enum_variant_or_tuple_like_struct( + ccx, + ctor_id, + 0, + param_substs, + llfndecl); +} + +fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext, + ctor_id: ast::NodeId, + disr: ty::Disr, + param_substs: ¶m_substs, + llfndecl: ValueRef) { + let ctor_ty = ty::node_id_to_type(ccx.tcx(), ctor_id); + let ctor_ty = ctor_ty.substp(ccx.tcx(), param_substs); + + let result_ty = match ty::get(ctor_ty).sty { + ty::ty_bare_fn(ref bft) => bft.sig.output, + _ => ccx.sess().bug( + format!("trans_enum_variant_or_tuple_like_struct: \ + unexpected ctor return type {}", + ty_to_string(ccx.tcx(), ctor_ty)).as_slice()) + }; + + let arena = TypedArena::new(); + let fcx = new_fn_ctxt(ccx, llfndecl, ctor_id, false, result_ty, + param_substs, None, &arena); + let bcx = init_function(&fcx, false, result_ty); + + assert!(!fcx.needs_ret_allocas); + + let arg_tys = ty::ty_fn_args(ctor_ty); + + let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice()); + + if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) { + let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot"); + let repr = adt::represent_type(ccx, result_ty.unwrap()); + for (i, arg_datum) in arg_datums.into_iter().enumerate() { + let lldestptr = adt::trans_field_ptr(bcx, + &*repr, + dest, + disr, + i); + arg_datum.store_to(bcx, lldestptr); + } + adt::trans_set_discr(bcx, &*repr, dest, disr); + } + + finish_fn(&fcx, bcx, result_ty); +} + +fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) { + let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully + + let levels = ccx.tcx().node_lint_levels.borrow(); + let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCES); + let lvlsrc = match levels.get(&(id, lint_id)) { + None | Some(&(lint::Allow, _)) => return, + Some(&lvlsrc) => lvlsrc, + }; + + let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); + match *avar { + adt::General(_, ref variants, _) => { + for var in variants.iter() { + let mut size = 0; + for field in var.fields.iter().skip(1) { + // skip the discriminant + size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); + } + sizes.push(size); + } + }, + _ => { /* its size is either constant or unimportant */ } + } + + let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), + |(l, s, li), (idx, &size)| + if size > l { + (size, l, idx) + } else if size > s { + (l, size, li) + } else { + (l, s, li) + } + ); + + // we only warn if the largest variant is at least thrice as large as + // the second-largest. + if largest > slargest * 3 && slargest > 0 { + // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing + // pass for the latter already ran. + lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::VARIANT_SIZE_DIFFERENCES, + lvlsrc, Some(sp), + format!("enum variant is more than three times larger \ + ({} bytes) than the next largest (ignoring padding)", + largest).as_slice()); + + ccx.sess().span_note(enum_def.variants[largest_index].span, + "this variant is the largest"); + } +} + +pub struct TransItemVisitor<'a, 'tcx: 'a> { + pub ccx: &'a CrateContext<'a, 'tcx>, +} + +impl<'a, 'tcx, 'v> Visitor<'v> for TransItemVisitor<'a, 'tcx> { + fn visit_item(&mut self, i: &ast::Item) { + trans_item(self.ccx, i); + } +} + +pub fn llvm_linkage_by_name(name: &str) -> Option { + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but whitelist them anyway and trust that + // the user knows what s/he's doing. Who knows, unanticipated use cases + // may pop up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Some(llvm::AppendingLinkage), + "available_externally" => Some(llvm::AvailableExternallyLinkage), + "common" => Some(llvm::CommonLinkage), + "extern_weak" => Some(llvm::ExternalWeakLinkage), + "external" => Some(llvm::ExternalLinkage), + "internal" => Some(llvm::InternalLinkage), + "linkonce" => Some(llvm::LinkOnceAnyLinkage), + "linkonce_odr" => Some(llvm::LinkOnceODRLinkage), + "private" => Some(llvm::PrivateLinkage), + "weak" => Some(llvm::WeakAnyLinkage), + "weak_odr" => Some(llvm::WeakODRLinkage), + _ => None, + } +} + + +/// Enum describing the origin of an LLVM `Value`, for linkage purposes. +pub enum ValueOrigin { + /// The LLVM `Value` is in this context because the corresponding item was + /// assigned to the current compilation unit. + OriginalTranslation, + /// The `Value`'s corresponding item was assigned to some other compilation + /// unit, but the `Value` was translated in this context anyway because the + /// item is marked `#[inline]`. + InlinedCopy, +} + +/// Set the appropriate linkage for an LLVM `ValueRef` (function or global). +/// If the `llval` is the direct translation of a specific Rust item, `id` +/// should be set to the `NodeId` of that item. (This mapping should be +/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to +/// `None`.) `llval_origin` indicates whether `llval` is the translation of an +/// item assigned to `ccx`'s compilation unit or an inlined copy of an item +/// assigned to a different compilation unit. +pub fn update_linkage(ccx: &CrateContext, + llval: ValueRef, + id: Option, + llval_origin: ValueOrigin) { + match llval_origin { + InlinedCopy => { + // `llval` is a translation of an item defined in a separate + // compilation unit. This only makes sense if there are at least + // two compilation units. + assert!(ccx.sess().opts.cg.codegen_units > 1); + // `llval` is a copy of something defined elsewhere, so use + // `AvailableExternallyLinkage` to avoid duplicating code in the + // output. + llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage); + return; + }, + OriginalTranslation => {}, + } + + match id { + Some(id) => { + let item = ccx.tcx().map.get(id); + if let ast_map::NodeItem(i) = item { + if let Some(name) = attr::first_attr_value_str_by_name(i.attrs[], "linkage") { + if let Some(linkage) = llvm_linkage_by_name(name.get()) { + llvm::SetLinkage(llval, linkage); + } else { + ccx.sess().span_fatal(i.span, "invalid linkage specified"); + } + return; + } + } + } + _ => {} + } + + match id { + Some(id) if ccx.reachable().contains(&id) => { + llvm::SetLinkage(llval, llvm::ExternalLinkage); + }, + _ => { + // `id` does not refer to an item in `ccx.reachable`. + if ccx.sess().opts.cg.codegen_units > 1 { + llvm::SetLinkage(llval, llvm::ExternalLinkage); + } else { + llvm::SetLinkage(llval, llvm::InternalLinkage); + } + }, + } +} + +pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { + let _icx = push_ctxt("trans_item"); + + let from_external = ccx.external_srcs().borrow().contains_key(&item.id); + + match item.node { + ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => { + if !generics.is_type_parameterized() { + let trans_everywhere = attr::requests_inline(item.attrs.as_slice()); + // Ignore `trans_everywhere` for cross-crate inlined items + // (`from_external`). `trans_item` will be called once for each + // compilation unit that references the item, so it will still get + // translated everywhere it's needed. + for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) { + let llfn = get_item_val(ccx, item.id); + if abi != Rust { + foreign::trans_rust_fn_with_foreign_abi(ccx, + &**decl, + &**body, + item.attrs.as_slice(), + llfn, + ¶m_substs::empty(), + item.id, + None); + } else { + trans_fn(ccx, + &**decl, + &**body, + llfn, + ¶m_substs::empty(), + item.id, + item.attrs.as_slice()); + } + update_linkage(ccx, + llfn, + Some(item.id), + if is_origin { OriginalTranslation } else { InlinedCopy }); + } + } + + // Be sure to travel more than just one layer deep to catch nested + // items in blocks and such. + let mut v = TransItemVisitor{ ccx: ccx }; + v.visit_block(&**body); + } + ast::ItemImpl(ref generics, _, _, ref impl_items) => { + meth::trans_impl(ccx, + item.ident, + impl_items.as_slice(), + generics, + item.id); + } + ast::ItemMod(ref m) => { + trans_mod(&ccx.rotate(), m); + } + ast::ItemEnum(ref enum_definition, _) => { + enum_variant_size_lint(ccx, enum_definition, item.span, item.id); + } + ast::ItemConst(_, ref expr) => { + // Recurse on the expression to catch items in blocks + let mut v = TransItemVisitor{ ccx: ccx }; + v.visit_expr(&**expr); + } + ast::ItemStatic(_, m, ref expr) => { + // Recurse on the expression to catch items in blocks + let mut v = TransItemVisitor{ ccx: ccx }; + v.visit_expr(&**expr); + + consts::trans_static(ccx, m, item.id); + let g = get_item_val(ccx, item.id); + update_linkage(ccx, g, Some(item.id), OriginalTranslation); + + // Do static_assert checking. It can't really be done much earlier + // because we need to get the value of the bool out of LLVM + if attr::contains_name(item.attrs.as_slice(), "static_assert") { + if m == ast::MutMutable { + ccx.sess().span_fatal(expr.span, + "cannot have static_assert on a mutable \ + static"); + } + + let v = ccx.static_values().borrow()[item.id].clone(); + unsafe { + if !(llvm::LLVMConstIntGetZExtValue(v) != 0) { + ccx.sess().span_fatal(expr.span, "static assertion failed"); + } + } + } + }, + ast::ItemForeignMod(ref foreign_mod) => { + foreign::trans_foreign_mod(ccx, foreign_mod); + } + ast::ItemTrait(..) => { + // Inside of this trait definition, we won't be actually translating any + // functions, but the trait still needs to be walked. Otherwise default + // methods with items will not get translated and will cause ICE's when + // metadata time comes around. + let mut v = TransItemVisitor{ ccx: ccx }; + visit::walk_item(&mut v, item); + } + _ => {/* fall through */ } + } +} + +// Translate a module. Doing this amounts to translating the items in the +// module; there ends up being no artifact (aside from linkage names) of +// separate modules in the compiled program. That's because modules exist +// only as a convenience for humans working with the code, to organize names +// and control visibility. +pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) { + let _icx = push_ctxt("trans_mod"); + for item in m.items.iter() { + trans_item(ccx, &**item); + } +} + +fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId, + llfn: ValueRef) { + ccx.item_symbols().borrow_mut().insert(node_id, sym); + + // The stack exhaustion lang item shouldn't have a split stack because + // otherwise it would continue to be exhausted (bad), and both it and the + // eh_personality functions need to be externally linkable. + let def = ast_util::local_def(node_id); + if ccx.tcx().lang_items.stack_exhausted() == Some(def) { + unset_split_stack(llfn); + llvm::SetLinkage(llfn, llvm::ExternalLinkage); + } + if ccx.tcx().lang_items.eh_personality() == Some(def) { + llvm::SetLinkage(llfn, llvm::ExternalLinkage); + } + + + if is_entry_fn(ccx.sess(), node_id) { + create_entry_wrapper(ccx, sp, llfn); + } +} + +fn register_fn(ccx: &CrateContext, + sp: Span, + sym: String, + node_id: ast::NodeId, + node_type: ty::t) + -> ValueRef { + match ty::get(node_type).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.abi == Rust || f.abi == RustCall); + } + _ => panic!("expected bare rust fn") + }; + + let llfn = decl_rust_fn(ccx, node_type, sym.as_slice()); + finish_register_fn(ccx, sp, sym, node_id, llfn); + llfn +} + +pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) + -> llvm::AttrBuilder { + use middle::ty::{BrAnon, ReLateBound}; + + let (fn_sig, abi, has_env) = match ty::get(fn_ty).sty { + ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true), + ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false), + ty::ty_unboxed_closure(closure_did, _, ref substs) => { + let unboxed_closures = ccx.tcx().unboxed_closures.borrow(); + let ref function_type = (*unboxed_closures)[closure_did] + .closure_type; + + (function_type.sig.subst(ccx.tcx(), substs), RustCall, true) + } + _ => ccx.sess().bug("expected closure or function.") + }; + + + // Since index 0 is the return value of the llvm func, we start + // at either 1 or 2 depending on whether there's an env slot or not + let mut first_arg_offset = if has_env { 2 } else { 1 }; + let mut attrs = llvm::AttrBuilder::new(); + let ret_ty = fn_sig.output; + + // These have an odd calling convention, so we need to manually + // unpack the input ty's + let input_tys = match ty::get(fn_ty).sty { + ty::ty_unboxed_closure(_, _, _) => { + assert!(abi == RustCall); + + match ty::get(fn_sig.inputs[0]).sty { + ty::ty_tup(ref inputs) => inputs.clone(), + _ => ccx.sess().bug("expected tuple'd inputs") + } + }, + ty::ty_bare_fn(_) if abi == RustCall => { + let mut inputs = vec![fn_sig.inputs[0]]; + + match ty::get(fn_sig.inputs[1]).sty { + ty::ty_tup(ref t_in) => { + inputs.push_all(t_in.as_slice()); + inputs + } + _ => ccx.sess().bug("expected tuple'd inputs") + } + } + _ => fn_sig.inputs.clone() + }; + + if let ty::FnConverging(ret_ty) = ret_ty { + // A function pointer is called without the declaration + // available, so we have to apply any attributes with ABI + // implications directly to the call instruction. Right now, + // the only attribute we need to worry about is `sret`. + if type_of::return_uses_outptr(ccx, ret_ty) { + let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty)); + + // The outptr can be noalias and nocapture because it's entirely + // invisible to the program. We also know it's nonnull as well + // as how many bytes we can dereference + attrs.arg(1, llvm::StructRetAttribute) + .arg(1, llvm::NoAliasAttribute) + .arg(1, llvm::NoCaptureAttribute) + .arg(1, llvm::DereferenceableAttribute(llret_sz)); + + // Add one more since there's an outptr + first_arg_offset += 1; + } else { + // The `noalias` attribute on the return value is useful to a + // function ptr caller. + match ty::get(ret_ty).sty { + // `~` pointer return values never alias because ownership + // is transferred + ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_uniq(_) => { + attrs.ret(llvm::NoAliasAttribute); + } + _ => {} + } + + // We can also mark the return value as `dereferenceable` in certain cases + match ty::get(ret_ty).sty { + // These are not really pointers but pairs, (pointer, len) + ty::ty_uniq(it) | + ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { + let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); + attrs.ret(llvm::DereferenceableAttribute(llret_sz)); + } + _ => {} + } + + match ty::get(ret_ty).sty { + ty::ty_bool => { + attrs.ret(llvm::ZExtAttribute); + } + _ => {} + } + } + } + + for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { + match ty::get(t).sty { + // this needs to be first to prevent fat pointers from falling through + _ if !type_is_immediate(ccx, t) => { + let llarg_sz = llsize_of_real(ccx, type_of::type_of(ccx, t)); + + // For non-immediate arguments the callee gets its own copy of + // the value on the stack, so there are no aliases. It's also + // program-invisible so can't possibly capture + attrs.arg(idx, llvm::NoAliasAttribute) + .arg(idx, llvm::NoCaptureAttribute) + .arg(idx, llvm::DereferenceableAttribute(llarg_sz)); + } + + ty::ty_bool => { + attrs.arg(idx, llvm::ZExtAttribute); + } + + // `~` pointer parameters never alias because ownership is transferred + ty::ty_uniq(inner) => { + let llsz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); + + attrs.arg(idx, llvm::NoAliasAttribute) + .arg(idx, llvm::DereferenceableAttribute(llsz)); + } + + // `&mut` pointer parameters never alias other parameters, or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, and can be marked as both + // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on + // memory dependencies rather than pointer equality + ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable || + !ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => { + + let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); + attrs.arg(idx, llvm::NoAliasAttribute) + .arg(idx, llvm::DereferenceableAttribute(llsz)); + + if mt.mutbl == ast::MutImmutable { + attrs.arg(idx, llvm::ReadOnlyAttribute); + } + + match b { + ReLateBound(_, BrAnon(_)) => { + attrs.arg(idx, llvm::NoCaptureAttribute); + } + _ => {} + } + } + + // When a reference in an argument has no named lifetime, it's impossible for that + // reference to escape this function (returned or stored beyond the call by a closure). + ty::ty_rptr(ReLateBound(_, BrAnon(_)), mt) => { + let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); + attrs.arg(idx, llvm::NoCaptureAttribute) + .arg(idx, llvm::DereferenceableAttribute(llsz)); + } + + // & pointer parameters are also never null and we know exactly how + // many bytes we can dereference + ty::ty_rptr(_, mt) => { + let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); + attrs.arg(idx, llvm::DereferenceableAttribute(llsz)); + } + _ => () + } + } + + attrs +} + +// only use this for foreign function ABIs and glue, use `register_fn` for Rust functions +pub fn register_fn_llvmty(ccx: &CrateContext, + sp: Span, + sym: String, + node_id: ast::NodeId, + cc: llvm::CallConv, + llfty: Type) -> ValueRef { + debug!("register_fn_llvmty id={} sym={}", node_id, sym); + + let llfn = decl_fn(ccx, sym.as_slice(), cc, llfty, ty::FnConverging(ty::mk_nil(ccx.tcx()))); + finish_register_fn(ccx, sp, sym, node_id, llfn); + llfn +} + +pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { + match *sess.entry_fn.borrow() { + Some((entry_id, _)) => node_id == entry_id, + None => false + } +} + +// Create a _rust_main(args: ~[str]) function which will be called from the +// runtime rust_start function +pub fn create_entry_wrapper(ccx: &CrateContext, + _sp: Span, + main_llfn: ValueRef) { + let et = ccx.sess().entry_type.get().unwrap(); + match et { + config::EntryMain => { + create_entry_fn(ccx, main_llfn, true); + } + config::EntryStart => create_entry_fn(ccx, main_llfn, false), + config::EntryNone => {} // Do nothing. + } + + fn create_entry_fn(ccx: &CrateContext, + rust_main: ValueRef, + use_start_lang_item: bool) { + let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], + &ccx.int_type()); + + let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil(ccx.tcx())); + + // FIXME: #16581: Marking a symbol in the executable with `dllexport` + // linkage forces MinGW's linker to output a `.reloc` section for ASLR + if ccx.sess().target.target.options.is_like_windows { + unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) } + } + + let llbb = "top".with_c_str(|buf| { + unsafe { + llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, buf) + } + }); + let bld = ccx.raw_builder(); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bld, llbb); + + let (start_fn, args) = if use_start_lang_item { + let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) { + Ok(id) => id, + Err(s) => { ccx.sess().fatal(s.as_slice()); } + }; + let start_fn = if start_def_id.krate == ast::LOCAL_CRATE { + get_item_val(ccx, start_def_id.node) + } else { + let start_fn_type = csearch::get_type(ccx.tcx(), + start_def_id).ty; + trans_external_path(ccx, start_def_id, start_fn_type) + }; + + let args = { + let opaque_rust_main = "rust_main".with_c_str(|buf| { + llvm::LLVMBuildPointerCast(bld, rust_main, Type::i8p(ccx).to_ref(), buf) + }); + + vec!( + opaque_rust_main, + get_param(llfn, 0), + get_param(llfn, 1) + ) + }; + (start_fn, args) + } else { + debug!("using user-defined start fn"); + let args = vec!( + get_param(llfn, 0 as c_uint), + get_param(llfn, 1 as c_uint) + ); + + (rust_main, args) + }; + + let result = llvm::LLVMBuildCall(bld, + start_fn, + args.as_ptr(), + args.len() as c_uint, + noname()); + + llvm::LLVMBuildRet(bld, result); + } + } +} + +fn exported_name(ccx: &CrateContext, id: ast::NodeId, + ty: ty::t, attrs: &[ast::Attribute]) -> String { + match ccx.external_srcs().borrow().get(&id) { + Some(&did) => { + let sym = csearch::get_symbol(&ccx.sess().cstore, did); + debug!("found item {} in other crate...", sym); + return sym; + } + None => {} + } + + match attr::first_attr_value_str_by_name(attrs, "export_name") { + // Use provided name + Some(name) => name.get().to_string(), + + _ => ccx.tcx().map.with_path(id, |mut path| { + if attr::contains_name(attrs, "no_mangle") { + // Don't mangle + path.last().unwrap().to_string() + } else { + match weak_lang_items::link_name(attrs) { + Some(name) => name.get().to_string(), + None => { + // Usual name mangling + mangle_exported_name(ccx, path, ty, id) + } + } + } + }) + } +} + +fn contains_null(s: &str) -> bool { + s.bytes().any(|b| b == 0) +} + +pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { + debug!("get_item_val(id=`{}`)", id); + + match ccx.item_vals().borrow().get(&id).cloned() { + Some(v) => return v, + None => {} + } + + let item = ccx.tcx().map.get(id); + let val = match item { + ast_map::NodeItem(i) => { + let ty = ty::node_id_to_type(ccx.tcx(), i.id); + let sym = || exported_name(ccx, id, ty, i.attrs.as_slice()); + + let v = match i.node { + ast::ItemStatic(_, _, ref expr) => { + // If this static came from an external crate, then + // we need to get the symbol from csearch instead of + // using the current crate's name/version + // information in the hash of the symbol + let sym = sym(); + debug!("making {}", sym); + + // We need the translated value here, because for enums the + // LLVM type is not fully determined by the Rust type. + let (v, ty) = consts::const_expr(ccx, &**expr); + ccx.static_values().borrow_mut().insert(id, v); + unsafe { + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let llty = if ty::type_is_bool(ty) { + llvm::LLVMInt8TypeInContext(ccx.llcx()) + } else { + llvm::LLVMTypeOf(v) + }; + if contains_null(sym.as_slice()) { + ccx.sess().fatal( + format!("Illegal null byte in export_name \ + value: `{}`", sym).as_slice()); + } + let g = sym.as_slice().with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), llty, buf) + }); + + if attr::contains_name(i.attrs.as_slice(), + "thread_local") { + llvm::set_thread_local(g, true); + } + ccx.item_symbols().borrow_mut().insert(i.id, sym); + g + } + } + + ast::ItemConst(_, ref expr) => { + let (v, _) = consts::const_expr(ccx, &**expr); + ccx.const_values().borrow_mut().insert(id, v); + v + } + + ast::ItemFn(_, _, abi, _, _) => { + let sym = sym(); + let llfn = if abi == Rust { + register_fn(ccx, i.span, sym, i.id, ty) + } else { + foreign::register_rust_fn_with_foreign_abi(ccx, + i.span, + sym, + i.id) + }; + set_llvm_fn_attrs(ccx, i.attrs.as_slice(), llfn); + llfn + } + + _ => panic!("get_item_val: weird result in table") + }; + + match attr::first_attr_value_str_by_name(i.attrs.as_slice(), + "link_section") { + Some(sect) => { + if contains_null(sect.get()) { + ccx.sess().fatal(format!("Illegal null byte in link_section value: `{}`", + sect.get()).as_slice()); + } + unsafe { + sect.get().with_c_str(|buf| { + llvm::LLVMSetSection(v, buf); + }) + } + }, + None => () + } + + v + } + + ast_map::NodeTraitItem(trait_method) => { + debug!("get_item_val(): processing a NodeTraitItem"); + match *trait_method { + ast::RequiredMethod(_) | ast::TypeTraitItem(_) => { + ccx.sess().bug("unexpected variant: required trait \ + method in get_item_val()"); + } + ast::ProvidedMethod(ref m) => { + register_method(ccx, id, &**m) + } + } + } + + ast_map::NodeImplItem(ii) => { + match *ii { + ast::MethodImplItem(ref m) => register_method(ccx, id, &**m), + ast::TypeImplItem(ref typedef) => { + ccx.sess().span_bug(typedef.span, + "unexpected variant: required impl \ + method in get_item_val()") + } + } + } + + ast_map::NodeForeignItem(ni) => { + match ni.node { + ast::ForeignItemFn(..) => { + let abi = ccx.tcx().map.get_foreign_abi(id); + let ty = ty::node_id_to_type(ccx.tcx(), ni.id); + let name = foreign::link_name(&*ni); + foreign::register_foreign_item_fn(ccx, abi, ty, name.get().as_slice()) + } + ast::ForeignItemStatic(..) => { + foreign::register_static(ccx, &*ni) + } + } + } + + ast_map::NodeVariant(ref v) => { + let llfn; + let args = match v.node.kind { + ast::TupleVariantKind(ref args) => args, + ast::StructVariantKind(_) => { + panic!("struct variant kind unexpected in get_item_val") + } + }; + assert!(args.len() != 0u); + let ty = ty::node_id_to_type(ccx.tcx(), id); + let parent = ccx.tcx().map.get_parent(id); + let enm = ccx.tcx().map.expect_item(parent); + let sym = exported_name(ccx, + id, + ty, + enm.attrs.as_slice()); + + llfn = match enm.node { + ast::ItemEnum(_, _) => { + register_fn(ccx, (*v).span, sym, id, ty) + } + _ => panic!("NodeVariant, shouldn't happen") + }; + set_inline_hint(llfn); + llfn + } + + ast_map::NodeStructCtor(struct_def) => { + // Only register the constructor if this is a tuple-like struct. + let ctor_id = match struct_def.ctor_id { + None => { + ccx.sess().bug("attempt to register a constructor of \ + a non-tuple-like struct") + } + Some(ctor_id) => ctor_id, + }; + let parent = ccx.tcx().map.get_parent(id); + let struct_item = ccx.tcx().map.expect_item(parent); + let ty = ty::node_id_to_type(ccx.tcx(), ctor_id); + let sym = exported_name(ccx, + id, + ty, + struct_item.attrs + .as_slice()); + let llfn = register_fn(ccx, struct_item.span, + sym, ctor_id, ty); + set_inline_hint(llfn); + llfn + } + + ref variant => { + ccx.sess().bug(format!("get_item_val(): unexpected variant: {}", + variant).as_slice()) + } + }; + + // All LLVM globals and functions are initially created as external-linkage + // declarations. If `trans_item`/`trans_fn` later turns the declaration + // into a definition, it adjusts the linkage then (using `update_linkage`). + // + // The exception is foreign items, which have their linkage set inside the + // call to `foreign::register_*` above. We don't touch the linkage after + // that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the + // other item translation functions do). + + ccx.item_vals().borrow_mut().insert(id, val); + val +} + +fn register_method(ccx: &CrateContext, id: ast::NodeId, + m: &ast::Method) -> ValueRef { + let mty = ty::node_id_to_type(ccx.tcx(), id); + + let sym = exported_name(ccx, id, mty, m.attrs.as_slice()); + + let llfn = register_fn(ccx, m.span, sym, id, mty); + set_llvm_fn_attrs(ccx, m.attrs.as_slice(), llfn); + llfn +} + +pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef { + unsafe { + return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref()); + } +} + +pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'tcx>, + ie: encoder::EncodeInlinedItem<'a>) + -> encoder::EncodeParams<'a, 'tcx> { + encoder::EncodeParams { + diag: cx.sess().diagnostic(), + tcx: cx.tcx(), + reexports2: cx.exp_map2(), + item_symbols: cx.item_symbols(), + link_meta: cx.link_meta(), + cstore: &cx.sess().cstore, + encode_inlined_item: ie, + reachable: cx.reachable(), + } +} + +pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { + use flate; + + let any_library = cx.sess().crate_types.borrow().iter().any(|ty| { + *ty != config::CrateTypeExecutable + }); + if !any_library { + return Vec::new() + } + + let encode_inlined_item: encoder::EncodeInlinedItem = + |ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii); + + let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item); + let metadata = encoder::encode_metadata(encode_parms, krate); + let mut compressed = encoder::metadata_encoding_version.to_vec(); + compressed.push_all(match flate::deflate_bytes(metadata.as_slice()) { + Some(compressed) => compressed, + None => cx.sess().fatal("failed to compress metadata"), + }.as_slice()); + let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice()); + let llconst = C_struct_in_context(cx.metadata_llcx(), &[llmeta], false); + let name = format!("rust_metadata_{}_{}", + cx.link_meta().crate_name, + cx.link_meta().crate_hash); + let llglobal = name.with_c_str(|buf| { + unsafe { + llvm::LLVMAddGlobal(cx.metadata_llmod(), val_ty(llconst).to_ref(), buf) + } + }); + unsafe { + llvm::LLVMSetInitializer(llglobal, llconst); + let name = loader::meta_section_name(cx.sess().target.target.options.is_like_osx); + name.with_c_str(|buf| { + llvm::LLVMSetSection(llglobal, buf) + }); + } + return metadata; +} + +/// Find any symbols that are defined in one compilation unit, but not declared +/// in any other compilation unit. Give these symbols internal linkage. +fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { + use std::c_str::CString; + + unsafe { + let mut declared = HashSet::new(); + + let iter_globals = |llmod| { + ValueIter { + cur: llvm::LLVMGetFirstGlobal(llmod), + step: llvm::LLVMGetNextGlobal, + } + }; + + let iter_functions = |llmod| { + ValueIter { + cur: llvm::LLVMGetFirstFunction(llmod), + step: llvm::LLVMGetNextFunction, + } + }; + + // Collect all external declarations in all compilation units. + for ccx in cx.iter() { + for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { + let linkage = llvm::LLVMGetLinkage(val); + // We only care about external declarations (not definitions) + // and available_externally definitions. + if !(linkage == llvm::ExternalLinkage as c_uint && + llvm::LLVMIsDeclaration(val) != 0) && + !(linkage == llvm::AvailableExternallyLinkage as c_uint) { + continue + } + + let name = CString::new(llvm::LLVMGetValueName(val), false); + declared.insert(name); + } + } + + // Examine each external definition. If the definition is not used in + // any other compilation unit, and is not reachable from other crates, + // then give it internal linkage. + for ccx in cx.iter() { + for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { + // We only care about external definitions. + if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint && + llvm::LLVMIsDeclaration(val) == 0) { + continue + } + + let name = CString::new(llvm::LLVMGetValueName(val), false); + if !declared.contains(&name) && + !reachable.contains(name.as_str().unwrap()) { + llvm::SetLinkage(val, llvm::InternalLinkage); + } + } + } + } + + + struct ValueIter { + cur: ValueRef, + step: unsafe extern "C" fn(ValueRef) -> ValueRef, + } + + impl Iterator for ValueIter { + fn next(&mut self) -> Option { + let old = self.cur; + if !old.is_null() { + self.cur = unsafe { (self.step)(old) }; + Some(old) + } else { + None + } + } + } +} + +pub fn trans_crate<'tcx>(analysis: CrateAnalysis<'tcx>) + -> (ty::ctxt<'tcx>, CrateTranslation) { + let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis; + let krate = tcx.map.krate(); + + // Before we touch LLVM, make sure that multithreading is enabled. + unsafe { + use std::sync::{Once, ONCE_INIT}; + static INIT: Once = ONCE_INIT; + static mut POISONED: bool = false; + INIT.doit(|| { + if llvm::LLVMStartMultithreaded() != 1 { + // use an extra bool to make sure that all future usage of LLVM + // cannot proceed despite the Once not running more than once. + POISONED = true; + } + }); + + if POISONED { + tcx.sess.bug("couldn't enable multi-threaded LLVM"); + } + } + + let link_meta = link::build_link_meta(&tcx.sess, krate, name); + + let codegen_units = tcx.sess.opts.cg.codegen_units; + let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(), + codegen_units, + tcx, + exp_map2, + Sha256::new(), + link_meta.clone(), + reachable); + + { + let ccx = shared_ccx.get_ccx(0); + + // First, verify intrinsics. + intrinsic::check_intrinsics(&ccx); + + // Next, translate the module. + { + let _icx = push_ctxt("text"); + trans_mod(&ccx, &krate.module); + } + } + + for ccx in shared_ccx.iter() { + glue::emit_tydescs(&ccx); + if ccx.sess().opts.debuginfo != NoDebugInfo { + debuginfo::finalize(&ccx); + } + } + + // Translate the metadata. + let metadata = write_metadata(&shared_ccx, krate); + + if shared_ccx.sess().trans_stats() { + let stats = shared_ccx.stats(); + println!("--- trans stats ---"); + println!("n_static_tydescs: {}", stats.n_static_tydescs.get()); + println!("n_glues_created: {}", stats.n_glues_created.get()); + println!("n_null_glues: {}", stats.n_null_glues.get()); + println!("n_real_glues: {}", stats.n_real_glues.get()); + + println!("n_fns: {}", stats.n_fns.get()); + println!("n_monos: {}", stats.n_monos.get()); + println!("n_inlines: {}", stats.n_inlines.get()); + println!("n_closures: {}", stats.n_closures.get()); + println!("fn stats:"); + stats.fn_stats.borrow_mut().sort_by(|&(_, insns_a), &(_, insns_b)| { + insns_b.cmp(&insns_a) + }); + for tuple in stats.fn_stats.borrow().iter() { + match *tuple { + (ref name, insns) => { + println!("{} insns, {}", insns, *name); + } + } + } + } + if shared_ccx.sess().count_llvm_insns() { + for (k, v) in shared_ccx.stats().llvm_insns.borrow().iter() { + println!("{:7u} {}", *v, *k); + } + } + + let modules = shared_ccx.iter() + .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) + .collect(); + + let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { + shared_ccx.item_symbols().borrow().get(id).map(|s| s.to_string()) + }).collect(); + + // For the purposes of LTO, we add to the reachable set all of the upstream + // reachable extern fns. These functions are all part of the public ABI of + // the final product, so LTO needs to preserve them. + shared_ccx.sess().cstore.iter_crate_data(|cnum, _| { + let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum); + reachable.extend(syms.into_iter().map(|did| { + csearch::get_symbol(&shared_ccx.sess().cstore, did) + })); + }); + + // Make sure that some other crucial symbols are not eliminated from the + // module. This includes the main function, the crate map (used for debug + // log settings and I/O), and finally the curious rust_stack_exhausted + // symbol. This symbol is required for use by the libmorestack library that + // we link in, so we must ensure that this symbol is not internalized (if + // defined in the crate). + reachable.push("main".to_string()); + reachable.push("rust_stack_exhausted".to_string()); + + // referenced from .eh_frame section on some platforms + reachable.push("rust_eh_personality".to_string()); + // referenced from rt/rust_try.ll + reachable.push("rust_eh_personality_catch".to_string()); + + if codegen_units > 1 { + internalize_symbols(&shared_ccx, &reachable.iter().map(|x| x.clone()).collect()); + } + + let metadata_module = ModuleTranslation { + llcx: shared_ccx.metadata_llcx(), + llmod: shared_ccx.metadata_llmod(), + }; + let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); + let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins"); + + let translation = CrateTranslation { + modules: modules, + metadata_module: metadata_module, + link: link_meta, + metadata: metadata, + reachable: reachable, + crate_formats: formats, + no_builtins: no_builtins, + }; + + (shared_ccx.take_tcx(), translation) +} diff --git a/src/librustc_trans/trans/basic_block.rs b/src/librustc_trans/trans/basic_block.rs new file mode 100644 index 00000000000..35afe7ac55e --- /dev/null +++ b/src/librustc_trans/trans/basic_block.rs @@ -0,0 +1,47 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; +use llvm::{BasicBlockRef}; +use trans::value::{Users, Value}; +use std::iter::{Filter, Map}; + +pub struct BasicBlock(pub BasicBlockRef); + +pub type Preds<'a> = Map<'a, Value, BasicBlock, Filter<'a, Value, Users>>; + +/** + * Wrapper for LLVM BasicBlockRef + */ +impl BasicBlock { + pub fn get(&self) -> BasicBlockRef { + let BasicBlock(v) = *self; v + } + + pub fn as_value(self) -> Value { + unsafe { + Value(llvm::LLVMBasicBlockAsValue(self.get())) + } + } + + pub fn pred_iter(self) -> Preds<'static> { + self.as_value().user_iter() + .filter(|user| user.is_a_terminator_inst()) + .map(|user| user.get_parent().unwrap()) + } + + pub fn get_single_predecessor(self) -> Option { + let mut iter = self.pred_iter(); + match (iter.next(), iter.next()) { + (Some(first), None) => Some(first), + _ => None + } + } +} diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs new file mode 100644 index 00000000000..1f77f625c9d --- /dev/null +++ b/src/librustc_trans/trans/build.rs @@ -0,0 +1,827 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // FFI wrappers +#![allow(non_snake_case)] + +use llvm; +use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; +use llvm::{Opcode, IntPredicate, RealPredicate}; +use llvm::{ValueRef, BasicBlockRef}; +use trans::common::*; +use syntax::codemap::Span; + +use trans::builder::Builder; +use trans::type_::Type; + +use libc::{c_uint, c_char}; + +pub fn terminate(cx: Block, _: &str) { + debug!("terminate({})", cx.to_str()); + cx.terminated.set(true); +} + +pub fn check_not_terminated(cx: Block) { + if cx.terminated.get() { + panic!("already terminated!"); + } +} + +pub fn B<'blk, 'tcx>(cx: Block<'blk, 'tcx>) -> Builder<'blk, 'tcx> { + let b = cx.fcx.ccx.builder(); + b.position_at_end(cx.llbb); + b +} + +// The difference between a block being unreachable and being terminated is +// somewhat obscure, and has to do with error checking. When a block is +// terminated, we're saying that trying to add any further statements in the +// block is an error. On the other hand, if something is unreachable, that +// means that the block was terminated in some way that we don't want to check +// for (panic/break/return statements, call to diverging functions, etc), and +// further instructions to the block should simply be ignored. + +pub fn RetVoid(cx: Block) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "RetVoid"); + B(cx).ret_void(); +} + +pub fn Ret(cx: Block, v: ValueRef) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "Ret"); + B(cx).ret(v); +} + +pub fn AggregateRet(cx: Block, ret_vals: &[ValueRef]) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "AggregateRet"); + B(cx).aggregate_ret(ret_vals); +} + +pub fn Br(cx: Block, dest: BasicBlockRef) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "Br"); + B(cx).br(dest); +} + +pub fn CondBr(cx: Block, + if_: ValueRef, + then: BasicBlockRef, + else_: BasicBlockRef) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "CondBr"); + B(cx).cond_br(if_, then, else_); +} + +pub fn Switch(cx: Block, v: ValueRef, else_: BasicBlockRef, num_cases: uint) + -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + check_not_terminated(cx); + terminate(cx, "Switch"); + B(cx).switch(v, else_, num_cases) +} + +pub fn AddCase(s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { + unsafe { + if llvm::LLVMIsUndef(s) == llvm::True { return; } + llvm::LLVMAddCase(s, on_val, dest); + } +} + +pub fn IndirectBr(cx: Block, addr: ValueRef, num_dests: uint) { + if cx.unreachable.get() { return; } + check_not_terminated(cx); + terminate(cx, "IndirectBr"); + B(cx).indirect_br(addr, num_dests); +} + +pub fn Invoke(cx: Block, + fn_: ValueRef, + args: &[ValueRef], + then: BasicBlockRef, + catch: BasicBlockRef, + attributes: Option) + -> ValueRef { + if cx.unreachable.get() { + return C_null(Type::i8(cx.ccx())); + } + check_not_terminated(cx); + terminate(cx, "Invoke"); + debug!("Invoke({} with arguments ({}))", + cx.val_to_string(fn_), + args.iter().map(|a| cx.val_to_string(*a)).collect::>().connect(", ")); + B(cx).invoke(fn_, args, then, catch, attributes) +} + +pub fn Unreachable(cx: Block) { + if cx.unreachable.get() { + return + } + cx.unreachable.set(true); + if !cx.terminated.get() { + B(cx).unreachable(); + } +} + +pub fn _Undef(val: ValueRef) -> ValueRef { + unsafe { + return llvm::LLVMGetUndef(val_ty(val).to_ref()); + } +} + +/* Arithmetic */ +pub fn Add(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).add(lhs, rhs) +} + +pub fn NSWAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nswadd(lhs, rhs) +} + +pub fn NUWAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nuwadd(lhs, rhs) +} + +pub fn FAdd(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).fadd(lhs, rhs) +} + +pub fn Sub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).sub(lhs, rhs) +} + +pub fn NSWSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nswsub(lhs, rhs) +} + +pub fn NUWSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nuwsub(lhs, rhs) +} + +pub fn FSub(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).fsub(lhs, rhs) +} + +pub fn Mul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).mul(lhs, rhs) +} + +pub fn NSWMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nswmul(lhs, rhs) +} + +pub fn NUWMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).nuwmul(lhs, rhs) +} + +pub fn FMul(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).fmul(lhs, rhs) +} + +pub fn UDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).udiv(lhs, rhs) +} + +pub fn SDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).sdiv(lhs, rhs) +} + +pub fn ExactSDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).exactsdiv(lhs, rhs) +} + +pub fn FDiv(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).fdiv(lhs, rhs) +} + +pub fn URem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).urem(lhs, rhs) +} + +pub fn SRem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).srem(lhs, rhs) +} + +pub fn FRem(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).frem(lhs, rhs) +} + +pub fn Shl(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).shl(lhs, rhs) +} + +pub fn LShr(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).lshr(lhs, rhs) +} + +pub fn AShr(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).ashr(lhs, rhs) +} + +pub fn And(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).and(lhs, rhs) +} + +pub fn Or(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).or(lhs, rhs) +} + +pub fn Xor(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).xor(lhs, rhs) +} + +pub fn BinOp(cx: Block, op: Opcode, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + if cx.unreachable.get() { return _Undef(lhs); } + B(cx).binop(op, lhs, rhs) +} + +pub fn Neg(cx: Block, v: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + B(cx).neg(v) +} + +pub fn NSWNeg(cx: Block, v: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + B(cx).nswneg(v) +} + +pub fn NUWNeg(cx: Block, v: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + B(cx).nuwneg(v) +} +pub fn FNeg(cx: Block, v: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + B(cx).fneg(v) +} + +pub fn Not(cx: Block, v: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(v); } + B(cx).not(v) +} + +/* Memory */ +pub fn Malloc(cx: Block, ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); + } + B(cx).malloc(ty) + } +} + +pub fn ArrayMalloc(cx: Block, ty: Type, val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); + } + B(cx).array_malloc(ty, val) + } +} + +pub fn Alloca(cx: Block, ty: Type, name: &str) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } + AllocaFcx(cx.fcx, ty, name) + } +} + +pub fn AllocaFcx(fcx: &FunctionContext, ty: Type, name: &str) -> ValueRef { + let b = fcx.ccx.builder(); + b.position_before(fcx.alloca_insert_pt.get().unwrap()); + b.alloca(ty, name) +} + +pub fn ArrayAlloca(cx: Block, ty: Type, val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } + let b = cx.fcx.ccx.builder(); + b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); + b.array_alloca(ty, val) + } +} + +pub fn Free(cx: Block, pointer_val: ValueRef) { + if cx.unreachable.get() { return; } + B(cx).free(pointer_val) +} + +pub fn Load(cx: Block, pointer_val: ValueRef) -> ValueRef { + unsafe { + let ccx = cx.fcx.ccx; + if cx.unreachable.get() { + let ty = val_ty(pointer_val); + let eltty = if ty.kind() == llvm::Array { + ty.element_type() + } else { + ccx.int_type() + }; + return llvm::LLVMGetUndef(eltty.to_ref()); + } + B(cx).load(pointer_val) + } +} + +pub fn VolatileLoad(cx: Block, pointer_val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).volatile_load(pointer_val) + } +} + +pub fn AtomicLoad(cx: Block, pointer_val: ValueRef, order: AtomicOrdering) -> ValueRef { + unsafe { + let ccx = cx.fcx.ccx; + if cx.unreachable.get() { + return llvm::LLVMGetUndef(ccx.int_type().to_ref()); + } + B(cx).atomic_load(pointer_val, order) + } +} + + +pub fn LoadRangeAssert(cx: Block, pointer_val: ValueRef, lo: u64, + hi: u64, signed: llvm::Bool) -> ValueRef { + if cx.unreachable.get() { + let ccx = cx.fcx.ccx; + let ty = val_ty(pointer_val); + let eltty = if ty.kind() == llvm::Array { + ty.element_type() + } else { + ccx.int_type() + }; + unsafe { + llvm::LLVMGetUndef(eltty.to_ref()) + } + } else { + B(cx).load_range_assert(pointer_val, lo, hi, signed) + } +} + +pub fn Store(cx: Block, val: ValueRef, ptr: ValueRef) { + if cx.unreachable.get() { return; } + B(cx).store(val, ptr) +} + +pub fn VolatileStore(cx: Block, val: ValueRef, ptr: ValueRef) { + if cx.unreachable.get() { return; } + B(cx).volatile_store(val, ptr) +} + +pub fn AtomicStore(cx: Block, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { + if cx.unreachable.get() { return; } + B(cx).atomic_store(val, ptr, order) +} + +pub fn GEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); + } + B(cx).gep(pointer, indices) + } +} + +// Simple wrapper around GEP that takes an array of ints and wraps them +// in C_i32() +#[inline] +pub fn GEPi(cx: Block, base: ValueRef, ixs: &[uint]) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); + } + B(cx).gepi(base, ixs) + } +} + +pub fn InBoundsGEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); + } + B(cx).inbounds_gep(pointer, indices) + } +} + +pub fn StructGEP(cx: Block, pointer: ValueRef, idx: uint) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); + } + B(cx).struct_gep(pointer, idx) + } +} + +pub fn GlobalString(cx: Block, _str: *const c_char) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); + } + B(cx).global_string(_str) + } +} + +pub fn GlobalStringPtr(cx: Block, _str: *const c_char) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); + } + B(cx).global_string_ptr(_str) + } +} + +/* Casts */ +pub fn Trunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).trunc(val, dest_ty) + } +} + +pub fn ZExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).zext(val, dest_ty) + } +} + +pub fn SExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).sext(val, dest_ty) + } +} + +pub fn FPToUI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).fptoui(val, dest_ty) + } +} + +pub fn FPToSI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).fptosi(val, dest_ty) + } +} + +pub fn UIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).uitofp(val, dest_ty) + } +} + +pub fn SIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).sitofp(val, dest_ty) + } +} + +pub fn FPTrunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).fptrunc(val, dest_ty) + } +} + +pub fn FPExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).fpext(val, dest_ty) + } +} + +pub fn PtrToInt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).ptrtoint(val, dest_ty) + } +} + +pub fn IntToPtr(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).inttoptr(val, dest_ty) + } +} + +pub fn BitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).bitcast(val, dest_ty) + } +} + +pub fn ZExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).zext_or_bitcast(val, dest_ty) + } +} + +pub fn SExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).sext_or_bitcast(val, dest_ty) + } +} + +pub fn TruncOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).trunc_or_bitcast(val, dest_ty) + } +} + +pub fn Cast(cx: Block, op: Opcode, val: ValueRef, dest_ty: Type, + _: *const u8) + -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).cast(op, val, dest_ty) + } +} + +pub fn PointerCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).pointercast(val, dest_ty) + } +} + +pub fn IntCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).intcast(val, dest_ty) + } +} + +pub fn FPCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } + B(cx).fpcast(val, dest_ty) + } +} + + +/* Comparisons */ +pub fn ICmp(cx: Block, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); + } + B(cx).icmp(op, lhs, rhs) + } +} + +pub fn FCmp(cx: Block, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); + } + B(cx).fcmp(op, lhs, rhs) + } +} + +/* Miscellaneous instructions */ +pub fn EmptyPhi(cx: Block, ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } + B(cx).empty_phi(ty) + } +} + +pub fn Phi(cx: Block, ty: Type, vals: &[ValueRef], + bbs: &[BasicBlockRef]) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } + B(cx).phi(ty, vals, bbs) + } +} + +pub fn AddIncomingToPhi(phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { + unsafe { + if llvm::LLVMIsUndef(phi) == llvm::True { return; } + llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); + } +} + +pub fn _UndefReturn(cx: Block, fn_: ValueRef) -> ValueRef { + unsafe { + let ccx = cx.fcx.ccx; + let ty = val_ty(fn_); + let retty = if ty.kind() == llvm::Function { + ty.return_type() + } else { + ccx.int_type() + }; + B(cx).count_insn("ret_undef"); + llvm::LLVMGetUndef(retty.to_ref()) + } +} + +pub fn add_span_comment(cx: Block, sp: Span, text: &str) { + B(cx).add_span_comment(sp, text) +} + +pub fn add_comment(cx: Block, text: &str) { + B(cx).add_comment(text) +} + +pub fn InlineAsmCall(cx: Block, asm: *const c_char, cons: *const c_char, + inputs: &[ValueRef], output: Type, + volatile: bool, alignstack: bool, + dia: AsmDialect) -> ValueRef { + B(cx).inline_asm_call(asm, cons, inputs, output, volatile, alignstack, dia) +} + +pub fn Call(cx: Block, fn_: ValueRef, args: &[ValueRef], + attributes: Option) -> ValueRef { + if cx.unreachable.get() { return _UndefReturn(cx, fn_); } + B(cx).call(fn_, args, attributes) +} + +pub fn CallWithConv(cx: Block, fn_: ValueRef, args: &[ValueRef], conv: CallConv, + attributes: Option) -> ValueRef { + if cx.unreachable.get() { return _UndefReturn(cx, fn_); } + B(cx).call_with_conv(fn_, args, conv, attributes) +} + +pub fn AtomicFence(cx: Block, order: AtomicOrdering) { + if cx.unreachable.get() { return; } + B(cx).atomic_fence(order) +} + +pub fn Select(cx: Block, if_: ValueRef, then: ValueRef, else_: ValueRef) -> ValueRef { + if cx.unreachable.get() { return _Undef(then); } + B(cx).select(if_, then, else_) +} + +pub fn VAArg(cx: Block, list: ValueRef, ty: Type) -> ValueRef { + unsafe { + if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } + B(cx).va_arg(list, ty) + } +} + +pub fn ExtractElement(cx: Block, vec_val: ValueRef, index: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).extract_element(vec_val, index) + } +} + +pub fn InsertElement(cx: Block, vec_val: ValueRef, elt_val: ValueRef, + index: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).insert_element(vec_val, elt_val, index) + } +} + +pub fn ShuffleVector(cx: Block, v1: ValueRef, v2: ValueRef, + mask: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).shuffle_vector(v1, v2, mask) + } +} + +pub fn VectorSplat(cx: Block, num_elts: uint, elt_val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).vector_splat(num_elts, elt_val) + } +} + +pub fn ExtractValue(cx: Block, agg_val: ValueRef, index: uint) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).extract_value(agg_val, index) + } +} + +pub fn InsertValue(cx: Block, agg_val: ValueRef, elt_val: ValueRef, index: uint) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); + } + B(cx).insert_value(agg_val, elt_val, index) + } +} + +pub fn IsNull(cx: Block, val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); + } + B(cx).is_null(val) + } +} + +pub fn IsNotNull(cx: Block, val: ValueRef) -> ValueRef { + unsafe { + if cx.unreachable.get() { + return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); + } + B(cx).is_not_null(val) + } +} + +pub fn PtrDiff(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + unsafe { + let ccx = cx.fcx.ccx; + if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type().to_ref()); } + B(cx).ptrdiff(lhs, rhs) + } +} + +pub fn Trap(cx: Block) { + if cx.unreachable.get() { return; } + B(cx).trap(); +} + +pub fn LandingPad(cx: Block, ty: Type, pers_fn: ValueRef, + num_clauses: uint) -> ValueRef { + check_not_terminated(cx); + assert!(!cx.unreachable.get()); + B(cx).landing_pad(ty, pers_fn, num_clauses) +} + +pub fn SetCleanup(cx: Block, landing_pad: ValueRef) { + B(cx).set_cleanup(landing_pad) +} + +pub fn Resume(cx: Block, exn: ValueRef) -> ValueRef { + check_not_terminated(cx); + terminate(cx, "Resume"); + B(cx).resume(exn) +} + +// Atomic Operations +pub fn AtomicCmpXchg(cx: Block, dst: ValueRef, + cmp: ValueRef, src: ValueRef, + order: AtomicOrdering, + failure_order: AtomicOrdering) -> ValueRef { + B(cx).atomic_cmpxchg(dst, cmp, src, order, failure_order) +} +pub fn AtomicRMW(cx: Block, op: AtomicBinOp, + dst: ValueRef, src: ValueRef, + order: AtomicOrdering) -> ValueRef { + B(cx).atomic_rmw(op, dst, src, order) +} diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs new file mode 100644 index 00000000000..526592181ae --- /dev/null +++ b/src/librustc_trans/trans/builder.rs @@ -0,0 +1,985 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // FFI wrappers + +use llvm; +use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; +use llvm::{Opcode, IntPredicate, RealPredicate, False}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; +use trans::base; +use trans::common::*; +use trans::machine::llalign_of_pref; +use trans::type_::Type; +use util::nodemap::FnvHashMap; +use libc::{c_uint, c_char}; +use std::string::String; +use syntax::codemap::Span; + +pub struct Builder<'a, 'tcx: 'a> { + pub llbuilder: BuilderRef, + pub ccx: &'a CrateContext<'a, 'tcx>, +} + +// This is a really awful way to get a zero-length c-string, but better (and a +// lot more efficient) than doing str::as_c_str("", ...) every time. +pub fn noname() -> *const c_char { + static CNULL: c_char = 0; + &CNULL as *const c_char +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + pub fn new(ccx: &'a CrateContext<'a, 'tcx>) -> Builder<'a, 'tcx> { + Builder { + llbuilder: ccx.raw_builder(), + ccx: ccx, + } + } + + pub fn count_insn(&self, category: &str) { + if self.ccx.sess().trans_stats() { + self.ccx.stats().n_llvm_insns.set(self.ccx + .stats() + .n_llvm_insns + .get() + 1); + } + self.ccx.count_llvm_insn(); + if self.ccx.sess().count_llvm_insns() { + base::with_insn_ctxt(|v| { + let mut h = self.ccx.stats().llvm_insns.borrow_mut(); + + // Build version of path with cycles removed. + + // Pass 1: scan table mapping str -> rightmost pos. + let mut mm = FnvHashMap::new(); + let len = v.len(); + let mut i = 0u; + while i < len { + mm.insert(v[i], i); + i += 1u; + } + + // Pass 2: concat strings for each elt, skipping + // forwards over any cycles by advancing to rightmost + // occurrence of each element in path. + let mut s = String::from_str("."); + i = 0u; + while i < len { + i = mm[v[i]]; + s.push('/'); + s.push_str(v[i]); + i += 1u; + } + + s.push('/'); + s.push_str(category); + + let n = match h.get(&s) { + Some(&n) => n, + _ => 0u + }; + h.insert(s, n+1u); + }) + } + } + + pub fn position_before(&self, insn: ValueRef) { + unsafe { + llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); + } + } + + pub fn position_at_end(&self, llbb: BasicBlockRef) { + unsafe { + llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); + } + } + + pub fn ret_void(&self) { + self.count_insn("retvoid"); + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + pub fn ret(&self, v: ValueRef) { + self.count_insn("ret"); + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } + + pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { + unsafe { + llvm::LLVMBuildAggregateRet(self.llbuilder, + ret_vals.as_ptr(), + ret_vals.len() as c_uint); + } + } + + pub fn br(&self, dest: BasicBlockRef) { + self.count_insn("br"); + unsafe { + llvm::LLVMBuildBr(self.llbuilder, dest); + } + } + + pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { + self.count_insn("condbr"); + unsafe { + llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); + } + } + + pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: uint) -> ValueRef { + unsafe { + llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) + } + } + + pub fn indirect_br(&self, addr: ValueRef, num_dests: uint) { + self.count_insn("indirectbr"); + unsafe { + llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); + } + } + + pub fn invoke(&self, + llfn: ValueRef, + args: &[ValueRef], + then: BasicBlockRef, + catch: BasicBlockRef, + attributes: Option) + -> ValueRef { + self.count_insn("invoke"); + + debug!("Invoke {} with args ({})", + self.ccx.tn().val_to_string(llfn), + args.iter() + .map(|&v| self.ccx.tn().val_to_string(v)) + .collect::>() + .connect(", ")); + + unsafe { + let v = llvm::LLVMBuildInvoke(self.llbuilder, + llfn, + args.as_ptr(), + args.len() as c_uint, + then, + catch, + noname()); + match attributes { + Some(a) => a.apply_callsite(v), + None => {} + } + v + } + } + + pub fn unreachable(&self) { + self.count_insn("unreachable"); + unsafe { + llvm::LLVMBuildUnreachable(self.llbuilder); + } + } + + /* Arithmetic */ + pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("add"); + unsafe { + llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswadd"); + unsafe { + llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwadd"); + unsafe { + llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fadd"); + unsafe { + llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nwsub"); + unsafe { + llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwsub"); + unsafe { + llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("mul"); + unsafe { + llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswmul"); + unsafe { + llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwmul"); + unsafe { + llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fmul"); + unsafe { + llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("udiv"); + unsafe { + llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sdiv"); + unsafe { + llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("exactsdiv"); + unsafe { + llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fdiv"); + unsafe { + llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("urem"); + unsafe { + llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("srem"); + unsafe { + llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("frem"); + unsafe { + llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("shl"); + unsafe { + llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("lshr"); + unsafe { + llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ashr"); + unsafe { + llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("and"); + unsafe { + llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("or"); + unsafe { + llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("xor"); + unsafe { + llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + self.count_insn("binop"); + unsafe { + llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) + } + } + + pub fn neg(&self, v: ValueRef) -> ValueRef { + self.count_insn("neg"); + unsafe { + llvm::LLVMBuildNeg(self.llbuilder, v, noname()) + } + } + + pub fn nswneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nswneg"); + unsafe { + llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) + } + } + + pub fn nuwneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nuwneg"); + unsafe { + llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) + } + } + pub fn fneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("fneg"); + unsafe { + llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) + } + } + + pub fn not(&self, v: ValueRef) -> ValueRef { + self.count_insn("not"); + unsafe { + llvm::LLVMBuildNot(self.llbuilder, v, noname()) + } + } + + /* Memory */ + pub fn malloc(&self, ty: Type) -> ValueRef { + self.count_insn("malloc"); + unsafe { + llvm::LLVMBuildMalloc(self.llbuilder, ty.to_ref(), noname()) + } + } + + pub fn array_malloc(&self, ty: Type, val: ValueRef) -> ValueRef { + self.count_insn("arraymalloc"); + unsafe { + llvm::LLVMBuildArrayMalloc(self.llbuilder, ty.to_ref(), val, noname()) + } + } + + pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + self.count_insn("alloca"); + unsafe { + if name.is_empty() { + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) + } else { + name.with_c_str(|c| { + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), c) + }) + } + } + } + + pub fn array_alloca(&self, ty: Type, val: ValueRef) -> ValueRef { + self.count_insn("arrayalloca"); + unsafe { + llvm::LLVMBuildArrayAlloca(self.llbuilder, ty.to_ref(), val, noname()) + } + } + + pub fn free(&self, ptr: ValueRef) { + self.count_insn("free"); + unsafe { + llvm::LLVMBuildFree(self.llbuilder, ptr); + } + } + + pub fn load(&self, ptr: ValueRef) -> ValueRef { + self.count_insn("load"); + unsafe { + llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()) + } + } + + pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { + self.count_insn("load.volatile"); + unsafe { + let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + llvm::LLVMSetVolatile(insn, llvm::True); + insn + } + } + + pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering) -> ValueRef { + self.count_insn("load.atomic"); + unsafe { + let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); + let align = llalign_of_pref(self.ccx, ty.element_type()); + llvm::LLVMBuildAtomicLoad(self.llbuilder, ptr, noname(), order, + align as c_uint) + } + } + + + pub fn load_range_assert(&self, ptr: ValueRef, lo: u64, + hi: u64, signed: llvm::Bool) -> ValueRef { + let value = self.load(ptr); + + unsafe { + let t = llvm::LLVMGetElementType(llvm::LLVMTypeOf(ptr)); + let min = llvm::LLVMConstInt(t, lo, signed); + let max = llvm::LLVMConstInt(t, hi, signed); + + let v = [min, max]; + + llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint, + llvm::LLVMMDNodeInContext(self.ccx.llcx(), + v.as_ptr(), + v.len() as c_uint)); + } + + value + } + + pub fn store(&self, val: ValueRef, ptr: ValueRef) { + debug!("Store {} -> {}", + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); + assert!(self.llbuilder.is_not_null()); + self.count_insn("store"); + unsafe { + llvm::LLVMBuildStore(self.llbuilder, val, ptr); + } + } + + pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) { + debug!("Store {} -> {}", + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); + assert!(self.llbuilder.is_not_null()); + self.count_insn("store.volatile"); + unsafe { + let insn = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + llvm::LLVMSetVolatile(insn, llvm::True); + } + } + + pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { + debug!("Store {} -> {}", + self.ccx.tn().val_to_string(val), + self.ccx.tn().val_to_string(ptr)); + self.count_insn("store.atomic"); + unsafe { + let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); + let align = llalign_of_pref(self.ccx, ty.element_type()); + llvm::LLVMBuildAtomicStore(self.llbuilder, val, ptr, order, align as c_uint); + } + } + + pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("gep"); + unsafe { + llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), + indices.len() as c_uint, noname()) + } + } + + // Simple wrapper around GEP that takes an array of ints and wraps them + // in C_i32() + #[inline] + pub fn gepi(&self, base: ValueRef, ixs: &[uint]) -> ValueRef { + // Small vector optimization. This should catch 100% of the cases that + // we care about. + if ixs.len() < 16 { + let mut small_vec = [ C_i32(self.ccx, 0), ..16 ]; + for (small_vec_e, &ix) in small_vec.iter_mut().zip(ixs.iter()) { + *small_vec_e = C_i32(self.ccx, ix as i32); + } + self.inbounds_gep(base, small_vec[..ixs.len()]) + } else { + let v = ixs.iter().map(|i| C_i32(self.ccx, *i as i32)).collect::>(); + self.count_insn("gepi"); + self.inbounds_gep(base, v.as_slice()) + } + } + + pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("inboundsgep"); + unsafe { + llvm::LLVMBuildInBoundsGEP( + self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) + } + } + + pub fn struct_gep(&self, ptr: ValueRef, idx: uint) -> ValueRef { + self.count_insn("structgep"); + unsafe { + llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) + } + } + + pub fn global_string(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstring"); + unsafe { + llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) + } + } + + pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstringptr"); + unsafe { + llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) + } + } + + /* Casts */ + pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("trunc"); + unsafe { + llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zext"); + unsafe { + llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sext"); + unsafe { + llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptoui"); + unsafe { + llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptosi"); + unsafe { + llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) + } + } + + pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("uitofp"); + unsafe { + llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sitofp"); + unsafe { + llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptrunc"); + unsafe { + llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpext"); + unsafe { + llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("ptrtoint"); + unsafe { + llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("inttoptr"); + unsafe { + llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("bitcast"); + unsafe { + llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zextorbitcast"); + unsafe { + llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sextorbitcast"); + unsafe { + llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("truncorbitcast"); + unsafe { + llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("cast"); + unsafe { + llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) + } + } + + pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("pointercast"); + unsafe { + llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn intcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("intcast"); + unsafe { + llvm::LLVMBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpcast"); + unsafe { + llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + + /* Comparisons */ + pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("icmp"); + unsafe { + llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fcmp"); + unsafe { + llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + /* Miscellaneous instructions */ + pub fn empty_phi(&self, ty: Type) -> ValueRef { + self.count_insn("emptyphi"); + unsafe { + llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) + } + } + + pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { + assert_eq!(vals.len(), bbs.len()); + let phi = self.empty_phi(ty); + self.count_insn("addincoming"); + unsafe { + llvm::LLVMAddIncoming(phi, vals.as_ptr(), + bbs.as_ptr(), + vals.len() as c_uint); + phi + } + } + + pub fn add_span_comment(&self, sp: Span, text: &str) { + if self.ccx.sess().asm_comments() { + let s = format!("{} ({})", + text, + self.ccx.sess().codemap().span_to_string(sp)); + debug!("{}", s.as_slice()); + self.add_comment(s.as_slice()); + } + } + + pub fn add_comment(&self, text: &str) { + if self.ccx.sess().asm_comments() { + let sanitized = text.replace("$", ""); + let comment_text = format!("{} {}", "#", + sanitized.replace("\n", "\n\t# ")); + self.count_insn("inlineasm"); + let asm = comment_text.as_slice().with_c_str(|c| { + unsafe { + llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.ccx)).to_ref(), + c, noname(), False, False) + } + }); + self.call(asm, &[], None); + } + } + + pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, + inputs: &[ValueRef], output: Type, + volatile: bool, alignstack: bool, + dia: AsmDialect) -> ValueRef { + self.count_insn("inlineasm"); + + let volatile = if volatile { llvm::True } + else { llvm::False }; + let alignstack = if alignstack { llvm::True } + else { llvm::False }; + + let argtys = inputs.iter().map(|v| { + debug!("Asm Input Type: {}", self.ccx.tn().val_to_string(*v)); + val_ty(*v) + }).collect::>(); + + debug!("Asm Output Type: {}", self.ccx.tn().type_to_string(output)); + let fty = Type::func(argtys.as_slice(), &output); + unsafe { + let v = llvm::LLVMInlineAsm( + fty.to_ref(), asm, cons, volatile, alignstack, dia as c_uint); + self.call(v, inputs, None) + } + } + + pub fn call(&self, llfn: ValueRef, args: &[ValueRef], + attributes: Option) -> ValueRef { + self.count_insn("call"); + + debug!("Call {} with args ({})", + self.ccx.tn().val_to_string(llfn), + args.iter() + .map(|&v| self.ccx.tn().val_to_string(v)) + .collect::>() + .connect(", ")); + + unsafe { + let v = llvm::LLVMBuildCall(self.llbuilder, llfn, args.as_ptr(), + args.len() as c_uint, noname()); + match attributes { + Some(a) => a.apply_callsite(v), + None => {} + } + v + } + } + + pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef], + conv: CallConv, attributes: Option) -> ValueRef { + self.count_insn("callwithconv"); + let v = self.call(llfn, args, attributes); + llvm::SetInstructionCallConv(v, conv); + v + } + + pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { + self.count_insn("select"); + unsafe { + llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) + } + } + + pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { + self.count_insn("vaarg"); + unsafe { + llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) + } + } + + pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("extractelement"); + unsafe { + llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) + } + } + + pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("insertelement"); + unsafe { + llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) + } + } + + pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { + self.count_insn("shufflevector"); + unsafe { + llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) + } + } + + pub fn vector_splat(&self, num_elts: uint, elt: ValueRef) -> ValueRef { + unsafe { + let elt_ty = val_ty(elt); + let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); + let vec = self.insert_element(undef, elt, C_i32(self.ccx, 0)); + let vec_i32_ty = Type::vector(&Type::i32(self.ccx), num_elts as u64); + self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) + } + } + + pub fn extract_value(&self, agg_val: ValueRef, idx: uint) -> ValueRef { + self.count_insn("extractvalue"); + unsafe { + llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) + } + } + + pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, + idx: uint) -> ValueRef { + self.count_insn("insertvalue"); + unsafe { + llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, + noname()) + } + } + + pub fn is_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnull"); + unsafe { + llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) + } + } + + pub fn is_not_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnotnull"); + unsafe { + llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) + } + } + + pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ptrdiff"); + unsafe { + llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn trap(&self) { + unsafe { + let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); + let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); + let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); + let t: ValueRef = "llvm.trap".with_c_str(|buf| { + llvm::LLVMGetNamedFunction(m, buf) + }); + assert!((t as int != 0)); + let args: &[ValueRef] = &[]; + self.count_insn("trap"); + llvm::LLVMBuildCall( + self.llbuilder, t, args.as_ptr(), args.len() as c_uint, noname()); + } + } + + pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, num_clauses: uint) -> ValueRef { + self.count_insn("landingpad"); + unsafe { + llvm::LLVMBuildLandingPad( + self.llbuilder, ty.to_ref(), pers_fn, num_clauses as c_uint, noname()) + } + } + + pub fn set_cleanup(&self, landing_pad: ValueRef) { + self.count_insn("setcleanup"); + unsafe { + llvm::LLVMSetCleanup(landing_pad, llvm::True); + } + } + + pub fn resume(&self, exn: ValueRef) -> ValueRef { + self.count_insn("resume"); + unsafe { + llvm::LLVMBuildResume(self.llbuilder, exn) + } + } + + // Atomic Operations + pub fn atomic_cmpxchg(&self, dst: ValueRef, + cmp: ValueRef, src: ValueRef, + order: AtomicOrdering, + failure_order: AtomicOrdering) -> ValueRef { + unsafe { + llvm::LLVMBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, + order, failure_order) + } + } + pub fn atomic_rmw(&self, op: AtomicBinOp, + dst: ValueRef, src: ValueRef, + order: AtomicOrdering) -> ValueRef { + unsafe { + llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) + } + } + + pub fn atomic_fence(&self, order: AtomicOrdering) { + unsafe { + llvm::LLVMBuildAtomicFence(self.llbuilder, order); + } + } +} diff --git a/src/librustc_trans/trans/cabi.rs b/src/librustc_trans/trans/cabi.rs new file mode 100644 index 00000000000..0214bf29eee --- /dev/null +++ b/src/librustc_trans/trans/cabi.rs @@ -0,0 +1,122 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::ArgKind::*; + +use llvm::Attribute; +use std::option; +use trans::context::CrateContext; +use trans::cabi_x86; +use trans::cabi_x86_64; +use trans::cabi_x86_win64; +use trans::cabi_arm; +use trans::cabi_mips; +use trans::type_::Type; + +#[deriving(Clone, PartialEq)] +pub enum ArgKind { + /// Pass the argument directly using the normal converted + /// LLVM type or by coercing to another specified type + Direct, + /// Pass the argument indirectly via a hidden pointer + Indirect, + /// Ignore the argument (useful for empty struct) + Ignore, +} + +/// Information about how a specific C type +/// should be passed to or returned from a function +/// +/// This is borrowed from clang's ABIInfo.h +#[deriving(Clone)] +pub struct ArgType { + pub kind: ArgKind, + /// Original LLVM type + pub ty: Type, + /// Coerced LLVM Type + pub cast: option::Option, + /// Dummy argument, which is emitted before the real argument + pub pad: option::Option, + /// LLVM attribute of argument + pub attr: option::Option +} + +impl ArgType { + pub fn direct(ty: Type, cast: option::Option, + pad: option::Option, + attr: option::Option) -> ArgType { + ArgType { + kind: Direct, + ty: ty, + cast: cast, + pad: pad, + attr: attr + } + } + + pub fn indirect(ty: Type, attr: option::Option) -> ArgType { + ArgType { + kind: Indirect, + ty: ty, + cast: option::None, + pad: option::None, + attr: attr + } + } + + pub fn ignore(ty: Type) -> ArgType { + ArgType { + kind: Ignore, + ty: ty, + cast: None, + pad: None, + attr: None, + } + } + + pub fn is_indirect(&self) -> bool { + return self.kind == Indirect; + } + + pub fn is_ignore(&self) -> bool { + return self.kind == Ignore; + } +} + +/// Metadata describing how the arguments to a native function +/// should be passed in order to respect the native ABI. +/// +/// I will do my best to describe this structure, but these +/// comments are reverse-engineered and may be inaccurate. -NDM +pub struct FnType { + /// The LLVM types of each argument. + pub arg_tys: Vec , + + /// LLVM return type. + pub ret_ty: ArgType, +} + +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + match ccx.sess().target.target.arch.as_slice() { + "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), + "x86_64" => if ccx.sess().target.target.options.is_like_windows { + cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def) + } else { + cabi_x86_64::compute_abi_info(ccx, atys, rty, ret_def) + }, + "arm" => cabi_arm::compute_abi_info(ccx, atys, rty, ret_def), + "mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def), + a => ccx.sess().fatal((format!("unrecognized arch \"{}\" in target specification", a)) + .as_slice()), + } +} diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs new file mode 100644 index 00000000000..46440fcf1a1 --- /dev/null +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -0,0 +1,151 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use llvm; +use llvm::{Integer, Pointer, Float, Double, Struct, Array}; +use llvm::{StructRetAttribute, ZExtAttribute}; +use trans::cabi::{FnType, ArgType}; +use trans::context::CrateContext; +use trans::type_::Type; + +use std::cmp; + +fn align_up_to(off: uint, a: uint) -> uint { + return (off + a - 1u) / a * a; +} + +fn align(off: uint, ty: Type) -> uint { + let a = ty_align(ty); + return align_up_to(off, a); +} + +fn ty_align(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 4, + Float => 4, + Double => 8, + Struct => { + if ty.is_packed() { + 1 + } else { + let str_tys = ty.field_types(); + str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) + } + } + Array => { + let elt = ty.element_type(); + ty_align(elt) + } + _ => panic!("ty_align: unhandled type") + } +} + +fn ty_size(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 4, + Float => 4, + Double => 8, + Struct => { + if ty.is_packed() { + let str_tys = ty.field_types(); + str_tys.iter().fold(0, |s, t| s + ty_size(*t)) + } else { + let str_tys = ty.field_types(); + let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); + align(size, ty) + } + } + Array => { + let len = ty.array_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } + _ => panic!("ty_size: unhandled type") + } +} + +fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { + if is_reg_ty(ty) { + let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + return ArgType::direct(ty, None, None, attr); + } + let size = ty_size(ty); + if size <= 4 { + let llty = if size <= 1 { + Type::i8(ccx) + } else if size <= 2 { + Type::i16(ccx) + } else { + Type::i32(ccx) + }; + return ArgType::direct(ty, Some(llty), None, None); + } + ArgType::indirect(ty, Some(StructRetAttribute)) +} + +fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType { + if is_reg_ty(ty) { + let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + return ArgType::direct(ty, None, None, attr); + } + let align = ty_align(ty); + let size = ty_size(ty); + let llty = if align <= 4 { + Type::array(&Type::i32(ccx), ((size + 3) / 4) as u64) + } else { + Type::array(&Type::i64(ccx), ((size + 7) / 8) as u64) + }; + ArgType::direct(ty, Some(llty), None, None) +} + +fn is_reg_ty(ty: Type) -> bool { + match ty.kind() { + Integer + | Pointer + | Float + | Double => true, + _ => false + } +} + +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + let mut arg_tys = Vec::new(); + for &aty in atys.iter() { + let ty = classify_arg_ty(ccx, aty); + arg_tys.push(ty); + } + + let ret_ty = if ret_def { + classify_ret_ty(ccx, rty) + } else { + ArgType::direct(Type::void(ccx), None, None, None) + }; + + return FnType { + arg_tys: arg_tys, + ret_ty: ret_ty, + }; +} diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs new file mode 100644 index 00000000000..4dfe8daf339 --- /dev/null +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -0,0 +1,184 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use libc::c_uint; +use std::cmp; +use llvm; +use llvm::{Integer, Pointer, Float, Double, Struct, Array}; +use llvm::{StructRetAttribute, ZExtAttribute}; +use trans::cabi::{ArgType, FnType}; +use trans::context::CrateContext; +use trans::type_::Type; + +fn align_up_to(off: uint, a: uint) -> uint { + return (off + a - 1u) / a * a; +} + +fn align(off: uint, ty: Type) -> uint { + let a = ty_align(ty); + return align_up_to(off, a); +} + +fn ty_align(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 4, + Float => 4, + Double => 8, + Struct => { + if ty.is_packed() { + 1 + } else { + let str_tys = ty.field_types(); + str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) + } + } + Array => { + let elt = ty.element_type(); + ty_align(elt) + } + _ => panic!("ty_size: unhandled type") + } +} + +fn ty_size(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 4, + Float => 4, + Double => 8, + Struct => { + if ty.is_packed() { + let str_tys = ty.field_types(); + str_tys.iter().fold(0, |s, t| s + ty_size(*t)) + } else { + let str_tys = ty.field_types(); + let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); + align(size, ty) + } + } + Array => { + let len = ty.array_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } + _ => panic!("ty_size: unhandled type") + } +} + +fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { + if is_reg_ty(ty) { + let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ArgType::direct(ty, None, None, attr) + } else { + ArgType::indirect(ty, Some(StructRetAttribute)) + } +} + +fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut uint) -> ArgType { + let orig_offset = *offset; + let size = ty_size(ty) * 8; + let mut align = ty_align(ty); + + align = cmp::min(cmp::max(align, 4), 8); + *offset = align_up_to(*offset, align); + *offset += align_up_to(size, align * 8) / 8; + + if is_reg_ty(ty) { + let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ArgType::direct(ty, None, None, attr) + } else { + ArgType::direct( + ty, + Some(struct_ty(ccx, ty)), + padding_ty(ccx, align, orig_offset), + None + ) + } +} + +fn is_reg_ty(ty: Type) -> bool { + return match ty.kind() { + Integer + | Pointer + | Float + | Double => true, + _ => false + }; +} + +fn padding_ty(ccx: &CrateContext, align: uint, offset: uint) -> Option { + if ((align - 1 ) & offset) > 0 { + Some(Type::i32(ccx)) + } else { + None + } +} + +fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec { + let int_ty = Type::i32(ccx); + let mut args = Vec::new(); + + let mut n = size / 32; + while n > 0 { + args.push(int_ty); + n -= 1; + } + + let r = size % 32; + if r > 0 { + unsafe { + args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint))); + } + } + + args +} + +fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { + let size = ty_size(ty) * 8; + Type::struct_(ccx, coerce_to_int(ccx, size).as_slice(), false) +} + +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + let ret_ty = if ret_def { + classify_ret_ty(ccx, rty) + } else { + ArgType::direct(Type::void(ccx), None, None, None) + }; + + let sret = ret_ty.is_indirect(); + let mut arg_tys = Vec::new(); + let mut offset = if sret { 4 } else { 0 }; + + for aty in atys.iter() { + let ty = classify_arg_ty(ccx, *aty, &mut offset); + arg_tys.push(ty); + }; + + return FnType { + arg_tys: arg_tys, + ret_ty: ret_ty, + }; +} diff --git a/src/librustc_trans/trans/cabi_x86.rs b/src/librustc_trans/trans/cabi_x86.rs new file mode 100644 index 00000000000..32f6eb060c0 --- /dev/null +++ b/src/librustc_trans/trans/cabi_x86.rs @@ -0,0 +1,85 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::Strategy::*; +use llvm::*; +use trans::cabi::{ArgType, FnType}; +use trans::type_::Type; +use super::common::*; +use super::machine::*; + +enum Strategy { RetValue(Type), RetPointer } +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + let mut arg_tys = Vec::new(); + + let ret_ty; + if !ret_def { + ret_ty = ArgType::direct(Type::void(ccx), None, None, None); + } else if rty.kind() == Struct { + // Returning a structure. Most often, this will use + // a hidden first argument. On some platforms, though, + // small structs are returned as integers. + // + // Some links: + // http://www.angelcode.com/dev/callconv/callconv.html + // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp + + let t = &ccx.sess().target.target; + let strategy = if t.options.is_like_osx || t.options.is_like_windows { + match llsize_of_alloc(ccx, rty) { + 1 => RetValue(Type::i8(ccx)), + 2 => RetValue(Type::i16(ccx)), + 4 => RetValue(Type::i32(ccx)), + 8 => RetValue(Type::i64(ccx)), + _ => RetPointer + } + } else { + RetPointer + }; + + match strategy { + RetValue(t) => { + ret_ty = ArgType::direct(rty, Some(t), None, None); + } + RetPointer => { + ret_ty = ArgType::indirect(rty, Some(StructRetAttribute)); + } + } + } else { + let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ret_ty = ArgType::direct(rty, None, None, attr); + } + + for &t in atys.iter() { + let ty = match t.kind() { + Struct => { + let size = llsize_of_alloc(ccx, t); + if size == 0 { + ArgType::ignore(t) + } else { + ArgType::indirect(t, Some(ByValAttribute)) + } + } + _ => { + let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ArgType::direct(t, None, None, attr) + } + }; + arg_tys.push(ty); + } + + return FnType { + arg_tys: arg_tys, + ret_ty: ret_ty, + }; +} diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs new file mode 100644 index 00000000000..69ee5301d18 --- /dev/null +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -0,0 +1,380 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// The classification code for the x86_64 ABI is taken from the clay language +// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp + +#![allow(non_upper_case_globals)] +use self::RegClass::*; + +use llvm; +use llvm::{Integer, Pointer, Float, Double}; +use llvm::{Struct, Array, Attribute}; +use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; +use trans::cabi::{ArgType, FnType}; +use trans::context::CrateContext; +use trans::type_::Type; + +use std::cmp; + +#[deriving(Clone, PartialEq)] +enum RegClass { + NoClass, + Int, + SSEFs, + SSEFv, + SSEDs, + SSEDv, + SSEInt, + SSEUp, + X87, + X87Up, + ComplexX87, + Memory +} + +trait TypeMethods { + fn is_reg_ty(&self) -> bool; +} + +impl TypeMethods for Type { + fn is_reg_ty(&self) -> bool { + match self.kind() { + Integer | Pointer | Float | Double => true, + _ => false + } + } +} + +impl RegClass { + fn is_sse(&self) -> bool { + match *self { + SSEFs | SSEFv | SSEDs | SSEDv => true, + _ => false + } + } +} + +trait ClassList for Sized? { + fn is_pass_byval(&self) -> bool; + fn is_ret_bysret(&self) -> bool; +} + +impl ClassList for [RegClass] { + fn is_pass_byval(&self) -> bool { + if self.len() == 0 { return false; } + + let class = self[0]; + class == Memory + || class == X87 + || class == ComplexX87 + } + + fn is_ret_bysret(&self) -> bool { + if self.len() == 0 { return false; } + + self[0] == Memory + } +} + +fn classify_ty(ty: Type) -> Vec { + fn align(off: uint, ty: Type) -> uint { + let a = ty_align(ty); + return (off + a - 1u) / a * a; + } + + fn ty_align(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 8, + Float => 4, + Double => 8, + Struct => { + if ty.is_packed() { + 1 + } else { + let str_tys = ty.field_types(); + str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t))) + } + } + Array => { + let elt = ty.element_type(); + ty_align(elt) + } + _ => panic!("ty_size: unhandled type") + } + } + + fn ty_size(ty: Type) -> uint { + match ty.kind() { + Integer => { + unsafe { + ((llvm::LLVMGetIntTypeWidth(ty.to_ref()) as uint) + 7) / 8 + } + } + Pointer => 8, + Float => 4, + Double => 8, + Struct => { + let str_tys = ty.field_types(); + if ty.is_packed() { + str_tys.iter().fold(0, |s, t| s + ty_size(*t)) + } else { + let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t)); + align(size, ty) + } + } + Array => { + let len = ty.array_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + len * eltsz + } + _ => panic!("ty_size: unhandled type") + } + } + + fn all_mem(cls: &mut [RegClass]) { + for elt in cls.iter_mut() { + *elt = Memory; + } + } + + fn unify(cls: &mut [RegClass], + i: uint, + newv: RegClass) { + if cls[i] == newv { + return; + } else if cls[i] == NoClass { + cls[i] = newv; + } else if newv == NoClass { + return; + } else if cls[i] == Memory || newv == Memory { + cls[i] = Memory; + } else if cls[i] == Int || newv == Int { + cls[i] = Int; + } else if cls[i] == X87 || + cls[i] == X87Up || + cls[i] == ComplexX87 || + newv == X87 || + newv == X87Up || + newv == ComplexX87 { + cls[i] = Memory; + } else { + cls[i] = newv; + } + } + + fn classify_struct(tys: &[Type], + cls: &mut [RegClass], + i: uint, + off: uint, + packed: bool) { + let mut field_off = off; + for ty in tys.iter() { + if !packed { + field_off = align(field_off, *ty); + } + classify(*ty, cls, i, field_off); + field_off += ty_size(*ty); + } + } + + fn classify(ty: Type, + cls: &mut [RegClass], ix: uint, + off: uint) { + let t_align = ty_align(ty); + let t_size = ty_size(ty); + + let misalign = off % t_align; + if misalign != 0u { + let mut i = off / 8u; + let e = (off + t_size + 7u) / 8u; + while i < e { + unify(cls, ix + i, Memory); + i += 1u; + } + return; + } + + match ty.kind() { + Integer | + Pointer => { + unify(cls, ix + off / 8u, Int); + } + Float => { + if off % 8u == 4u { + unify(cls, ix + off / 8u, SSEFv); + } else { + unify(cls, ix + off / 8u, SSEFs); + } + } + Double => { + unify(cls, ix + off / 8u, SSEDs); + } + Struct => { + classify_struct(ty.field_types().as_slice(), cls, ix, off, ty.is_packed()); + } + Array => { + let len = ty.array_length(); + let elt = ty.element_type(); + let eltsz = ty_size(elt); + let mut i = 0u; + while i < len { + classify(elt, cls, ix, off + i * eltsz); + i += 1u; + } + } + _ => panic!("classify: unhandled type") + } + } + + fn fixup(ty: Type, cls: &mut [RegClass]) { + let mut i = 0u; + let ty_kind = ty.kind(); + let e = cls.len(); + if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array) { + if cls[i].is_sse() { + i += 1u; + while i < e { + if cls[i] != SSEUp { + all_mem(cls); + return; + } + i += 1u; + } + } else { + all_mem(cls); + return + } + } else { + while i < e { + if cls[i] == Memory { + all_mem(cls); + return; + } + if cls[i] == X87Up { + // for darwin + // cls[i] = SSEDs; + all_mem(cls); + return; + } + if cls[i] == SSEUp { + cls[i] = SSEDv; + } else if cls[i].is_sse() { + i += 1; + while i != e && cls[i] == SSEUp { i += 1u; } + } else if cls[i] == X87 { + i += 1; + while i != e && cls[i] == X87Up { i += 1u; } + } else { + i += 1; + } + } + } + } + + let words = (ty_size(ty) + 7) / 8; + let mut cls = Vec::from_elem(words, NoClass); + if words > 4 { + all_mem(cls.as_mut_slice()); + return cls; + } + classify(ty, cls.as_mut_slice(), 0, 0); + fixup(ty, cls.as_mut_slice()); + return cls; +} + +fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type { + fn llvec_len(cls: &[RegClass]) -> uint { + let mut len = 1u; + for c in cls.iter() { + if *c != SSEUp { + break; + } + len += 1u; + } + return len; + } + + let mut tys = Vec::new(); + let mut i = 0u; + let e = cls.len(); + while i < e { + match cls[i] { + Int => { + tys.push(Type::i64(ccx)); + } + SSEFv => { + let vec_len = llvec_len(cls[i + 1u..]); + let vec_ty = Type::vector(&Type::f32(ccx), (vec_len * 2u) as u64); + tys.push(vec_ty); + i += vec_len; + continue; + } + SSEFs => { + tys.push(Type::f32(ccx)); + } + SSEDs => { + tys.push(Type::f64(ccx)); + } + _ => panic!("llregtype: unhandled class") + } + i += 1u; + } + return Type::struct_(ccx, tys.as_slice(), false); +} + +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + fn x86_64_ty(ccx: &CrateContext, + ty: Type, + is_mem_cls: |cls: &[RegClass]| -> bool, + ind_attr: Attribute) + -> ArgType { + if !ty.is_reg_ty() { + let cls = classify_ty(ty); + if is_mem_cls(cls.as_slice()) { + ArgType::indirect(ty, Some(ind_attr)) + } else { + ArgType::direct(ty, + Some(llreg_ty(ccx, cls.as_slice())), + None, + None) + } + } else { + let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ArgType::direct(ty, None, None, attr) + } + } + + let mut arg_tys = Vec::new(); + for t in atys.iter() { + let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), ByValAttribute); + arg_tys.push(ty); + } + + let ret_ty = if ret_def { + x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), StructRetAttribute) + } else { + ArgType::direct(Type::void(ccx), None, None, None) + }; + + return FnType { + arg_tys: arg_tys, + ret_ty: ret_ty, + }; +} diff --git a/src/librustc_trans/trans/cabi_x86_win64.rs b/src/librustc_trans/trans/cabi_x86_win64.rs new file mode 100644 index 00000000000..79e309dacaa --- /dev/null +++ b/src/librustc_trans/trans/cabi_x86_win64.rs @@ -0,0 +1,64 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::*; +use super::common::*; +use super::machine::*; +use trans::cabi::{ArgType, FnType}; +use trans::type_::Type; + +// Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx + +pub fn compute_abi_info(ccx: &CrateContext, + atys: &[Type], + rty: Type, + ret_def: bool) -> FnType { + let mut arg_tys = Vec::new(); + + let ret_ty; + if !ret_def { + ret_ty = ArgType::direct(Type::void(ccx), None, None, None); + } else if rty.kind() == Struct { + ret_ty = match llsize_of_alloc(ccx, rty) { + 1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None), + 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), + 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), + 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), + _ => ArgType::indirect(rty, Some(StructRetAttribute)) + }; + } else { + let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ret_ty = ArgType::direct(rty, None, None, attr); + } + + for &t in atys.iter() { + let ty = match t.kind() { + Struct => { + match llsize_of_alloc(ccx, t) { + 1 => ArgType::direct(rty, Some(Type::i8(ccx)), None, None), + 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), + 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), + 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), + _ => ArgType::indirect(t, Some(ByValAttribute)) + } + } + _ => { + let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + ArgType::direct(t, None, None, attr) + } + }; + arg_tys.push(ty); + } + + return FnType { + arg_tys: arg_tys, + ret_ty: ret_ty, + }; +} diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs new file mode 100644 index 00000000000..0544c9d43a8 --- /dev/null +++ b/src/librustc_trans/trans/callee.rs @@ -0,0 +1,1167 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Handles translation of callees as well as other call-related + * things. Callees are a superset of normal rust values and sometimes + * have different representations. In particular, top-level fn items + * and methods are represented as just a fn ptr and not a full + * closure. + */ + +pub use self::AutorefArg::*; +pub use self::CalleeData::*; +pub use self::CallArgs::*; + +use arena::TypedArena; +use back::abi; +use back::link; +use session; +use llvm::{ValueRef, get_param}; +use llvm; +use metadata::csearch; +use middle::def; +use middle::subst; +use middle::subst::{Subst}; +use trans::adt; +use trans::base; +use trans::base::*; +use trans::build::*; +use trans::callee; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::closure; +use trans::common; +use trans::common::*; +use trans::datum::*; +use trans::expr; +use trans::glue; +use trans::inline; +use trans::foreign; +use trans::intrinsic; +use trans::meth; +use trans::monomorphize; +use trans::type_::Type; +use trans::type_of; +use middle::ty; +use middle::typeck::coherence::make_substs_for_receiver_types; +use middle::typeck::MethodCall; +use util::ppaux::Repr; +use util::ppaux::ty_to_string; + +use syntax::abi as synabi; +use syntax::ast; +use syntax::ast_map; +use syntax::ptr::P; + +pub struct MethodData { + pub llfn: ValueRef, + pub llself: ValueRef, +} + +pub enum CalleeData { + Closure(Datum), + + // Constructor for enum variant/tuple-like-struct + // i.e. Some, Ok + NamedTupleConstructor(subst::Substs, ty::Disr), + + // Represents a (possibly monomorphized) top-level fn item or method + // item. Note that this is just the fn-ptr and is not a Rust closure + // value (which is a pair). + Fn(/* llfn */ ValueRef), + + Intrinsic(ast::NodeId, subst::Substs), + + TraitItem(MethodData) +} + +pub struct Callee<'blk, 'tcx: 'blk> { + pub bcx: Block<'blk, 'tcx>, + pub data: CalleeData, +} + +fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) + -> Callee<'blk, 'tcx> { + let _icx = push_ctxt("trans_callee"); + debug!("callee::trans(expr={})", expr.repr(bcx.tcx())); + + // pick out special kinds of expressions that can be called: + match expr.node { + ast::ExprPath(_) => { + return trans_def(bcx, bcx.def(expr.id), expr); + } + _ => {} + } + + // any other expressions are closures: + return datum_callee(bcx, expr); + + fn datum_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) + -> Callee<'blk, 'tcx> { + let DatumBlock {mut bcx, datum} = expr::trans(bcx, expr); + match ty::get(datum.ty).sty { + ty::ty_bare_fn(..) => { + let llval = datum.to_llscalarish(bcx); + return Callee { + bcx: bcx, + data: Fn(llval), + }; + } + ty::ty_closure(..) => { + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "callee", expr.id)); + return Callee { + bcx: bcx, + data: Closure(datum), + }; + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("type of callee is neither bare-fn nor closure: \ + {}", + bcx.ty_to_string(datum.ty)).as_slice()); + } + } + } + + fn fn_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, llfn: ValueRef) + -> Callee<'blk, 'tcx> { + return Callee { + bcx: bcx, + data: Fn(llfn), + }; + } + + fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + def: def::Def, + ref_expr: &ast::Expr) + -> Callee<'blk, 'tcx> { + debug!("trans_def(def={}, ref_expr={})", def.repr(bcx.tcx()), ref_expr.repr(bcx.tcx())); + let expr_ty = node_id_type(bcx, ref_expr.id); + match def { + def::DefFn(did, _) if { + let maybe_def_id = inline::get_local_instance(bcx.ccx(), did); + let maybe_ast_node = maybe_def_id.and_then(|def_id| bcx.tcx().map + .find(def_id.node)); + match maybe_ast_node { + Some(ast_map::NodeStructCtor(_)) => true, + _ => false + } + } => { + let substs = node_id_substs(bcx, ExprId(ref_expr.id)); + Callee { + bcx: bcx, + data: NamedTupleConstructor(substs, 0) + } + } + def::DefFn(did, _) if match ty::get(expr_ty).sty { + ty::ty_bare_fn(ref f) => f.abi == synabi::RustIntrinsic, + _ => false + } => { + let substs = node_id_substs(bcx, ExprId(ref_expr.id)); + let def_id = inline::maybe_instantiate_inline(bcx.ccx(), did); + Callee { bcx: bcx, data: Intrinsic(def_id.node, substs) } + } + def::DefFn(did, _) | def::DefMethod(did, _, def::FromImpl(_)) | + def::DefStaticMethod(did, def::FromImpl(_)) => { + fn_callee(bcx, trans_fn_ref(bcx, did, ExprId(ref_expr.id))) + } + def::DefStaticMethod(meth_did, def::FromTrait(trait_did)) | + def::DefMethod(meth_did, _, def::FromTrait(trait_did)) => { + fn_callee(bcx, meth::trans_static_method_callee(bcx, meth_did, + trait_did, + ref_expr.id)) + } + def::DefVariant(tid, vid, _) => { + let vinfo = ty::enum_variant_with_id(bcx.tcx(), tid, vid); + let substs = node_id_substs(bcx, ExprId(ref_expr.id)); + + // Nullary variants are not callable + assert!(vinfo.args.len() > 0u); + + Callee { + bcx: bcx, + data: NamedTupleConstructor(substs, vinfo.disr_val) + } + } + def::DefStruct(_) => { + let substs = node_id_substs(bcx, ExprId(ref_expr.id)); + Callee { + bcx: bcx, + data: NamedTupleConstructor(substs, 0) + } + } + def::DefStatic(..) | + def::DefConst(..) | + def::DefLocal(..) | + def::DefUpvar(..) => { + datum_callee(bcx, ref_expr) + } + def::DefMod(..) | def::DefForeignMod(..) | def::DefTrait(..) | + def::DefTy(..) | def::DefPrimTy(..) | def::DefAssociatedTy(..) | + def::DefUse(..) | def::DefTyParamBinder(..) | + def::DefRegion(..) | def::DefLabel(..) | def::DefTyParam(..) | + def::DefSelfTy(..) => { + bcx.tcx().sess.span_bug( + ref_expr.span, + format!("cannot translate def {} \ + to a callable thing!", def).as_slice()); + } + } + } +} + +pub fn trans_fn_ref(bcx: Block, def_id: ast::DefId, node: ExprOrMethodCall) -> ValueRef { + /*! + * Translates a reference (with id `ref_id`) to the fn/method + * with id `def_id` into a function pointer. This may require + * monomorphization or inlining. + */ + + let _icx = push_ctxt("trans_fn_ref"); + + let substs = node_id_substs(bcx, node); + debug!("trans_fn_ref(def_id={}, node={}, substs={})", + def_id.repr(bcx.tcx()), + node, + substs.repr(bcx.tcx())); + trans_fn_ref_with_substs(bcx, def_id, node, substs) +} + +fn trans_fn_ref_with_substs_to_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + def_id: ast::DefId, + ref_id: ast::NodeId, + substs: subst::Substs) + -> Callee<'blk, 'tcx> { + Callee { + bcx: bcx, + data: Fn(trans_fn_ref_with_substs(bcx, + def_id, + ExprId(ref_id), + substs)), + } +} + +/// Translates the adapter that deconstructs a `Box` object into +/// `Trait` so that a by-value self method can be called. +pub fn trans_unboxing_shim(bcx: Block, + llshimmedfn: ValueRef, + fty: &ty::BareFnTy, + method_id: ast::DefId, + substs: &subst::Substs) + -> ValueRef { + let _icx = push_ctxt("trans_unboxing_shim"); + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + + let fty = fty.subst(tcx, substs); + + // Transform the self type to `Box`. + let self_type = fty.sig.inputs[0]; + let boxed_self_type = ty::mk_uniq(tcx, self_type); + let boxed_function_type = ty::FnSig { + binder_id: fty.sig.binder_id, + inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| { + if i == 0 { + boxed_self_type + } else { + *typ + } + }).collect(), + output: fty.sig.output, + variadic: false, + }; + let boxed_function_type = ty::BareFnTy { + fn_style: fty.fn_style, + abi: fty.abi, + sig: boxed_function_type, + }; + let boxed_function_type = ty::mk_bare_fn(tcx, boxed_function_type); + let function_type = match fty.abi { + synabi::RustCall => { + // We're passing through to a RustCall ABI function, but + // because the shim will already perform untupling, we + // need to pretend the shimmed function does not use + // RustCall so the untupled arguments can be passed + // through verbatim. This is kind of ugly. + let fake_ty = ty::FnSig { + binder_id: fty.sig.binder_id, + inputs: type_of::untuple_arguments_if_necessary(ccx, + fty.sig.inputs.as_slice(), + fty.abi), + output: fty.sig.output, + variadic: false, + }; + let fake_ty = ty::BareFnTy { + fn_style: fty.fn_style, + abi: synabi::Rust, + sig: fake_ty, + }; + ty::mk_bare_fn(tcx, fake_ty) + } + _ => { + ty::mk_bare_fn(tcx, fty) + } + }; + + let function_name = ty::with_path(tcx, method_id, |path| { + link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim") + }); + let llfn = decl_internal_rust_fn(ccx, + boxed_function_type, + function_name.as_slice()); + + let block_arena = TypedArena::new(); + let empty_param_substs = param_substs::empty(); + let return_type = ty::ty_fn_ret(boxed_function_type); + let fcx = new_fn_ctxt(ccx, + llfn, + ast::DUMMY_NODE_ID, + false, + return_type, + &empty_param_substs, + None, + &block_arena); + let mut bcx = init_function(&fcx, false, return_type); + + // Create the substituted versions of the self type. + let arg_scope = fcx.push_custom_cleanup_scope(); + let arg_scope_id = cleanup::CustomScope(arg_scope); + let boxed_arg_types = ty::ty_fn_args(boxed_function_type); + let boxed_self_type = boxed_arg_types[0]; + let arg_types = ty::ty_fn_args(function_type); + let self_type = arg_types[0]; + let boxed_self_kind = arg_kind(&fcx, boxed_self_type); + + // Create a datum for self. + let llboxedself = get_param(fcx.llfn, fcx.arg_pos(0) as u32); + let llboxedself = Datum::new(llboxedself, + boxed_self_type, + boxed_self_kind); + let boxed_self = + unpack_datum!(bcx, + llboxedself.to_lvalue_datum_in_scope(bcx, + "boxedself", + arg_scope_id)); + + // This `Load` is needed because lvalue data are always by-ref. + let llboxedself = Load(bcx, boxed_self.val); + + let llself = if type_is_immediate(ccx, self_type) { + let llboxedself = Load(bcx, llboxedself); + immediate_rvalue(llboxedself, self_type) + } else { + let llself = rvalue_scratch_datum(bcx, self_type, "self"); + memcpy_ty(bcx, llself.val, llboxedself, self_type); + llself + }; + + // Make sure we don't free the box twice! + boxed_self.kind.post_store(bcx, boxed_self.val, boxed_self_type); + + // Schedule a cleanup to free the box. + fcx.schedule_free_value(arg_scope_id, + llboxedself, + cleanup::HeapExchange, + self_type); + + // Now call the function. + let mut llshimmedargs = vec!(llself.val); + for i in range(1, arg_types.len()) { + llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32)); + } + assert!(!fcx.needs_ret_allocas); + let dest = fcx.llretslotptr.get().map(|_| + expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot")) + ); + bcx = trans_call_inner(bcx, + None, + function_type, + |bcx, _| { + Callee { + bcx: bcx, + data: Fn(llshimmedfn), + } + }, + ArgVals(llshimmedargs.as_slice()), + dest).bcx; + + bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope); + finish_fn(&fcx, bcx, return_type); + + llfn +} + +pub fn trans_fn_ref_with_substs( + bcx: Block, // + def_id: ast::DefId, // def id of fn + node: ExprOrMethodCall, // node id of use of fn; may be zero if N/A + substs: subst::Substs) // vtables for the call + -> ValueRef +{ + /*! + * Translates a reference to a fn/method item, monomorphizing and + * inlining as it goes. + * + * # Parameters + * + * - `bcx`: the current block where the reference to the fn occurs + * - `def_id`: def id of the fn or method item being referenced + * - `node`: node id of the reference to the fn/method, if applicable. + * This parameter may be zero; but, if so, the resulting value may not + * have the right type, so it must be cast before being used. + * - `substs`: values for each of the fn/method's parameters + */ + + let _icx = push_ctxt("trans_fn_ref_with_substs"); + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + + debug!("trans_fn_ref_with_substs(bcx={}, def_id={}, node={}, \ + substs={})", + bcx.to_str(), + def_id.repr(tcx), + node, + substs.repr(tcx)); + + assert!(substs.types.all(|t| !ty::type_needs_infer(*t))); + + // Load the info for the appropriate trait if necessary. + match ty::trait_of_item(tcx, def_id) { + None => {} + Some(trait_id) => { + ty::populate_implementations_for_trait_if_necessary(tcx, trait_id) + } + } + + // We need to do a bunch of special handling for default methods. + // We need to modify the def_id and our substs in order to monomorphize + // the function. + let (is_default, def_id, substs) = match ty::provided_source(tcx, def_id) { + None => (false, def_id, substs), + Some(source_id) => { + // There are two relevant substitutions when compiling + // default methods. First, there is the substitution for + // the type parameters of the impl we are using and the + // method we are calling. This substitution is the substs + // argument we already have. + // In order to compile a default method, though, we need + // to consider another substitution: the substitution for + // the type parameters on trait; the impl we are using + // implements the trait at some particular type + // parameters, and we need to substitute for those first. + // So, what we need to do is find this substitution and + // compose it with the one we already have. + + let impl_id = ty::impl_or_trait_item(tcx, def_id).container() + .id(); + let impl_or_trait_item = ty::impl_or_trait_item(tcx, source_id); + match impl_or_trait_item { + ty::MethodTraitItem(method) => { + let trait_ref = ty::impl_trait_ref(tcx, impl_id) + .expect("could not find trait_ref for impl with \ + default methods"); + + // Compute the first substitution + let first_subst = make_substs_for_receiver_types( + tcx, &*trait_ref, &*method); + + // And compose them + let new_substs = first_subst.subst(tcx, &substs); + + debug!("trans_fn_with_vtables - default method: \ + substs = {}, trait_subst = {}, \ + first_subst = {}, new_subst = {}", + substs.repr(tcx), trait_ref.substs.repr(tcx), + first_subst.repr(tcx), new_substs.repr(tcx)); + + (true, source_id, new_substs) + } + ty::TypeTraitItem(_) => { + bcx.tcx().sess.bug("trans_fn_ref_with_vtables() tried \ + to translate an associated type?!") + } + } + } + }; + + // If this is an unboxed closure, redirect to it. + match closure::get_or_create_declaration_if_unboxed_closure(bcx, + def_id, + &substs) { + None => {} + Some(llfn) => return llfn, + } + + // Check whether this fn has an inlined copy and, if so, redirect + // def_id to the local id of the inlined copy. + let def_id = inline::maybe_instantiate_inline(ccx, def_id); + + // We must monomorphise if the fn has type parameters, is a default method, + // or is a named tuple constructor. + let must_monomorphise = if !substs.types.is_empty() || is_default { + true + } else if def_id.krate == ast::LOCAL_CRATE { + let map_node = session::expect( + ccx.sess(), + tcx.map.find(def_id.node), + || "local item should be in ast map".to_string()); + + match map_node { + ast_map::NodeVariant(v) => match v.node.kind { + ast::TupleVariantKind(ref args) => args.len() > 0, + _ => false + }, + ast_map::NodeStructCtor(_) => true, + _ => false + } + } else { + false + }; + + // Create a monomorphic version of generic functions + if must_monomorphise { + // Should be either intra-crate or inlined. + assert_eq!(def_id.krate, ast::LOCAL_CRATE); + + let opt_ref_id = match node { + ExprId(id) => if id != 0 { Some(id) } else { None }, + MethodCall(_) => None, + }; + + let (val, must_cast) = + monomorphize::monomorphic_fn(ccx, def_id, &substs, opt_ref_id); + let mut val = val; + if must_cast && node != ExprId(0) { + // Monotype of the REFERENCE to the function (type params + // are subst'd) + let ref_ty = match node { + ExprId(id) => node_id_type(bcx, id), + MethodCall(method_call) => { + let t = (*bcx.tcx().method_map.borrow())[method_call].ty; + monomorphize_type(bcx, t) + } + }; + + val = PointerCast( + bcx, val, type_of::type_of_fn_from_ty(ccx, ref_ty).ptr_to()); + } + return val; + } + + // Polytype of the function item (may have type params) + let fn_tpt = ty::lookup_item_type(tcx, def_id); + + // Find the actual function pointer. + let mut val = { + if def_id.krate == ast::LOCAL_CRATE { + // Internal reference. + get_item_val(ccx, def_id.node) + } else { + // External reference. + trans_external_path(ccx, def_id, fn_tpt.ty) + } + }; + + // This is subtle and surprising, but sometimes we have to bitcast + // the resulting fn pointer. The reason has to do with external + // functions. If you have two crates that both bind the same C + // library, they may not use precisely the same types: for + // example, they will probably each declare their own structs, + // which are distinct types from LLVM's point of view (nominal + // types). + // + // Now, if those two crates are linked into an application, and + // they contain inlined code, you can wind up with a situation + // where both of those functions wind up being loaded into this + // application simultaneously. In that case, the same function + // (from LLVM's point of view) requires two types. But of course + // LLVM won't allow one function to have two types. + // + // What we currently do, therefore, is declare the function with + // one of the two types (whichever happens to come first) and then + // bitcast as needed when the function is referenced to make sure + // it has the type we expect. + // + // This can occur on either a crate-local or crate-external + // reference. It also occurs when testing libcore and in some + // other weird situations. Annoying. + let llty = type_of::type_of_fn_from_ty(ccx, fn_tpt.ty); + let llptrty = llty.ptr_to(); + if val_ty(val) != llptrty { + debug!("trans_fn_ref_with_vtables(): casting pointer!"); + val = BitCast(bcx, val, llptrty); + } else { + debug!("trans_fn_ref_with_vtables(): not casting pointer!"); + } + + val +} + +// ______________________________________________________________________ +// Translating calls + +pub fn trans_call<'blk, 'tcx>(in_cx: Block<'blk, 'tcx>, + call_ex: &ast::Expr, + f: &ast::Expr, + args: CallArgs, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_call"); + trans_call_inner(in_cx, + Some(common::expr_info(call_ex)), + expr_ty(in_cx, f), + |cx, _| trans(cx, f), + args, + Some(dest)).bcx +} + +pub fn trans_method_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + call_ex: &ast::Expr, + rcvr: &ast::Expr, + args: CallArgs, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_method_call"); + debug!("trans_method_call(call_ex={})", call_ex.repr(bcx.tcx())); + let method_call = MethodCall::expr(call_ex.id); + let method_ty = (*bcx.tcx().method_map.borrow())[method_call].ty; + trans_call_inner( + bcx, + Some(common::expr_info(call_ex)), + monomorphize_type(bcx, method_ty), + |cx, arg_cleanup_scope| { + meth::trans_method_callee(cx, method_call, Some(rcvr), arg_cleanup_scope) + }, + args, + Some(dest)).bcx +} + +pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + did: ast::DefId, + args: &[ValueRef], + dest: Option) + -> Result<'blk, 'tcx> { + let fty = if did.krate == ast::LOCAL_CRATE { + ty::node_id_to_type(bcx.tcx(), did.node) + } else { + csearch::get_type(bcx.tcx(), did).ty + }; + callee::trans_call_inner(bcx, + None, + fty, + |bcx, _| { + trans_fn_ref_with_substs_to_callee(bcx, + did, + 0, + subst::Substs::empty()) + }, + ArgVals(args), + dest) +} + +pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + call_info: Option, + callee_ty: ty::t, + get_callee: |bcx: Block<'blk, 'tcx>, + arg_cleanup_scope: cleanup::ScopeId| + -> Callee<'blk, 'tcx>, + args: CallArgs, + dest: Option) + -> Result<'blk, 'tcx> { + /*! + * This behemoth of a function translates function calls. + * Unfortunately, in order to generate more efficient LLVM + * output at -O0, it has quite a complex signature (refactoring + * this into two functions seems like a good idea). + * + * In particular, for lang items, it is invoked with a dest of + * None, and in that case the return value contains the result of + * the fn. The lang item must not return a structural type or else + * all heck breaks loose. + * + * For non-lang items, `dest` is always Some, and hence the result + * is written into memory somewhere. Nonetheless we return the + * actual return value of the function. + */ + + // Introduce a temporary cleanup scope that will contain cleanups + // for the arguments while they are being evaluated. The purpose + // this cleanup is to ensure that, should a panic occur while + // evaluating argument N, the values for arguments 0...N-1 are all + // cleaned up. If no panic occurs, the values are handed off to + // the callee, and hence none of the cleanups in this temporary + // scope will ever execute. + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); + + let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope)); + let mut bcx = callee.bcx; + + let (abi, ret_ty) = match ty::get(callee_ty).sty { + ty::ty_bare_fn(ref f) => (f.abi, f.sig.output), + ty::ty_closure(ref f) => (f.abi, f.sig.output), + _ => panic!("expected bare rust fn or closure in trans_call_inner") + }; + + let (llfn, llenv, llself) = match callee.data { + Fn(llfn) => { + (llfn, None, None) + } + TraitItem(d) => { + (d.llfn, None, Some(d.llself)) + } + Closure(d) => { + // Closures are represented as (llfn, llclosure) pair: + // load the requisite values out. + let pair = d.to_llref(); + let llfn = GEPi(bcx, pair, &[0u, abi::fn_field_code]); + let llfn = Load(bcx, llfn); + let llenv = GEPi(bcx, pair, &[0u, abi::fn_field_box]); + let llenv = Load(bcx, llenv); + (llfn, Some(llenv), None) + } + Intrinsic(node, substs) => { + assert!(abi == synabi::RustIntrinsic); + assert!(dest.is_some()); + + let call_info = call_info.expect("no call info for intrinsic call?"); + return intrinsic::trans_intrinsic_call(bcx, node, callee_ty, + arg_cleanup_scope, args, + dest.unwrap(), substs, + call_info); + } + NamedTupleConstructor(substs, disr) => { + assert!(dest.is_some()); + fcx.pop_custom_cleanup_scope(arg_cleanup_scope); + + let ctor_ty = callee_ty.subst(bcx.tcx(), &substs); + return base::trans_named_tuple_constructor(bcx, + ctor_ty, + disr, + args, + dest.unwrap(), + call_info); + } + }; + + // Intrinsics should not become actual functions. + // We trans them in place in `trans_intrinsic_call` + assert!(abi != synabi::RustIntrinsic); + + let is_rust_fn = abi == synabi::Rust || abi == synabi::RustCall; + + // Generate a location to store the result. If the user does + // not care about the result, just make a stack slot. + let opt_llretslot = dest.and_then(|dest| match dest { + expr::SaveIn(dst) => Some(dst), + expr::Ignore => { + let ret_ty = match ret_ty { + ty::FnConverging(ret_ty) => ret_ty, + ty::FnDiverging => ty::mk_nil(ccx.tcx()) + }; + if !is_rust_fn || + type_of::return_uses_outptr(ccx, ret_ty) || + ty::type_needs_drop(bcx.tcx(), ret_ty) { + // Push the out-pointer if we use an out-pointer for this + // return type, otherwise push "undef". + if type_is_zero_size(ccx, ret_ty) { + let llty = type_of::type_of(ccx, ret_ty); + Some(C_undef(llty.ptr_to())) + } else { + Some(alloc_ty(bcx, ret_ty, "__llret")) + } + } else { + None + } + } + }); + + let mut llresult = unsafe { + llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref()) + }; + + // The code below invokes the function, using either the Rust + // conventions (if it is a rust fn) or the native conventions + // (otherwise). The important part is that, when all is said + // and done, either the return value of the function will have been + // written in opt_llretslot (if it is Some) or `llresult` will be + // set appropriately (otherwise). + if is_rust_fn { + let mut llargs = Vec::new(); + + if let (ty::FnConverging(ret_ty), Some(llretslot)) = (ret_ty, opt_llretslot) { + if type_of::return_uses_outptr(ccx, ret_ty) { + llargs.push(llretslot); + } + } + + // Push the environment (or a trait object's self). + match (llenv, llself) { + (Some(llenv), None) => llargs.push(llenv), + (None, Some(llself)) => llargs.push(llself), + _ => {} + } + + // Push the arguments. + bcx = trans_args(bcx, + args, + callee_ty, + &mut llargs, + cleanup::CustomScope(arg_cleanup_scope), + llself.is_some(), + abi); + + fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); + + // Invoke the actual rust fn and update bcx/llresult. + let (llret, b) = base::invoke(bcx, + llfn, + llargs, + callee_ty, + call_info, + dest.is_none()); + bcx = b; + llresult = llret; + + // If the Rust convention for this type is return via + // the return value, copy it into llretslot. + match (opt_llretslot, ret_ty) { + (Some(llretslot), ty::FnConverging(ret_ty)) => { + if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && + !type_is_zero_size(bcx.ccx(), ret_ty) + { + store_ty(bcx, llret, llretslot, ret_ty) + } + } + (_, _) => {} + } + } else { + // Lang items are the only case where dest is None, and + // they are always Rust fns. + assert!(dest.is_some()); + + let mut llargs = Vec::new(); + let arg_tys = match args { + ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, &**x)).collect(), + _ => panic!("expected arg exprs.") + }; + bcx = trans_args(bcx, + args, + callee_ty, + &mut llargs, + cleanup::CustomScope(arg_cleanup_scope), + false, + abi); + fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); + + bcx = foreign::trans_native_call(bcx, callee_ty, + llfn, opt_llretslot.unwrap(), + llargs.as_slice(), arg_tys); + } + + fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_cleanup_scope); + + // If the caller doesn't care about the result of this fn call, + // drop the temporary slot we made. + match (dest, opt_llretslot, ret_ty) { + (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => { + // drop the value if it is not being saved. + bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info); + call_lifetime_end(bcx, llretslot); + } + _ => {} + } + + if ret_ty == ty::FnDiverging { + Unreachable(bcx); + } + + Result::new(bcx, llresult) +} + +pub enum CallArgs<'a> { + // Supply value of arguments as a list of expressions that must be + // translated. This is used in the common case of `foo(bar, qux)`. + ArgExprs(&'a [P]), + + // Supply value of arguments as a list of LLVM value refs; frequently + // used with lang items and so forth, when the argument is an internal + // value. + ArgVals(&'a [ValueRef]), + + // For overloaded operators: `(lhs, Vec(rhs, rhs_id))`. `lhs` + // is the left-hand-side and `rhs/rhs_id` is the datum/expr-id of + // the right-hand-side arguments (if any). + ArgOverloadedOp(Datum, Vec<(Datum, ast::NodeId)>), + + // Supply value of arguments as a list of expressions that must be + // translated, for overloaded call operators. + ArgOverloadedCall(Vec<&'a ast::Expr>), +} + +fn trans_args_under_call_abi<'blk, 'tcx>( + mut bcx: Block<'blk, 'tcx>, + arg_exprs: &[P], + fn_ty: ty::t, + llargs: &mut Vec, + arg_cleanup_scope: cleanup::ScopeId, + ignore_self: bool) + -> Block<'blk, 'tcx> { + // Translate the `self` argument first. + let arg_tys = ty::ty_fn_args(fn_ty); + if !ignore_self { + let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &*arg_exprs[0])); + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, + arg_tys[0], + arg_datum, + arg_cleanup_scope, + DontAutorefArg) + })) + } + + // Now untuple the rest of the arguments. + let tuple_expr = &arg_exprs[1]; + let tuple_type = node_id_type(bcx, tuple_expr.id); + + match ty::get(tuple_type).sty { + ty::ty_tup(ref field_types) => { + let tuple_datum = unpack_datum!(bcx, + expr::trans(bcx, &**tuple_expr)); + let tuple_lvalue_datum = + unpack_datum!(bcx, + tuple_datum.to_lvalue_datum(bcx, + "args", + tuple_expr.id)); + let repr = adt::represent_type(bcx.ccx(), tuple_type); + let repr_ptr = &*repr; + for i in range(0, field_types.len()) { + let arg_datum = tuple_lvalue_datum.get_element( + bcx, + field_types[i], + |srcval| { + adt::trans_field_ptr(bcx, repr_ptr, srcval, 0, i) + }); + let arg_datum = arg_datum.to_expr_datum(); + let arg_datum = + unpack_datum!(bcx, arg_datum.to_rvalue_datum(bcx, "arg")); + let arg_datum = + unpack_datum!(bcx, arg_datum.to_appropriate_datum(bcx)); + llargs.push(arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)); + } + } + _ => { + bcx.sess().span_bug(tuple_expr.span, + "argument to `.call()` wasn't a tuple?!") + } + }; + + bcx +} + +fn trans_overloaded_call_args<'blk, 'tcx>( + mut bcx: Block<'blk, 'tcx>, + arg_exprs: Vec<&ast::Expr>, + fn_ty: ty::t, + llargs: &mut Vec, + arg_cleanup_scope: cleanup::ScopeId, + ignore_self: bool) + -> Block<'blk, 'tcx> { + // Translate the `self` argument first. + let arg_tys = ty::ty_fn_args(fn_ty); + if !ignore_self { + let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0])); + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, + arg_tys[0], + arg_datum, + arg_cleanup_scope, + DontAutorefArg) + })) + } + + // Now untuple the rest of the arguments. + let tuple_type = arg_tys[1]; + match ty::get(tuple_type).sty { + ty::ty_tup(ref field_types) => { + for (i, &field_type) in field_types.iter().enumerate() { + let arg_datum = + unpack_datum!(bcx, expr::trans(bcx, arg_exprs[i + 1])); + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, + field_type, + arg_datum, + arg_cleanup_scope, + DontAutorefArg) + })) + } + } + _ => { + bcx.sess().span_bug(arg_exprs[0].span, + "argument to `.call()` wasn't a tuple?!") + } + }; + + bcx +} + +pub fn trans_args<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + args: CallArgs, + fn_ty: ty::t, + llargs: &mut Vec , + arg_cleanup_scope: cleanup::ScopeId, + ignore_self: bool, + abi: synabi::Abi) + -> Block<'blk, 'tcx> { + debug!("trans_args(abi={})", abi); + + let _icx = push_ctxt("trans_args"); + let arg_tys = ty::ty_fn_args(fn_ty); + let variadic = ty::fn_is_variadic(fn_ty); + + let mut bcx = cx; + + // First we figure out the caller's view of the types of the arguments. + // This will be needed if this is a generic call, because the callee has + // to cast her view of the arguments to the caller's view. + match args { + ArgExprs(arg_exprs) => { + if abi == synabi::RustCall { + // This is only used for direct calls to the `call`, + // `call_mut` or `call_once` functions. + return trans_args_under_call_abi(cx, + arg_exprs, + fn_ty, + llargs, + arg_cleanup_scope, + ignore_self) + } + + let num_formal_args = arg_tys.len(); + for (i, arg_expr) in arg_exprs.iter().enumerate() { + if i == 0 && ignore_self { + continue; + } + let arg_ty = if i >= num_formal_args { + assert!(variadic); + expr_ty_adjusted(cx, &**arg_expr) + } else { + arg_tys[i] + }; + + let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &**arg_expr)); + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, arg_ty, arg_datum, + arg_cleanup_scope, + DontAutorefArg) + })); + } + } + ArgOverloadedCall(arg_exprs) => { + return trans_overloaded_call_args(cx, + arg_exprs, + fn_ty, + llargs, + arg_cleanup_scope, + ignore_self) + } + ArgOverloadedOp(lhs, rhs) => { + assert!(!variadic); + + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, arg_tys[0], lhs, + arg_cleanup_scope, + DontAutorefArg) + })); + + assert_eq!(arg_tys.len(), 1 + rhs.len()); + for (rhs, rhs_id) in rhs.into_iter() { + llargs.push(unpack_result!(bcx, { + trans_arg_datum(bcx, arg_tys[1], rhs, + arg_cleanup_scope, + DoAutorefArg(rhs_id)) + })); + } + } + ArgVals(vs) => { + llargs.push_all(vs); + } + } + + bcx +} + +pub enum AutorefArg { + DontAutorefArg, + DoAutorefArg(ast::NodeId) +} + +pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + formal_arg_ty: ty::t, + arg_datum: Datum, + arg_cleanup_scope: cleanup::ScopeId, + autoref_arg: AutorefArg) + -> Result<'blk, 'tcx> { + let _icx = push_ctxt("trans_arg_datum"); + let mut bcx = bcx; + let ccx = bcx.ccx(); + + debug!("trans_arg_datum({})", + formal_arg_ty.repr(bcx.tcx())); + + let arg_datum_ty = arg_datum.ty; + + debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx())); + + let mut val; + // FIXME(#3548) use the adjustments table + match autoref_arg { + DoAutorefArg(arg_id) => { + // We will pass argument by reference + // We want an lvalue, so that we can pass by reference and + let arg_datum = unpack_datum!( + bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); + val = arg_datum.val; + } + DontAutorefArg => { + // Make this an rvalue, since we are going to be + // passing ownership. + let arg_datum = unpack_datum!( + bcx, arg_datum.to_rvalue_datum(bcx, "arg")); + + // Now that arg_datum is owned, get it into the appropriate + // mode (ref vs value). + let arg_datum = unpack_datum!( + bcx, arg_datum.to_appropriate_datum(bcx)); + + // Technically, ownership of val passes to the callee. + // However, we must cleanup should we panic before the + // callee is actually invoked. + val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope); + } + } + + if formal_arg_ty != arg_datum_ty { + // this could happen due to e.g. subtyping + let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); + debug!("casting actual type ({}) to match formal ({})", + bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); + debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty), + ty_to_string(bcx.tcx(), formal_arg_ty)); + val = PointerCast(bcx, val, llformal_arg_ty); + } + + debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); + Result::new(bcx, val) +} diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs new file mode 100644 index 00000000000..02909b0e3a9 --- /dev/null +++ b/src/librustc_trans/trans/cleanup.rs @@ -0,0 +1,1218 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Code pertaining to cleanup of temporaries as well as execution of + * drop glue. See discussion in `doc.rs` for a high-level summary. + */ + +pub use self::ScopeId::*; +pub use self::CleanupScopeKind::*; +pub use self::EarlyExitLabel::*; +pub use self::Heap::*; + +use llvm::{BasicBlockRef, ValueRef}; +use trans::base; +use trans::build; +use trans::callee; +use trans::common; +use trans::common::{Block, FunctionContext, ExprId, NodeInfo}; +use trans::debuginfo; +use trans::glue; +use trans::type_::Type; +use middle::ty; +use std::fmt; +use syntax::ast; +use util::ppaux::Repr; + +pub struct CleanupScope<'blk, 'tcx: 'blk> { + // The id of this cleanup scope. If the id is None, + // this is a *temporary scope* that is pushed during trans to + // cleanup miscellaneous garbage that trans may generate whose + // lifetime is a subset of some expression. See module doc for + // more details. + kind: CleanupScopeKind<'blk, 'tcx>, + + // Cleanups to run upon scope exit. + cleanups: Vec, + + // The debug location any drop calls generated for this scope will be + // associated with. + debug_loc: Option, + + cached_early_exits: Vec, + cached_landing_pad: Option, +} + +#[deriving(Show)] +pub struct CustomScopeIndex { + index: uint +} + +pub const EXIT_BREAK: uint = 0; +pub const EXIT_LOOP: uint = 1; +pub const EXIT_MAX: uint = 2; + +pub enum CleanupScopeKind<'blk, 'tcx: 'blk> { + CustomScopeKind, + AstScopeKind(ast::NodeId), + LoopScopeKind(ast::NodeId, [Block<'blk, 'tcx>, ..EXIT_MAX]) +} + +impl<'blk, 'tcx: 'blk> fmt::Show for CleanupScopeKind<'blk, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::FormatError> { + match *self { + CustomScopeKind => write!(f, "CustomScopeKind"), + AstScopeKind(nid) => write!(f, "AstScopeKind({})", nid), + LoopScopeKind(nid, ref blks) => { + try!(write!(f, "LoopScopeKind({}, [", nid)); + for blk in blks.iter() { + try!(write!(f, "{:p}, ", blk)); + } + write!(f, "])") + } + } + } +} + +#[deriving(PartialEq, Show)] +pub enum EarlyExitLabel { + UnwindExit, + ReturnExit, + LoopExit(ast::NodeId, uint) +} + +pub struct CachedEarlyExit { + label: EarlyExitLabel, + cleanup_block: BasicBlockRef, +} + +pub trait Cleanup { + fn must_unwind(&self) -> bool; + fn clean_on_unwind(&self) -> bool; + fn is_lifetime_end(&self) -> bool; + fn trans<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + debug_loc: Option) + -> Block<'blk, 'tcx>; +} + +pub type CleanupObj = Box; + +#[deriving(Show)] +pub enum ScopeId { + AstScope(ast::NodeId), + CustomScope(CustomScopeIndex) +} + +impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { + fn push_ast_cleanup_scope(&self, debug_loc: NodeInfo) { + /*! + * Invoked when we start to trans the code contained + * within a new cleanup scope. + */ + + debug!("push_ast_cleanup_scope({})", + self.ccx.tcx().map.node_to_string(debug_loc.id)); + + // FIXME(#2202) -- currently closure bodies have a parent + // region, which messes up the assertion below, since there + // are no cleanup scopes on the stack at the start of + // trans'ing a closure body. I think though that this should + // eventually be fixed by closure bodies not having a parent + // region, though that's a touch unclear, and it might also be + // better just to narrow this assertion more (i.e., by + // excluding id's that correspond to closure bodies only). For + // now we just say that if there is already an AST scope on the stack, + // this new AST scope had better be its immediate child. + let top_scope = self.top_ast_scope(); + if top_scope.is_some() { + assert_eq!(self.ccx + .tcx() + .region_maps + .opt_encl_scope(debug_loc.id), + top_scope); + } + + self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id), + Some(debug_loc))); + } + + fn push_loop_cleanup_scope(&self, + id: ast::NodeId, + exits: [Block<'blk, 'tcx>, ..EXIT_MAX]) { + debug!("push_loop_cleanup_scope({})", + self.ccx.tcx().map.node_to_string(id)); + assert_eq!(Some(id), self.top_ast_scope()); + + // Just copy the debuginfo source location from the enclosing scope + let debug_loc = self.scopes + .borrow() + .last() + .unwrap() + .debug_loc; + + self.push_scope(CleanupScope::new(LoopScopeKind(id, exits), debug_loc)); + } + + fn push_custom_cleanup_scope(&self) -> CustomScopeIndex { + let index = self.scopes_len(); + debug!("push_custom_cleanup_scope(): {}", index); + + // Just copy the debuginfo source location from the enclosing scope + let debug_loc = self.scopes + .borrow() + .last() + .map(|opt_scope| opt_scope.debug_loc) + .unwrap_or(None); + + self.push_scope(CleanupScope::new(CustomScopeKind, debug_loc)); + CustomScopeIndex { index: index } + } + + fn push_custom_cleanup_scope_with_debug_loc(&self, + debug_loc: NodeInfo) + -> CustomScopeIndex { + let index = self.scopes_len(); + debug!("push_custom_cleanup_scope(): {}", index); + + self.push_scope(CleanupScope::new(CustomScopeKind, Some(debug_loc))); + CustomScopeIndex { index: index } + } + + fn pop_and_trans_ast_cleanup_scope(&self, + bcx: Block<'blk, 'tcx>, + cleanup_scope: ast::NodeId) + -> Block<'blk, 'tcx> { + /*! + * Removes the cleanup scope for id `cleanup_scope`, which + * must be at the top of the cleanup stack, and generates the + * code to do its cleanups for normal exit. + */ + + debug!("pop_and_trans_ast_cleanup_scope({})", + self.ccx.tcx().map.node_to_string(cleanup_scope)); + + assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope))); + + let scope = self.pop_scope(); + self.trans_scope_cleanups(bcx, &scope) + } + + fn pop_loop_cleanup_scope(&self, + cleanup_scope: ast::NodeId) { + /*! + * Removes the loop cleanup scope for id `cleanup_scope`, which + * must be at the top of the cleanup stack. Does not generate + * any cleanup code, since loop scopes should exit by + * branching to a block generated by `normal_exit_block`. + */ + + debug!("pop_loop_cleanup_scope({})", + self.ccx.tcx().map.node_to_string(cleanup_scope)); + + assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope))); + + let _ = self.pop_scope(); + } + + fn pop_custom_cleanup_scope(&self, + custom_scope: CustomScopeIndex) { + /*! + * Removes the top cleanup scope from the stack without + * executing its cleanups. The top cleanup scope must + * be the temporary scope `custom_scope`. + */ + + debug!("pop_custom_cleanup_scope({})", custom_scope.index); + assert!(self.is_valid_to_pop_custom_scope(custom_scope)); + let _ = self.pop_scope(); + } + + fn pop_and_trans_custom_cleanup_scope(&self, + bcx: Block<'blk, 'tcx>, + custom_scope: CustomScopeIndex) + -> Block<'blk, 'tcx> { + /*! + * Removes the top cleanup scope from the stack, which must be + * a temporary scope, and generates the code to do its + * cleanups for normal exit. + */ + + debug!("pop_and_trans_custom_cleanup_scope({})", custom_scope); + assert!(self.is_valid_to_pop_custom_scope(custom_scope)); + + let scope = self.pop_scope(); + self.trans_scope_cleanups(bcx, &scope) + } + + fn top_loop_scope(&self) -> ast::NodeId { + /*! + * Returns the id of the top-most loop scope + */ + + for scope in self.scopes.borrow().iter().rev() { + match scope.kind { + LoopScopeKind(id, _) => { + return id; + } + _ => {} + } + } + self.ccx.sess().bug("no loop scope found"); + } + + fn normal_exit_block(&'blk self, + cleanup_scope: ast::NodeId, + exit: uint) -> BasicBlockRef { + /*! + * Returns a block to branch to which will perform all pending + * cleanups and then break/continue (depending on `exit`) out + * of the loop with id `cleanup_scope` + */ + + self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit)) + } + + fn return_exit_block(&'blk self) -> BasicBlockRef { + /*! + * Returns a block to branch to which will perform all pending + * cleanups and then return from this function + */ + + self.trans_cleanups_to_exit_scope(ReturnExit) + } + + fn schedule_lifetime_end(&self, + cleanup_scope: ScopeId, + val: ValueRef) { + let drop = box LifetimeEnd { + ptr: val, + }; + + debug!("schedule_lifetime_end({}, val={})", + cleanup_scope, + self.ccx.tn().val_to_string(val)); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_drop_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t) { + /*! + * Schedules a (deep) drop of `val`, which is a pointer to an + * instance of `ty` + */ + + if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + let drop = box DropValue { + is_immediate: false, + must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + val: val, + ty: ty, + zero: false + }; + + debug!("schedule_drop_mem({}, val={}, ty={})", + cleanup_scope, + self.ccx.tn().val_to_string(val), + ty.repr(self.ccx.tcx())); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_drop_and_zero_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t) { + /*! + * Schedules a (deep) drop and zero-ing of `val`, which is a pointer + * to an instance of `ty` + */ + + if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + let drop = box DropValue { + is_immediate: false, + must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + val: val, + ty: ty, + zero: true + }; + + debug!("schedule_drop_and_zero_mem({}, val={}, ty={}, zero={})", + cleanup_scope, + self.ccx.tn().val_to_string(val), + ty.repr(self.ccx.tcx()), + true); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_drop_immediate(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t) { + /*! + * Schedules a (deep) drop of `val`, which is an instance of `ty` + */ + + if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + let drop = box DropValue { + is_immediate: true, + must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + val: val, + ty: ty, + zero: false + }; + + debug!("schedule_drop_immediate({}, val={}, ty={})", + cleanup_scope, + self.ccx.tn().val_to_string(val), + ty.repr(self.ccx.tcx())); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_free_value(&self, + cleanup_scope: ScopeId, + val: ValueRef, + heap: Heap, + content_ty: ty::t) { + /*! + * Schedules a call to `free(val)`. Note that this is a shallow + * operation. + */ + + let drop = box FreeValue { ptr: val, heap: heap, content_ty: content_ty }; + + debug!("schedule_free_value({}, val={}, heap={})", + cleanup_scope, + self.ccx.tn().val_to_string(val), + heap); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_free_slice(&self, + cleanup_scope: ScopeId, + val: ValueRef, + size: ValueRef, + align: ValueRef, + heap: Heap) { + /*! + * Schedules a call to `free(val)`. Note that this is a shallow + * operation. + */ + + let drop = box FreeSlice { ptr: val, size: size, align: align, heap: heap }; + + debug!("schedule_free_slice({}, val={}, heap={})", + cleanup_scope, + self.ccx.tn().val_to_string(val), + heap); + + self.schedule_clean(cleanup_scope, drop as CleanupObj); + } + + fn schedule_clean(&self, + cleanup_scope: ScopeId, + cleanup: CleanupObj) { + match cleanup_scope { + AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup), + CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup), + } + } + + fn schedule_clean_in_ast_scope(&self, + cleanup_scope: ast::NodeId, + cleanup: CleanupObj) { + /*! + * Schedules a cleanup to occur upon exit from `cleanup_scope`. + * If `cleanup_scope` is not provided, then the cleanup is scheduled + * in the topmost scope, which must be a temporary scope. + */ + + debug!("schedule_clean_in_ast_scope(cleanup_scope={})", + cleanup_scope); + + for scope in self.scopes.borrow_mut().iter_mut().rev() { + if scope.kind.is_ast_with_id(cleanup_scope) { + scope.cleanups.push(cleanup); + scope.clear_cached_exits(); + return; + } else { + // will be adding a cleanup to some enclosing scope + scope.clear_cached_exits(); + } + } + + self.ccx.sess().bug( + format!("no cleanup scope {} found", + self.ccx.tcx().map.node_to_string(cleanup_scope)).as_slice()); + } + + fn schedule_clean_in_custom_scope(&self, + custom_scope: CustomScopeIndex, + cleanup: CleanupObj) { + /*! + * Schedules a cleanup to occur in the top-most scope, + * which must be a temporary scope. + */ + + debug!("schedule_clean_in_custom_scope(custom_scope={})", + custom_scope.index); + + assert!(self.is_valid_custom_scope(custom_scope)); + + let mut scopes = self.scopes.borrow_mut(); + let scope = &mut (*scopes)[custom_scope.index]; + scope.cleanups.push(cleanup); + scope.clear_cached_exits(); + } + + fn needs_invoke(&self) -> bool { + /*! + * Returns true if there are pending cleanups that should + * execute on panic. + */ + + self.scopes.borrow().iter().rev().any(|s| s.needs_invoke()) + } + + fn get_landing_pad(&'blk self) -> BasicBlockRef { + /*! + * Returns a basic block to branch to in the event of a panic. + * This block will run the panic cleanups and eventually + * invoke the LLVM `Resume` instruction. + */ + + let _icx = base::push_ctxt("get_landing_pad"); + + debug!("get_landing_pad"); + + let orig_scopes_len = self.scopes_len(); + assert!(orig_scopes_len > 0); + + // Remove any scopes that do not have cleanups on panic: + let mut popped_scopes = vec!(); + while !self.top_scope(|s| s.needs_invoke()) { + debug!("top scope does not need invoke"); + popped_scopes.push(self.pop_scope()); + } + + // Check for an existing landing pad in the new topmost scope: + let llbb = self.get_or_create_landing_pad(); + + // Push the scopes we removed back on: + loop { + match popped_scopes.pop() { + Some(scope) => self.push_scope(scope), + None => break + } + } + + assert_eq!(self.scopes_len(), orig_scopes_len); + + return llbb; + } +} + +impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { + fn top_ast_scope(&self) -> Option { + /*! + * Returns the id of the current top-most AST scope, if any. + */ + for scope in self.scopes.borrow().iter().rev() { + match scope.kind { + CustomScopeKind | LoopScopeKind(..) => {} + AstScopeKind(i) => { + return Some(i); + } + } + } + None + } + + fn top_nonempty_cleanup_scope(&self) -> Option { + self.scopes.borrow().iter().rev().position(|s| !s.cleanups.is_empty()) + } + + fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { + self.is_valid_custom_scope(custom_scope) && + custom_scope.index == self.scopes.borrow().len() - 1 + } + + fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { + let scopes = self.scopes.borrow(); + custom_scope.index < scopes.len() && + (*scopes)[custom_scope.index].kind.is_temp() + } + + fn trans_scope_cleanups(&self, // cannot borrow self, will recurse + bcx: Block<'blk, 'tcx>, + scope: &CleanupScope) -> Block<'blk, 'tcx> { + /*! Generates the cleanups for `scope` into `bcx` */ + + let mut bcx = bcx; + if !bcx.unreachable.get() { + for cleanup in scope.cleanups.iter().rev() { + bcx = cleanup.trans(bcx, scope.debug_loc); + } + } + bcx + } + + fn scopes_len(&self) -> uint { + self.scopes.borrow().len() + } + + fn push_scope(&self, scope: CleanupScope<'blk, 'tcx>) { + self.scopes.borrow_mut().push(scope) + } + + fn pop_scope(&self) -> CleanupScope<'blk, 'tcx> { + debug!("popping cleanup scope {}, {} scopes remaining", + self.top_scope(|s| s.block_name("")), + self.scopes_len() - 1); + + self.scopes.borrow_mut().pop().unwrap() + } + + fn top_scope(&self, f: |&CleanupScope<'blk, 'tcx>| -> R) -> R { + f(self.scopes.borrow().last().unwrap()) + } + + fn trans_cleanups_to_exit_scope(&'blk self, + label: EarlyExitLabel) + -> BasicBlockRef { + /*! + * Used when the caller wishes to jump to an early exit, such + * as a return, break, continue, or unwind. This function will + * generate all cleanups between the top of the stack and the + * exit `label` and return a basic block that the caller can + * branch to. + * + * For example, if the current stack of cleanups were as follows: + * + * AST 22 + * Custom 1 + * AST 23 + * Loop 23 + * Custom 2 + * AST 24 + * + * and the `label` specifies a break from `Loop 23`, then this + * function would generate a series of basic blocks as follows: + * + * Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk + * + * where `break_blk` is the block specified in `Loop 23` as + * the target for breaks. The return value would be the first + * basic block in that sequence (`Cleanup(AST 24)`). The + * caller could then branch to `Cleanup(AST 24)` and it will + * perform all cleanups and finally branch to the `break_blk`. + */ + + debug!("trans_cleanups_to_exit_scope label={} scopes={}", + label, self.scopes_len()); + + let orig_scopes_len = self.scopes_len(); + let mut prev_llbb; + let mut popped_scopes = vec!(); + + // First we pop off all the cleanup stacks that are + // traversed until the exit is reached, pushing them + // onto the side vector `popped_scopes`. No code is + // generated at this time. + // + // So, continuing the example from above, we would wind up + // with a `popped_scopes` vector of `[AST 24, Custom 2]`. + // (Presuming that there are no cached exits) + loop { + if self.scopes_len() == 0 { + match label { + UnwindExit => { + // Generate a block that will `Resume`. + let prev_bcx = self.new_block(true, "resume", None); + let personality = self.personality.get().expect( + "create_landing_pad() should have set this"); + build::Resume(prev_bcx, + build::Load(prev_bcx, personality)); + prev_llbb = prev_bcx.llbb; + break; + } + + ReturnExit => { + prev_llbb = self.get_llreturn(); + break; + } + + LoopExit(id, _) => { + self.ccx.sess().bug(format!( + "cannot exit from scope {}, \ + not in scope", id).as_slice()); + } + } + } + + // Check if we have already cached the unwinding of this + // scope for this label. If so, we can stop popping scopes + // and branch to the cached label, since it contains the + // cleanups for any subsequent scopes. + match self.top_scope(|s| s.cached_early_exit(label)) { + Some(cleanup_block) => { + prev_llbb = cleanup_block; + break; + } + None => { } + } + + // Pop off the scope, since we will be generating + // unwinding code for it. If we are searching for a loop exit, + // and this scope is that loop, then stop popping and set + // `prev_llbb` to the appropriate exit block from the loop. + popped_scopes.push(self.pop_scope()); + let scope = popped_scopes.last().unwrap(); + match label { + UnwindExit | ReturnExit => { } + LoopExit(id, exit) => { + match scope.kind.early_exit_block(id, exit) { + Some(exitllbb) => { + prev_llbb = exitllbb; + break; + } + + None => { } + } + } + } + } + + debug!("trans_cleanups_to_exit_scope: popped {} scopes", + popped_scopes.len()); + + // Now push the popped scopes back on. As we go, + // we track in `prev_llbb` the exit to which this scope + // should branch when it's done. + // + // So, continuing with our example, we will start out with + // `prev_llbb` being set to `break_blk` (or possibly a cached + // early exit). We will then pop the scopes from `popped_scopes` + // and generate a basic block for each one, prepending it in the + // series and updating `prev_llbb`. So we begin by popping `Custom 2` + // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)` + // branch to `prev_llbb == break_blk`, giving us a sequence like: + // + // Cleanup(Custom 2) -> prev_llbb + // + // We then pop `AST 24` and repeat the process, giving us the sequence: + // + // Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb + // + // At this point, `popped_scopes` is empty, and so the final block + // that we return to the user is `Cleanup(AST 24)`. + while !popped_scopes.is_empty() { + let mut scope = popped_scopes.pop().unwrap(); + + if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(&**c, label)) + { + let name = scope.block_name("clean"); + debug!("generating cleanups for {}", name); + let bcx_in = self.new_block(label.is_unwind(), + name.as_slice(), + None); + let mut bcx_out = bcx_in; + for cleanup in scope.cleanups.iter().rev() { + if cleanup_is_suitable_for(&**cleanup, label) { + bcx_out = cleanup.trans(bcx_out, + scope.debug_loc); + } + } + build::Br(bcx_out, prev_llbb); + prev_llbb = bcx_in.llbb; + } else { + debug!("no suitable cleanups in {}", + scope.block_name("clean")); + } + + scope.add_cached_early_exit(label, prev_llbb); + self.push_scope(scope); + } + + debug!("trans_cleanups_to_exit_scope: prev_llbb={}", prev_llbb); + + assert_eq!(self.scopes_len(), orig_scopes_len); + prev_llbb + } + + fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef { + /*! + * Creates a landing pad for the top scope, if one does not + * exist. The landing pad will perform all cleanups necessary + * for an unwind and then `resume` to continue error + * propagation: + * + * landing_pad -> ... cleanups ... -> [resume] + * + * (The cleanups and resume instruction are created by + * `trans_cleanups_to_exit_scope()`, not in this function + * itself.) + */ + + let pad_bcx; + + debug!("get_or_create_landing_pad"); + + // Check if a landing pad block exists; if not, create one. + { + let mut scopes = self.scopes.borrow_mut(); + let last_scope = scopes.last_mut().unwrap(); + match last_scope.cached_landing_pad { + Some(llbb) => { return llbb; } + None => { + let name = last_scope.block_name("unwind"); + pad_bcx = self.new_block(true, name.as_slice(), None); + last_scope.cached_landing_pad = Some(pad_bcx.llbb); + } + } + } + + // The landing pad return type (the type being propagated). Not sure what + // this represents but it's determined by the personality function and + // this is what the EH proposal example uses. + let llretty = Type::struct_(self.ccx, + &[Type::i8p(self.ccx), Type::i32(self.ccx)], + false); + + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to translate that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { + Some(def_id) => callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)), + None => { + let mut personality = self.ccx.eh_personality().borrow_mut(); + match *personality { + Some(llpersonality) => llpersonality, + None => { + let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); + let f = base::decl_cdecl_fn(self.ccx, + "rust_eh_personality", + fty, + ty::mk_i32()); + *personality = Some(f); + f + } + } + } + }; + + // The only landing pad clause will be 'cleanup' + let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u); + + // The landing pad block is a cleanup + build::SetCleanup(pad_bcx, llretval); + + // We store the retval in a function-central alloca, so that calls to + // Resume can find it. + match self.personality.get() { + Some(addr) => { + build::Store(pad_bcx, llretval, addr); + } + None => { + let addr = base::alloca(pad_bcx, common::val_ty(llretval), ""); + self.personality.set(Some(addr)); + build::Store(pad_bcx, llretval, addr); + } + } + + // Generate the cleanup block and branch to it. + let cleanup_llbb = self.trans_cleanups_to_exit_scope(UnwindExit); + build::Br(pad_bcx, cleanup_llbb); + + return pad_bcx.llbb; + } +} + +impl<'blk, 'tcx> CleanupScope<'blk, 'tcx> { + fn new(kind: CleanupScopeKind<'blk, 'tcx>, + debug_loc: Option) + -> CleanupScope<'blk, 'tcx> { + CleanupScope { + kind: kind, + debug_loc: debug_loc, + cleanups: vec!(), + cached_early_exits: vec!(), + cached_landing_pad: None, + } + } + + fn clear_cached_exits(&mut self) { + self.cached_early_exits = vec!(); + self.cached_landing_pad = None; + } + + fn cached_early_exit(&self, + label: EarlyExitLabel) + -> Option { + self.cached_early_exits.iter(). + find(|e| e.label == label). + map(|e| e.cleanup_block) + } + + fn add_cached_early_exit(&mut self, + label: EarlyExitLabel, + blk: BasicBlockRef) { + self.cached_early_exits.push( + CachedEarlyExit { label: label, + cleanup_block: blk }); + } + + fn needs_invoke(&self) -> bool { + /*! True if this scope has cleanups that need unwinding */ + + self.cached_landing_pad.is_some() || + self.cleanups.iter().any(|c| c.must_unwind()) + } + + fn block_name(&self, prefix: &str) -> String { + /*! + * Returns a suitable name to use for the basic block that + * handles this cleanup scope + */ + + match self.kind { + CustomScopeKind => format!("{}_custom_", prefix), + AstScopeKind(id) => format!("{}_ast_{}_", prefix, id), + LoopScopeKind(id, _) => format!("{}_loop_{}_", prefix, id), + } + } + + pub fn drop_non_lifetime_clean(&mut self) { + self.cleanups.retain(|c| c.is_lifetime_end()); + } +} + +impl<'blk, 'tcx> CleanupScopeKind<'blk, 'tcx> { + fn is_temp(&self) -> bool { + match *self { + CustomScopeKind => true, + LoopScopeKind(..) | AstScopeKind(..) => false, + } + } + + fn is_ast_with_id(&self, id: ast::NodeId) -> bool { + match *self { + CustomScopeKind | LoopScopeKind(..) => false, + AstScopeKind(i) => i == id + } + } + + fn is_loop_with_id(&self, id: ast::NodeId) -> bool { + match *self { + CustomScopeKind | AstScopeKind(..) => false, + LoopScopeKind(i, _) => i == id + } + } + + fn early_exit_block(&self, + id: ast::NodeId, + exit: uint) -> Option { + /*! + * If this is a loop scope with id `id`, return the early + * exit block `exit`, else `None` + */ + + match *self { + LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb), + _ => None, + } + } +} + +impl EarlyExitLabel { + fn is_unwind(&self) -> bool { + match *self { + UnwindExit => true, + _ => false + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Cleanup types + +pub struct DropValue { + is_immediate: bool, + must_unwind: bool, + val: ValueRef, + ty: ty::t, + zero: bool +} + +impl Cleanup for DropValue { + fn must_unwind(&self) -> bool { + self.must_unwind + } + + fn clean_on_unwind(&self) -> bool { + self.must_unwind + } + + fn is_lifetime_end(&self) -> bool { + false + } + + fn trans<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + debug_loc: Option) + -> Block<'blk, 'tcx> { + let bcx = if self.is_immediate { + glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc) + } else { + glue::drop_ty(bcx, self.val, self.ty, debug_loc) + }; + if self.zero { + base::zero_mem(bcx, self.val, self.ty); + } + bcx + } +} + +#[deriving(Show)] +pub enum Heap { + HeapExchange +} + +pub struct FreeValue { + ptr: ValueRef, + heap: Heap, + content_ty: ty::t +} + +impl Cleanup for FreeValue { + fn must_unwind(&self) -> bool { + true + } + + fn clean_on_unwind(&self) -> bool { + true + } + + fn is_lifetime_end(&self) -> bool { + false + } + + fn trans<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + debug_loc: Option) + -> Block<'blk, 'tcx> { + apply_debug_loc(bcx.fcx, debug_loc); + + match self.heap { + HeapExchange => { + glue::trans_exchange_free_ty(bcx, self.ptr, self.content_ty) + } + } + } +} + +pub struct FreeSlice { + ptr: ValueRef, + size: ValueRef, + align: ValueRef, + heap: Heap, +} + +impl Cleanup for FreeSlice { + fn must_unwind(&self) -> bool { + true + } + + fn clean_on_unwind(&self) -> bool { + true + } + + fn is_lifetime_end(&self) -> bool { + false + } + + fn trans<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + debug_loc: Option) + -> Block<'blk, 'tcx> { + apply_debug_loc(bcx.fcx, debug_loc); + + match self.heap { + HeapExchange => { + glue::trans_exchange_free_dyn(bcx, self.ptr, self.size, self.align) + } + } + } +} + +pub struct LifetimeEnd { + ptr: ValueRef, +} + +impl Cleanup for LifetimeEnd { + fn must_unwind(&self) -> bool { + false + } + + fn clean_on_unwind(&self) -> bool { + true + } + + fn is_lifetime_end(&self) -> bool { + true + } + + fn trans<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + debug_loc: Option) + -> Block<'blk, 'tcx> { + apply_debug_loc(bcx.fcx, debug_loc); + base::call_lifetime_end(bcx, self.ptr); + bcx + } +} + +pub fn temporary_scope(tcx: &ty::ctxt, + id: ast::NodeId) + -> ScopeId { + match tcx.region_maps.temporary_scope(id) { + Some(scope) => { + let r = AstScope(scope); + debug!("temporary_scope({}) = {}", id, r); + r + } + None => { + tcx.sess.bug(format!("no temporary scope available for expr {}", + id).as_slice()) + } + } +} + +pub fn var_scope(tcx: &ty::ctxt, + id: ast::NodeId) + -> ScopeId { + let r = AstScope(tcx.region_maps.var_scope(id)); + debug!("var_scope({}) = {}", id, r); + r +} + +fn cleanup_is_suitable_for(c: &Cleanup, + label: EarlyExitLabel) -> bool { + !label.is_unwind() || c.clean_on_unwind() +} + +fn apply_debug_loc(fcx: &FunctionContext, debug_loc: Option) { + match debug_loc { + Some(ref src_loc) => { + debuginfo::set_source_location(fcx, src_loc.id, src_loc.span); + } + None => { + debuginfo::clear_source_location(fcx); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// These traits just exist to put the methods into this file. + +pub trait CleanupMethods<'blk, 'tcx> { + fn push_ast_cleanup_scope(&self, id: NodeInfo); + fn push_loop_cleanup_scope(&self, + id: ast::NodeId, + exits: [Block<'blk, 'tcx>, ..EXIT_MAX]); + fn push_custom_cleanup_scope(&self) -> CustomScopeIndex; + fn push_custom_cleanup_scope_with_debug_loc(&self, + debug_loc: NodeInfo) + -> CustomScopeIndex; + fn pop_and_trans_ast_cleanup_scope(&self, + bcx: Block<'blk, 'tcx>, + cleanup_scope: ast::NodeId) + -> Block<'blk, 'tcx>; + fn pop_loop_cleanup_scope(&self, + cleanup_scope: ast::NodeId); + fn pop_custom_cleanup_scope(&self, + custom_scope: CustomScopeIndex); + fn pop_and_trans_custom_cleanup_scope(&self, + bcx: Block<'blk, 'tcx>, + custom_scope: CustomScopeIndex) + -> Block<'blk, 'tcx>; + fn top_loop_scope(&self) -> ast::NodeId; + fn normal_exit_block(&'blk self, + cleanup_scope: ast::NodeId, + exit: uint) -> BasicBlockRef; + fn return_exit_block(&'blk self) -> BasicBlockRef; + fn schedule_lifetime_end(&self, + cleanup_scope: ScopeId, + val: ValueRef); + fn schedule_drop_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t); + fn schedule_drop_and_zero_mem(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t); + fn schedule_drop_immediate(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: ty::t); + fn schedule_free_value(&self, + cleanup_scope: ScopeId, + val: ValueRef, + heap: Heap, + content_ty: ty::t); + fn schedule_free_slice(&self, + cleanup_scope: ScopeId, + val: ValueRef, + size: ValueRef, + align: ValueRef, + heap: Heap); + fn schedule_clean(&self, + cleanup_scope: ScopeId, + cleanup: CleanupObj); + fn schedule_clean_in_ast_scope(&self, + cleanup_scope: ast::NodeId, + cleanup: CleanupObj); + fn schedule_clean_in_custom_scope(&self, + custom_scope: CustomScopeIndex, + cleanup: CleanupObj); + fn needs_invoke(&self) -> bool; + fn get_landing_pad(&'blk self) -> BasicBlockRef; +} + +trait CleanupHelperMethods<'blk, 'tcx> { + fn top_ast_scope(&self) -> Option; + fn top_nonempty_cleanup_scope(&self) -> Option; + fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; + fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool; + fn trans_scope_cleanups(&self, + bcx: Block<'blk, 'tcx>, + scope: &CleanupScope<'blk, 'tcx>) -> Block<'blk, 'tcx>; + fn trans_cleanups_to_exit_scope(&'blk self, + label: EarlyExitLabel) + -> BasicBlockRef; + fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef; + fn scopes_len(&self) -> uint; + fn push_scope(&self, scope: CleanupScope<'blk, 'tcx>); + fn pop_scope(&self) -> CleanupScope<'blk, 'tcx>; + fn top_scope(&self, f: |&CleanupScope<'blk, 'tcx>| -> R) -> R; +} diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs new file mode 100644 index 00000000000..139087507ff --- /dev/null +++ b/src/librustc_trans/trans/closure.rs @@ -0,0 +1,659 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use back::abi; +use back::link::mangle_internal_name_by_path_and_seq; +use llvm::ValueRef; +use middle::def; +use middle::mem_categorization::Typer; +use trans::adt; +use trans::base::*; +use trans::build::*; +use trans::cleanup::{CleanupMethods, ScopeId}; +use trans::common::*; +use trans::datum::{Datum, DatumBlock, Expr, Lvalue, rvalue_scratch_datum}; +use trans::debuginfo; +use trans::expr; +use trans::monomorphize::MonoId; +use trans::type_of::*; +use trans::type_::Type; +use middle::ty; +use middle::subst::{Subst, Substs}; +use session::config::FullDebugInfo; +use util::ppaux::Repr; +use util::ppaux::ty_to_string; + +use arena::TypedArena; +use syntax::ast; +use syntax::ast_util; + +// ___Good to know (tm)__________________________________________________ +// +// The layout of a closure environment in memory is +// roughly as follows: +// +// struct rust_opaque_box { // see rust_internal.h +// unsigned ref_count; // obsolete (part of @T's header) +// fn(void*) *drop_glue; // destructor (for proc) +// rust_opaque_box *prev; // obsolete (part of @T's header) +// rust_opaque_box *next; // obsolete (part of @T's header) +// struct closure_data { +// upvar1_t upvar1; +// ... +// upvarN_t upvarN; +// } +// }; +// +// Note that the closure is itself a rust_opaque_box. This is true +// even for ~fn and ||, because we wish to keep binary compatibility +// between all kinds of closures. The allocation strategy for this +// closure depends on the closure type. For a sendfn, the closure +// (and the referenced type descriptors) will be allocated in the +// exchange heap. For a fn, the closure is allocated in the task heap +// and is reference counted. For a block, the closure is allocated on +// the stack. +// +// ## Opaque closures and the embedded type descriptor ## +// +// One interesting part of closures is that they encapsulate the data +// that they close over. So when I have a ptr to a closure, I do not +// know how many type descriptors it contains nor what upvars are +// captured within. That means I do not know precisely how big it is +// nor where its fields are located. This is called an "opaque +// closure". +// +// Typically an opaque closure suffices because we only manipulate it +// by ptr. The routine Type::at_box().ptr_to() returns an appropriate +// type for such an opaque closure; it allows access to the box fields, +// but not the closure_data itself. +// +// But sometimes, such as when cloning or freeing a closure, we need +// to know the full information. That is where the type descriptor +// that defines the closure comes in handy. We can use its take and +// drop glue functions to allocate/free data as needed. +// +// ## Subtleties concerning alignment ## +// +// It is important that we be able to locate the closure data *without +// knowing the kind of data that is being bound*. This can be tricky +// because the alignment requirements of the bound data affects the +// alignment requires of the closure_data struct as a whole. However, +// right now this is a non-issue in any case, because the size of the +// rust_opaque_box header is always a multiple of 16-bytes, which is +// the maximum alignment requirement we ever have to worry about. +// +// The only reason alignment matters is that, in order to learn what data +// is bound, we would normally first load the type descriptors: but their +// location is ultimately depend on their content! There is, however, a +// workaround. We can load the tydesc from the rust_opaque_box, which +// describes the closure_data struct and has self-contained derived type +// descriptors, and read the alignment from there. It's just annoying to +// do. Hopefully should this ever become an issue we'll have monomorphized +// and type descriptors will all be a bad dream. +// +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +pub struct EnvValue { + action: ast::CaptureClause, + datum: Datum +} + +impl EnvValue { + pub fn to_string(&self, ccx: &CrateContext) -> String { + format!("{}({})", self.action, self.datum.to_string(ccx)) + } +} + +// Given a closure ty, emits a corresponding tuple ty +pub fn mk_closure_tys(tcx: &ty::ctxt, + bound_values: &[EnvValue]) + -> ty::t { + // determine the types of the values in the env. Note that this + // is the actual types that will be stored in the map, not the + // logical types as the user sees them, so by-ref upvars must be + // converted to ptrs. + let bound_tys = bound_values.iter().map(|bv| { + match bv.action { + ast::CaptureByValue => bv.datum.ty, + ast::CaptureByRef => ty::mk_mut_ptr(tcx, bv.datum.ty) + } + }).collect(); + let cdata_ty = ty::mk_tup(tcx, bound_tys); + debug!("cdata_ty={}", ty_to_string(tcx, cdata_ty)); + return cdata_ty; +} + +fn tuplify_box_ty(tcx: &ty::ctxt, t: ty::t) -> ty::t { + let ptr = ty::mk_imm_ptr(tcx, ty::mk_i8()); + ty::mk_tup(tcx, vec!(ty::mk_uint(), ty::mk_nil_ptr(tcx), ptr, ptr, t)) +} + +fn allocate_cbox<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + store: ty::TraitStore, + cdata_ty: ty::t) + -> Result<'blk, 'tcx> { + let _icx = push_ctxt("closure::allocate_cbox"); + let tcx = bcx.tcx(); + + // Allocate and initialize the box: + let cbox_ty = tuplify_box_ty(tcx, cdata_ty); + match store { + ty::UniqTraitStore => { + malloc_raw_dyn_proc(bcx, cbox_ty) + } + ty::RegionTraitStore(..) => { + let llbox = alloc_ty(bcx, cbox_ty, "__closure"); + Result::new(bcx, llbox) + } + } +} + +pub struct ClosureResult<'blk, 'tcx: 'blk> { + llbox: ValueRef, // llvalue of ptr to closure + cdata_ty: ty::t, // type of the closure data + bcx: Block<'blk, 'tcx> // final bcx +} + +// Given a block context and a list of tydescs and values to bind +// construct a closure out of them. If copying is true, it is a +// heap allocated closure that copies the upvars into environment. +// Otherwise, it is stack allocated and copies pointers to the upvars. +pub fn store_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + bound_values: Vec , + store: ty::TraitStore) + -> ClosureResult<'blk, 'tcx> { + let _icx = push_ctxt("closure::store_environment"); + let ccx = bcx.ccx(); + let tcx = ccx.tcx(); + + // compute the type of the closure + let cdata_ty = mk_closure_tys(tcx, bound_values.as_slice()); + + // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a + // tuple. This could be a ptr in uniq or a box or on stack, + // whatever. + let cbox_ty = tuplify_box_ty(tcx, cdata_ty); + let cboxptr_ty = ty::mk_ptr(tcx, ty::mt {ty:cbox_ty, mutbl:ast::MutImmutable}); + let llboxptr_ty = type_of(ccx, cboxptr_ty); + + // If there are no bound values, no point in allocating anything. + if bound_values.is_empty() { + return ClosureResult {llbox: C_null(llboxptr_ty), + cdata_ty: cdata_ty, + bcx: bcx}; + } + + // allocate closure in the heap + let Result {bcx, val: llbox} = allocate_cbox(bcx, store, cdata_ty); + + let llbox = PointerCast(bcx, llbox, llboxptr_ty); + debug!("tuplify_box_ty = {}", ty_to_string(tcx, cbox_ty)); + + // Copy expr values into boxed bindings. + let mut bcx = bcx; + for (i, bv) in bound_values.into_iter().enumerate() { + debug!("Copy {} into closure", bv.to_string(ccx)); + + if ccx.sess().asm_comments() { + add_comment(bcx, format!("Copy {} into closure", + bv.to_string(ccx)).as_slice()); + } + + let bound_data = GEPi(bcx, llbox, &[0u, abi::box_field_body, i]); + + match bv.action { + ast::CaptureByValue => { + bcx = bv.datum.store_to(bcx, bound_data); + } + ast::CaptureByRef => { + Store(bcx, bv.datum.to_llref(), bound_data); + } + } + } + + ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx } +} + +// Given a context and a list of upvars, build a closure. This just +// collects the upvars and packages them up for store_environment. +fn build_closure<'blk, 'tcx>(bcx0: Block<'blk, 'tcx>, + freevar_mode: ast::CaptureClause, + freevars: &Vec, + store: ty::TraitStore) + -> ClosureResult<'blk, 'tcx> { + let _icx = push_ctxt("closure::build_closure"); + + // If we need to, package up the iterator body to call + let bcx = bcx0; + + // Package up the captured upvars + let mut env_vals = Vec::new(); + for freevar in freevars.iter() { + let datum = expr::trans_local_var(bcx, freevar.def); + env_vals.push(EnvValue {action: freevar_mode, datum: datum}); + } + + store_environment(bcx, env_vals, store) +} + +// Given an enclosing block context, a new function context, a closure type, +// and a list of upvars, generate code to load and populate the environment +// with the upvars and type descriptors. +fn load_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + cdata_ty: ty::t, + freevars: &Vec, + store: ty::TraitStore) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("closure::load_environment"); + + // Don't bother to create the block if there's nothing to load + if freevars.len() == 0 { + return bcx; + } + + // Load a pointer to the closure data, skipping over the box header: + let llcdata = at_box_body(bcx, cdata_ty, bcx.fcx.llenv.unwrap()); + + // Store the pointer to closure data in an alloca for debug info because that's what the + // llvm.dbg.declare intrinsic expects + let env_pointer_alloca = if bcx.sess().opts.debuginfo == FullDebugInfo { + let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr"); + Store(bcx, llcdata, alloc); + Some(alloc) + } else { + None + }; + + // Populate the upvars from the environment + let mut i = 0u; + for freevar in freevars.iter() { + let mut upvarptr = GEPi(bcx, llcdata, &[0u, i]); + match store { + ty::RegionTraitStore(..) => { upvarptr = Load(bcx, upvarptr); } + ty::UniqTraitStore => {} + } + let def_id = freevar.def.def_id(); + + bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvarptr); + for &env_pointer_alloca in env_pointer_alloca.iter() { + debuginfo::create_captured_var_metadata( + bcx, + def_id.node, + cdata_ty, + env_pointer_alloca, + i, + store, + freevar.span); + } + + i += 1u; + } + + bcx +} + +fn load_unboxed_closure_environment<'blk, 'tcx>( + bcx: Block<'blk, 'tcx>, + arg_scope_id: ScopeId, + freevar_mode: ast::CaptureClause, + freevars: &Vec, + closure_id: ast::DefId) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("closure::load_environment"); + + if freevars.len() == 0 { + return bcx + } + + // Special case for small by-value selfs. + let self_type = self_type_for_unboxed_closure(bcx.ccx(), closure_id, + node_id_type(bcx, closure_id.node)); + let kind = kind_for_unboxed_closure(bcx.ccx(), closure_id); + let llenv = if kind == ty::FnOnceUnboxedClosureKind && + !arg_is_indirect(bcx.ccx(), self_type) { + let datum = rvalue_scratch_datum(bcx, + self_type, + "unboxed_closure_env"); + store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type); + datum.val + } else { + bcx.fcx.llenv.unwrap() + }; + + for (i, freevar) in freevars.iter().enumerate() { + let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]); + if freevar_mode == ast::CaptureByRef { + upvar_ptr = Load(bcx, upvar_ptr); + } + let def_id = freevar.def.def_id(); + bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr); + + if kind == ty::FnOnceUnboxedClosureKind && freevar_mode == ast::CaptureByValue { + bcx.fcx.schedule_drop_mem(arg_scope_id, + upvar_ptr, + node_id_type(bcx, def_id.node)) + } + } + + bcx +} + +fn fill_fn_pair(bcx: Block, pair: ValueRef, llfn: ValueRef, llenvptr: ValueRef) { + Store(bcx, llfn, GEPi(bcx, pair, &[0u, abi::fn_field_code])); + let llenvptr = PointerCast(bcx, llenvptr, Type::i8p(bcx.ccx())); + Store(bcx, llenvptr, GEPi(bcx, pair, &[0u, abi::fn_field_box])); +} + +pub fn trans_expr_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + store: ty::TraitStore, + decl: &ast::FnDecl, + body: &ast::Block, + id: ast::NodeId, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + /*! + * + * Translates the body of a closure expression. + * + * - `store` + * - `decl` + * - `body` + * - `id`: The id of the closure expression. + * - `cap_clause`: information about captured variables, if any. + * - `dest`: where to write the closure value, which must be a + (fn ptr, env) pair + */ + + let _icx = push_ctxt("closure::trans_expr_fn"); + + let dest_addr = match dest { + expr::SaveIn(p) => p, + expr::Ignore => { + return bcx; // closure construction is non-side-effecting + } + }; + + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + let fty = node_id_type(bcx, id); + let s = tcx.map.with_path(id, |path| { + mangle_internal_name_by_path_and_seq(path, "closure") + }); + let llfn = decl_internal_rust_fn(ccx, fty, s.as_slice()); + + // set an inline hint for all closures + set_inline_hint(llfn); + + let freevar_mode = tcx.capture_mode(id); + let freevars: Vec = + ty::with_freevars(tcx, id, |fv| fv.iter().map(|&fv| fv).collect()); + + let ClosureResult { + llbox, + cdata_ty, + bcx + } = build_closure(bcx, freevar_mode, &freevars, store); + trans_closure(ccx, + decl, + body, + llfn, + bcx.fcx.param_substs, + id, + &[], + ty::ty_fn_ret(fty), + ty::ty_fn_abi(fty), + true, + NotUnboxedClosure, + |bcx, _| load_environment(bcx, cdata_ty, &freevars, store)); + fill_fn_pair(bcx, dest_addr, llfn, llbox); + bcx +} + +/// Returns the LLVM function declaration for an unboxed closure, creating it +/// if necessary. If the ID does not correspond to a closure ID, returns None. +pub fn get_or_create_declaration_if_unboxed_closure<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + closure_id: ast::DefId, + substs: &Substs) + -> Option { + let ccx = bcx.ccx(); + if !ccx.tcx().unboxed_closures.borrow().contains_key(&closure_id) { + // Not an unboxed closure. + return None + } + + let function_type = ty::node_id_to_type(bcx.tcx(), closure_id.node); + let function_type = function_type.subst(bcx.tcx(), substs); + + // Normalize type so differences in regions and typedefs don't cause + // duplicate declarations + let function_type = ty::normalize_ty(bcx.tcx(), function_type); + let params = match ty::get(function_type).sty { + ty::ty_unboxed_closure(_, _, ref substs) => substs.types.clone(), + _ => unreachable!() + }; + let mono_id = MonoId { + def: closure_id, + params: params + }; + + match ccx.unboxed_closure_vals().borrow().get(&mono_id) { + Some(llfn) => { + debug!("get_or_create_declaration_if_unboxed_closure(): found \ + closure"); + return Some(*llfn) + } + None => {} + } + + let symbol = ccx.tcx().map.with_path(closure_id.node, |path| { + mangle_internal_name_by_path_and_seq(path, "unboxed_closure") + }); + + let llfn = decl_internal_rust_fn(ccx, function_type, symbol.as_slice()); + + // set an inline hint for all closures + set_inline_hint(llfn); + + debug!("get_or_create_declaration_if_unboxed_closure(): inserting new \ + closure {} (type {})", + mono_id, + ccx.tn().type_to_string(val_ty(llfn))); + ccx.unboxed_closure_vals().borrow_mut().insert(mono_id, llfn); + + Some(llfn) +} + +pub fn trans_unboxed_closure<'blk, 'tcx>( + mut bcx: Block<'blk, 'tcx>, + decl: &ast::FnDecl, + body: &ast::Block, + id: ast::NodeId, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("closure::trans_unboxed_closure"); + + debug!("trans_unboxed_closure()"); + + let closure_id = ast_util::local_def(id); + let llfn = get_or_create_declaration_if_unboxed_closure( + bcx, + closure_id, + &bcx.fcx.param_substs.substs).unwrap(); + + let function_type = (*bcx.tcx().unboxed_closures.borrow())[closure_id] + .closure_type + .clone(); + let function_type = ty::mk_closure(bcx.tcx(), function_type); + + let freevars: Vec = + ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect()); + let freevars_ptr = &freevars; + let freevar_mode = bcx.tcx().capture_mode(id); + + trans_closure(bcx.ccx(), + decl, + body, + llfn, + bcx.fcx.param_substs, + id, + &[], + ty::ty_fn_ret(function_type), + ty::ty_fn_abi(function_type), + true, + IsUnboxedClosure, + |bcx, arg_scope| { + load_unboxed_closure_environment(bcx, + arg_scope, + freevar_mode, + freevars_ptr, + closure_id) + }); + + // Don't hoist this to the top of the function. It's perfectly legitimate + // to have a zero-size unboxed closure (in which case dest will be + // `Ignore`) and we must still generate the closure body. + let dest_addr = match dest { + expr::SaveIn(p) => p, + expr::Ignore => { + debug!("trans_unboxed_closure() ignoring result"); + return bcx + } + }; + + let repr = adt::represent_type(bcx.ccx(), node_id_type(bcx, id)); + + // Create the closure. + for (i, freevar) in freevars_ptr.iter().enumerate() { + let datum = expr::trans_local_var(bcx, freevar.def); + let upvar_slot_dest = adt::trans_field_ptr(bcx, + &*repr, + dest_addr, + 0, + i); + match freevar_mode { + ast::CaptureByValue => { + bcx = datum.store_to(bcx, upvar_slot_dest); + } + ast::CaptureByRef => { + Store(bcx, datum.to_llref(), upvar_slot_dest); + } + } + } + adt::trans_set_discr(bcx, &*repr, dest_addr, 0); + + bcx +} + +pub fn get_wrapper_for_bare_fn(ccx: &CrateContext, + closure_ty: ty::t, + def: def::Def, + fn_ptr: ValueRef, + is_local: bool) -> ValueRef { + + let def_id = match def { + def::DefFn(did, _) | def::DefStaticMethod(did, _) | + def::DefVariant(_, did, _) | def::DefStruct(did) => did, + _ => { + ccx.sess().bug(format!("get_wrapper_for_bare_fn: \ + expected a statically resolved fn, got \ + {}", + def).as_slice()); + } + }; + + match ccx.closure_bare_wrapper_cache().borrow().get(&fn_ptr) { + Some(&llval) => return llval, + None => {} + } + + let tcx = ccx.tcx(); + + debug!("get_wrapper_for_bare_fn(closure_ty={})", closure_ty.repr(tcx)); + + let f = match ty::get(closure_ty).sty { + ty::ty_closure(ref f) => f, + _ => { + ccx.sess().bug(format!("get_wrapper_for_bare_fn: \ + expected a closure ty, got {}", + closure_ty.repr(tcx)).as_slice()); + } + }; + + let name = ty::with_path(tcx, def_id, |path| { + mangle_internal_name_by_path_and_seq(path, "as_closure") + }); + let llfn = if is_local { + decl_internal_rust_fn(ccx, closure_ty, name.as_slice()) + } else { + decl_rust_fn(ccx, closure_ty, name.as_slice()) + }; + + ccx.closure_bare_wrapper_cache().borrow_mut().insert(fn_ptr, llfn); + + // This is only used by statics inlined from a different crate. + if !is_local { + // Don't regenerate the wrapper, just reuse the original one. + return llfn; + } + + let _icx = push_ctxt("closure::get_wrapper_for_bare_fn"); + + let arena = TypedArena::new(); + let empty_param_substs = param_substs::empty(); + let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output, + &empty_param_substs, None, &arena); + let bcx = init_function(&fcx, true, f.sig.output); + + let args = create_datums_for_fn_args(&fcx, + ty::ty_fn_args(closure_ty) + .as_slice()); + let mut llargs = Vec::new(); + match fcx.llretslotptr.get() { + Some(llretptr) => { + assert!(!fcx.needs_ret_allocas); + llargs.push(llretptr); + } + None => {} + } + llargs.extend(args.iter().map(|arg| arg.val)); + + let retval = Call(bcx, fn_ptr, llargs.as_slice(), None); + match f.sig.output { + ty::FnConverging(output_type) => { + if return_type_is_void(ccx, output_type) || fcx.llretslotptr.get().is_some() { + RetVoid(bcx); + } else { + Ret(bcx, retval); + } + } + ty::FnDiverging => { + RetVoid(bcx); + } + } + + // HACK(eddyb) finish_fn cannot be used here, we returned directly. + debuginfo::clear_source_location(&fcx); + fcx.cleanup(); + + llfn +} + +pub fn make_closure_from_bare_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + closure_ty: ty::t, + def: def::Def, + fn_ptr: ValueRef) + -> DatumBlock<'blk, 'tcx, Expr> { + let scratch = rvalue_scratch_datum(bcx, closure_ty, "__adjust"); + let wrapper = get_wrapper_for_bare_fn(bcx.ccx(), closure_ty, def, fn_ptr, true); + fill_fn_pair(bcx, scratch.val, wrapper, C_null(Type::i8p(bcx.ccx()))); + + DatumBlock::new(bcx, scratch.to_expr_datum()) +} diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs new file mode 100644 index 00000000000..d06cfa4a027 --- /dev/null +++ b/src/librustc_trans/trans/common.rs @@ -0,0 +1,955 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types, non_snake_case)] + +//! Code that is useful in various trans modules. + +pub use self::ExprOrMethodCall::*; + +use session::Session; +use llvm; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; +use llvm::{True, False, Bool}; +use middle::def; +use middle::lang_items::LangItem; +use middle::mem_categorization as mc; +use middle::subst; +use middle::subst::Subst; +use trans::base; +use trans::build; +use trans::cleanup; +use trans::datum; +use trans::debuginfo; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use middle::traits; +use middle::ty; +use middle::ty_fold; +use middle::ty_fold::TypeFoldable; +use middle::typeck; +use middle::typeck::infer; +use util::ppaux::Repr; +use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; + +use arena::TypedArena; +use libc::{c_uint, c_char}; +use std::c_str::ToCStr; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use std::vec::Vec; +use syntax::ast::Ident; +use syntax::ast; +use syntax::ast_map::{PathElem, PathName}; +use syntax::codemap::Span; +use syntax::parse::token::InternedString; +use syntax::parse::token; + +pub use trans::context::CrateContext; + +fn type_is_newtype_immediate(ccx: &CrateContext, ty: ty::t) -> bool { + match ty::get(ty).sty { + ty::ty_struct(def_id, ref substs) => { + let fields = ty::struct_fields(ccx.tcx(), def_id, substs); + fields.len() == 1 && + fields[0].name == + token::special_idents::unnamed_field.name && + type_is_immediate(ccx, fields[0].mt.ty) + } + _ => false + } +} + +pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { + use trans::machine::llsize_of_alloc; + use trans::type_of::sizing_type_of; + + let tcx = ccx.tcx(); + let simple = ty::type_is_scalar(ty) || + ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || + type_is_newtype_immediate(ccx, ty) || + ty::type_is_simd(tcx, ty); + if simple && !ty::type_is_fat_ptr(tcx, ty) { + return true; + } + if !ty::type_is_sized(tcx, ty) { + return false; + } + match ty::get(ty).sty { + ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | + ty::ty_unboxed_closure(..) => { + let llty = sizing_type_of(ccx, ty); + llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type()) + } + _ => type_is_zero_size(ccx, ty) + } +} + +pub fn type_is_zero_size(ccx: &CrateContext, ty: ty::t) -> bool { + /*! + * Identify types which have size zero at runtime. + */ + + use trans::machine::llsize_of_alloc; + use trans::type_of::sizing_type_of; + let llty = sizing_type_of(ccx, ty); + llsize_of_alloc(ccx, llty) == 0 +} + +pub fn return_type_is_void(ccx: &CrateContext, ty: ty::t) -> bool { + /*! + * Identifies types which we declare to be equivalent to `void` + * in C for the purpose of function return types. These are + * `()`, bot, and uninhabited enums. Note that all such types + * are also zero-size, but not all zero-size types use a `void` + * return type (in order to aid with C ABI compatibility). + */ + + ty::type_is_nil(ty) || ty::type_is_empty(ccx.tcx(), ty) +} + +/// Generates a unique symbol based off the name given. This is used to create +/// unique symbols for things like closures. +pub fn gensym_name(name: &str) -> PathElem { + let num = token::gensym(name).uint(); + // use one colon which will get translated to a period by the mangler, and + // we're guaranteed that `num` is globally unique for this crate. + PathName(token::gensym(format!("{}:{}", name, num).as_slice())) +} + +pub struct tydesc_info { + pub ty: ty::t, + pub tydesc: ValueRef, + pub size: ValueRef, + pub align: ValueRef, + pub name: ValueRef, +} + +/* + * A note on nomenclature of linking: "extern", "foreign", and "upcall". + * + * An "extern" is an LLVM symbol we wind up emitting an undefined external + * reference to. This means "we don't have the thing in this compilation unit, + * please make sure you link it in at runtime". This could be a reference to + * C code found in a C library, or rust code found in a rust crate. + * + * Most "externs" are implicitly declared (automatically) as a result of a + * user declaring an extern _module_ dependency; this causes the rust driver + * to locate an extern crate, scan its compilation metadata, and emit extern + * declarations for any symbols used by the declaring crate. + * + * A "foreign" is an extern that references C (or other non-rust ABI) code. + * There is no metadata to scan for extern references so in these cases either + * a header-digester like bindgen, or manual function prototypes, have to + * serve as declarators. So these are usually given explicitly as prototype + * declarations, in rust code, with ABI attributes on them noting which ABI to + * link via. + * + * An "upcall" is a foreign call generated by the compiler (not corresponding + * to any user-written call in the code) into the runtime library, to perform + * some helper task such as bringing a task to life, allocating memory, etc. + * + */ + +pub struct NodeInfo { + pub id: ast::NodeId, + pub span: Span, +} + +pub fn expr_info(expr: &ast::Expr) -> NodeInfo { + NodeInfo { id: expr.id, span: expr.span } +} + +pub struct BuilderRef_res { + pub b: BuilderRef, +} + +impl Drop for BuilderRef_res { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeBuilder(self.b); + } + } +} + +pub fn BuilderRef_res(b: BuilderRef) -> BuilderRef_res { + BuilderRef_res { + b: b + } +} + +pub type ExternMap = FnvHashMap; + +// Here `self_ty` is the real type of the self parameter to this method. It +// will only be set in the case of default methods. +pub struct param_substs { + pub substs: subst::Substs, +} + +impl param_substs { + pub fn empty() -> param_substs { + param_substs { + substs: subst::Substs::trans_empty(), + } + } + + pub fn validate(&self) { + assert!(self.substs.types.all(|t| !ty::type_needs_infer(*t))); + } +} + +impl Repr for param_substs { + fn repr(&self, tcx: &ty::ctxt) -> String { + self.substs.repr(tcx) + } +} + +pub trait SubstP { + fn substp(&self, tcx: &ty::ctxt, param_substs: ¶m_substs) + -> Self; +} + +impl SubstP for T { + fn substp(&self, tcx: &ty::ctxt, substs: ¶m_substs) -> T { + self.subst(tcx, &substs.substs) + } +} + +// work around bizarre resolve errors +pub type RvalueDatum = datum::Datum; +pub type LvalueDatum = datum::Datum; + +// Function context. Every LLVM function we create will have one of +// these. +pub struct FunctionContext<'a, 'tcx: 'a> { + // The ValueRef returned from a call to llvm::LLVMAddFunction; the + // address of the first instruction in the sequence of + // instructions for this function that will go in the .text + // section of the executable we're generating. + pub llfn: ValueRef, + + // The environment argument in a closure. + pub llenv: Option, + + // A pointer to where to store the return value. If the return type is + // immediate, this points to an alloca in the function. Otherwise, it's a + // pointer to the hidden first parameter of the function. After function + // construction, this should always be Some. + pub llretslotptr: Cell>, + + // These pub elements: "hoisted basic blocks" containing + // administrative activities that have to happen in only one place in + // the function, due to LLVM's quirks. + // A marker for the place where we want to insert the function's static + // allocas, so that LLVM will coalesce them into a single alloca call. + pub alloca_insert_pt: Cell>, + pub llreturn: Cell>, + + // If the function has any nested return's, including something like: + // fn foo() -> Option { Some(Foo { x: return None }) }, then + // we use a separate alloca for each return + pub needs_ret_allocas: bool, + + // The a value alloca'd for calls to upcalls.rust_personality. Used when + // outputting the resume instruction. + pub personality: Cell>, + + // True if the caller expects this fn to use the out pointer to + // return. Either way, your code should write into the slot llretslotptr + // points to, but if this value is false, that slot will be a local alloca. + pub caller_expects_out_pointer: bool, + + // Maps the DefId's for local variables to the allocas created for + // them in llallocas. + pub lllocals: RefCell>, + + // Same as above, but for closure upvars + pub llupvars: RefCell>, + + // The NodeId of the function, or -1 if it doesn't correspond to + // a user-defined function. + pub id: ast::NodeId, + + // If this function is being monomorphized, this contains the type + // substitutions used. + pub param_substs: &'a param_substs, + + // The source span and nesting context where this function comes from, for + // error reporting and symbol generation. + pub span: Option, + + // The arena that blocks are allocated from. + pub block_arena: &'a TypedArena>, + + // This function's enclosing crate context. + pub ccx: &'a CrateContext<'a, 'tcx>, + + // Used and maintained by the debuginfo module. + pub debug_context: debuginfo::FunctionDebugContext, + + // Cleanup scopes. + pub scopes: RefCell>>, +} + +impl<'a, 'tcx> FunctionContext<'a, 'tcx> { + pub fn arg_pos(&self, arg: uint) -> uint { + let arg = self.env_arg_pos() + arg; + if self.llenv.is_some() { + arg + 1 + } else { + arg + } + } + + pub fn out_arg_pos(&self) -> uint { + assert!(self.caller_expects_out_pointer); + 0u + } + + pub fn env_arg_pos(&self) -> uint { + if self.caller_expects_out_pointer { + 1u + } else { + 0u + } + } + + pub fn cleanup(&self) { + unsafe { + llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt + .get() + .unwrap()); + } + } + + pub fn get_llreturn(&self) -> BasicBlockRef { + if self.llreturn.get().is_none() { + + self.llreturn.set(Some(unsafe { + "return".with_c_str(|buf| { + llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf) + }) + })) + } + + self.llreturn.get().unwrap() + } + + pub fn get_ret_slot(&self, bcx: Block, output: ty::FnOutput, name: &str) -> ValueRef { + if self.needs_ret_allocas { + base::alloca_no_lifetime(bcx, match output { + ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type), + ty::FnDiverging => Type::void(bcx.ccx()) + }, name) + } else { + self.llretslotptr.get().unwrap() + } + } + + pub fn new_block(&'a self, + is_lpad: bool, + name: &str, + opt_node_id: Option) + -> Block<'a, 'tcx> { + unsafe { + let llbb = name.with_c_str(|buf| { + llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), + self.llfn, + buf) + }); + BlockS::new(llbb, is_lpad, opt_node_id, self) + } + } + + pub fn new_id_block(&'a self, + name: &str, + node_id: ast::NodeId) + -> Block<'a, 'tcx> { + self.new_block(false, name, Some(node_id)) + } + + pub fn new_temp_block(&'a self, + name: &str) + -> Block<'a, 'tcx> { + self.new_block(false, name, None) + } + + pub fn join_blocks(&'a self, + id: ast::NodeId, + in_cxs: &[Block<'a, 'tcx>]) + -> Block<'a, 'tcx> { + let out = self.new_id_block("join", id); + let mut reachable = false; + for bcx in in_cxs.iter() { + if !bcx.unreachable.get() { + build::Br(*bcx, out.llbb); + reachable = true; + } + } + if !reachable { + build::Unreachable(out); + } + return out; + } +} + +// Basic block context. We create a block context for each basic block +// (single-entry, single-exit sequence of instructions) we generate from Rust +// code. Each basic block we generate is attached to a function, typically +// with many basic blocks per function. All the basic blocks attached to a +// function are organized as a directed graph. +pub struct BlockS<'blk, 'tcx: 'blk> { + // The BasicBlockRef returned from a call to + // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic + // block to the function pointed to by llfn. We insert + // instructions into that block by way of this block context. + // The block pointing to this one in the function's digraph. + pub llbb: BasicBlockRef, + pub terminated: Cell, + pub unreachable: Cell, + + // Is this block part of a landing pad? + pub is_lpad: bool, + + // AST node-id associated with this block, if any. Used for + // debugging purposes only. + pub opt_node_id: Option, + + // The function context for the function to which this block is + // attached. + pub fcx: &'blk FunctionContext<'blk, 'tcx>, +} + +pub type Block<'blk, 'tcx> = &'blk BlockS<'blk, 'tcx>; + +impl<'blk, 'tcx> BlockS<'blk, 'tcx> { + pub fn new(llbb: BasicBlockRef, + is_lpad: bool, + opt_node_id: Option, + fcx: &'blk FunctionContext<'blk, 'tcx>) + -> Block<'blk, 'tcx> { + fcx.block_arena.alloc(BlockS { + llbb: llbb, + terminated: Cell::new(false), + unreachable: Cell::new(false), + is_lpad: is_lpad, + opt_node_id: opt_node_id, + fcx: fcx + }) + } + + pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> { + self.fcx.ccx + } + pub fn tcx(&self) -> &'blk ty::ctxt<'tcx> { + self.fcx.ccx.tcx() + } + pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() } + + pub fn ident(&self, ident: Ident) -> String { + token::get_ident(ident).get().to_string() + } + + pub fn node_id_to_string(&self, id: ast::NodeId) -> String { + self.tcx().map.node_to_string(id).to_string() + } + + pub fn expr_to_string(&self, e: &ast::Expr) -> String { + e.repr(self.tcx()) + } + + pub fn def(&self, nid: ast::NodeId) -> def::Def { + match self.tcx().def_map.borrow().get(&nid) { + Some(v) => v.clone(), + None => { + self.tcx().sess.bug(format!( + "no def associated with node id {}", nid).as_slice()); + } + } + } + + pub fn val_to_string(&self, val: ValueRef) -> String { + self.ccx().tn().val_to_string(val) + } + + pub fn llty_str(&self, ty: Type) -> String { + self.ccx().tn().type_to_string(ty) + } + + pub fn ty_to_string(&self, t: ty::t) -> String { + t.repr(self.tcx()) + } + + pub fn to_str(&self) -> String { + format!("[block {:p}]", self) + } +} + +impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { + fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { + self.tcx() + } + + fn node_ty(&self, id: ast::NodeId) -> mc::McResult { + Ok(node_id_type(self, id)) + } + + fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { + self.tcx() + .method_map + .borrow() + .get(&method_call) + .map(|method| monomorphize_type(self, method.ty)) + } + + fn adjustments<'a>(&'a self) -> &'a RefCell> { + &self.tcx().adjustments + } + + fn is_method_call(&self, id: ast::NodeId) -> bool { + self.tcx().method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) + } + + fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { + self.tcx().region_maps.temporary_scope(rvalue_id) + } + + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell> { + &self.tcx().unboxed_closures + } + + fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { + self.tcx().upvar_borrow_map.borrow()[upvar_id].clone() + } + + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> ast::CaptureClause { + self.tcx().capture_modes.borrow()[closure_expr_id].clone() + } +} + +pub struct Result<'blk, 'tcx: 'blk> { + pub bcx: Block<'blk, 'tcx>, + pub val: ValueRef +} + +impl<'b, 'tcx> Result<'b, 'tcx> { + pub fn new(bcx: Block<'b, 'tcx>, val: ValueRef) -> Result<'b, 'tcx> { + Result { + bcx: bcx, + val: val, + } + } +} + +pub fn val_ty(v: ValueRef) -> Type { + unsafe { + Type::from_ref(llvm::LLVMTypeOf(v)) + } +} + +// LLVM constant constructors. +pub fn C_null(t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstNull(t.to_ref()) + } +} + +pub fn C_undef(t: Type) -> ValueRef { + unsafe { + llvm::LLVMGetUndef(t.to_ref()) + } +} + +pub fn C_integral(t: Type, u: u64, sign_extend: bool) -> ValueRef { + unsafe { + llvm::LLVMConstInt(t.to_ref(), u, sign_extend as Bool) + } +} + +pub fn C_floating(s: &str, t: Type) -> ValueRef { + unsafe { + s.with_c_str(|buf| llvm::LLVMConstRealOfString(t.to_ref(), buf)) + } +} + +pub fn C_nil(ccx: &CrateContext) -> ValueRef { + C_struct(ccx, &[], false) +} + +pub fn C_bool(ccx: &CrateContext, val: bool) -> ValueRef { + C_integral(Type::i1(ccx), val as u64, false) +} + +pub fn C_i32(ccx: &CrateContext, i: i32) -> ValueRef { + C_integral(Type::i32(ccx), i as u64, true) +} + +pub fn C_i64(ccx: &CrateContext, i: i64) -> ValueRef { + C_integral(Type::i64(ccx), i as u64, true) +} + +pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef { + C_integral(Type::i64(ccx), i, false) +} + +pub fn C_int(ccx: &CrateContext, i: I) -> ValueRef { + let v = i.as_i64(); + + match machine::llbitsize_of_real(ccx, ccx.int_type()) { + 32 => assert!(v < (1<<31) && v >= -(1<<31)), + 64 => {}, + n => panic!("unsupported target size: {}", n) + } + + C_integral(ccx.int_type(), v as u64, true) +} + +pub fn C_uint(ccx: &CrateContext, i: I) -> ValueRef { + let v = i.as_u64(); + + match machine::llbitsize_of_real(ccx, ccx.int_type()) { + 32 => assert!(v < (1<<32)), + 64 => {}, + n => panic!("unsupported target size: {}", n) + } + + C_integral(ccx.int_type(), v, false) +} + +pub trait AsI64 { fn as_i64(self) -> i64; } +pub trait AsU64 { fn as_u64(self) -> u64; } + +// FIXME: remove the intptr conversions, because they +// are host-architecture-dependent +impl AsI64 for i64 { fn as_i64(self) -> i64 { self as i64 }} +impl AsI64 for i32 { fn as_i64(self) -> i64 { self as i64 }} +impl AsI64 for int { fn as_i64(self) -> i64 { self as i64 }} + +impl AsU64 for u64 { fn as_u64(self) -> u64 { self as u64 }} +impl AsU64 for u32 { fn as_u64(self) -> u64 { self as u64 }} +impl AsU64 for uint { fn as_u64(self) -> u64 { self as u64 }} + +pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef { + C_integral(Type::i8(ccx), i as u64, false) +} + + +// This is a 'c-like' raw string, which differs from +// our boxed-and-length-annotated strings. +pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> ValueRef { + unsafe { + match cx.const_cstr_cache().borrow().get(&s) { + Some(&llval) => return llval, + None => () + } + + let sc = llvm::LLVMConstStringInContext(cx.llcx(), + s.get().as_ptr() as *const c_char, + s.get().len() as c_uint, + !null_terminated as Bool); + + let gsym = token::gensym("str"); + let g = format!("str{}", gsym.uint()).with_c_str(|buf| { + llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf) + }); + llvm::LLVMSetInitializer(g, sc); + llvm::LLVMSetGlobalConstant(g, True); + llvm::SetLinkage(g, llvm::InternalLinkage); + + cx.const_cstr_cache().borrow_mut().insert(s, g); + g + } +} + +// NB: Do not use `do_spill_noroot` to make this into a constant string, or +// you will be kicked off fast isel. See issue #4352 for an example of this. +pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef { + unsafe { + let len = s.get().len(); + let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s, false), + Type::i8p(cx).to_ref()); + C_named_struct(cx.tn().find_type("str_slice").unwrap(), &[cs, C_uint(cx, len)]) + } +} + +pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef { + unsafe { + let len = data.len(); + let lldata = C_bytes(cx, data); + + let gsym = token::gensym("binary"); + let g = format!("binary{}", gsym.uint()).with_c_str(|buf| { + llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), buf) + }); + llvm::LLVMSetInitializer(g, lldata); + llvm::LLVMSetGlobalConstant(g, True); + llvm::SetLinkage(g, llvm::InternalLinkage); + + let cs = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); + C_struct(cx, &[cs, C_uint(cx, len)], false) + } +} + +pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { + C_struct_in_context(cx.llcx(), elts, packed) +} + +pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { + unsafe { + llvm::LLVMConstStructInContext(llcx, + elts.as_ptr(), elts.len() as c_uint, + packed as Bool) + } +} + +pub fn C_named_struct(t: Type, elts: &[ValueRef]) -> ValueRef { + unsafe { + llvm::LLVMConstNamedStruct(t.to_ref(), elts.as_ptr(), elts.len() as c_uint) + } +} + +pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); + } +} + +pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef { + C_bytes_in_context(cx.llcx(), bytes) +} + +pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); + } +} + +pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint]) + -> ValueRef { + unsafe { + let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); + + debug!("const_get_elt(v={}, us={}, r={})", + cx.tn().val_to_string(v), us, cx.tn().val_to_string(r)); + + return r; + } +} + +pub fn is_const(v: ValueRef) -> bool { + unsafe { + llvm::LLVMIsConstant(v) == True + } +} + +pub fn const_to_int(v: ValueRef) -> i64 { + unsafe { + llvm::LLVMConstIntGetSExtValue(v) + } +} + +pub fn const_to_uint(v: ValueRef) -> u64 { + unsafe { + llvm::LLVMConstIntGetZExtValue(v) + } +} + +pub fn is_undef(val: ValueRef) -> bool { + unsafe { + llvm::LLVMIsUndef(val) != False + } +} + +pub fn is_null(val: ValueRef) -> bool { + unsafe { + llvm::LLVMIsNull(val) != False + } +} + +pub fn monomorphize_type(bcx: &BlockS, t: ty::t) -> ty::t { + t.subst(bcx.tcx(), &bcx.fcx.param_substs.substs) +} + +pub fn node_id_type(bcx: &BlockS, id: ast::NodeId) -> ty::t { + let tcx = bcx.tcx(); + let t = ty::node_id_to_type(tcx, id); + monomorphize_type(bcx, t) +} + +pub fn expr_ty(bcx: Block, ex: &ast::Expr) -> ty::t { + node_id_type(bcx, ex.id) +} + +pub fn expr_ty_adjusted(bcx: Block, ex: &ast::Expr) -> ty::t { + monomorphize_type(bcx, ty::expr_ty_adjusted(bcx.tcx(), ex)) +} + +pub fn fulfill_obligation(ccx: &CrateContext, + span: Span, + trait_ref: Rc) + -> traits::Vtable<()> +{ + /*! + * Attempts to resolve an obligation. The result is a shallow + * vtable resolution -- meaning that we do not (necessarily) resolve + * all nested obligations on the impl. Note that type check should + * guarantee to us that all nested obligations *could be* resolved + * if we wanted to. + */ + + let tcx = ccx.tcx(); + + // Remove any references to regions; this helps improve caching. + let trait_ref = ty_fold::erase_regions(tcx, trait_ref); + + // First check the cache. + match ccx.trait_cache().borrow().get(&trait_ref) { + Some(vtable) => { + info!("Cache hit: {}", trait_ref.repr(ccx.tcx())); + return (*vtable).clone(); + } + None => { } + } + + ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id); + let infcx = infer::new_infer_ctxt(tcx); + + // Parameter environment is used to give details about type parameters, + // but since we are in trans, everything is fully monomorphized. + let param_env = ty::empty_parameter_environment(); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env, tcx); + let obligation = traits::Obligation::misc(span, trait_ref.clone()); + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{}` during trans, \ + presuming due to overflow", + trait_ref.repr(tcx)); + ccx.sess().span_fatal( + span, + "reached the recursion limit during monomorphization"); + } + Err(e) => { + tcx.sess.span_bug( + span, + format!("Encountered error `{}` selecting `{}` during trans", + e.repr(tcx), + trait_ref.repr(tcx)).as_slice()) + } + }; + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. However, in principle, + // we only need to do this until the impl's type parameters are + // fully bound. It could be a slight optimization to stop + // iterating early. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map_move_nested(|obligation| { + fulfill_cx.register_obligation(tcx, obligation); + }); + match fulfill_cx.select_all_or_error(&infcx, ¶m_env, tcx) { + Ok(()) => { } + Err(errors) => { + if errors.iter().all(|e| e.is_overflow()) { + // See Ok(None) case above. + ccx.sess().span_fatal( + span, + "reached the recursion limit during monomorphization"); + } else { + tcx.sess.span_bug( + span, + format!("Encountered errors `{}` fulfilling `{}` during trans", + errors.repr(tcx), + trait_ref.repr(tcx)).as_slice()); + } + } + } + + // Use skolemize to simultaneously replace all type variables with + // their bindings and replace all regions with 'static. This is + // sort of overkill because we do not expect there to be any + // unbound type variables, hence no skolemized types should ever + // be inserted. + let vtable = vtable.fold_with(&mut infcx.skolemizer()); + + info!("Cache miss: {}", trait_ref.repr(ccx.tcx())); + ccx.trait_cache().borrow_mut().insert(trait_ref, + vtable.clone()); + + vtable +} + +// Key used to lookup values supplied for type parameters in an expr. +#[deriving(PartialEq, Show)] +pub enum ExprOrMethodCall { + // Type parameters for a path like `None::` + ExprId(ast::NodeId), + + // Type parameters for a method call like `a.foo::()` + MethodCall(typeck::MethodCall) +} + +pub fn node_id_substs(bcx: Block, + node: ExprOrMethodCall) + -> subst::Substs +{ + let tcx = bcx.tcx(); + + let substs = match node { + ExprId(id) => { + ty::node_id_item_substs(tcx, id).substs + } + MethodCall(method_call) => { + (*tcx.method_map.borrow())[method_call].substs.clone() + } + }; + + if substs.types.any(|t| ty::type_needs_infer(*t)) { + bcx.sess().bug( + format!("type parameters for node {} include inference types: \ + {}", + node, + substs.repr(bcx.tcx())).as_slice()); + } + + let substs = substs.erase_regions(); + substs.substp(tcx, bcx.fcx.param_substs) +} + +pub fn langcall(bcx: Block, + span: Option, + msg: &str, + li: LangItem) + -> ast::DefId { + match bcx.tcx().lang_items.require(li) { + Ok(id) => id, + Err(s) => { + let msg = format!("{} {}", msg, s); + match span { + Some(span) => bcx.tcx().sess.span_fatal(span, msg.as_slice()), + None => bcx.tcx().sess.fatal(msg.as_slice()), + } + } + } +} diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs new file mode 100644 index 00000000000..545b12d2267 --- /dev/null +++ b/src/librustc_trans/trans/consts.rs @@ -0,0 +1,731 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use back::abi; +use llvm; +use llvm::{ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True, False}; +use llvm::{IntEQ, IntNE, IntUGT, IntUGE, IntULT, IntULE, IntSGT, IntSGE, IntSLT, IntSLE, + RealOEQ, RealOGT, RealOGE, RealOLT, RealOLE, RealONE}; +use metadata::csearch; +use middle::const_eval; +use middle::def; +use trans::adt; +use trans::base; +use trans::base::push_ctxt; +use trans::closure; +use trans::common::*; +use trans::consts; +use trans::expr; +use trans::inline; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use trans::debuginfo; +use middle::ty; +use util::ppaux::{Repr, ty_to_string}; + +use std::c_str::ToCStr; +use libc::c_uint; +use syntax::{ast, ast_util}; +use syntax::ptr::P; + +pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit) + -> ValueRef { + let _icx = push_ctxt("trans_lit"); + debug!("const_lit: {}", lit); + match lit.node { + ast::LitByte(b) => C_integral(Type::uint_from_ty(cx, ast::TyU8), b as u64, false), + ast::LitChar(i) => C_integral(Type::char(cx), i as u64, false), + ast::LitInt(i, ast::SignedIntLit(t, _)) => { + C_integral(Type::int_from_ty(cx, t), i, true) + } + ast::LitInt(u, ast::UnsignedIntLit(t)) => { + C_integral(Type::uint_from_ty(cx, t), u, false) + } + ast::LitInt(i, ast::UnsuffixedIntLit(_)) => { + let lit_int_ty = ty::node_id_to_type(cx.tcx(), e.id); + match ty::get(lit_int_ty).sty { + ty::ty_int(t) => { + C_integral(Type::int_from_ty(cx, t), i as u64, true) + } + ty::ty_uint(t) => { + C_integral(Type::uint_from_ty(cx, t), i as u64, false) + } + _ => cx.sess().span_bug(lit.span, + format!("integer literal has type {} (expected int \ + or uint)", + ty_to_string(cx.tcx(), lit_int_ty)).as_slice()) + } + } + ast::LitFloat(ref fs, t) => { + C_floating(fs.get(), Type::float_from_ty(cx, t)) + } + ast::LitFloatUnsuffixed(ref fs) => { + let lit_float_ty = ty::node_id_to_type(cx.tcx(), e.id); + match ty::get(lit_float_ty).sty { + ty::ty_float(t) => { + C_floating(fs.get(), Type::float_from_ty(cx, t)) + } + _ => { + cx.sess().span_bug(lit.span, + "floating point literal doesn't have the right type"); + } + } + } + ast::LitBool(b) => C_bool(cx, b), + ast::LitStr(ref s, _) => C_str_slice(cx, (*s).clone()), + ast::LitBinary(ref data) => C_binary_slice(cx, data.as_slice()), + } +} + +pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef { + unsafe { + let b = llvm::LLVMConstPointerCast(a, t.ptr_to().to_ref()); + assert!(cx.const_globals().borrow_mut().insert(b as int, a).is_none()); + b + } +} + +fn const_vec(cx: &CrateContext, e: &ast::Expr, + es: &[P]) -> (ValueRef, Type) { + let vec_ty = ty::expr_ty(cx.tcx(), e); + let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); + let llunitty = type_of::type_of(cx, unit_ty); + let vs = es.iter().map(|e| const_expr(cx, &**e).val0()) + .collect::>(); + // If the vector contains enums, an LLVM array won't work. + let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) { + C_struct(cx, vs.as_slice(), false) + } else { + C_array(llunitty, vs.as_slice()) + }; + (v, llunitty) +} + +pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef { + unsafe { + let gv = "const".with_c_str(|name| { + llvm::LLVMAddGlobal(cx.llmod(), val_ty(cv).to_ref(), name) + }); + llvm::LLVMSetInitializer(gv, cv); + llvm::LLVMSetGlobalConstant(gv, + if mutbl == ast::MutImmutable {True} else {False}); + SetLinkage(gv, PrivateLinkage); + gv + } +} + +fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { + let v = match cx.const_globals().borrow().get(&(v as int)) { + Some(&v) => v, + None => v + }; + unsafe { + llvm::LLVMGetInitializer(v) + } +} + +fn const_deref_newtype(cx: &CrateContext, v: ValueRef, t: ty::t) + -> ValueRef { + let repr = adt::represent_type(cx, t); + adt::const_get_field(cx, &*repr, v, 0, 0) +} + +fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool) + -> (ValueRef, ty::t) { + match ty::deref(t, explicit) { + Some(ref mt) => { + match ty::get(t).sty { + ty::ty_ptr(mt) | ty::ty_rptr(_, mt) => { + if ty::type_is_sized(cx.tcx(), mt.ty) { + (const_deref_ptr(cx, v), mt.ty) + } else { + // Derefing a fat pointer does not change the representation, + // just the type to ty_open. + (v, ty::mk_open(cx.tcx(), mt.ty)) + } + } + ty::ty_enum(..) | ty::ty_struct(..) => { + assert!(mt.mutbl != ast::MutMutable); + (const_deref_newtype(cx, v, t), mt.ty) + } + _ => { + cx.sess().bug(format!("unexpected dereferenceable type {}", + ty_to_string(cx.tcx(), t)).as_slice()) + } + } + } + None => { + cx.sess().bug(format!("cannot dereference const of type {}", + ty_to_string(cx.tcx(), t)).as_slice()) + } + } +} + +pub fn get_const_val(cx: &CrateContext, + mut def_id: ast::DefId) -> ValueRef { + let contains_key = cx.const_values().borrow().contains_key(&def_id.node); + if !ast_util::is_local(def_id) || !contains_key { + if !ast_util::is_local(def_id) { + def_id = inline::maybe_instantiate_inline(cx, def_id); + } + + match cx.tcx().map.expect_item(def_id.node).node { + ast::ItemConst(..) => { base::get_item_val(cx, def_id.node); } + _ => {} + } + } + + cx.const_values().borrow()[def_id.node].clone() +} + +pub fn const_expr(cx: &CrateContext, e: &ast::Expr) -> (ValueRef, ty::t) { + let llconst = const_expr_unadjusted(cx, e); + let mut llconst = llconst; + let ety = ty::expr_ty(cx.tcx(), e); + let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); + let opt_adj = cx.tcx().adjustments.borrow().get(&e.id).cloned(); + match opt_adj { + None => { } + Some(adj) => { + match adj { + ty::AdjustAddEnv(ty::RegionTraitStore(ty::ReStatic, _)) => { + let def = ty::resolve_expr(cx.tcx(), e); + let wrapper = closure::get_wrapper_for_bare_fn(cx, + ety_adjusted, + def, + llconst, + true); + llconst = C_struct(cx, &[wrapper, C_null(Type::i8p(cx))], false) + } + ty::AdjustAddEnv(store) => { + cx.sess() + .span_bug(e.span, + format!("unexpected static function: {}", + store).as_slice()) + } + ty::AdjustDerefRef(ref adj) => { + let mut ty = ety; + // Save the last autoderef in case we can avoid it. + if adj.autoderefs > 0 { + for _ in range(0, adj.autoderefs-1) { + let (dv, dt) = const_deref(cx, llconst, ty, false); + llconst = dv; + ty = dt; + } + } + + match adj.autoref { + None => { + let (dv, dt) = const_deref(cx, llconst, ty, false); + llconst = dv; + + // If we derefed a fat pointer then we will have an + // open type here. So we need to update the type with + // the one returned from const_deref. + ety_adjusted = dt; + } + Some(ref autoref) => { + match *autoref { + ty::AutoUnsafe(_, None) | + ty::AutoPtr(ty::ReStatic, _, None) => { + // Don't copy data to do a deref+ref + // (i.e., skip the last auto-deref). + if adj.autoderefs == 0 { + llconst = const_addr_of(cx, llconst, ast::MutImmutable); + } + } + ty::AutoPtr(ty::ReStatic, _, Some(box ty::AutoUnsize(..))) => { + if adj.autoderefs > 0 { + // Seeing as we are deref'ing here and take a reference + // again to make the pointer part of the far pointer below, + // we just skip the whole thing. We still need the type + // though. This works even if we don't need to deref + // because of byref semantics. Note that this is not just + // an optimisation, it is necessary for mutable vectors to + // work properly. + let (_, dt) = const_deref(cx, llconst, ty, false); + ty = dt; + } else { + llconst = const_addr_of(cx, llconst, ast::MutImmutable) + } + + match ty::get(ty).sty { + ty::ty_vec(unit_ty, Some(len)) => { + let llunitty = type_of::type_of(cx, unit_ty); + let llptr = const_ptrcast(cx, llconst, llunitty); + assert_eq!(abi::slice_elt_base, 0); + assert_eq!(abi::slice_elt_len, 1); + llconst = C_struct(cx, &[ + llptr, + C_uint(cx, len) + ], false); + } + _ => cx.sess().span_bug(e.span, + format!("unimplemented type in const unsize: {}", + ty_to_string(cx.tcx(), ty)).as_slice()) + } + } + _ => { + cx.sess() + .span_bug(e.span, + format!("unimplemented const \ + autoref {}", + autoref).as_slice()) + } + } + } + } + } + } + } + } + + let llty = type_of::sizing_type_of(cx, ety_adjusted); + let csize = machine::llsize_of_alloc(cx, val_ty(llconst)); + let tsize = machine::llsize_of_alloc(cx, llty); + if csize != tsize { + unsafe { + // FIXME these values could use some context + llvm::LLVMDumpValue(llconst); + llvm::LLVMDumpValue(C_undef(llty)); + } + cx.sess().bug(format!("const {} of type {} has size {} instead of {}", + e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety), + csize, tsize).as_slice()); + } + (llconst, ety_adjusted) +} + +// the bool returned is whether this expression can be inlined into other crates +// if it's assigned to a static. +fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef { + let map_list = |exprs: &[P]| { + exprs.iter().map(|e| const_expr(cx, &**e).val0()) + .fold(Vec::new(), |mut l, val| { l.push(val); l }) + }; + unsafe { + let _icx = push_ctxt("const_expr"); + return match e.node { + ast::ExprLit(ref lit) => { + consts::const_lit(cx, e, &**lit) + } + ast::ExprBinary(b, ref e1, ref e2) => { + let (te1, _) = const_expr(cx, &**e1); + let (te2, _) = const_expr(cx, &**e2); + + let te2 = base::cast_shift_const_rhs(b, te1, te2); + + /* Neither type is bottom, and we expect them to be unified + * already, so the following is safe. */ + let ty = ty::expr_ty(cx.tcx(), &**e1); + let is_float = ty::type_is_fp(ty); + let signed = ty::type_is_signed(ty); + return match b { + ast::BiAdd => { + if is_float { llvm::LLVMConstFAdd(te1, te2) } + else { llvm::LLVMConstAdd(te1, te2) } + } + ast::BiSub => { + if is_float { llvm::LLVMConstFSub(te1, te2) } + else { llvm::LLVMConstSub(te1, te2) } + } + ast::BiMul => { + if is_float { llvm::LLVMConstFMul(te1, te2) } + else { llvm::LLVMConstMul(te1, te2) } + } + ast::BiDiv => { + if is_float { llvm::LLVMConstFDiv(te1, te2) } + else if signed { llvm::LLVMConstSDiv(te1, te2) } + else { llvm::LLVMConstUDiv(te1, te2) } + } + ast::BiRem => { + if is_float { llvm::LLVMConstFRem(te1, te2) } + else if signed { llvm::LLVMConstSRem(te1, te2) } + else { llvm::LLVMConstURem(te1, te2) } + } + ast::BiAnd => llvm::LLVMConstAnd(te1, te2), + ast::BiOr => llvm::LLVMConstOr(te1, te2), + ast::BiBitXor => llvm::LLVMConstXor(te1, te2), + ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2), + ast::BiBitOr => llvm::LLVMConstOr(te1, te2), + ast::BiShl => llvm::LLVMConstShl(te1, te2), + ast::BiShr => { + if signed { llvm::LLVMConstAShr(te1, te2) } + else { llvm::LLVMConstLShr(te1, te2) } + } + ast::BiEq => { + if is_float { ConstFCmp(RealOEQ, te1, te2) } + else { ConstICmp(IntEQ, te1, te2) } + }, + ast::BiLt => { + if is_float { ConstFCmp(RealOLT, te1, te2) } + else { + if signed { ConstICmp(IntSLT, te1, te2) } + else { ConstICmp(IntULT, te1, te2) } + } + }, + ast::BiLe => { + if is_float { ConstFCmp(RealOLE, te1, te2) } + else { + if signed { ConstICmp(IntSLE, te1, te2) } + else { ConstICmp(IntULE, te1, te2) } + } + }, + ast::BiNe => { + if is_float { ConstFCmp(RealONE, te1, te2) } + else { ConstICmp(IntNE, te1, te2) } + }, + ast::BiGe => { + if is_float { ConstFCmp(RealOGE, te1, te2) } + else { + if signed { ConstICmp(IntSGE, te1, te2) } + else { ConstICmp(IntUGE, te1, te2) } + } + }, + ast::BiGt => { + if is_float { ConstFCmp(RealOGT, te1, te2) } + else { + if signed { ConstICmp(IntSGT, te1, te2) } + else { ConstICmp(IntUGT, te1, te2) } + } + }, + } + }, + ast::ExprUnary(u, ref e) => { + let (te, _) = const_expr(cx, &**e); + let ty = ty::expr_ty(cx.tcx(), &**e); + let is_float = ty::type_is_fp(ty); + return match u { + ast::UnUniq | ast::UnDeref => { + let (dv, _dt) = const_deref(cx, te, ty, true); + dv + } + ast::UnNot => llvm::LLVMConstNot(te), + ast::UnNeg => { + if is_float { llvm::LLVMConstFNeg(te) } + else { llvm::LLVMConstNeg(te) } + } + } + } + ast::ExprField(ref base, field, _) => { + let (bv, bt) = const_expr(cx, &**base); + let brepr = adt::represent_type(cx, bt); + expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { + let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys); + adt::const_get_field(cx, &*brepr, bv, discr, ix) + }) + } + ast::ExprTupField(ref base, idx, _) => { + let (bv, bt) = const_expr(cx, &**base); + let brepr = adt::represent_type(cx, bt); + expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { + adt::const_get_field(cx, &*brepr, bv, discr, idx.node) + }) + } + + ast::ExprIndex(ref base, ref index) => { + let (bv, bt) = const_expr(cx, &**base); + let iv = match const_eval::eval_const_expr(cx.tcx(), &**index) { + const_eval::const_int(i) => i as u64, + const_eval::const_uint(u) => u, + _ => cx.sess().span_bug(index.span, + "index is not an integer-constant expression") + }; + let (arr, len) = match ty::get(bt).sty { + ty::ty_vec(_, Some(u)) => (bv, C_uint(cx, u)), + ty::ty_open(ty) => match ty::get(ty).sty { + ty::ty_vec(_, None) | ty::ty_str => { + let e1 = const_get_elt(cx, bv, &[0]); + (const_deref_ptr(cx, e1), const_get_elt(cx, bv, &[1])) + }, + _ => cx.sess().span_bug(base.span, + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) + }, + ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { + ty::ty_vec(_, Some(u)) => { + (const_deref_ptr(cx, bv), C_uint(cx, u)) + }, + _ => cx.sess().span_bug(base.span, + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) + }, + _ => cx.sess().span_bug(base.span, + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) + }; + + let len = llvm::LLVMConstIntGetZExtValue(len) as u64; + let len = match ty::get(bt).sty { + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { + ty::ty_str => { + assert!(len > 0); + len - 1 + } + _ => len + }, + _ => len + }; + if iv >= len { + // FIXME #3170: report this earlier on in the const-eval + // pass. Reporting here is a bit late. + cx.sess().span_err(e.span, + "const index-expr is out of bounds"); + } + const_get_elt(cx, arr, &[iv as c_uint]) + } + ast::ExprCast(ref base, _) => { + let ety = ty::expr_ty(cx.tcx(), e); + let llty = type_of::type_of(cx, ety); + let (v, basety) = const_expr(cx, &**base); + return match (expr::cast_type_kind(cx.tcx(), basety), + expr::cast_type_kind(cx.tcx(), ety)) { + + (expr::cast_integral, expr::cast_integral) => { + let s = ty::type_is_signed(basety) as Bool; + llvm::LLVMConstIntCast(v, llty.to_ref(), s) + } + (expr::cast_integral, expr::cast_float) => { + if ty::type_is_signed(basety) { + llvm::LLVMConstSIToFP(v, llty.to_ref()) + } else { + llvm::LLVMConstUIToFP(v, llty.to_ref()) + } + } + (expr::cast_float, expr::cast_float) => { + llvm::LLVMConstFPCast(v, llty.to_ref()) + } + (expr::cast_float, expr::cast_integral) => { + if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) } + else { llvm::LLVMConstFPToUI(v, llty.to_ref()) } + } + (expr::cast_enum, expr::cast_integral) => { + let repr = adt::represent_type(cx, basety); + let discr = adt::const_get_discrim(cx, &*repr, v); + let iv = C_integral(cx.int_type(), discr, false); + let ety_cast = expr::cast_type_kind(cx.tcx(), ety); + match ety_cast { + expr::cast_integral => { + let s = ty::type_is_signed(ety) as Bool; + llvm::LLVMConstIntCast(iv, llty.to_ref(), s) + } + _ => cx.sess().bug("enum cast destination is not \ + integral") + } + } + (expr::cast_pointer, expr::cast_pointer) => { + llvm::LLVMConstPointerCast(v, llty.to_ref()) + } + (expr::cast_integral, expr::cast_pointer) => { + llvm::LLVMConstIntToPtr(v, llty.to_ref()) + } + (expr::cast_pointer, expr::cast_integral) => { + llvm::LLVMConstPtrToInt(v, llty.to_ref()) + } + _ => { + cx.sess().impossible_case(e.span, + "bad combination of types for cast") + } + } + } + ast::ExprAddrOf(mutbl, ref sub) => { + // If this is the address of some static, then we need to return + // the actual address of the static itself (short circuit the rest + // of const eval). + let mut cur = sub; + loop { + match cur.node { + ast::ExprParen(ref sub) => cur = sub, + _ => break, + } + } + let opt_def = cx.tcx().def_map.borrow().get(&cur.id).cloned(); + match opt_def { + Some(def::DefStatic(def_id, _)) => { + let ty = ty::expr_ty(cx.tcx(), e); + return get_static_val(cx, def_id, ty); + } + _ => {} + } + + // If this isn't the address of a static, then keep going through + // normal constant evaluation. + let (e, _) = const_expr(cx, &**sub); + const_addr_of(cx, e, mutbl) + } + ast::ExprTup(ref es) => { + let ety = ty::expr_ty(cx.tcx(), e); + let repr = adt::represent_type(cx, ety); + let vals = map_list(es.as_slice()); + adt::trans_const(cx, &*repr, 0, vals.as_slice()) + } + ast::ExprStruct(_, ref fs, ref base_opt) => { + let ety = ty::expr_ty(cx.tcx(), e); + let repr = adt::represent_type(cx, ety); + let tcx = cx.tcx(); + + let base_val = match *base_opt { + Some(ref base) => Some(const_expr(cx, &**base)), + None => None + }; + + expr::with_field_tys(tcx, ety, Some(e.id), |discr, field_tys| { + let cs = field_tys.iter().enumerate() + .map(|(ix, &field_ty)| { + match fs.iter().find(|f| field_ty.name == f.ident.node.name) { + Some(ref f) => const_expr(cx, &*f.expr).val0(), + None => { + match base_val { + Some((bv, _)) => { + adt::const_get_field(cx, &*repr, bv, + discr, ix) + } + None => { + cx.sess().span_bug(e.span, + "missing struct field") + } + } + } + } + }).collect::>(); + adt::trans_const(cx, &*repr, discr, cs.as_slice()) + }) + } + ast::ExprVec(ref es) => { + const_vec(cx, e, es.as_slice()).val0() + } + ast::ExprRepeat(ref elem, ref count) => { + let vec_ty = ty::expr_ty(cx.tcx(), e); + let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); + let llunitty = type_of::type_of(cx, unit_ty); + let n = match const_eval::eval_const_expr(cx.tcx(), &**count) { + const_eval::const_int(i) => i as uint, + const_eval::const_uint(i) => i as uint, + _ => cx.sess().span_bug(count.span, "count must be integral const expression.") + }; + let vs = Vec::from_elem(n, const_expr(cx, &**elem).val0()); + if vs.iter().any(|vi| val_ty(*vi) != llunitty) { + C_struct(cx, vs.as_slice(), false) + } else { + C_array(llunitty, vs.as_slice()) + } + } + ast::ExprPath(ref pth) => { + // Assert that there are no type parameters in this path. + assert!(pth.segments.iter().all(|seg| !seg.parameters.has_types())); + + let opt_def = cx.tcx().def_map.borrow().get(&e.id).cloned(); + match opt_def { + Some(def::DefFn(def_id, _)) => { + if !ast_util::is_local(def_id) { + let ty = csearch::get_type(cx.tcx(), def_id).ty; + base::trans_external_path(cx, def_id, ty) + } else { + assert!(ast_util::is_local(def_id)); + base::get_item_val(cx, def_id.node) + } + } + Some(def::DefConst(def_id)) => { + get_const_val(cx, def_id) + } + Some(def::DefVariant(enum_did, variant_did, _)) => { + let ety = ty::expr_ty(cx.tcx(), e); + let repr = adt::represent_type(cx, ety); + let vinfo = ty::enum_variant_with_id(cx.tcx(), + enum_did, + variant_did); + adt::trans_const(cx, &*repr, vinfo.disr_val, &[]) + } + Some(def::DefStruct(_)) => { + let ety = ty::expr_ty(cx.tcx(), e); + let llty = type_of::type_of(cx, ety); + C_null(llty) + } + _ => { + cx.sess().span_bug(e.span, "expected a const, fn, struct, \ + or variant def") + } + } + } + ast::ExprCall(ref callee, ref args) => { + let opt_def = cx.tcx().def_map.borrow().get(&callee.id).cloned(); + match opt_def { + Some(def::DefStruct(_)) => { + let ety = ty::expr_ty(cx.tcx(), e); + let repr = adt::represent_type(cx, ety); + let arg_vals = map_list(args.as_slice()); + adt::trans_const(cx, &*repr, 0, arg_vals.as_slice()) + } + Some(def::DefVariant(enum_did, variant_did, _)) => { + let ety = ty::expr_ty(cx.tcx(), e); + let repr = adt::represent_type(cx, ety); + let vinfo = ty::enum_variant_with_id(cx.tcx(), + enum_did, + variant_did); + let arg_vals = map_list(args.as_slice()); + adt::trans_const(cx, + &*repr, + vinfo.disr_val, + arg_vals.as_slice()) + } + _ => cx.sess().span_bug(e.span, "expected a struct or variant def") + } + } + ast::ExprParen(ref e) => const_expr(cx, &**e).val0(), + ast::ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => const_expr(cx, &**expr).val0(), + None => C_nil(cx) + } + } + _ => cx.sess().span_bug(e.span, + "bad constant expression type in consts::const_expr") + }; + } +} + +pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) { + unsafe { + let _icx = push_ctxt("trans_static"); + let g = base::get_item_val(ccx, id); + // At this point, get_item_val has already translated the + // constant's initializer to determine its LLVM type. + let v = ccx.static_values().borrow()[id].clone(); + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let v = if llvm::LLVMTypeOf(v) == Type::i1(ccx).to_ref() { + llvm::LLVMConstZExt(v, Type::i8(ccx).to_ref()) + } else { + v + }; + llvm::LLVMSetInitializer(g, v); + + // As an optimization, all shared statics which do not have interior + // mutability are placed into read-only memory. + if m != ast::MutMutable { + let node_ty = ty::node_id_to_type(ccx.tcx(), id); + let tcontents = ty::type_contents(ccx.tcx(), node_ty); + if !tcontents.interior_unsafe() { + llvm::LLVMSetGlobalConstant(g, True); + } + } + debuginfo::create_global_var_metadata(ccx, id, g); + } +} + +fn get_static_val(ccx: &CrateContext, did: ast::DefId, ty: ty::t) -> ValueRef { + if ast_util::is_local(did) { return base::get_item_val(ccx, did.node) } + base::trans_external_path(ccx, did, ty) +} diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs new file mode 100644 index 00000000000..c2c1f8bb5f5 --- /dev/null +++ b/src/librustc_trans/trans/context.rs @@ -0,0 +1,886 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; +use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; +use llvm::{TargetData}; +use llvm::mk_target_data; +use metadata::common::LinkMeta; +use middle::resolve; +use middle::traits; +use trans::adt; +use trans::base; +use trans::builder::Builder; +use trans::common::{ExternMap,tydesc_info,BuilderRef_res}; +use trans::debuginfo; +use trans::monomorphize::MonoId; +use trans::type_::{Type, TypeNames}; +use middle::ty; +use session::config::NoDebugInfo; +use session::Session; +use util::ppaux::Repr; +use util::sha2::Sha256; +use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet}; + +use std::cell::{Cell, RefCell}; +use std::c_str::ToCStr; +use std::ptr; +use std::rc::Rc; +use syntax::ast; +use syntax::parse::token::InternedString; + +pub struct Stats { + pub n_static_tydescs: Cell, + pub n_glues_created: Cell, + pub n_null_glues: Cell, + pub n_real_glues: Cell, + pub n_fns: Cell, + pub n_monos: Cell, + pub n_inlines: Cell, + pub n_closures: Cell, + pub n_llvm_insns: Cell, + pub llvm_insns: RefCell>, + // (ident, llvm-instructions) + pub fn_stats: RefCell >, +} + +/// The shared portion of a `CrateContext`. There is one `SharedCrateContext` +/// per crate. The data here is shared between all compilation units of the +/// crate, so it must not contain references to any LLVM data structures +/// (aside from metadata-related ones). +pub struct SharedCrateContext<'tcx> { + local_ccxs: Vec, + + metadata_llmod: ModuleRef, + metadata_llcx: ContextRef, + + exp_map2: resolve::ExportMap2, + reachable: NodeSet, + item_symbols: RefCell>, + link_meta: LinkMeta, + symbol_hasher: RefCell, + tcx: ty::ctxt<'tcx>, + stats: Stats, + + available_monomorphizations: RefCell>, + available_drop_glues: RefCell>, +} + +/// The local portion of a `CrateContext`. There is one `LocalCrateContext` +/// per compilation unit. Each one has its own LLVM `ContextRef` so that +/// several compilation units may be optimized in parallel. All other LLVM +/// data structures in the `LocalCrateContext` are tied to that `ContextRef`. +pub struct LocalCrateContext { + llmod: ModuleRef, + llcx: ContextRef, + td: TargetData, + tn: TypeNames, + externs: RefCell, + item_vals: RefCell>, + drop_glues: RefCell>, + tydescs: RefCell>>, + /// Set when running emit_tydescs to enforce that no more tydescs are + /// created. + finished_tydescs: Cell, + /// Track mapping of external ids to local items imported for inlining + external: RefCell>>, + /// Backwards version of the `external` map (inlined items to where they + /// came from) + external_srcs: RefCell>, + /// Cache instances of monomorphized functions + monomorphized: RefCell>, + monomorphizing: RefCell>, + /// Cache generated vtables + vtables: RefCell), ValueRef>>, + /// Cache of constant strings, + const_cstr_cache: RefCell>, + + /// Reverse-direction for const ptrs cast from globals. + /// Key is an int, cast from a ValueRef holding a *T, + /// Val is a ValueRef holding a *[T]. + /// + /// Needed because LLVM loses pointer->pointee association + /// when we ptrcast, and we have to ptrcast during translation + /// of a [T] const because we form a slice, a [*T,int] pair, not + /// a pointer to an LLVM array type. + const_globals: RefCell>, + + /// Cache of emitted const values + const_values: RefCell>, + + /// Cache of emitted static values + static_values: RefCell>, + + /// Cache of external const values + extern_const_values: RefCell>, + + impl_method_cache: RefCell>, + + /// Cache of closure wrappers for bare fn's. + closure_bare_wrapper_cache: RefCell>, + + lltypes: RefCell>, + llsizingtypes: RefCell>, + adt_reprs: RefCell>>, + type_hashcodes: RefCell>, + all_llvm_symbols: RefCell>, + int_type: Type, + opaque_vec_type: Type, + builder: BuilderRef_res, + + /// Holds the LLVM values for closure IDs. + unboxed_closure_vals: RefCell>, + + dbg_cx: Option, + + eh_personality: RefCell>, + + intrinsics: RefCell>, + + /// Number of LLVM instructions translated into this `LocalCrateContext`. + /// This is used to perform some basic load-balancing to keep all LLVM + /// contexts around the same size. + n_llvm_insns: Cell, + + trait_cache: RefCell, + traits::Vtable<()>>>, +} + +pub struct CrateContext<'a, 'tcx: 'a> { + shared: &'a SharedCrateContext<'tcx>, + local: &'a LocalCrateContext, + /// The index of `local` in `shared.local_ccxs`. This is used in + /// `maybe_iter(true)` to identify the original `LocalCrateContext`. + index: uint, +} + +pub struct CrateContextIterator<'a, 'tcx: 'a> { + shared: &'a SharedCrateContext<'tcx>, + index: uint, +} + +impl<'a, 'tcx> Iterator> for CrateContextIterator<'a,'tcx> { + fn next(&mut self) -> Option> { + if self.index >= self.shared.local_ccxs.len() { + return None; + } + + let index = self.index; + self.index += 1; + + Some(CrateContext { + shared: self.shared, + local: &self.shared.local_ccxs[index], + index: index, + }) + } +} + +/// The iterator produced by `CrateContext::maybe_iter`. +pub struct CrateContextMaybeIterator<'a, 'tcx: 'a> { + shared: &'a SharedCrateContext<'tcx>, + index: uint, + single: bool, + origin: uint, +} + +impl<'a, 'tcx> Iterator<(CrateContext<'a, 'tcx>, bool)> for CrateContextMaybeIterator<'a, 'tcx> { + fn next(&mut self) -> Option<(CrateContext<'a, 'tcx>, bool)> { + if self.index >= self.shared.local_ccxs.len() { + return None; + } + + let index = self.index; + self.index += 1; + if self.single { + self.index = self.shared.local_ccxs.len(); + } + + let ccx = CrateContext { + shared: self.shared, + local: &self.shared.local_ccxs[index], + index: index, + }; + Some((ccx, index == self.origin)) + } +} + + +unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { + let llcx = llvm::LLVMContextCreate(); + let llmod = mod_name.with_c_str(|buf| { + llvm::LLVMModuleCreateWithNameInContext(buf, llcx) + }); + sess.target + .target + .data_layout + .as_slice() + .with_c_str(|buf| { + llvm::LLVMSetDataLayout(llmod, buf); + }); + sess.target + .target + .llvm_target + .as_slice() + .with_c_str(|buf| { + llvm::LLVMRustSetNormalizedTarget(llmod, buf); + }); + (llcx, llmod) +} + +impl<'tcx> SharedCrateContext<'tcx> { + pub fn new(crate_name: &str, + local_count: uint, + tcx: ty::ctxt<'tcx>, + emap2: resolve::ExportMap2, + symbol_hasher: Sha256, + link_meta: LinkMeta, + reachable: NodeSet) + -> SharedCrateContext<'tcx> { + let (metadata_llcx, metadata_llmod) = unsafe { + create_context_and_module(&tcx.sess, "metadata") + }; + + let mut shared_ccx = SharedCrateContext { + local_ccxs: Vec::with_capacity(local_count), + metadata_llmod: metadata_llmod, + metadata_llcx: metadata_llcx, + exp_map2: emap2, + reachable: reachable, + item_symbols: RefCell::new(NodeMap::new()), + link_meta: link_meta, + symbol_hasher: RefCell::new(symbol_hasher), + tcx: tcx, + stats: Stats { + n_static_tydescs: Cell::new(0u), + n_glues_created: Cell::new(0u), + n_null_glues: Cell::new(0u), + n_real_glues: Cell::new(0u), + n_fns: Cell::new(0u), + n_monos: Cell::new(0u), + n_inlines: Cell::new(0u), + n_closures: Cell::new(0u), + n_llvm_insns: Cell::new(0u), + llvm_insns: RefCell::new(FnvHashMap::new()), + fn_stats: RefCell::new(Vec::new()), + }, + available_monomorphizations: RefCell::new(FnvHashSet::new()), + available_drop_glues: RefCell::new(FnvHashMap::new()), + }; + + for i in range(0, local_count) { + // Append ".rs" to crate name as LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}.{}.rs", crate_name, i); + let local_ccx = LocalCrateContext::new(&shared_ccx, llmod_id.as_slice()); + shared_ccx.local_ccxs.push(local_ccx); + } + + shared_ccx + } + + pub fn iter<'a>(&'a self) -> CrateContextIterator<'a, 'tcx> { + CrateContextIterator { + shared: self, + index: 0, + } + } + + pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a, 'tcx> { + CrateContext { + shared: self, + local: &self.local_ccxs[index], + index: index, + } + } + + fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a, 'tcx> { + let (local_ccx, index) = + self.local_ccxs + .iter() + .zip(range(0, self.local_ccxs.len())) + .min_by(|&(local_ccx, _idx)| local_ccx.n_llvm_insns.get()) + .unwrap(); + CrateContext { + shared: self, + local: local_ccx, + index: index, + } + } + + + pub fn metadata_llmod(&self) -> ModuleRef { + self.metadata_llmod + } + + pub fn metadata_llcx(&self) -> ContextRef { + self.metadata_llcx + } + + pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { + &self.exp_map2 + } + + pub fn reachable<'a>(&'a self) -> &'a NodeSet { + &self.reachable + } + + pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { + &self.item_symbols + } + + pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { + &self.link_meta + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &self.symbol_hasher + } + + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { + &self.tcx + } + + pub fn take_tcx(self) -> ty::ctxt<'tcx> { + self.tcx + } + + pub fn sess<'a>(&'a self) -> &'a Session { + &self.tcx.sess + } + + pub fn stats<'a>(&'a self) -> &'a Stats { + &self.stats + } +} + +impl LocalCrateContext { + fn new(shared: &SharedCrateContext, + name: &str) + -> LocalCrateContext { + unsafe { + let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, name); + + let td = mk_target_data(shared.tcx + .sess + .target + .target + .data_layout + .as_slice()); + + let dbg_cx = if shared.tcx.sess.opts.debuginfo != NoDebugInfo { + Some(debuginfo::CrateDebugContext::new(llmod)) + } else { + None + }; + + let mut local_ccx = LocalCrateContext { + llmod: llmod, + llcx: llcx, + td: td, + tn: TypeNames::new(), + externs: RefCell::new(FnvHashMap::new()), + item_vals: RefCell::new(NodeMap::new()), + drop_glues: RefCell::new(FnvHashMap::new()), + tydescs: RefCell::new(FnvHashMap::new()), + finished_tydescs: Cell::new(false), + external: RefCell::new(DefIdMap::new()), + external_srcs: RefCell::new(NodeMap::new()), + monomorphized: RefCell::new(FnvHashMap::new()), + monomorphizing: RefCell::new(DefIdMap::new()), + vtables: RefCell::new(FnvHashMap::new()), + const_cstr_cache: RefCell::new(FnvHashMap::new()), + const_globals: RefCell::new(FnvHashMap::new()), + const_values: RefCell::new(NodeMap::new()), + static_values: RefCell::new(NodeMap::new()), + extern_const_values: RefCell::new(DefIdMap::new()), + impl_method_cache: RefCell::new(FnvHashMap::new()), + closure_bare_wrapper_cache: RefCell::new(FnvHashMap::new()), + lltypes: RefCell::new(FnvHashMap::new()), + llsizingtypes: RefCell::new(FnvHashMap::new()), + adt_reprs: RefCell::new(FnvHashMap::new()), + type_hashcodes: RefCell::new(FnvHashMap::new()), + all_llvm_symbols: RefCell::new(FnvHashSet::new()), + int_type: Type::from_ref(ptr::null_mut()), + opaque_vec_type: Type::from_ref(ptr::null_mut()), + builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)), + unboxed_closure_vals: RefCell::new(FnvHashMap::new()), + dbg_cx: dbg_cx, + eh_personality: RefCell::new(None), + intrinsics: RefCell::new(FnvHashMap::new()), + n_llvm_insns: Cell::new(0u), + trait_cache: RefCell::new(FnvHashMap::new()), + }; + + local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared)); + local_ccx.opaque_vec_type = Type::opaque_vec(&local_ccx.dummy_ccx(shared)); + + // Done mutating local_ccx directly. (The rest of the + // initialization goes through RefCell.) + { + let ccx = local_ccx.dummy_ccx(shared); + + let mut str_slice_ty = Type::named_struct(&ccx, "str_slice"); + str_slice_ty.set_struct_body(&[Type::i8p(&ccx), ccx.int_type()], false); + ccx.tn().associate_type("str_slice", &str_slice_ty); + + ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty)); + + if ccx.sess().count_llvm_insns() { + base::init_insn_ctxt() + } + } + + local_ccx + } + } + + /// Create a dummy `CrateContext` from `self` and the provided + /// `SharedCrateContext`. This is somewhat dangerous because `self` may + /// not actually be an element of `shared.local_ccxs`, which can cause some + /// operations to panic unexpectedly. + /// + /// This is used in the `LocalCrateContext` constructor to allow calling + /// functions that expect a complete `CrateContext`, even before the local + /// portion is fully initialized and attached to the `SharedCrateContext`. + fn dummy_ccx<'a, 'tcx>(&'a self, shared: &'a SharedCrateContext<'tcx>) + -> CrateContext<'a, 'tcx> { + CrateContext { + shared: shared, + local: self, + index: -1 as uint, + } + } +} + +impl<'b, 'tcx> CrateContext<'b, 'tcx> { + pub fn shared(&self) -> &'b SharedCrateContext<'tcx> { + self.shared + } + + pub fn local(&self) -> &'b LocalCrateContext { + self.local + } + + + /// Get a (possibly) different `CrateContext` from the same + /// `SharedCrateContext`. + pub fn rotate(&self) -> CrateContext<'b, 'tcx> { + self.shared.get_smallest_ccx() + } + + /// Either iterate over only `self`, or iterate over all `CrateContext`s in + /// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)` + /// pairs, where `is_origin` is `true` if `ccx` is `self` and `false` + /// otherwise. This method is useful for avoiding code duplication in + /// cases where it may or may not be necessary to translate code into every + /// context. + pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b, 'tcx> { + CrateContextMaybeIterator { + shared: self.shared, + index: if iter_all { 0 } else { self.index }, + single: !iter_all, + origin: self.index, + } + } + + + pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { + &self.shared.tcx + } + + pub fn sess<'a>(&'a self) -> &'a Session { + &self.shared.tcx.sess + } + + pub fn builder<'a>(&'a self) -> Builder<'a, 'tcx> { + Builder::new(self) + } + + pub fn raw_builder<'a>(&'a self) -> BuilderRef { + self.local.builder.b + } + + pub fn tydesc_type(&self) -> Type { + self.local.tn.find_type("tydesc").unwrap() + } + + pub fn get_intrinsic(&self, key: & &'static str) -> ValueRef { + match self.intrinsics().borrow().get(key).cloned() { + Some(v) => return v, + _ => {} + } + match declare_intrinsic(self, key) { + Some(v) => return v, + None => panic!() + } + } + + pub fn is_split_stack_supported(&self) -> bool { + self.sess().target.target.options.morestack + } + + + pub fn llmod(&self) -> ModuleRef { + self.local.llmod + } + + pub fn llcx(&self) -> ContextRef { + self.local.llcx + } + + pub fn td<'a>(&'a self) -> &'a TargetData { + &self.local.td + } + + pub fn tn<'a>(&'a self) -> &'a TypeNames { + &self.local.tn + } + + pub fn externs<'a>(&'a self) -> &'a RefCell { + &self.local.externs + } + + pub fn item_vals<'a>(&'a self) -> &'a RefCell> { + &self.local.item_vals + } + + pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 { + &self.shared.exp_map2 + } + + pub fn reachable<'a>(&'a self) -> &'a NodeSet { + &self.shared.reachable + } + + pub fn item_symbols<'a>(&'a self) -> &'a RefCell> { + &self.shared.item_symbols + } + + pub fn link_meta<'a>(&'a self) -> &'a LinkMeta { + &self.shared.link_meta + } + + pub fn drop_glues<'a>(&'a self) -> &'a RefCell> { + &self.local.drop_glues + } + + pub fn tydescs<'a>(&'a self) -> &'a RefCell>> { + &self.local.tydescs + } + + pub fn finished_tydescs<'a>(&'a self) -> &'a Cell { + &self.local.finished_tydescs + } + + pub fn external<'a>(&'a self) -> &'a RefCell>> { + &self.local.external + } + + pub fn external_srcs<'a>(&'a self) -> &'a RefCell> { + &self.local.external_srcs + } + + pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { + &self.local.monomorphized + } + + pub fn monomorphizing<'a>(&'a self) -> &'a RefCell> { + &self.local.monomorphizing + } + + pub fn vtables<'a>(&'a self) -> &'a RefCell), ValueRef>> { + &self.local.vtables + } + + pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell> { + &self.local.const_cstr_cache + } + + pub fn const_globals<'a>(&'a self) -> &'a RefCell> { + &self.local.const_globals + } + + pub fn const_values<'a>(&'a self) -> &'a RefCell> { + &self.local.const_values + } + + pub fn static_values<'a>(&'a self) -> &'a RefCell> { + &self.local.static_values + } + + pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { + &self.local.extern_const_values + } + + pub fn impl_method_cache<'a>(&'a self) + -> &'a RefCell> { + &self.local.impl_method_cache + } + + pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell> { + &self.local.closure_bare_wrapper_cache + } + + pub fn lltypes<'a>(&'a self) -> &'a RefCell> { + &self.local.lltypes + } + + pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell> { + &self.local.llsizingtypes + } + + pub fn adt_reprs<'a>(&'a self) -> &'a RefCell>> { + &self.local.adt_reprs + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &self.shared.symbol_hasher + } + + pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell> { + &self.local.type_hashcodes + } + + pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell> { + &self.local.all_llvm_symbols + } + + pub fn stats<'a>(&'a self) -> &'a Stats { + &self.shared.stats + } + + pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_monomorphizations + } + + pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_drop_glues + } + + pub fn int_type(&self) -> Type { + self.local.int_type + } + + pub fn opaque_vec_type(&self) -> Type { + self.local.opaque_vec_type + } + + pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell> { + &self.local.unboxed_closure_vals + } + + pub fn dbg_cx<'a>(&'a self) -> &'a Option { + &self.local.dbg_cx + } + + pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { + &self.local.eh_personality + } + + fn intrinsics<'a>(&'a self) -> &'a RefCell> { + &self.local.intrinsics + } + + pub fn count_llvm_insn(&self) { + self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1); + } + + pub fn trait_cache(&self) -> &RefCell, traits::Vtable<()>>> { + &self.local.trait_cache + } + + pub fn max_obj_size(&self) -> u64 { + 1<<31 /* FIXME #18069: select based on architecture */ + } + + pub fn report_overbig_object(&self, obj: ty::t) -> ! { + self.sess().fatal( + format!("the type `{}` is too big for the current architecture", + obj.repr(self.tcx())).as_slice()) + } +} + +fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { + macro_rules! ifn ( + ($name:expr fn() -> $ret:expr) => ( + if *key == $name { + let f = base::decl_cdecl_fn( + ccx, $name, Type::func(&[], &$ret), + ty::mk_nil(ccx.tcx())); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr fn($($arg:expr),*) -> $ret:expr) => ( + if *key == $name { + let f = base::decl_cdecl_fn(ccx, $name, + Type::func(&[$($arg),*], &$ret), ty::mk_nil(ccx.tcx())); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); + return Some(f); + } + ) + ) + macro_rules! mk_struct ( + ($($field_ty:expr),*) => (Type::struct_(ccx, &[$($field_ty),*], false)) + ) + + let i8p = Type::i8p(ccx); + let void = Type::void(ccx); + let i1 = Type::i1(ccx); + let t_i8 = Type::i8(ccx); + let t_i16 = Type::i16(ccx); + let t_i32 = Type::i32(ccx); + let t_i64 = Type::i64(ccx); + let t_f32 = Type::f32(ccx); + let t_f64 = Type::f64(ccx); + + ifn!("llvm.memcpy.p0i8.p0i8.i32" fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i32" fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i64" fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i32" fn(i8p, t_i8, t_i32, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i64" fn(i8p, t_i8, t_i64, t_i32, i1) -> void); + + ifn!("llvm.trap" fn() -> void); + ifn!("llvm.debugtrap" fn() -> void); + ifn!("llvm.frameaddress" fn(t_i32) -> i8p); + + ifn!("llvm.powi.f32" fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64" fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.pow.f32" fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.pow.f64" fn(t_f64, t_f64) -> t_f64); + + ifn!("llvm.sqrt.f32" fn(t_f32) -> t_f32); + ifn!("llvm.sqrt.f64" fn(t_f64) -> t_f64); + ifn!("llvm.sin.f32" fn(t_f32) -> t_f32); + ifn!("llvm.sin.f64" fn(t_f64) -> t_f64); + ifn!("llvm.cos.f32" fn(t_f32) -> t_f32); + ifn!("llvm.cos.f64" fn(t_f64) -> t_f64); + ifn!("llvm.exp.f32" fn(t_f32) -> t_f32); + ifn!("llvm.exp.f64" fn(t_f64) -> t_f64); + ifn!("llvm.exp2.f32" fn(t_f32) -> t_f32); + ifn!("llvm.exp2.f64" fn(t_f64) -> t_f64); + ifn!("llvm.log.f32" fn(t_f32) -> t_f32); + ifn!("llvm.log.f64" fn(t_f64) -> t_f64); + ifn!("llvm.log10.f32" fn(t_f32) -> t_f32); + ifn!("llvm.log10.f64" fn(t_f64) -> t_f64); + ifn!("llvm.log2.f32" fn(t_f32) -> t_f32); + ifn!("llvm.log2.f64" fn(t_f64) -> t_f64); + + ifn!("llvm.fma.f32" fn(t_f32, t_f32, t_f32) -> t_f32); + ifn!("llvm.fma.f64" fn(t_f64, t_f64, t_f64) -> t_f64); + + ifn!("llvm.fabs.f32" fn(t_f32) -> t_f32); + ifn!("llvm.fabs.f64" fn(t_f64) -> t_f64); + + ifn!("llvm.floor.f32" fn(t_f32) -> t_f32); + ifn!("llvm.floor.f64" fn(t_f64) -> t_f64); + ifn!("llvm.ceil.f32" fn(t_f32) -> t_f32); + ifn!("llvm.ceil.f64" fn(t_f64) -> t_f64); + ifn!("llvm.trunc.f32" fn(t_f32) -> t_f32); + ifn!("llvm.trunc.f64" fn(t_f64) -> t_f64); + + ifn!("llvm.rint.f32" fn(t_f32) -> t_f32); + ifn!("llvm.rint.f64" fn(t_f64) -> t_f64); + ifn!("llvm.nearbyint.f32" fn(t_f32) -> t_f32); + ifn!("llvm.nearbyint.f64" fn(t_f64) -> t_f64); + + ifn!("llvm.ctpop.i8" fn(t_i8) -> t_i8); + ifn!("llvm.ctpop.i16" fn(t_i16) -> t_i16); + ifn!("llvm.ctpop.i32" fn(t_i32) -> t_i32); + ifn!("llvm.ctpop.i64" fn(t_i64) -> t_i64); + + ifn!("llvm.ctlz.i8" fn(t_i8 , i1) -> t_i8); + ifn!("llvm.ctlz.i16" fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i32" fn(t_i32, i1) -> t_i32); + ifn!("llvm.ctlz.i64" fn(t_i64, i1) -> t_i64); + + ifn!("llvm.cttz.i8" fn(t_i8 , i1) -> t_i8); + ifn!("llvm.cttz.i16" fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i32" fn(t_i32, i1) -> t_i32); + ifn!("llvm.cttz.i64" fn(t_i64, i1) -> t_i64); + + ifn!("llvm.bswap.i16" fn(t_i16) -> t_i16); + ifn!("llvm.bswap.i32" fn(t_i32) -> t_i32); + ifn!("llvm.bswap.i64" fn(t_i64) -> t_i64); + + ifn!("llvm.sadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.sadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.sadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.sadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.uadd.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.uadd.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.uadd.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.uadd.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.ssub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.ssub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.ssub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.ssub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.usub.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.usub.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.usub.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.usub.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.smul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.smul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.smul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.smul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.umul.with.overflow.i8" fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.umul.with.overflow.i16" fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.umul.with.overflow.i32" fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.umul.with.overflow.i64" fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + + ifn!("llvm.lifetime.start" fn(t_i64,i8p) -> void); + ifn!("llvm.lifetime.end" fn(t_i64, i8p) -> void); + + ifn!("llvm.expect.i1" fn(i1, i1) -> i1); + ifn!("llvm.assume" fn(i1) -> void); + + // Some intrinsics were introduced in later versions of LLVM, but they have + // fallbacks in libc or libm and such. Currently, all of these intrinsics + // were introduced in LLVM 3.4, so we case on that. + macro_rules! compatible_ifn ( + ($name:expr, $cname:ident ($($arg:expr),*) -> $ret:expr) => ( + if unsafe { llvm::LLVMVersionMinor() >= 4 } { + // The `if key == $name` is already in ifn! + ifn!($name fn($($arg),*) -> $ret); + } else if *key == $name { + let f = base::decl_cdecl_fn(ccx, stringify!($cname), + Type::func(&[$($arg),*], &$ret), + ty::mk_nil(ccx.tcx())); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); + return Some(f); + } + ) + ) + + compatible_ifn!("llvm.copysign.f32", copysignf(t_f32, t_f32) -> t_f32); + compatible_ifn!("llvm.copysign.f64", copysign(t_f64, t_f64) -> t_f64); + compatible_ifn!("llvm.round.f32", roundf(t_f32) -> t_f32); + compatible_ifn!("llvm.round.f64", round(t_f64) -> t_f64); + + + if ccx.sess().opts.debuginfo != NoDebugInfo { + ifn!("llvm.dbg.declare" fn(Type::metadata(ccx), Type::metadata(ccx)) -> void); + ifn!("llvm.dbg.value" fn(Type::metadata(ccx), t_i64, Type::metadata(ccx)) -> void); + } + return None; +} diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs new file mode 100644 index 00000000000..10a73033b64 --- /dev/null +++ b/src/librustc_trans/trans/controlflow.rs @@ -0,0 +1,535 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::*; +use middle::def; +use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem}; +use trans::_match; +use trans::adt; +use trans::base::*; +use trans::build::*; +use trans::callee; +use trans::cleanup::CleanupMethods; +use trans::cleanup; +use trans::common::*; +use trans::consts; +use trans::datum; +use trans::debuginfo; +use trans::expr; +use trans::meth; +use trans::type_::Type; +use trans; +use middle::ty; +use middle::typeck::MethodCall; +use session::config::FullDebugInfo; +use util::ppaux::Repr; +use util::ppaux; + +use syntax::ast; +use syntax::ast::Ident; +use syntax::ast_util; +use syntax::codemap::Span; +use syntax::parse::token::InternedString; +use syntax::parse::token; +use syntax::visit::Visitor; + +pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + s: &ast::Stmt) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_stmt"); + let fcx = cx.fcx; + debug!("trans_stmt({})", s.repr(cx.tcx())); + + if cx.sess().asm_comments() { + add_span_comment(cx, s.span, s.repr(cx.tcx()).as_slice()); + } + + let mut bcx = cx; + + let id = ast_util::stmt_id(s); + let cleanup_debug_loc = + debuginfo::get_cleanup_debug_loc_for_ast_node(id, s.span, false); + fcx.push_ast_cleanup_scope(cleanup_debug_loc); + + match s.node { + ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => { + bcx = trans_stmt_semi(bcx, &**e); + } + ast::StmtDecl(ref d, _) => { + match d.node { + ast::DeclLocal(ref local) => { + bcx = init_local(bcx, &**local); + if cx.sess().opts.debuginfo == FullDebugInfo { + trans::debuginfo::create_local_var_metadata(bcx, + &**local); + } + } + // Inner items are visited by `trans_item`/`trans_meth`. + ast::DeclItem(_) => {}, + } + } + ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro") + } + + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s)); + + return bcx; +} + +pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_stmt_semi"); + let ty = expr_ty(cx, e); + if ty::type_needs_drop(cx.tcx(), ty) { + expr::trans_to_lvalue(cx, e, "stmt").bcx + } else { + expr::trans_into(cx, e, expr::Ignore) + } +} + +pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + b: &ast::Block, + mut dest: expr::Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_block"); + let fcx = bcx.fcx; + let mut bcx = bcx; + + let cleanup_debug_loc = + debuginfo::get_cleanup_debug_loc_for_ast_node(b.id, b.span, true); + fcx.push_ast_cleanup_scope(cleanup_debug_loc); + + for s in b.stmts.iter() { + bcx = trans_stmt(bcx, &**s); + } + + if dest != expr::Ignore { + let block_ty = node_id_type(bcx, b.id); + if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) { + dest = expr::Ignore; + } + } + + match b.expr { + Some(ref e) => { + bcx = expr::trans_into(bcx, &**e, dest); + } + None => { + assert!(dest == expr::Ignore || bcx.unreachable.get()); + } + } + + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id); + + return bcx; +} + +pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + if_id: ast::NodeId, + cond: &ast::Expr, + thn: &ast::Block, + els: Option<&ast::Expr>, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})", + bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id, + dest.to_string(bcx.ccx())); + let _icx = push_ctxt("trans_if"); + let mut bcx = bcx; + + let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool()); + + // Drop branches that are known to be impossible + if is_const(cond_val) && !is_undef(cond_val) { + if const_to_uint(cond_val) == 1 { + match els { + Some(elexpr) => { + let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx }; + trans.visit_expr(&*elexpr); + } + None => {} + } + // if true { .. } [else { .. }] + bcx = trans_block(bcx, &*thn, dest); + trans::debuginfo::clear_source_location(bcx.fcx); + } else { + let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ; + trans.visit_block(&*thn); + + match els { + // if false { .. } else { .. } + Some(elexpr) => { + bcx = expr::trans_into(bcx, &*elexpr, dest); + trans::debuginfo::clear_source_location(bcx.fcx); + } + + // if false { .. } + None => { } + } + } + + return bcx; + } + + let name = format!("then-block-{}-", thn.id); + let then_bcx_in = bcx.fcx.new_id_block(name.as_slice(), thn.id); + let then_bcx_out = trans_block(then_bcx_in, &*thn, dest); + trans::debuginfo::clear_source_location(bcx.fcx); + + let next_bcx; + match els { + Some(elexpr) => { + let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id); + let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest); + next_bcx = bcx.fcx.join_blocks(if_id, + &[then_bcx_out, else_bcx_out]); + CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); + } + + None => { + next_bcx = bcx.fcx.new_id_block("next-block", if_id); + Br(then_bcx_out, next_bcx.llbb); + CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb); + } + } + + // Clear the source location because it is still set to whatever has been translated + // right before. + trans::debuginfo::clear_source_location(next_bcx.fcx); + + next_bcx +} + +pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + loop_id: ast::NodeId, + cond: &ast::Expr, + body: &ast::Block) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_while"); + let fcx = bcx.fcx; + + // bcx + // | + // cond_bcx_in <--------+ + // | | + // cond_bcx_out | + // | | | + // | body_bcx_in | + // cleanup_blk | | + // | body_bcx_out --+ + // next_bcx_in + + let next_bcx_in = fcx.new_id_block("while_exit", loop_id); + let cond_bcx_in = fcx.new_id_block("while_cond", cond.id); + let body_bcx_in = fcx.new_id_block("while_body", body.id); + + fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]); + + Br(bcx, cond_bcx_in.llbb); + + // compile the block where we will handle loop cleanups + let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK); + + // compile the condition + let Result {bcx: cond_bcx_out, val: cond_val} = + expr::trans(cond_bcx_in, cond).to_llbool(); + CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb); + + // loop body: + let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); + Br(body_bcx_out, cond_bcx_in.llbb); + + fcx.pop_loop_cleanup_scope(loop_id); + return next_bcx_in; +} + +/// Translates a `for` loop. +pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + loop_info: NodeInfo, + pat: &ast::Pat, + head: &ast::Expr, + body: &ast::Block) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_for"); + + // bcx + // | + // loopback_bcx_in <-------+ + // | | + // loopback_bcx_out | + // | | | + // | body_bcx_in | + // cleanup_blk | | + // | body_bcx_out --+ + // next_bcx_in + + // Codegen the head to create the iterator value. + let iterator_datum = + unpack_datum!(bcx, expr::trans_to_lvalue(bcx, head, "for_head")); + let iterator_type = node_id_type(bcx, head.id); + debug!("iterator type is {}, datum type is {}", + ppaux::ty_to_string(bcx.tcx(), iterator_type), + ppaux::ty_to_string(bcx.tcx(), iterator_datum.ty)); + let lliterator = load_ty(bcx, iterator_datum.val, iterator_datum.ty); + + // Create our basic blocks and set up our loop cleanups. + let next_bcx_in = bcx.fcx.new_id_block("for_exit", loop_info.id); + let loopback_bcx_in = bcx.fcx.new_id_block("for_loopback", head.id); + let body_bcx_in = bcx.fcx.new_id_block("for_body", body.id); + bcx.fcx.push_loop_cleanup_scope(loop_info.id, + [next_bcx_in, loopback_bcx_in]); + Br(bcx, loopback_bcx_in.llbb); + let cleanup_llbb = bcx.fcx.normal_exit_block(loop_info.id, + cleanup::EXIT_BREAK); + + // Set up the method call (to `.next()`). + let method_call = MethodCall::expr(loop_info.id); + let method_type = (*loopback_bcx_in.tcx() + .method_map + .borrow())[method_call] + .ty; + let method_type = monomorphize_type(loopback_bcx_in, method_type); + let method_result_type = ty::ty_fn_ret(method_type).unwrap(); + let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); + let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope); + + // Compile the method call (to `.next()`). + let mut loopback_bcx_out = loopback_bcx_in; + let option_datum = + unpack_datum!(loopback_bcx_out, + datum::lvalue_scratch_datum(loopback_bcx_out, + method_result_type, + "loop_option", + false, + option_cleanup_scope_id, + (), + |(), bcx, lloption| { + let Result { + bcx, + val: _ + } = callee::trans_call_inner(bcx, + Some(loop_info), + method_type, + |bcx, arg_cleanup_scope| { + meth::trans_method_callee( + bcx, + method_call, + None, + arg_cleanup_scope) + }, + callee::ArgVals(&[lliterator]), + Some(expr::SaveIn(lloption))); + bcx + })); + + // Check the discriminant; if the `None` case, exit the loop. + let option_representation = adt::represent_type(loopback_bcx_out.ccx(), + method_result_type); + let lldiscriminant = adt::trans_get_discr(loopback_bcx_out, + &*option_representation, + option_datum.val, + None); + let i1_type = Type::i1(loopback_bcx_out.ccx()); + let llcondition = Trunc(loopback_bcx_out, lldiscriminant, i1_type); + CondBr(loopback_bcx_out, llcondition, body_bcx_in.llbb, cleanup_llbb); + + // Now we're in the body. Unpack the `Option` value into the programmer- + // supplied pattern. + let llpayload = adt::trans_field_ptr(body_bcx_in, + &*option_representation, + option_datum.val, + 1, + 0); + let binding_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); + let binding_cleanup_scope_id = + cleanup::CustomScope(binding_cleanup_scope); + let mut body_bcx_out = + _match::store_for_loop_binding(body_bcx_in, + pat, + llpayload, + binding_cleanup_scope_id); + + // Codegen the body. + body_bcx_out = trans_block(body_bcx_out, body, expr::Ignore); + body_bcx_out = + body_bcx_out.fcx + .pop_and_trans_custom_cleanup_scope(body_bcx_out, + binding_cleanup_scope); + body_bcx_out = + body_bcx_out.fcx + .pop_and_trans_custom_cleanup_scope(body_bcx_out, + option_cleanup_scope); + Br(body_bcx_out, loopback_bcx_in.llbb); + + // Codegen cleanups and leave. + next_bcx_in.fcx.pop_loop_cleanup_scope(loop_info.id); + next_bcx_in +} + +pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + loop_id: ast::NodeId, + body: &ast::Block) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_loop"); + let fcx = bcx.fcx; + + // bcx + // | + // body_bcx_in + // | + // body_bcx_out + // + // next_bcx + // + // Links between body_bcx_in and next_bcx are created by + // break statements. + + let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id); + let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id); + + fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]); + + Br(bcx, body_bcx_in.llbb); + let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); + Br(body_bcx_out, body_bcx_in.llbb); + + fcx.pop_loop_cleanup_scope(loop_id); + + return next_bcx_in; +} + +pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr_id: ast::NodeId, + opt_label: Option, + exit: uint) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_break_cont"); + let fcx = bcx.fcx; + + if bcx.unreachable.get() { + return bcx; + } + + // Locate loop that we will break to + let loop_id = match opt_label { + None => fcx.top_loop_scope(), + Some(_) => { + match bcx.tcx().def_map.borrow().get(&expr_id) { + Some(&def::DefLabel(loop_id)) => loop_id, + ref r => { + bcx.tcx().sess.bug(format!("{} in def-map for label", + r).as_slice()) + } + } + } + }; + + // Generate appropriate cleanup code and branch + let cleanup_llbb = fcx.normal_exit_block(loop_id, exit); + Br(bcx, cleanup_llbb); + Unreachable(bcx); // anything afterwards should be ignored + return bcx; +} + +pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr_id: ast::NodeId, + label_opt: Option) + -> Block<'blk, 'tcx> { + return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK); +} + +pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr_id: ast::NodeId, + label_opt: Option) + -> Block<'blk, 'tcx> { + return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP); +} + +pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + e: Option<&ast::Expr>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_ret"); + let fcx = bcx.fcx; + let mut bcx = bcx; + let dest = match (fcx.llretslotptr.get(), e) { + (Some(_), Some(e)) => { + let ret_ty = expr_ty(bcx, &*e); + expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot")) + } + _ => expr::Ignore, + }; + match e { + Some(x) => { + bcx = expr::trans_into(bcx, &*x, dest); + match dest { + expr::SaveIn(slot) if fcx.needs_ret_allocas => { + Store(bcx, slot, fcx.llretslotptr.get().unwrap()); + } + _ => {} + } + } + _ => {} + } + let cleanup_llbb = fcx.return_exit_block(); + Br(bcx, cleanup_llbb); + Unreachable(bcx); + return bcx; +} + +pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + sp: Span, + fail_str: InternedString) + -> Block<'blk, 'tcx> { + let ccx = bcx.ccx(); + let _icx = push_ctxt("trans_fail_value"); + + let v_str = C_str_slice(ccx, fail_str); + let loc = bcx.sess().codemap().lookup_char_pos(sp.lo); + let filename = token::intern_and_get_ident(loc.file.name.as_slice()); + let filename = C_str_slice(ccx, filename); + let line = C_uint(ccx, loc.line); + let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false); + let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable); + let args = vec!(expr_file_line); + let did = langcall(bcx, Some(sp), "", PanicFnLangItem); + let bcx = callee::trans_lang_call(bcx, + did, + args.as_slice(), + Some(expr::Ignore)).bcx; + Unreachable(bcx); + return bcx; +} + +pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + sp: Span, + index: ValueRef, + len: ValueRef) + -> Block<'blk, 'tcx> { + let ccx = bcx.ccx(); + let _icx = push_ctxt("trans_fail_bounds_check"); + + // Extract the file/line from the span + let loc = bcx.sess().codemap().lookup_char_pos(sp.lo); + let filename = token::intern_and_get_ident(loc.file.name.as_slice()); + + // Invoke the lang item + let filename = C_str_slice(ccx, filename); + let line = C_uint(ccx, loc.line); + let file_line_const = C_struct(ccx, &[filename, line], false); + let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable); + let args = vec!(file_line, index, len); + let did = langcall(bcx, Some(sp), "", PanicBoundsCheckFnLangItem); + let bcx = callee::trans_lang_call(bcx, + did, + args.as_slice(), + Some(expr::Ignore)).bcx; + Unreachable(bcx); + return bcx; +} diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs new file mode 100644 index 00000000000..c4be6bf27b8 --- /dev/null +++ b/src/librustc_trans/trans/datum.rs @@ -0,0 +1,685 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * See the section on datums in `doc.rs` for an overview of what + * Datums are and how they are intended to be used. + */ + +pub use self::Expr::*; +pub use self::RvalueMode::*; + +use llvm::ValueRef; +use trans::base::*; +use trans::build::Load; +use trans::common::*; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::expr; +use trans::tvec; +use trans::type_of; +use middle::ty; +use util::ppaux::{ty_to_string}; + +use std::fmt; +use syntax::ast; + +/** + * A `Datum` encapsulates the result of evaluating an expression. It + * describes where the value is stored, what Rust type the value has, + * whether it is addressed by reference, and so forth. Please refer + * the section on datums in `doc.rs` for more details. + */ +#[deriving(Clone)] +pub struct Datum { + /// The llvm value. This is either a pointer to the Rust value or + /// the value itself, depending on `kind` below. + pub val: ValueRef, + + /// The rust type of the value. + pub ty: ty::t, + + /// Indicates whether this is by-ref or by-value. + pub kind: K, +} + +pub struct DatumBlock<'blk, 'tcx: 'blk, K> { + pub bcx: Block<'blk, 'tcx>, + pub datum: Datum, +} + +#[deriving(Show)] +pub enum Expr { + /// a fresh value that was produced and which has no cleanup yet + /// because it has not yet "landed" into its permanent home + RvalueExpr(Rvalue), + + /// `val` is a pointer into memory for which a cleanup is scheduled + /// (and thus has type *T). If you move out of an Lvalue, you must + /// zero out the memory (FIXME #5016). + LvalueExpr, +} + +#[deriving(Clone, Show)] +pub struct Lvalue; + +#[deriving(Show)] +pub struct Rvalue { + pub mode: RvalueMode +} + +impl Rvalue { + pub fn new(m: RvalueMode) -> Rvalue { + Rvalue { mode: m } + } +} + +// Make Datum linear for more type safety. +impl Drop for Rvalue { + fn drop(&mut self) { } +} + +#[deriving(PartialEq, Eq, Hash, Show)] +pub enum RvalueMode { + /// `val` is a pointer to the actual value (and thus has type *T) + ByRef, + + /// `val` is the actual value (*only used for immediates* like ints, ptrs) + ByValue, +} + +pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { + return Datum::new(val, ty, Rvalue::new(ByValue)); +} + +pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + val: ValueRef, + ty: ty::t) + -> DatumBlock<'blk, 'tcx, Rvalue> { + return DatumBlock::new(bcx, immediate_rvalue(val, ty)) +} + + +pub fn lvalue_scratch_datum<'blk, 'tcx, A>(bcx: Block<'blk, 'tcx>, + ty: ty::t, + name: &str, + zero: bool, + scope: cleanup::ScopeId, + arg: A, + populate: |A, Block<'blk, 'tcx>, ValueRef| + -> Block<'blk, 'tcx>) + -> DatumBlock<'blk, 'tcx, Lvalue> { + /*! + * Allocates temporary space on the stack using alloca() and + * returns a by-ref Datum pointing to it. The memory will be + * dropped upon exit from `scope`. The callback `populate` should + * initialize the memory. If `zero` is true, the space will be + * zeroed when it is allocated; this is not necessary unless `bcx` + * does not dominate the end of `scope`. + */ + + let scratch = if zero { + alloca_zeroed(bcx, ty, name) + } else { + let llty = type_of::type_of(bcx.ccx(), ty); + alloca(bcx, llty, name) + }; + + // Subtle. Populate the scratch memory *before* scheduling cleanup. + let bcx = populate(arg, bcx, scratch); + bcx.fcx.schedule_lifetime_end(scope, scratch); + bcx.fcx.schedule_drop_mem(scope, scratch, ty); + + DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue)) +} + +pub fn rvalue_scratch_datum(bcx: Block, + ty: ty::t, + name: &str) + -> Datum { + /*! + * Allocates temporary space on the stack using alloca() and + * returns a by-ref Datum pointing to it. If `zero` is true, the + * space will be zeroed when it is allocated; this is normally not + * necessary, but in the case of automatic rooting in match + * statements it is possible to have temporaries that may not get + * initialized if a certain arm is not taken, so we must zero + * them. You must arrange any cleanups etc yourself! + */ + + let llty = type_of::type_of(bcx.ccx(), ty); + let scratch = alloca(bcx, llty, name); + Datum::new(scratch, ty, Rvalue::new(ByRef)) +} + +pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode { + /*! + * Indicates the "appropriate" mode for this value, + * which is either by ref or by value, depending + * on whether type is immediate or not. + */ + + if type_is_immediate(ccx, ty) { + ByValue + } else { + ByRef + } +} + +fn add_rvalue_clean(mode: RvalueMode, + fcx: &FunctionContext, + scope: cleanup::ScopeId, + val: ValueRef, + ty: ty::t) { + match mode { + ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } + ByRef => { + fcx.schedule_lifetime_end(scope, val); + fcx.schedule_drop_mem(scope, val, ty); + } + } +} + +pub trait KindOps { + + /** + * Take appropriate action after the value in `datum` has been + * stored to a new location. + */ + fn post_store<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + val: ValueRef, + ty: ty::t) + -> Block<'blk, 'tcx>; + + /** + * True if this mode is a reference mode, meaning that the datum's + * val field is a pointer to the actual value + */ + fn is_by_ref(&self) -> bool; + + /** + * Converts to an Expr kind + */ + fn to_expr_kind(self) -> Expr; + +} + +impl KindOps for Rvalue { + fn post_store<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + _val: ValueRef, + _ty: ty::t) + -> Block<'blk, 'tcx> { + // No cleanup is scheduled for an rvalue, so we don't have + // to do anything after a move to cancel or duplicate it. + bcx + } + + fn is_by_ref(&self) -> bool { + self.mode == ByRef + } + + fn to_expr_kind(self) -> Expr { + RvalueExpr(self) + } +} + +impl KindOps for Lvalue { + fn post_store<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + val: ValueRef, + ty: ty::t) + -> Block<'blk, 'tcx> { + /*! + * If an lvalue is moved, we must zero out the memory in which + * it resides so as to cancel cleanup. If an @T lvalue is + * copied, we must increment the reference count. + */ + + if ty::type_needs_drop(bcx.tcx(), ty) { + // cancel cleanup of affine values by zeroing out + let () = zero_mem(bcx, val, ty); + bcx + } else { + bcx + } + } + + fn is_by_ref(&self) -> bool { + true + } + + fn to_expr_kind(self) -> Expr { + LvalueExpr + } +} + +impl KindOps for Expr { + fn post_store<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + val: ValueRef, + ty: ty::t) + -> Block<'blk, 'tcx> { + match *self { + LvalueExpr => Lvalue.post_store(bcx, val, ty), + RvalueExpr(ref r) => r.post_store(bcx, val, ty), + } + } + + fn is_by_ref(&self) -> bool { + match *self { + LvalueExpr => Lvalue.is_by_ref(), + RvalueExpr(ref r) => r.is_by_ref() + } + } + + fn to_expr_kind(self) -> Expr { + self + } +} + +impl Datum { + pub fn add_clean(self, + fcx: &FunctionContext, + scope: cleanup::ScopeId) + -> ValueRef { + /*! + * Schedules a cleanup for this datum in the given scope. + * That means that this datum is no longer an rvalue datum; + * hence, this function consumes the datum and returns the + * contained ValueRef. + */ + + add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty); + self.val + } + + pub fn to_lvalue_datum_in_scope<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + name: &str, + scope: cleanup::ScopeId) + -> DatumBlock<'blk, 'tcx, Lvalue> { + /*! + * Returns an lvalue datum (that is, a by ref datum with + * cleanup scheduled). If `self` is not already an lvalue, + * cleanup will be scheduled in the temporary scope for `expr_id`. + */ + let fcx = bcx.fcx; + + match self.kind.mode { + ByRef => { + add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); + DatumBlock::new(bcx, Datum::new(self.val, self.ty, Lvalue)) + } + + ByValue => { + lvalue_scratch_datum( + bcx, self.ty, name, false, scope, self, + |this, bcx, llval| this.store_to(bcx, llval)) + } + } + } + + pub fn to_ref_datum<'blk, 'tcx>(self, bcx: Block<'blk, 'tcx>) + -> DatumBlock<'blk, 'tcx, Rvalue> { + let mut bcx = bcx; + match self.kind.mode { + ByRef => DatumBlock::new(bcx, self), + ByValue => { + let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref"); + bcx = self.store_to(bcx, scratch.val); + DatumBlock::new(bcx, scratch) + } + } + } + + pub fn to_appropriate_datum<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>) + -> DatumBlock<'blk, 'tcx, Rvalue> { + match self.appropriate_rvalue_mode(bcx.ccx()) { + ByRef => { + self.to_ref_datum(bcx) + } + ByValue => { + match self.kind.mode { + ByValue => DatumBlock::new(bcx, self), + ByRef => { + let llval = load_ty(bcx, self.val, self.ty); + DatumBlock::new(bcx, Datum::new(llval, self.ty, Rvalue::new(ByValue))) + } + } + } + } + } +} + +/** + * Methods suitable for "expr" datums that could be either lvalues or + * rvalues. These include coercions into lvalues/rvalues but also a number + * of more general operations. (Some of those operations could be moved to + * the more general `impl Datum`, but it's convenient to have them + * here since we can `match self.kind` rather than having to implement + * generic methods in `KindOps`.) + */ +impl Datum { + fn match_kind(self, + if_lvalue: |Datum| -> R, + if_rvalue: |Datum| -> R) + -> R { + let Datum { val, ty, kind } = self; + match kind { + LvalueExpr => if_lvalue(Datum::new(val, ty, Lvalue)), + RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)), + } + } + + #[allow(dead_code)] // potentially useful + pub fn assert_lvalue(self, bcx: Block) -> Datum { + /*! + * Asserts that this datum *is* an lvalue and returns it. + */ + + self.match_kind( + |d| d, + |_| bcx.sess().bug("assert_lvalue given rvalue")) + } + + pub fn assert_rvalue(self, bcx: Block) -> Datum { + /*! + * Asserts that this datum *is* an lvalue and returns it. + */ + + self.match_kind( + |_| bcx.sess().bug("assert_rvalue given lvalue"), + |r| r) + } + + pub fn store_to_dest<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + dest: expr::Dest, + expr_id: ast::NodeId) + -> Block<'blk, 'tcx> { + match dest { + expr::Ignore => { + self.add_clean_if_rvalue(bcx, expr_id); + bcx + } + expr::SaveIn(addr) => { + self.store_to(bcx, addr) + } + } + } + + pub fn add_clean_if_rvalue<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + expr_id: ast::NodeId) { + /*! + * Arranges cleanup for `self` if it is an rvalue. Use when + * you are done working with a value that may need drop. + */ + + self.match_kind( + |_| { /* Nothing to do, cleanup already arranged */ }, + |r| { + let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); + r.add_clean(bcx.fcx, scope); + }) + } + + pub fn clean<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + name: &'static str, + expr_id: ast::NodeId) + -> Block<'blk, 'tcx> { + /*! + * Ensures that `self` will get cleaned up, if it is not an lvalue + * already. + */ + + self.to_lvalue_datum(bcx, name, expr_id).bcx + } + + pub fn to_lvalue_datum<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + name: &str, + expr_id: ast::NodeId) + -> DatumBlock<'blk, 'tcx, Lvalue> { + debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx())); + + assert!(ty::lltype_is_sized(bcx.tcx(), self.ty), + "Trying to convert unsized value to lval"); + self.match_kind( + |l| DatumBlock::new(bcx, l), + |r| { + let scope = cleanup::temporary_scope(bcx.tcx(), expr_id); + r.to_lvalue_datum_in_scope(bcx, name, scope) + }) + } + + pub fn to_rvalue_datum<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + name: &'static str) + -> DatumBlock<'blk, 'tcx, Rvalue> { + /*! + * Ensures that we have an rvalue datum (that is, a datum with + * no cleanup scheduled). + */ + + self.match_kind( + |l| { + let mut bcx = bcx; + match l.appropriate_rvalue_mode(bcx.ccx()) { + ByRef => { + let scratch = rvalue_scratch_datum(bcx, l.ty, name); + bcx = l.store_to(bcx, scratch.val); + DatumBlock::new(bcx, scratch) + } + ByValue => { + let v = load_ty(bcx, l.val, l.ty); + bcx = l.kind.post_store(bcx, l.val, l.ty); + DatumBlock::new(bcx, Datum::new(v, l.ty, Rvalue::new(ByValue))) + } + } + }, + |r| DatumBlock::new(bcx, r)) + } + +} + +/** + * Methods suitable only for lvalues. These include the various + * operations to extract components out of compound data structures, + * such as extracting the field from a struct or a particular element + * from an array. + */ +impl Datum { + pub fn to_llref(self) -> ValueRef { + /*! + * Converts a datum into a by-ref value. The datum type must + * be one which is always passed by reference. + */ + + self.val + } + + // Extracts a component of a compound data structure (e.g., a field from a + // struct). Note that if self is an opened, unsized type then the returned + // datum may also be unsized _without the size information_. It is the + // callers responsibility to package the result in some way to make a valid + // datum in that case (e.g., by making a fat pointer or opened pair). + pub fn get_element(&self, bcx: Block, ty: ty::t, + gep: |ValueRef| -> ValueRef) + -> Datum { + let val = match ty::get(self.ty).sty { + _ if ty::type_is_sized(bcx.tcx(), self.ty) => gep(self.val), + ty::ty_open(_) => { + let base = Load(bcx, expr::get_dataptr(bcx, self.val)); + gep(base) + } + _ => bcx.tcx().sess.bug( + format!("Unexpected unsized type in get_element: {}", + bcx.ty_to_string(self.ty)).as_slice()) + }; + Datum { + val: val, + kind: Lvalue, + ty: ty, + } + } + + pub fn get_vec_base_and_len(&self, bcx: Block) -> (ValueRef, ValueRef) { + //! Converts a vector into the slice pair. + + tvec::get_base_and_len(bcx, self.val, self.ty) + } +} + +/** + * Generic methods applicable to any sort of datum. + */ +impl Datum { + pub fn new(val: ValueRef, ty: ty::t, kind: K) -> Datum { + Datum { val: val, ty: ty, kind: kind } + } + + pub fn to_expr_datum(self) -> Datum { + let Datum { val, ty, kind } = self; + Datum { val: val, ty: ty, kind: kind.to_expr_kind() } + } + + pub fn store_to<'blk, 'tcx>(self, + bcx: Block<'blk, 'tcx>, + dst: ValueRef) + -> Block<'blk, 'tcx> { + /*! + * Moves or copies this value into a new home, as appropriate + * depending on the type of the datum. This method consumes + * the datum, since it would be incorrect to go on using the + * datum if the value represented is affine (and hence the value + * is moved). + */ + + self.shallow_copy_raw(bcx, dst); + + self.kind.post_store(bcx, self.val, self.ty) + } + + fn shallow_copy_raw<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + dst: ValueRef) + -> Block<'blk, 'tcx> { + /*! + * Helper function that performs a shallow copy of this value + * into `dst`, which should be a pointer to a memory location + * suitable for `self.ty`. `dst` should contain uninitialized + * memory (either newly allocated, zeroed, or dropped). + * + * This function is private to datums because it leaves memory + * in an unstable state, where the source value has been + * copied but not zeroed. Public methods are `store_to` + * (if you no longer need the source value) or `shallow_copy` + * (if you wish the source value to remain valid). + */ + + let _icx = push_ctxt("copy_to_no_check"); + + if type_is_zero_size(bcx.ccx(), self.ty) { + return bcx; + } + + if self.kind.is_by_ref() { + memcpy_ty(bcx, dst, self.val, self.ty); + } else { + store_ty(bcx, self.val, dst, self.ty); + } + + return bcx; + } + + pub fn shallow_copy<'blk, 'tcx>(&self, + bcx: Block<'blk, 'tcx>, + dst: ValueRef) + -> Block<'blk, 'tcx> { + /*! + * Copies the value into a new location. This function always + * preserves the existing datum as a valid value. Therefore, + * it does not consume `self` and, also, cannot be applied to + * affine values (since they must never be duplicated). + */ + + assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty)); + self.shallow_copy_raw(bcx, dst) + } + + #[allow(dead_code)] // useful for debugging + pub fn to_string(&self, ccx: &CrateContext) -> String { + format!("Datum({}, {}, {})", + ccx.tn().val_to_string(self.val), + ty_to_string(ccx.tcx(), self.ty), + self.kind) + } + + pub fn appropriate_rvalue_mode(&self, ccx: &CrateContext) -> RvalueMode { + /*! See the `appropriate_rvalue_mode()` function */ + + appropriate_rvalue_mode(ccx, self.ty) + } + + pub fn to_llscalarish(self, bcx: Block) -> ValueRef { + /*! + * Converts `self` into a by-value `ValueRef`. Consumes this + * datum (i.e., absolves you of responsibility to cleanup the + * value). For this to work, the value must be something + * scalar-ish (like an int or a pointer) which (1) does not + * require drop glue and (2) is naturally passed around by + * value, and not by reference. + */ + + assert!(!ty::type_needs_drop(bcx.tcx(), self.ty)); + assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); + if self.kind.is_by_ref() { + load_ty(bcx, self.val, self.ty) + } else { + self.val + } + } + + pub fn to_llbool(self, bcx: Block) -> ValueRef { + assert!(ty::type_is_bool(self.ty)) + self.to_llscalarish(bcx) + } +} + +impl<'blk, 'tcx, K> DatumBlock<'blk, 'tcx, K> { + pub fn new(bcx: Block<'blk, 'tcx>, datum: Datum) -> DatumBlock<'blk, 'tcx, K> { + DatumBlock { bcx: bcx, datum: datum } + } +} + +impl<'blk, 'tcx, K: KindOps + fmt::Show> DatumBlock<'blk, 'tcx, K> { + pub fn to_expr_datumblock(self) -> DatumBlock<'blk, 'tcx, Expr> { + DatumBlock::new(self.bcx, self.datum.to_expr_datum()) + } +} + +impl<'blk, 'tcx> DatumBlock<'blk, 'tcx, Expr> { + pub fn store_to_dest(self, + dest: expr::Dest, + expr_id: ast::NodeId) -> Block<'blk, 'tcx> { + let DatumBlock { bcx, datum } = self; + datum.store_to_dest(bcx, dest, expr_id) + } + + pub fn to_llbool(self) -> Result<'blk, 'tcx> { + let DatumBlock { datum, bcx } = self; + Result::new(bcx, datum.to_llbool(bcx)) + } +} diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs new file mode 100644 index 00000000000..e3e36ee53fd --- /dev/null +++ b/src/librustc_trans/trans/debuginfo.rs @@ -0,0 +1,4045 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! +# Debug Info Module + +This module serves the purpose of generating debug symbols. We use LLVM's +[source level debugging](http://llvm.org/docs/SourceLevelDebugging.html) +features for generating the debug information. The general principle is this: + +Given the right metadata in the LLVM IR, the LLVM code generator is able to +create DWARF debug symbols for the given code. The +[metadata](http://llvm.org/docs/LangRef.html#metadata-type) is structured much +like DWARF *debugging information entries* (DIE), representing type information +such as datatype layout, function signatures, block layout, variable location +and scope information, etc. It is the purpose of this module to generate correct +metadata and insert it into the LLVM IR. + +As the exact format of metadata trees may change between different LLVM +versions, we now use LLVM +[DIBuilder](http://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) to +create metadata where possible. This will hopefully ease the adaption of this +module to future LLVM versions. + +The public API of the module is a set of functions that will insert the correct +metadata into the LLVM IR when called with the right parameters. The module is +thus driven from an outside client with functions like +`debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`. + +Internally the module will try to reuse already created metadata by utilizing a +cache. The way to get a shared metadata node when needed is thus to just call +the corresponding function in this module: + + let file_metadata = file_metadata(crate_context, path); + +The function will take care of probing the cache for an existing node for that +exact file path. + +All private state used by the module is stored within either the +CrateDebugContext struct (owned by the CrateContext) or the FunctionDebugContext +(owned by the FunctionContext). + +This file consists of three conceptual sections: +1. The public interface of the module +2. Module-internal metadata creation functions +3. Minor utility functions + + +## Recursive Types + +Some kinds of types, such as structs and enums can be recursive. That means that +the type definition of some type X refers to some other type which in turn +(transitively) refers to X. This introduces cycles into the type referral graph. +A naive algorithm doing an on-demand, depth-first traversal of this graph when +describing types, can get trapped in an endless loop when it reaches such a +cycle. + +For example, the following simple type for a singly-linked list... + +``` +struct List { + value: int, + tail: Option>, +} +``` + +will generate the following callstack with a naive DFS algorithm: + +``` +describe(t = List) + describe(t = int) + describe(t = Option>) + describe(t = Box) + describe(t = List) // at the beginning again... + ... +``` + +To break cycles like these, we use "forward declarations". That is, when the +algorithm encounters a possibly recursive type (any struct or enum), it +immediately creates a type description node and inserts it into the cache +*before* describing the members of the type. This type description is just a +stub (as type members are not described and added to it yet) but it allows the +algorithm to already refer to the type. After the stub is inserted into the +cache, the algorithm continues as before. If it now encounters a recursive +reference, it will hit the cache and does not try to describe the type anew. + +This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which +represents a kind of continuation, storing all state needed to continue +traversal at the type members after the type has been registered with the cache. +(This implementation approach might be a tad over-engineered and may change in +the future) + + +## Source Locations and Line Information + +In addition to data type descriptions the debugging information must also allow +to map machine code locations back to source code locations in order to be useful. +This functionality is also handled in this module. The following functions allow +to control source mappings: + ++ set_source_location() ++ clear_source_location() ++ start_emitting_source_locations() + +`set_source_location()` allows to set the current source location. All IR +instructions created after a call to this function will be linked to the given +source location, until another location is specified with +`set_source_location()` or the source location is cleared with +`clear_source_location()`. In the later case, subsequent IR instruction will not +be linked to any source location. As you can see, this is a stateful API +(mimicking the one in LLVM), so be careful with source locations set by previous +calls. It's probably best to not rely on any specific state being present at a +given point in code. + +One topic that deserves some extra attention is *function prologues*. At the +beginning of a function's machine code there are typically a few instructions +for loading argument values into allocas and checking if there's enough stack +space for the function to execute. This *prologue* is not visible in the source +code and LLVM puts a special PROLOGUE END marker into the line table at the +first non-prologue instruction of the function. In order to find out where the +prologue ends, LLVM looks for the first instruction in the function body that is +linked to a source location. So, when generating prologue instructions we have +to make sure that we don't emit source location information until the 'real' +function body begins. For this reason, source location emission is disabled by +default for any new function being translated and is only activated after a call +to the third function from the list above, `start_emitting_source_locations()`. +This function should be called right before regularly starting to translate the +top-level block of the given function. + +There is one exception to the above rule: `llvm.dbg.declare` instruction must be +linked to the source location of the variable being declared. For function +parameters these `llvm.dbg.declare` instructions typically occur in the middle +of the prologue, however, they are ignored by LLVM's prologue detection. The +`create_argument_metadata()` and related functions take care of linking the +`llvm.dbg.declare` instructions to the correct source locations even while +source location emission is still disabled, so there is no need to do anything +special with source location handling here. + +## Unique Type Identification + +In order for link-time optimization to work properly, LLVM needs a unique type +identifier that tells it across compilation units which types are the same as +others. This type identifier is created by TypeMap::get_unique_type_id_of_type() +using the following algorithm: + +(1) Primitive types have their name as ID +(2) Structs, enums and traits have a multipart identifier + + (1) The first part is the SVH (strict version hash) of the crate they were + originally defined in + + (2) The second part is the ast::NodeId of the definition in their original + crate + + (3) The final part is a concatenation of the type IDs of their concrete type + arguments if they are generic types. + +(3) Tuple-, pointer and function types are structurally identified, which means + that they are equivalent if their component types are equivalent (i.e. (int, + int) is the same regardless in which crate it is used). + +This algorithm also provides a stable ID for types that are defined in one crate +but instantiated from metadata within another crate. We just have to take care +to always map crate and node IDs back to the original crate context. + +As a side-effect these unique type IDs also help to solve a problem arising from +lifetime parameters. Since lifetime parameters are completely omitted in +debuginfo, more than one `ty::t` instance may map to the same debuginfo type +metadata, that is, some struct `Struct<'a>` may have N instantiations with +different concrete substitutions for `'a`, and thus there will be N `ty::t` +instances for the type `Struct<'a>` even though it is not generic otherwise. +Unfortunately this means that we cannot use `ty::type_id()` as cheap identifier +for type metadata---we have done this in the past, but it led to unnecessary +metadata duplication in the best case and LLVM assertions in the worst. However, +the unique type ID as described above *can* be used as identifier. Since it is +comparatively expensive to construct, though, `ty::type_id()` is still used +additionally as an optimization for cases where the exact same type has been +seen before (which is most of the time). */ +use self::FunctionDebugContextRepr::*; +use self::VariableAccess::*; +use self::VariableKind::*; +use self::MemberOffset::*; +use self::MemberDescriptionFactory::*; +use self::RecursiveTypeDescription::*; +use self::EnumDiscriminantInfo::*; +use self::DebugLocation::*; + +use llvm; +use llvm::{ModuleRef, ContextRef, ValueRef}; +use llvm::debuginfo::*; +use metadata::csearch; +use middle::subst::{mod, Subst}; +use trans::adt; +use trans::common::*; +use trans::machine; +use trans::_match::{BindingInfo, TrByCopy, TrByMove, TrByRef}; +use trans::type_of; +use trans::type_::Type; +use trans; +use middle::ty; +use middle::pat_util; +use session::config::{mod, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; +use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet}; +use util::ppaux; + +use libc::c_uint; +use std::c_str::{CString, ToCStr}; +use std::cell::{Cell, RefCell}; +use std::ptr; +use std::rc::{Rc, Weak}; +use syntax::util::interner::Interner; +use syntax::codemap::{Span, Pos}; +use syntax::{ast, codemap, ast_util, ast_map}; +use syntax::ast_util::PostExpansionMethod; +use syntax::parse::token; +use syntax::parse::token::special_idents; + +static DW_LANG_RUST: c_uint = 0x9000; + +#[allow(non_upper_case_globals)] +static DW_TAG_auto_variable: c_uint = 0x100; +#[allow(non_upper_case_globals)] +static DW_TAG_arg_variable: c_uint = 0x101; + +#[allow(non_upper_case_globals)] +static DW_ATE_boolean: c_uint = 0x02; +#[allow(non_upper_case_globals)] +static DW_ATE_float: c_uint = 0x04; +#[allow(non_upper_case_globals)] +static DW_ATE_signed: c_uint = 0x05; +#[allow(non_upper_case_globals)] +static DW_ATE_unsigned: c_uint = 0x07; +#[allow(non_upper_case_globals)] +static DW_ATE_unsigned_char: c_uint = 0x08; + +static UNKNOWN_LINE_NUMBER: c_uint = 0; +static UNKNOWN_COLUMN_NUMBER: c_uint = 0; + +// ptr::null() doesn't work :( +static UNKNOWN_FILE_METADATA: DIFile = (0 as DIFile); +static UNKNOWN_SCOPE_METADATA: DIScope = (0 as DIScope); + +static FLAGS_NONE: c_uint = 0; + +//=----------------------------------------------------------------------------- +// Public Interface of debuginfo module +//=----------------------------------------------------------------------------- + +#[deriving(Show, Hash, Eq, PartialEq, Clone)] +struct UniqueTypeId(ast::Name); + +// The TypeMap is where the CrateDebugContext holds the type metadata nodes +// created so far. The metadata nodes are indexed by UniqueTypeId, and, for +// faster lookup, also by ty::t. The TypeMap is responsible for creating +// UniqueTypeIds. +struct TypeMap { + // The UniqueTypeIds created so far + unique_id_interner: Interner>, + // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. + unique_id_to_metadata: FnvHashMap, + // A map from types to debuginfo metadata. This is a N:1 mapping. + type_to_metadata: FnvHashMap, + // A map from types to UniqueTypeId. This is a N:1 mapping. + type_to_unique_id: FnvHashMap +} + +impl TypeMap { + + fn new() -> TypeMap { + TypeMap { + unique_id_interner: Interner::new(), + type_to_metadata: FnvHashMap::new(), + unique_id_to_metadata: FnvHashMap::new(), + type_to_unique_id: FnvHashMap::new(), + } + } + + // Adds a ty::t to metadata mapping to the TypeMap. The method will fail if + // the mapping already exists. + fn register_type_with_metadata(&mut self, + cx: &CrateContext, + type_: ty::t, + metadata: DIType) { + if self.type_to_metadata.insert(type_, metadata).is_some() { + cx.sess().bug(format!("Type metadata for ty::t '{}' is already in the TypeMap!", + ppaux::ty_to_string(cx.tcx(), type_)).as_slice()); + } + } + + // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will + // fail if the mapping already exists. + fn register_unique_id_with_metadata(&mut self, + cx: &CrateContext, + unique_type_id: UniqueTypeId, + metadata: DIType) { + if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { + let unique_type_id_str = self.get_unique_type_id_as_string(unique_type_id); + cx.sess().bug(format!("Type metadata for unique id '{}' is already in the TypeMap!", + unique_type_id_str.as_slice()).as_slice()); + } + } + + fn find_metadata_for_type(&self, type_: ty::t) -> Option { + self.type_to_metadata.get(&type_).cloned() + } + + fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { + self.unique_id_to_metadata.get(&unique_type_id).cloned() + } + + // Get the string representation of a UniqueTypeId. This method will fail if + // the id is unknown. + fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> Rc { + let UniqueTypeId(interner_key) = unique_type_id; + self.unique_id_interner.get(interner_key) + } + + // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given + // type has been requested before, this is just a table lookup. Otherwise an + // ID will be generated and stored for later lookup. + fn get_unique_type_id_of_type(&mut self, cx: &CrateContext, type_: ty::t) -> UniqueTypeId { + + // basic type -> {:name of the type:} + // tuple -> {tuple_(:param-uid:)*} + // struct -> {struct_:svh: / :node-id:_<(:param-uid:),*> } + // enum -> {enum_:svh: / :node-id:_<(:param-uid:),*> } + // enum variant -> {variant_:variant-name:_:enum-uid:} + // reference (&) -> {& :pointee-uid:} + // mut reference (&mut) -> {&mut :pointee-uid:} + // ptr (*) -> {* :pointee-uid:} + // mut ptr (*mut) -> {*mut :pointee-uid:} + // unique ptr (~) -> {~ :pointee-uid:} + // @-ptr (@) -> {@ :pointee-uid:} + // sized vec ([T, ..x]) -> {[:size:] :element-uid:} + // unsized vec ([T]) -> {[] :element-uid:} + // trait (T) -> {trait_:svh: / :node-id:_<(:param-uid:),*> } + // closure -> { :store-sigil: |(:param-uid:),* <,_...>| -> \ + // :return-type-uid: : (:bounds:)*} + // function -> { fn( (:param-uid:)* <,_...> ) -> \ + // :return-type-uid:} + // unique vec box (~[]) -> {HEAP_VEC_BOX<:pointee-uid:>} + // gc box -> {GC_BOX<:pointee-uid:>} + + match self.type_to_unique_id.get(&type_).cloned() { + Some(unique_type_id) => return unique_type_id, + None => { /* generate one */} + }; + + let mut unique_type_id = String::with_capacity(256); + unique_type_id.push('{'); + + match ty::get(type_).sty { + ty::ty_bool | + ty::ty_char | + ty::ty_str | + ty::ty_int(_) | + ty::ty_uint(_) | + ty::ty_float(_) => { + push_debuginfo_type_name(cx, type_, false, &mut unique_type_id); + }, + ty::ty_enum(def_id, ref substs) => { + unique_type_id.push_str("enum "); + from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id); + }, + ty::ty_struct(def_id, ref substs) => { + unique_type_id.push_str("struct "); + from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id); + }, + ty::ty_tup(ref component_types) if component_types.is_empty() => { + push_debuginfo_type_name(cx, type_, false, &mut unique_type_id); + }, + ty::ty_tup(ref component_types) => { + unique_type_id.push_str("tuple "); + for &component_type in component_types.iter() { + let component_type_id = + self.get_unique_type_id_of_type(cx, component_type); + let component_type_id = + self.get_unique_type_id_as_string(component_type_id); + unique_type_id.push_str(component_type_id.as_slice()); + } + }, + ty::ty_uniq(inner_type) => { + unique_type_id.push('~'); + let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); + let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); + unique_type_id.push_str(inner_type_id.as_slice()); + }, + ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { + unique_type_id.push('*'); + if mutbl == ast::MutMutable { + unique_type_id.push_str("mut"); + } + + let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); + let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); + unique_type_id.push_str(inner_type_id.as_slice()); + }, + ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { + unique_type_id.push('&'); + if mutbl == ast::MutMutable { + unique_type_id.push_str("mut"); + } + + let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); + let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); + unique_type_id.push_str(inner_type_id.as_slice()); + }, + ty::ty_vec(inner_type, optional_length) => { + match optional_length { + Some(len) => { + unique_type_id.push_str(format!("[{}]", len).as_slice()); + } + None => { + unique_type_id.push_str("[]"); + } + }; + + let inner_type_id = self.get_unique_type_id_of_type(cx, inner_type); + let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); + unique_type_id.push_str(inner_type_id.as_slice()); + }, + ty::ty_trait(ref trait_data) => { + unique_type_id.push_str("trait "); + + from_def_id_and_substs(self, + cx, + trait_data.principal.def_id, + &trait_data.principal.substs, + &mut unique_type_id); + }, + ty::ty_bare_fn(ty::BareFnTy{ fn_style, abi, ref sig } ) => { + if fn_style == ast::UnsafeFn { + unique_type_id.push_str("unsafe "); + } + + unique_type_id.push_str(abi.name()); + + unique_type_id.push_str(" fn("); + + for ¶meter_type in sig.inputs.iter() { + let parameter_type_id = + self.get_unique_type_id_of_type(cx, parameter_type); + let parameter_type_id = + self.get_unique_type_id_as_string(parameter_type_id); + unique_type_id.push_str(parameter_type_id.as_slice()); + unique_type_id.push(','); + } + + if sig.variadic { + unique_type_id.push_str("..."); + } + + unique_type_id.push_str(")->"); + match sig.output { + ty::FnConverging(ret_ty) => { + let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty); + let return_type_id = self.get_unique_type_id_as_string(return_type_id); + unique_type_id.push_str(return_type_id.as_slice()); + } + ty::FnDiverging => { + unique_type_id.push_str("!"); + } + } + }, + ty::ty_closure(box ref closure_ty) => { + self.get_unique_type_id_of_closure_type(cx, + closure_ty.clone(), + &mut unique_type_id); + }, + ty::ty_unboxed_closure(ref def_id, _, ref substs) => { + let closure_ty = cx.tcx().unboxed_closures.borrow() + .get(def_id).unwrap().closure_type.subst(cx.tcx(), substs); + self.get_unique_type_id_of_closure_type(cx, + closure_ty, + &mut unique_type_id); + }, + _ => { + cx.sess().bug(format!("get_unique_type_id_of_type() - unexpected type: {}, {}", + ppaux::ty_to_string(cx.tcx(), type_).as_slice(), + ty::get(type_).sty).as_slice()) + } + }; + + unique_type_id.push('}'); + + // Trim to size before storing permanently + unique_type_id.shrink_to_fit(); + + let key = self.unique_id_interner.intern(Rc::new(unique_type_id)); + self.type_to_unique_id.insert(type_, UniqueTypeId(key)); + + return UniqueTypeId(key); + + fn from_def_id_and_substs(type_map: &mut TypeMap, + cx: &CrateContext, + def_id: ast::DefId, + substs: &subst::Substs, + output: &mut String) { + // First, find out the 'real' def_id of the type. Items inlined from + // other crates have to be mapped back to their source. + let source_def_id = if def_id.krate == ast::LOCAL_CRATE { + match cx.external_srcs().borrow().get(&def_id.node).cloned() { + Some(source_def_id) => { + // The given def_id identifies the inlined copy of a + // type definition, let's take the source of the copy. + source_def_id + } + None => def_id + } + } else { + def_id + }; + + // Get the crate hash as first part of the identifier. + let crate_hash = if source_def_id.krate == ast::LOCAL_CRATE { + cx.link_meta().crate_hash.clone() + } else { + cx.sess().cstore.get_crate_hash(source_def_id.krate) + }; + + output.push_str(crate_hash.as_str()); + output.push_str("/"); + output.push_str(format!("{:x}", def_id.node).as_slice()); + + // Maybe check that there is no self type here. + + let tps = substs.types.get_slice(subst::TypeSpace); + if tps.len() > 0 { + output.push('<'); + + for &type_parameter in tps.iter() { + let param_type_id = + type_map.get_unique_type_id_of_type(cx, type_parameter); + let param_type_id = + type_map.get_unique_type_id_as_string(param_type_id); + output.push_str(param_type_id.as_slice()); + output.push(','); + } + + output.push('>'); + } + } + } + + fn get_unique_type_id_of_closure_type(&mut self, + cx: &CrateContext, + closure_ty: ty::ClosureTy, + unique_type_id: &mut String) { + let ty::ClosureTy { fn_style, + onceness, + store, + ref bounds, + ref sig, + abi: _ } = closure_ty; + if fn_style == ast::UnsafeFn { + unique_type_id.push_str("unsafe "); + } + + if onceness == ast::Once { + unique_type_id.push_str("once "); + } + + match store { + ty::UniqTraitStore => unique_type_id.push_str("~|"), + ty::RegionTraitStore(_, ast::MutMutable) => { + unique_type_id.push_str("&mut|") + } + ty::RegionTraitStore(_, ast::MutImmutable) => { + unique_type_id.push_str("&|") + } + }; + + for ¶meter_type in sig.inputs.iter() { + let parameter_type_id = + self.get_unique_type_id_of_type(cx, parameter_type); + let parameter_type_id = + self.get_unique_type_id_as_string(parameter_type_id); + unique_type_id.push_str(parameter_type_id.as_slice()); + unique_type_id.push(','); + } + + if sig.variadic { + unique_type_id.push_str("..."); + } + + unique_type_id.push_str("|->"); + + match sig.output { + ty::FnConverging(ret_ty) => { + let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty); + let return_type_id = self.get_unique_type_id_as_string(return_type_id); + unique_type_id.push_str(return_type_id.as_slice()); + } + ty::FnDiverging => { + unique_type_id.push_str("!"); + } + } + + unique_type_id.push(':'); + + for bound in bounds.builtin_bounds.iter() { + match bound { + ty::BoundSend => unique_type_id.push_str("Send"), + ty::BoundSized => unique_type_id.push_str("Sized"), + ty::BoundCopy => unique_type_id.push_str("Copy"), + ty::BoundSync => unique_type_id.push_str("Sync"), + }; + unique_type_id.push('+'); + } + } + + // Get the UniqueTypeId for an enum variant. Enum variants are not really + // types of their own, so they need special handling. We still need a + // UniqueTypeId for them, since to debuginfo they *are* real types. + fn get_unique_type_id_of_enum_variant(&mut self, + cx: &CrateContext, + enum_type: ty::t, + variant_name: &str) + -> UniqueTypeId { + let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); + let enum_variant_type_id = format!("{}::{}", + self.get_unique_type_id_as_string(enum_type_id) + .as_slice(), + variant_name); + let interner_key = self.unique_id_interner.intern(Rc::new(enum_variant_type_id)); + UniqueTypeId(interner_key) + } +} + +// Returns from the enclosing function if the type metadata with the given +// unique id can be found in the type map +macro_rules! return_if_metadata_created_in_meantime( + ($cx: expr, $unique_type_id: expr) => ( + match debug_context($cx).type_map + .borrow() + .find_metadata_for_unique_id($unique_type_id) { + Some(metadata) => return MetadataCreationResult::new(metadata, true), + None => { /* proceed normally */ } + }; + ) +) + + +/// A context object for maintaining all state needed by the debuginfo module. +pub struct CrateDebugContext { + llcontext: ContextRef, + builder: DIBuilderRef, + current_debug_location: Cell, + created_files: RefCell>, + created_enum_disr_types: RefCell>, + + type_map: RefCell, + namespace_map: RefCell, Rc>>, + + // This collection is used to assert that composite types (structs, enums, + // ...) have their members only set once: + composite_types_completed: RefCell>, +} + +impl CrateDebugContext { + pub fn new(llmod: ModuleRef) -> CrateDebugContext { + debug!("CrateDebugContext::new"); + let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) }; + // DIBuilder inherits context from the module, so we'd better use the same one + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + return CrateDebugContext { + llcontext: llcontext, + builder: builder, + current_debug_location: Cell::new(UnknownLocation), + created_files: RefCell::new(FnvHashMap::new()), + created_enum_disr_types: RefCell::new(DefIdMap::new()), + type_map: RefCell::new(TypeMap::new()), + namespace_map: RefCell::new(FnvHashMap::new()), + composite_types_completed: RefCell::new(FnvHashSet::new()), + }; + } +} + +pub struct FunctionDebugContext { + repr: FunctionDebugContextRepr, +} + +enum FunctionDebugContextRepr { + DebugInfo(Box), + DebugInfoDisabled, + FunctionWithoutDebugInfo, +} + +impl FunctionDebugContext { + fn get_ref<'a>(&'a self, + cx: &CrateContext, + span: Span) + -> &'a FunctionDebugContextData { + match self.repr { + DebugInfo(box ref data) => data, + DebugInfoDisabled => { + cx.sess().span_bug(span, + FunctionDebugContext::debuginfo_disabled_message()); + } + FunctionWithoutDebugInfo => { + cx.sess().span_bug(span, + FunctionDebugContext::should_be_ignored_message()); + } + } + } + + fn debuginfo_disabled_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" + } + + fn should_be_ignored_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext for function that should be \ + ignored by debug info!" + } +} + +struct FunctionDebugContextData { + scope_map: RefCell>, + fn_metadata: DISubprogram, + argument_counter: Cell, + source_locations_enabled: Cell, +} + +enum VariableAccess<'a> { + // The llptr given is an alloca containing the variable's value + DirectVariable { alloca: ValueRef }, + // The llptr given is an alloca containing the start of some pointer chain + // leading to the variable's content. + IndirectVariable { alloca: ValueRef, address_operations: &'a [ValueRef] } +} + +enum VariableKind { + ArgumentVariable(uint /*index*/), + LocalVariable, + CapturedVariable, +} + +/// Create any deferred debug metadata nodes +pub fn finalize(cx: &CrateContext) { + if cx.dbg_cx().is_none() { + return; + } + + debug!("finalize"); + compile_unit_metadata(cx); + unsafe { + llvm::LLVMDIBuilderFinalize(DIB(cx)); + llvm::LLVMDIBuilderDispose(DIB(cx)); + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than OS X currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for OS X to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + if cx.sess().target.target.options.is_like_osx { + "Dwarf Version".with_c_str( + |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 2)); + } + + // Prevent bitcode readers from deleting the debug info. + "Debug Info Version".with_c_str( + |s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, + llvm::LLVMRustDebugMetadataVersion)); + }; +} + +/// Creates debug information for the given global variable. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_global_var_metadata(cx: &CrateContext, + node_id: ast::NodeId, + global: ValueRef) { + if cx.dbg_cx().is_none() { + return; + } + + // Don't create debuginfo for globals inlined from other crates. The other + // crate should already contain debuginfo for it. More importantly, the + // global might not even exist in un-inlined form anywhere which would lead + // to a linker errors. + if cx.external_srcs().borrow().contains_key(&node_id) { + return; + } + + let var_item = cx.tcx().map.get(node_id); + + let (ident, span) = match var_item { + ast_map::NodeItem(item) => { + match item.node { + ast::ItemStatic(..) => (item.ident, item.span), + ast::ItemConst(..) => (item.ident, item.span), + _ => { + cx.sess() + .span_bug(item.span, + format!("debuginfo::\ + create_global_var_metadata() - + Captured var-id refers to \ + unexpected ast_item variant: {}", + var_item).as_slice()) + } + } + }, + _ => cx.sess().bug(format!("debuginfo::create_global_var_metadata() \ + - Captured var-id refers to unexpected \ + ast_map variant: {}", + var_item).as_slice()) + }; + + let (file_metadata, line_number) = if span != codemap::DUMMY_SP { + let loc = span_start(cx, span); + (file_metadata(cx, loc.file.name.as_slice()), loc.line as c_uint) + } else { + (UNKNOWN_FILE_METADATA, UNKNOWN_LINE_NUMBER) + }; + + let is_local_to_unit = is_node_local_to_unit(cx, node_id); + let variable_type = ty::node_id_to_type(cx.tcx(), node_id); + let type_metadata = type_metadata(cx, variable_type, span); + let namespace_node = namespace_for_item(cx, ast_util::local_def(node_id)); + let var_name = token::get_ident(ident).get().to_string(); + let linkage_name = + namespace_node.mangled_name_of_contained_item(var_name.as_slice()); + let var_scope = namespace_node.scope; + + var_name.as_slice().with_c_str(|var_name| { + linkage_name.as_slice().with_c_str(|linkage_name| { + unsafe { + llvm::LLVMDIBuilderCreateStaticVariable(DIB(cx), + var_scope, + var_name, + linkage_name, + file_metadata, + line_number, + type_metadata, + is_local_to_unit, + global, + ptr::null_mut()); + } + }) + }); +} + +/// Creates debug information for the given local variable. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_local_var_metadata(bcx: Block, local: &ast::Local) { + if fn_should_be_ignored(bcx.fcx) { + return; + } + + let cx = bcx.ccx(); + let def_map = &cx.tcx().def_map; + + pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, path1| { + let var_ident = path1.node; + + let datum = match bcx.fcx.lllocals.borrow().get(&node_id).cloned() { + Some(datum) => datum, + None => { + bcx.sess().span_bug(span, + format!("no entry in lllocals table for {}", + node_id).as_slice()); + } + }; + + let scope_metadata = scope_metadata(bcx.fcx, node_id, span); + + declare_local(bcx, + var_ident, + datum.ty, + scope_metadata, + DirectVariable { alloca: datum.val }, + LocalVariable, + span); + }) +} + +/// Creates debug information for a variable captured in a closure. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_captured_var_metadata(bcx: Block, + node_id: ast::NodeId, + env_data_type: ty::t, + env_pointer: ValueRef, + env_index: uint, + closure_store: ty::TraitStore, + span: Span) { + if fn_should_be_ignored(bcx.fcx) { + return; + } + + let cx = bcx.ccx(); + + let ast_item = cx.tcx().map.find(node_id); + + let variable_ident = match ast_item { + None => { + cx.sess().span_bug(span, "debuginfo::create_captured_var_metadata: node not found"); + } + Some(ast_map::NodeLocal(pat)) | Some(ast_map::NodeArg(pat)) => { + match pat.node { + ast::PatIdent(_, ref path1, _) => { + path1.node + } + _ => { + cx.sess() + .span_bug(span, + format!( + "debuginfo::create_captured_var_metadata() - \ + Captured var-id refers to unexpected \ + ast_map variant: {}", + ast_item).as_slice()); + } + } + } + _ => { + cx.sess() + .span_bug(span, + format!("debuginfo::create_captured_var_metadata() - \ + Captured var-id refers to unexpected \ + ast_map variant: {}", + ast_item).as_slice()); + } + }; + + let variable_type = node_id_type(bcx, node_id); + let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata; + + let llvm_env_data_type = type_of::type_of(cx, env_data_type); + let byte_offset_of_var_in_env = machine::llelement_offset(cx, + llvm_env_data_type, + env_index); + + let address_operations = unsafe { + [llvm::LLVMDIBuilderCreateOpDeref(Type::i64(cx).to_ref()), + llvm::LLVMDIBuilderCreateOpPlus(Type::i64(cx).to_ref()), + C_i64(cx, byte_offset_of_var_in_env as i64), + llvm::LLVMDIBuilderCreateOpDeref(Type::i64(cx).to_ref())] + }; + + let address_op_count = match closure_store { + ty::RegionTraitStore(..) => { + address_operations.len() + } + ty::UniqTraitStore => { + address_operations.len() - 1 + } + }; + + let variable_access = IndirectVariable { + alloca: env_pointer, + address_operations: address_operations[..address_op_count] + }; + + declare_local(bcx, + variable_ident, + variable_type, + scope_metadata, + variable_access, + CapturedVariable, + span); +} + +/// Creates debug information for a local variable introduced in the head of a +/// match-statement arm. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_match_binding_metadata(bcx: Block, + variable_ident: ast::Ident, + binding: BindingInfo) { + if fn_should_be_ignored(bcx.fcx) { + return; + } + + let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span); + let aops = unsafe { + [llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type().to_ref())] + }; + // Regardless of the actual type (`T`) we're always passed the stack slot (alloca) + // for the binding. For ByRef bindings that's a `T*` but for ByMove bindings we + // actually have `T**`. So to get the actual variable we need to dereference once + // more. For ByCopy we just use the stack slot we created for the binding. + let var_type = match binding.trmode { + TrByCopy(llbinding) => DirectVariable { + alloca: llbinding + }, + TrByMove => IndirectVariable { + alloca: binding.llmatch, + address_operations: &aops + }, + TrByRef => DirectVariable { + alloca: binding.llmatch + } + }; + + declare_local(bcx, + variable_ident, + binding.ty, + scope_metadata, + var_type, + LocalVariable, + binding.span); +} + +/// Creates debug information for the given function argument. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_argument_metadata(bcx: Block, arg: &ast::Arg) { + if fn_should_be_ignored(bcx.fcx) { + return; + } + + let fcx = bcx.fcx; + let cx = fcx.ccx; + + let def_map = &cx.tcx().def_map; + let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata; + + pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, path1| { + let llarg = match bcx.fcx.lllocals.borrow().get(&node_id).cloned() { + Some(v) => v, + None => { + bcx.sess().span_bug(span, + format!("no entry in lllocals table for {}", + node_id).as_slice()); + } + }; + + if unsafe { llvm::LLVMIsAAllocaInst(llarg.val) } == ptr::null_mut() { + cx.sess().span_bug(span, "debuginfo::create_argument_metadata() - \ + Referenced variable location is not an alloca!"); + } + + let argument_index = { + let counter = &fcx.debug_context.get_ref(cx, span).argument_counter; + let argument_index = counter.get(); + counter.set(argument_index + 1); + argument_index + }; + + declare_local(bcx, + path1.node, + llarg.ty, + scope_metadata, + DirectVariable { alloca: llarg.val }, + ArgumentVariable(argument_index), + span); + }) +} + +pub fn get_cleanup_debug_loc_for_ast_node(node_id: ast::NodeId, + node_span: Span, + is_block: bool) + -> NodeInfo { + // A debug location needs two things: + // (1) A span (of which only the beginning will actually be used) + // (2) An AST node-id which will be used to look up the lexical scope + // for the location in the functions scope-map + // + // This function will calculate the debug location for compiler-generated + // cleanup calls that are executed when control-flow leaves the + // scope identified by `node_id`. + // + // For everything but block-like things we can simply take id and span of + // the given expression, meaning that from a debugger's view cleanup code is + // executed at the same source location as the statement/expr itself. + // + // Blocks are a special case. Here we want the cleanup to be linked to the + // closing curly brace of the block. The *scope* the cleanup is executed in + // is up to debate: It could either still be *within* the block being + // cleaned up, meaning that locals from the block are still visible in the + // debugger. + // Or it could be in the scope that the block is contained in, so any locals + // from within the block are already considered out-of-scope and thus not + // accessible in the debugger anymore. + // + // The current implementation opts for the second option: cleanup of a block + // already happens in the parent scope of the block. The main reason for + // this decision is that scoping becomes controlflow dependent when variable + // shadowing is involved and it's impossible to decide statically which + // scope is actually left when the cleanup code is executed. + // In practice it shouldn't make much of a difference. + + let cleanup_span = if is_block { + Span { + lo: node_span.hi - codemap::BytePos(1), // closing brace should always be 1 byte... + hi: node_span.hi, + expn_id: node_span.expn_id + } + } else { + node_span + }; + + NodeInfo { + id: node_id, + span: cleanup_span + } +} + +/// Sets the current debug location at the beginning of the span. +/// +/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id +/// parameter is used to reliably find the correct visibility scope for the code +/// position. +pub fn set_source_location(fcx: &FunctionContext, + node_id: ast::NodeId, + span: Span) { + match fcx.debug_context.repr { + DebugInfoDisabled => return, + FunctionWithoutDebugInfo => { + set_debug_location(fcx.ccx, UnknownLocation); + return; + } + DebugInfo(box ref function_debug_context) => { + let cx = fcx.ccx; + + debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); + + if function_debug_context.source_locations_enabled.get() { + let loc = span_start(cx, span); + let scope = scope_metadata(fcx, node_id, span); + + set_debug_location(cx, DebugLocation::new(scope, + loc.line, + loc.col.to_uint())); + } else { + set_debug_location(cx, UnknownLocation); + } + } + } +} + +/// Clears the current debug location. +/// +/// Instructions generated hereafter won't be assigned a source location. +pub fn clear_source_location(fcx: &FunctionContext) { + if fn_should_be_ignored(fcx) { + return; + } + + set_debug_location(fcx.ccx, UnknownLocation); +} + +/// Enables emitting source locations for the given functions. +/// +/// Since we don't want source locations to be emitted for the function prelude, +/// they are disabled when beginning to translate a new function. This functions +/// switches source location emitting on and must therefore be called before the +/// first real statement/expression of the function is translated. +pub fn start_emitting_source_locations(fcx: &FunctionContext) { + match fcx.debug_context.repr { + DebugInfo(box ref data) => { + data.source_locations_enabled.set(true) + }, + _ => { /* safe to ignore */ } + } +} + +/// Creates the function-specific debug context. +/// +/// Returns the FunctionDebugContext for the function which holds state needed +/// for debug info creation. The function may also return another variant of the +/// FunctionDebugContext enum which indicates why no debuginfo should be created +/// for the function. +pub fn create_function_debug_context(cx: &CrateContext, + fn_ast_id: ast::NodeId, + param_substs: ¶m_substs, + llfn: ValueRef) -> FunctionDebugContext { + if cx.sess().opts.debuginfo == NoDebugInfo { + return FunctionDebugContext { repr: DebugInfoDisabled }; + } + + // Clear the debug location so we don't assign them in the function prelude. + // Do this here already, in case we do an early exit from this function. + set_debug_location(cx, UnknownLocation); + + if fn_ast_id == ast::DUMMY_NODE_ID { + // This is a function not linked to any source location, so don't + // generate debuginfo for it. + return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; + } + + let empty_generics = ast_util::empty_generics(); + + let fnitem = cx.tcx().map.get(fn_ast_id); + + let (ident, fn_decl, generics, top_level_block, span, has_path) = match fnitem { + ast_map::NodeItem(ref item) => { + if contains_nodebug_attribute(item.attrs.as_slice()) { + return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; + } + + match item.node { + ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => { + (item.ident, &**fn_decl, generics, &**top_level_block, item.span, true) + } + _ => { + cx.sess().span_bug(item.span, + "create_function_debug_context: item bound to non-function"); + } + } + } + ast_map::NodeImplItem(ref item) => { + match **item { + ast::MethodImplItem(ref method) => { + if contains_nodebug_attribute(method.attrs.as_slice()) { + return FunctionDebugContext { + repr: FunctionWithoutDebugInfo + }; + } + + (method.pe_ident(), + method.pe_fn_decl(), + method.pe_generics(), + method.pe_body(), + method.span, + true) + } + ast::TypeImplItem(ref typedef) => { + cx.sess().span_bug(typedef.span, + "create_function_debug_context() \ + called on associated type?!") + } + } + } + ast_map::NodeExpr(ref expr) => { + match expr.node { + ast::ExprFnBlock(_, ref fn_decl, ref top_level_block) | + ast::ExprProc(ref fn_decl, ref top_level_block) | + ast::ExprUnboxedFn(_, _, ref fn_decl, ref top_level_block) => { + let name = format!("fn{}", token::gensym("fn")); + let name = token::str_to_ident(name.as_slice()); + (name, &**fn_decl, + // This is not quite right. It should actually inherit + // the generics of the enclosing function. + &empty_generics, + &**top_level_block, + expr.span, + // Don't try to lookup the item path: + false) + } + _ => cx.sess().span_bug(expr.span, + "create_function_debug_context: expected an expr_fn_block here") + } + } + ast_map::NodeTraitItem(ref trait_method) => { + match **trait_method { + ast::ProvidedMethod(ref method) => { + if contains_nodebug_attribute(method.attrs.as_slice()) { + return FunctionDebugContext { + repr: FunctionWithoutDebugInfo + }; + } + + (method.pe_ident(), + method.pe_fn_decl(), + method.pe_generics(), + method.pe_body(), + method.span, + true) + } + _ => { + cx.sess() + .bug(format!("create_function_debug_context: \ + unexpected sort of node: {}", + fnitem).as_slice()) + } + } + } + ast_map::NodeForeignItem(..) | + ast_map::NodeVariant(..) | + ast_map::NodeStructCtor(..) => { + return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; + } + _ => cx.sess().bug(format!("create_function_debug_context: \ + unexpected sort of node: {}", + fnitem).as_slice()) + }; + + // This can be the case for functions inlined from another crate + if span == codemap::DUMMY_SP { + return FunctionDebugContext { repr: FunctionWithoutDebugInfo }; + } + + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, loc.file.name.as_slice()); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(cx, + fn_ast_id, + &*fn_decl, + param_substs, + span); + llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) + }; + + // Get_template_parameters() will append a `<...>` clause to the function + // name if necessary. + let mut function_name = String::from_str(token::get_ident(ident).get()); + let template_parameters = get_template_parameters(cx, + generics, + param_substs, + file_metadata, + &mut function_name); + + // There is no ast_map::Path for ast::ExprFnBlock-type functions. For now, + // just don't put them into a namespace. In the future this could be improved + // somehow (storing a path in the ast_map, or construct a path using the + // enclosing function). + let (linkage_name, containing_scope) = if has_path { + let namespace_node = namespace_for_item(cx, ast_util::local_def(fn_ast_id)); + let linkage_name = namespace_node.mangled_name_of_contained_item( + function_name.as_slice()); + let containing_scope = namespace_node.scope; + (linkage_name, containing_scope) + } else { + (function_name.as_slice().to_string(), file_metadata) + }; + + // Clang sets this parameter to the opening brace of the function's block, + // so let's do this too. + let scope_line = span_start(cx, top_level_block.span).line; + + let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id); + + let fn_metadata = function_name.as_slice().with_c_str(|function_name| { + linkage_name.as_slice().with_c_str(|linkage_name| { + unsafe { + llvm::LLVMDIBuilderCreateFunction( + DIB(cx), + containing_scope, + function_name, + linkage_name, + file_metadata, + loc.line as c_uint, + function_type_metadata, + is_local_to_unit, + true, + scope_line as c_uint, + FlagPrototyped as c_uint, + cx.sess().opts.optimize != config::No, + llfn, + template_parameters, + ptr::null_mut()) + } + }) + }); + + // Initialize fn debug context (including scope map and namespace map) + let fn_debug_context = box FunctionDebugContextData { + scope_map: RefCell::new(NodeMap::new()), + fn_metadata: fn_metadata, + argument_counter: Cell::new(1), + source_locations_enabled: Cell::new(false), + }; + + populate_scope_map(cx, + fn_decl.inputs.as_slice(), + &*top_level_block, + fn_metadata, + fn_ast_id, + &mut *fn_debug_context.scope_map.borrow_mut()); + + return FunctionDebugContext { repr: DebugInfo(fn_debug_context) }; + + fn get_function_signature(cx: &CrateContext, + fn_ast_id: ast::NodeId, + fn_decl: &ast::FnDecl, + param_substs: ¶m_substs, + error_reporting_span: Span) -> DIArray { + if cx.sess().opts.debuginfo == LimitedDebugInfo { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(fn_decl.inputs.len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + match fn_decl.output { + ast::Return(ref ret_ty) if ret_ty.node == ast::TyTup(vec![]) => + signature.push(ptr::null_mut()), + _ => { + assert_type_for_node_id(cx, fn_ast_id, error_reporting_span); + + let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id); + let return_type = return_type.substp(cx.tcx(), param_substs); + signature.push(type_metadata(cx, return_type, codemap::DUMMY_SP)); + } + } + + // Arguments types + for arg in fn_decl.inputs.iter() { + assert_type_for_node_id(cx, arg.pat.id, arg.pat.span); + let arg_type = ty::node_id_to_type(cx.tcx(), arg.pat.id); + let arg_type = arg_type.substp(cx.tcx(), param_substs); + signature.push(type_metadata(cx, arg_type, codemap::DUMMY_SP)); + } + + return create_DIArray(DIB(cx), signature.as_slice()); + } + + fn get_template_parameters(cx: &CrateContext, + generics: &ast::Generics, + param_substs: ¶m_substs, + file_metadata: DIFile, + name_to_append_suffix_to: &mut String) + -> DIArray { + let self_type = param_substs.substs.self_ty(); + + // Only true for static default methods: + let has_self_type = self_type.is_some(); + + if !generics.is_type_parameterized() && !has_self_type { + return create_DIArray(DIB(cx), &[]); + } + + name_to_append_suffix_to.push('<'); + + // The list to be filled with template parameters: + let mut template_params: Vec = + Vec::with_capacity(generics.ty_params.len() + 1); + + // Handle self type + if has_self_type { + let actual_self_type = self_type.unwrap(); + // Add self type name to <...> clause of function name + let actual_self_type_name = compute_debuginfo_type_name( + cx, + actual_self_type, + true); + + name_to_append_suffix_to.push_str(actual_self_type_name.as_slice()); + + if generics.is_type_parameterized() { + name_to_append_suffix_to.push_str(","); + } + + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == FullDebugInfo { + let actual_self_type_metadata = type_metadata(cx, + actual_self_type, + codemap::DUMMY_SP); + + let ident = special_idents::type_self; + + let param_metadata = token::get_ident(ident).get() + .with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateTemplateTypeParameter( + DIB(cx), + file_metadata, + name, + actual_self_type_metadata, + ptr::null_mut(), + 0, + 0) + } + }); + + template_params.push(param_metadata); + } + } + + // Handle other generic parameters + let actual_types = param_substs.substs.types.get_slice(subst::FnSpace); + for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() { + let actual_type = actual_types[index]; + // Add actual type name to <...> clause of function name + let actual_type_name = compute_debuginfo_type_name(cx, + actual_type, + true); + name_to_append_suffix_to.push_str(actual_type_name.as_slice()); + + if index != generics.ty_params.len() - 1 { + name_to_append_suffix_to.push_str(","); + } + + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == FullDebugInfo { + let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP); + let param_metadata = token::get_ident(ident).get() + .with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateTemplateTypeParameter( + DIB(cx), + file_metadata, + name, + actual_type_metadata, + ptr::null_mut(), + 0, + 0) + } + }); + template_params.push(param_metadata); + } + } + + name_to_append_suffix_to.push('>'); + + return create_DIArray(DIB(cx), template_params.as_slice()); + } +} + +//=----------------------------------------------------------------------------- +// Module-Internal debug info creation functions +//=----------------------------------------------------------------------------- + +fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool +{ + // The is_local_to_unit flag indicates whether a function is local to the + // current compilation unit (i.e. if it is *static* in the C-sense). The + // *reachable* set should provide a good approximation of this, as it + // contains everything that might leak out of the current crate (by being + // externally visible or by being inlined into something externally visible). + // It might better to use the `exported_items` set from `driver::CrateAnalysis` + // in the future, but (atm) this set is not available in the translation pass. + !cx.reachable().contains(&node_id) +} + +#[allow(non_snake_case)] +fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { + return unsafe { + llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) + }; +} + +fn compile_unit_metadata(cx: &CrateContext) { + let work_dir = &cx.sess().working_dir; + let compile_unit_name = match cx.sess().local_crate_source_file { + None => fallback_path(cx), + Some(ref abs_path) => { + if abs_path.is_relative() { + cx.sess().warn("debuginfo: Invalid path to crate's local root source file!"); + fallback_path(cx) + } else { + match abs_path.path_relative_from(work_dir) { + Some(ref p) if p.is_relative() => { + // prepend "./" if necessary + let dotdot = b".."; + let prefix = &[dotdot[0], ::std::path::SEP_BYTE]; + let mut path_bytes = p.as_vec().to_vec(); + + if path_bytes.slice_to(2) != prefix && + path_bytes.slice_to(2) != dotdot { + path_bytes.insert(0, prefix[0]); + path_bytes.insert(1, prefix[1]); + } + + path_bytes.as_slice().to_c_str() + } + _ => fallback_path(cx) + } + } + } + }; + + debug!("compile_unit_metadata: {}", compile_unit_name); + let producer = format!("rustc version {}", + (option_env!("CFG_VERSION")).expect("CFG_VERSION")); + + let compile_unit_name = compile_unit_name.as_ptr(); + work_dir.as_vec().with_c_str(|work_dir| { + producer.with_c_str(|producer| { + "".with_c_str(|flags| { + "".with_c_str(|split_name| { + unsafe { + llvm::LLVMDIBuilderCreateCompileUnit( + debug_context(cx).builder, + DW_LANG_RUST, + compile_unit_name, + work_dir, + producer, + cx.sess().opts.optimize != config::No, + flags, + 0, + split_name); + } + }) + }) + }) + }); + + fn fallback_path(cx: &CrateContext) -> CString { + cx.link_meta().crate_name.as_slice().to_c_str() + } +} + +fn declare_local(bcx: Block, + variable_ident: ast::Ident, + variable_type: ty::t, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { + let cx: &CrateContext = bcx.ccx(); + + let filename = span_start(cx, span).file.name.clone(); + let file_metadata = file_metadata(cx, filename.as_slice()); + + let name = token::get_ident(variable_ident); + let loc = span_start(cx, span); + let type_metadata = type_metadata(cx, variable_type, span); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable | + CapturedVariable => (0, DW_TAG_auto_variable) + }; + + let (var_alloca, var_metadata) = name.get().with_c_str(|name| { + match variable_access { + DirectVariable { alloca } => ( + alloca, + unsafe { + llvm::LLVMDIBuilderCreateLocalVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name, + file_metadata, + loc.line as c_uint, + type_metadata, + cx.sess().opts.optimize != config::No, + 0, + argument_index) + } + ), + IndirectVariable { alloca, address_operations } => ( + alloca, + unsafe { + llvm::LLVMDIBuilderCreateComplexVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name, + file_metadata, + loc.line as c_uint, + type_metadata, + address_operations.as_ptr(), + address_operations.len() as c_uint, + argument_index) + } + ) + } + }); + + set_debug_location(cx, DebugLocation::new(scope_metadata, + loc.line, + loc.col.to_uint())); + unsafe { + let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( + DIB(cx), + var_alloca, + var_metadata, + bcx.llbb); + + llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); + } + + match variable_kind { + ArgumentVariable(_) | CapturedVariable => { + assert!(!bcx.fcx + .debug_context + .get_ref(cx, span) + .source_locations_enabled + .get()); + set_debug_location(cx, UnknownLocation); + } + _ => { /* nothing to do */ } + } +} + +fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile { + match debug_context(cx).created_files.borrow().get(full_path) { + Some(file_metadata) => return *file_metadata, + None => () + } + + debug!("file_metadata: {}", full_path); + + // FIXME (#9639): This needs to handle non-utf8 paths + let work_dir = cx.sess().working_dir.as_str().unwrap(); + let file_name = + if full_path.starts_with(work_dir) { + full_path.slice(work_dir.len() + 1u, full_path.len()) + } else { + full_path + }; + + let file_metadata = + file_name.with_c_str(|file_name| { + work_dir.with_c_str(|work_dir| { + unsafe { + llvm::LLVMDIBuilderCreateFile(DIB(cx), file_name, work_dir) + } + }) + }); + + let mut created_files = debug_context(cx).created_files.borrow_mut(); + created_files.insert(full_path.to_string(), file_metadata); + return file_metadata; +} + +/// Finds the scope metadata node for the given AST node. +fn scope_metadata(fcx: &FunctionContext, + node_id: ast::NodeId, + error_reporting_span: Span) + -> DIScope { + let scope_map = &fcx.debug_context + .get_ref(fcx.ccx, error_reporting_span) + .scope_map; + match scope_map.borrow().get(&node_id).cloned() { + Some(scope_metadata) => scope_metadata, + None => { + let node = fcx.ccx.tcx().map.get(node_id); + + fcx.ccx.sess().span_bug(error_reporting_span, + format!("debuginfo: Could not find scope info for node {}", + node).as_slice()); + } + } +} + +fn diverging_type_metadata(cx: &CrateContext) -> DIType { + "!".with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateBasicType( + DIB(cx), + name, + bytes_to_bits(0), + bytes_to_bits(0), + DW_ATE_unsigned) + } + }) +} + +fn basic_type_metadata(cx: &CrateContext, t: ty::t) -> DIType { + + debug!("basic_type_metadata: {}", ty::get(t)); + + let (name, encoding) = match ty::get(t).sty { + ty::ty_tup(ref elements) if elements.is_empty() => + ("()".to_string(), DW_ATE_unsigned), + ty::ty_bool => ("bool".to_string(), DW_ATE_boolean), + ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char), + ty::ty_int(int_ty) => match int_ty { + ast::TyI => ("int".to_string(), DW_ATE_signed), + ast::TyI8 => ("i8".to_string(), DW_ATE_signed), + ast::TyI16 => ("i16".to_string(), DW_ATE_signed), + ast::TyI32 => ("i32".to_string(), DW_ATE_signed), + ast::TyI64 => ("i64".to_string(), DW_ATE_signed) + }, + ty::ty_uint(uint_ty) => match uint_ty { + ast::TyU => ("uint".to_string(), DW_ATE_unsigned), + ast::TyU8 => ("u8".to_string(), DW_ATE_unsigned), + ast::TyU16 => ("u16".to_string(), DW_ATE_unsigned), + ast::TyU32 => ("u32".to_string(), DW_ATE_unsigned), + ast::TyU64 => ("u64".to_string(), DW_ATE_unsigned) + }, + ty::ty_float(float_ty) => match float_ty { + ast::TyF32 => ("f32".to_string(), DW_ATE_float), + ast::TyF64 => ("f64".to_string(), DW_ATE_float), + }, + _ => cx.sess().bug("debuginfo::basic_type_metadata - t is invalid type") + }; + + let llvm_type = type_of::type_of(cx, t); + let (size, align) = size_and_align_of(cx, llvm_type); + let ty_metadata = name.with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateBasicType( + DIB(cx), + name, + bytes_to_bits(size), + bytes_to_bits(align), + encoding) + } + }); + + return ty_metadata; +} + +fn pointer_type_metadata(cx: &CrateContext, + pointer_type: ty::t, + pointee_type_metadata: DIType) + -> DIType { + let pointer_llvm_type = type_of::type_of(cx, pointer_type); + let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); + let name = compute_debuginfo_type_name(cx, pointer_type, false); + let ptr_metadata = name.as_slice().with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + bytes_to_bits(pointer_size), + bytes_to_bits(pointer_align), + name) + } + }); + return ptr_metadata; +} + +//=----------------------------------------------------------------------------- +// Common facilities for record-like types (structs, enums, tuples) +//=----------------------------------------------------------------------------- + +enum MemberOffset { + FixedMemberOffset { bytes: uint }, + // For ComputedMemberOffset, the offset is read from the llvm type definition + ComputedMemberOffset +} + +// Description of a type member, which can either be a regular field (as in +// structs or tuples) or an enum variant +struct MemberDescription { + name: String, + llvm_type: Type, + type_metadata: DIType, + offset: MemberOffset, + flags: c_uint +} + +// A factory for MemberDescriptions. It produces a list of member descriptions +// for some record-like type. MemberDescriptionFactories are used to defer the +// creation of type member descriptions in order to break cycles arising from +// recursive type definitions. +enum MemberDescriptionFactory { + StructMDF(StructMemberDescriptionFactory), + TupleMDF(TupleMemberDescriptionFactory), + EnumMDF(EnumMemberDescriptionFactory), + VariantMDF(VariantMemberDescriptionFactory) +} + +impl MemberDescriptionFactory { + fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { + match *self { + StructMDF(ref this) => { + this.create_member_descriptions(cx) + } + TupleMDF(ref this) => { + this.create_member_descriptions(cx) + } + EnumMDF(ref this) => { + this.create_member_descriptions(cx) + } + VariantMDF(ref this) => { + this.create_member_descriptions(cx) + } + } + } +} + +// A description of some recursive type. It can either be already finished (as +// with FinalMetadata) or it is not yet finished, but contains all information +// needed to generate the missing parts of the description. See the documentation +// section on Recursive Types at the top of this file for more information. +enum RecursiveTypeDescription { + UnfinishedMetadata { + unfinished_type: ty::t, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + llvm_type: Type, + member_description_factory: MemberDescriptionFactory, + }, + FinalMetadata(DICompositeType) +} + +fn create_and_register_recursive_type_forward_declaration( + cx: &CrateContext, + unfinished_type: ty::t, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + llvm_type: Type, + member_description_factory: MemberDescriptionFactory) + -> RecursiveTypeDescription { + + // Insert the stub into the TypeMap in order to allow for recursive references + let mut type_map = debug_context(cx).type_map.borrow_mut(); + type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata_stub); + type_map.register_type_with_metadata(cx, unfinished_type, metadata_stub); + + UnfinishedMetadata { + unfinished_type: unfinished_type, + unique_type_id: unique_type_id, + metadata_stub: metadata_stub, + llvm_type: llvm_type, + member_description_factory: member_description_factory, + } +} + +impl RecursiveTypeDescription { + // Finishes up the description of the type in question (mostly by providing + // descriptions of the fields of the given type) and returns the final type metadata. + fn finalize(&self, cx: &CrateContext) -> MetadataCreationResult { + match *self { + FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + llvm_type, + ref member_description_factory, + .. + } => { + // Make sure that we have a forward declaration of the type in + // the TypeMap so that recursive references are possible. This + // will always be the case if the RecursiveTypeDescription has + // been properly created through the + // create_and_register_recursive_type_forward_declaration() function. + { + let type_map = debug_context(cx).type_map.borrow(); + if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || + type_map.find_metadata_for_type(unfinished_type).is_none() { + cx.sess().bug(format!("Forward declaration of potentially recursive type \ + '{}' was not found in TypeMap!", + ppaux::ty_to_string(cx.tcx(), unfinished_type)) + .as_slice()); + } + } + + // ... then create the member descriptions ... + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + // ... and attach them to the stub to complete it. + set_members_of_composite_type(cx, + metadata_stub, + llvm_type, + member_descriptions.as_slice()); + return MetadataCreationResult::new(metadata_stub, true); + } + } + } +} + + +//=----------------------------------------------------------------------------- +// Structs +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a struct +struct StructMemberDescriptionFactory { + fields: Vec, + is_simd: bool, + span: Span, +} + +impl StructMemberDescriptionFactory { + fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { + if self.fields.len() == 0 { + return Vec::new(); + } + + let field_size = if self.is_simd { + machine::llsize_of_alloc(cx, type_of::type_of(cx, self.fields[0].mt.ty)) as uint + } else { + 0xdeadbeef + }; + + self.fields.iter().enumerate().map(|(i, field)| { + let name = if field.name == special_idents::unnamed_field.name { + "".to_string() + } else { + token::get_name(field.name).get().to_string() + }; + + let offset = if self.is_simd { + assert!(field_size != 0xdeadbeef); + FixedMemberOffset { bytes: i * field_size } + } else { + ComputedMemberOffset + }; + + MemberDescription { + name: name, + llvm_type: type_of::type_of(cx, field.mt.ty), + type_metadata: type_metadata(cx, field.mt.ty, self.span), + offset: offset, + flags: FLAGS_NONE, + } + }).collect() + } +} + + +fn prepare_struct_metadata(cx: &CrateContext, + struct_type: ty::t, + def_id: ast::DefId, + substs: &subst::Substs, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription { + let struct_name = compute_debuginfo_type_name(cx, struct_type, false); + let struct_llvm_type = type_of::type_of(cx, struct_type); + + let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); + + let struct_metadata_stub = create_struct_stub(cx, + struct_llvm_type, + struct_name.as_slice(), + unique_type_id, + containing_scope); + + let fields = ty::struct_fields(cx.tcx(), def_id, substs); + + create_and_register_recursive_type_forward_declaration( + cx, + struct_type, + unique_type_id, + struct_metadata_stub, + struct_llvm_type, + StructMDF(StructMemberDescriptionFactory { + fields: fields, + is_simd: ty::type_is_simd(cx.tcx(), struct_type), + span: span, + }) + ) +} + + +//=----------------------------------------------------------------------------- +// Tuples +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a tuple +struct TupleMemberDescriptionFactory { + component_types: Vec , + span: Span, +} + +impl TupleMemberDescriptionFactory { + fn create_member_descriptions(&self, cx: &CrateContext) + -> Vec { + self.component_types.iter().map(|&component_type| { + MemberDescription { + name: "".to_string(), + llvm_type: type_of::type_of(cx, component_type), + type_metadata: type_metadata(cx, component_type, self.span), + offset: ComputedMemberOffset, + flags: FLAGS_NONE, + } + }).collect() + } +} + +fn prepare_tuple_metadata(cx: &CrateContext, + tuple_type: ty::t, + component_types: &[ty::t], + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription { + let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); + let tuple_llvm_type = type_of::type_of(cx, tuple_type); + + create_and_register_recursive_type_forward_declaration( + cx, + tuple_type, + unique_type_id, + create_struct_stub(cx, + tuple_llvm_type, + tuple_name.as_slice(), + unique_type_id, + UNKNOWN_SCOPE_METADATA), + tuple_llvm_type, + TupleMDF(TupleMemberDescriptionFactory { + component_types: component_types.to_vec(), + span: span, + }) + ) +} + + +//=----------------------------------------------------------------------------- +// Enums +//=----------------------------------------------------------------------------- + +// Describes the members of an enum value: An enum is described as a union of +// structs in DWARF. This MemberDescriptionFactory provides the description for +// the members of this union; so for every variant of the given enum, this factory +// will produce one MemberDescription (all with no name and a fixed offset of +// zero bytes). +struct EnumMemberDescriptionFactory { + enum_type: ty::t, + type_rep: Rc, + variants: Rc>>, + discriminant_type_metadata: Option, + containing_scope: DIScope, + file_metadata: DIFile, + span: Span, +} + +impl EnumMemberDescriptionFactory { + fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { + match *self.type_rep { + adt::General(_, ref struct_defs, _) => { + let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata + .expect("")); + + struct_defs + .iter() + .enumerate() + .map(|(i, struct_def)| { + let (variant_type_metadata, + variant_llvm_type, + member_desc_factory) = + describe_enum_variant(cx, + self.enum_type, + struct_def, + &*(*self.variants)[i], + discriminant_info, + self.containing_scope, + self.span); + + let member_descriptions = member_desc_factory + .create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + variant_llvm_type, + member_descriptions.as_slice()); + MemberDescription { + name: "".to_string(), + llvm_type: variant_llvm_type, + type_metadata: variant_type_metadata, + offset: FixedMemberOffset { bytes: 0 }, + flags: FLAGS_NONE + } + }).collect() + }, + adt::Univariant(ref struct_def, _) => { + assert!(self.variants.len() <= 1); + + if self.variants.len() == 0 { + vec![] + } else { + let (variant_type_metadata, + variant_llvm_type, + member_description_factory) = + describe_enum_variant(cx, + self.enum_type, + struct_def, + &*(*self.variants)[0], + NoDiscriminant, + self.containing_scope, + self.span); + + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + variant_llvm_type, + member_descriptions.as_slice()); + vec![ + MemberDescription { + name: "".to_string(), + llvm_type: variant_llvm_type, + type_metadata: variant_type_metadata, + offset: FixedMemberOffset { bytes: 0 }, + flags: FLAGS_NONE + } + ] + } + } + adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => { + // As far as debuginfo is concerned, the pointer this enum + // represents is still wrapped in a struct. This is to make the + // DWARF representation of enums uniform. + + // First create a description of the artificial wrapper struct: + let non_null_variant = &(*self.variants)[non_null_variant_index as uint]; + let non_null_variant_name = token::get_name(non_null_variant.name); + + // The llvm type and metadata of the pointer + let non_null_llvm_type = type_of::type_of(cx, nnty); + let non_null_type_metadata = type_metadata(cx, nnty, self.span); + + // The type of the artificial struct wrapping the pointer + let artificial_struct_llvm_type = Type::struct_(cx, + &[non_null_llvm_type], + false); + + // For the metadata of the wrapper struct, we need to create a + // MemberDescription of the struct's single field. + let sole_struct_member_description = MemberDescription { + name: match non_null_variant.arg_names { + Some(ref names) => token::get_ident(names[0]).get().to_string(), + None => "".to_string() + }, + llvm_type: non_null_llvm_type, + type_metadata: non_null_type_metadata, + offset: FixedMemberOffset { bytes: 0 }, + flags: FLAGS_NONE + }; + + let unique_type_id = debug_context(cx).type_map + .borrow_mut() + .get_unique_type_id_of_enum_variant( + cx, + self.enum_type, + non_null_variant_name.get()); + + // Now we can create the metadata of the artificial struct + let artificial_struct_metadata = + composite_type_metadata(cx, + artificial_struct_llvm_type, + non_null_variant_name.get(), + unique_type_id, + &[sole_struct_member_description], + self.containing_scope, + self.file_metadata, + codemap::DUMMY_SP); + + // Encode the information about the null variant in the union + // member's name. + let null_variant_index = (1 - non_null_variant_index) as uint; + let null_variant_name = token::get_name((*self.variants)[null_variant_index].name); + let union_member_name = format!("RUST$ENCODED$ENUM${}${}", + 0u, + null_variant_name); + + // Finally create the (singleton) list of descriptions of union + // members. + vec![ + MemberDescription { + name: union_member_name, + llvm_type: artificial_struct_llvm_type, + type_metadata: artificial_struct_metadata, + offset: FixedMemberOffset { bytes: 0 }, + flags: FLAGS_NONE + } + ] + }, + adt::StructWrappedNullablePointer { nonnull: ref struct_def, + nndiscr, + ptrfield, ..} => { + // Create a description of the non-null variant + let (variant_type_metadata, variant_llvm_type, member_description_factory) = + describe_enum_variant(cx, + self.enum_type, + struct_def, + &*(*self.variants)[nndiscr as uint], + OptimizedDiscriminant(ptrfield), + self.containing_scope, + self.span); + + let variant_member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + variant_llvm_type, + variant_member_descriptions.as_slice()); + + // Encode the information about the null variant in the union + // member's name. + let null_variant_index = (1 - nndiscr) as uint; + let null_variant_name = token::get_name((*self.variants)[null_variant_index].name); + let discrfield = match ptrfield { + adt::ThinPointer(field) => format!("{}", field), + adt::FatPointer(field) => format!("{}", field) + }; + let union_member_name = format!("RUST$ENCODED$ENUM${}${}", + discrfield, + null_variant_name); + + // Create the (singleton) list of descriptions of union members. + vec![ + MemberDescription { + name: union_member_name, + llvm_type: variant_llvm_type, + type_metadata: variant_type_metadata, + offset: FixedMemberOffset { bytes: 0 }, + flags: FLAGS_NONE + } + ] + }, + adt::CEnum(..) => cx.sess().span_bug(self.span, "This should be unreachable.") + } + } +} + +// Creates MemberDescriptions for the fields of a single enum variant. +struct VariantMemberDescriptionFactory { + args: Vec<(String, ty::t)> , + discriminant_type_metadata: Option, + span: Span, +} + +impl VariantMemberDescriptionFactory { + fn create_member_descriptions(&self, cx: &CrateContext) -> Vec { + self.args.iter().enumerate().map(|(i, &(ref name, ty))| { + MemberDescription { + name: name.to_string(), + llvm_type: type_of::type_of(cx, ty), + type_metadata: match self.discriminant_type_metadata { + Some(metadata) if i == 0 => metadata, + _ => type_metadata(cx, ty, self.span) + }, + offset: ComputedMemberOffset, + flags: FLAGS_NONE + } + }).collect() + } +} + +enum EnumDiscriminantInfo { + RegularDiscriminant(DIType), + OptimizedDiscriminant(adt::PointerField), + NoDiscriminant +} + +// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type +// of the variant, and (3) a MemberDescriptionFactory for producing the +// descriptions of the fields of the variant. This is a rudimentary version of a +// full RecursiveTypeDescription. +fn describe_enum_variant(cx: &CrateContext, + enum_type: ty::t, + struct_def: &adt::Struct, + variant_info: &ty::VariantInfo, + discriminant_info: EnumDiscriminantInfo, + containing_scope: DIScope, + span: Span) + -> (DICompositeType, Type, MemberDescriptionFactory) { + let variant_llvm_type = + Type::struct_(cx, struct_def.fields + .iter() + .map(|&t| type_of::type_of(cx, t)) + .collect::>() + .as_slice(), + struct_def.packed); + // Could do some consistency checks here: size, align, field count, discr type + + let variant_name = token::get_name(variant_info.name); + let variant_name = variant_name.get(); + let unique_type_id = debug_context(cx).type_map + .borrow_mut() + .get_unique_type_id_of_enum_variant( + cx, + enum_type, + variant_name); + + let metadata_stub = create_struct_stub(cx, + variant_llvm_type, + variant_name, + unique_type_id, + containing_scope); + + // Get the argument names from the enum variant info + let mut arg_names: Vec<_> = match variant_info.arg_names { + Some(ref names) => { + names.iter() + .map(|ident| { + token::get_ident(*ident).get().to_string().into_string() + }).collect() + } + None => variant_info.args.iter().map(|_| "".to_string()).collect() + }; + + // If this is not a univariant enum, there is also the discriminant field. + match discriminant_info { + RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), + _ => { /* do nothing */ } + }; + + // Build an array of (field name, field type) pairs to be captured in the factory closure. + let args: Vec<(String, ty::t)> = arg_names.iter() + .zip(struct_def.fields.iter()) + .map(|(s, &t)| (s.to_string(), t)) + .collect(); + + let member_description_factory = + VariantMDF(VariantMemberDescriptionFactory { + args: args, + discriminant_type_metadata: match discriminant_info { + RegularDiscriminant(discriminant_type_metadata) => { + Some(discriminant_type_metadata) + } + _ => None + }, + span: span, + }); + + (metadata_stub, variant_llvm_type, member_description_factory) +} + +fn prepare_enum_metadata(cx: &CrateContext, + enum_type: ty::t, + enum_def_id: ast::DefId, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription { + let enum_name = compute_debuginfo_type_name(cx, enum_type, false); + + let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, enum_def_id); + let loc = span_start(cx, definition_span); + let file_metadata = file_metadata(cx, loc.file.name.as_slice()); + + let variants = ty::enum_variants(cx.tcx(), enum_def_id); + + let enumerators_metadata: Vec = variants + .iter() + .map(|v| { + token::get_name(v.name).get().with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateEnumerator( + DIB(cx), + name, + v.disr_val as u64) + } + }) + }) + .collect(); + + let discriminant_type_metadata = |inttype| { + // We can reuse the type of the discriminant for all monomorphized + // instances of an enum because it doesn't depend on any type parameters. + // The def_id, uniquely identifying the enum's polytype acts as key in + // this cache. + let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types + .borrow() + .get(&enum_def_id).cloned(); + match cached_discriminant_type_metadata { + Some(discriminant_type_metadata) => discriminant_type_metadata, + None => { + let discriminant_llvm_type = adt::ll_inttype(cx, inttype); + let (discriminant_size, discriminant_align) = + size_and_align_of(cx, discriminant_llvm_type); + let discriminant_base_type_metadata = type_metadata(cx, + adt::ty_of_inttype(inttype), + codemap::DUMMY_SP); + let discriminant_name = get_enum_discriminant_name(cx, enum_def_id); + + let discriminant_type_metadata = discriminant_name.get().with_c_str(|name| { + unsafe { + llvm::LLVMDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + name, + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(discriminant_size), + bytes_to_bits(discriminant_align), + create_DIArray(DIB(cx), enumerators_metadata.as_slice()), + discriminant_base_type_metadata) + } + }); + + debug_context(cx).created_enum_disr_types + .borrow_mut() + .insert(enum_def_id, discriminant_type_metadata); + + discriminant_type_metadata + } + } + }; + + let type_rep = adt::represent_type(cx, enum_type); + + let discriminant_type_metadata = match *type_rep { + adt::CEnum(inttype, _, _) => { + return FinalMetadata(discriminant_type_metadata(inttype)) + }, + adt::RawNullablePointer { .. } | + adt::StructWrappedNullablePointer { .. } | + adt::Univariant(..) => None, + adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)), + }; + + let enum_llvm_type = type_of::type_of(cx, enum_type); + let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); + + let unique_type_id_str = debug_context(cx) + .type_map + .borrow() + .get_unique_type_id_as_string(unique_type_id); + + let enum_metadata = enum_name.as_slice().with_c_str(|enum_name| { + unique_type_id_str.as_slice().with_c_str(|unique_type_id_str| { + unsafe { + llvm::LLVMDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + enum_name, + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(enum_type_size), + bytes_to_bits(enum_type_align), + 0, // Flags + ptr::null_mut(), + 0, // RuntimeLang + unique_type_id_str) + } + }) + }); + + return create_and_register_recursive_type_forward_declaration( + cx, + enum_type, + unique_type_id, + enum_metadata, + enum_llvm_type, + EnumMDF(EnumMemberDescriptionFactory { + enum_type: enum_type, + type_rep: type_rep.clone(), + variants: variants, + discriminant_type_metadata: discriminant_type_metadata, + containing_scope: containing_scope, + file_metadata: file_metadata, + span: span, + }), + ); + + fn get_enum_discriminant_name(cx: &CrateContext, + def_id: ast::DefId) + -> token::InternedString { + let name = if def_id.krate == ast::LOCAL_CRATE { + cx.tcx().map.get_path_elem(def_id.node).name() + } else { + csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name() + }; + + token::get_name(name) + } +} + +/// Creates debug information for a composite type, that is, anything that +/// results in a LLVM struct. +/// +/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. +fn composite_type_metadata(cx: &CrateContext, + composite_llvm_type: Type, + composite_type_name: &str, + composite_type_unique_id: UniqueTypeId, + member_descriptions: &[MemberDescription], + containing_scope: DIScope, + + // Ignore source location information as long as it + // can't be reconstructed for non-local crates. + _file_metadata: DIFile, + _definition_span: Span) + -> DICompositeType { + // Create the (empty) struct metadata node ... + let composite_type_metadata = create_struct_stub(cx, + composite_llvm_type, + composite_type_name, + composite_type_unique_id, + containing_scope); + // ... and immediately create and add the member descriptions. + set_members_of_composite_type(cx, + composite_type_metadata, + composite_llvm_type, + member_descriptions); + + return composite_type_metadata; +} + +fn set_members_of_composite_type(cx: &CrateContext, + composite_type_metadata: DICompositeType, + composite_llvm_type: Type, + member_descriptions: &[MemberDescription]) { + // In some rare cases LLVM metadata uniquing would lead to an existing type + // description being used instead of a new one created in create_struct_stub. + // This would cause a hard to trace assertion in DICompositeType::SetTypeArray(). + // The following check makes sure that we get a better error message if this + // should happen again due to some regression. + { + let mut composite_types_completed = + debug_context(cx).composite_types_completed.borrow_mut(); + if composite_types_completed.contains(&composite_type_metadata) { + let (llvm_version_major, llvm_version_minor) = unsafe { + (llvm::LLVMVersionMajor(), llvm::LLVMVersionMinor()) + }; + + let actual_llvm_version = llvm_version_major * 1000000 + llvm_version_minor * 1000; + let min_supported_llvm_version = 3 * 1000000 + 4 * 1000; + + if actual_llvm_version < min_supported_llvm_version { + cx.sess().warn(format!("This version of rustc was built with LLVM \ + {}.{}. Rustc just ran into a known \ + debuginfo corruption problem thatoften \ + occurs with LLVM versions below 3.4. \ + Please use a rustc built with anewer \ + version of LLVM.", + llvm_version_major, + llvm_version_minor).as_slice()); + } else { + cx.sess().bug("debuginfo::set_members_of_composite_type() - \ + Already completed forward declaration re-encountered."); + } + } else { + composite_types_completed.insert(composite_type_metadata); + } + } + + let member_metadata: Vec = member_descriptions + .iter() + .enumerate() + .map(|(i, member_description)| { + let (member_size, member_align) = size_and_align_of(cx, member_description.llvm_type); + let member_offset = match member_description.offset { + FixedMemberOffset { bytes } => bytes as u64, + ComputedMemberOffset => machine::llelement_offset(cx, composite_llvm_type, i) + }; + + member_description.name.as_slice().with_c_str(|member_name| { + unsafe { + llvm::LLVMDIBuilderCreateMemberType( + DIB(cx), + composite_type_metadata, + member_name, + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(member_size), + bytes_to_bits(member_align), + bytes_to_bits(member_offset), + member_description.flags, + member_description.type_metadata) + } + }) + }) + .collect(); + + unsafe { + let type_array = create_DIArray(DIB(cx), member_metadata.as_slice()); + llvm::LLVMDICompositeTypeSetTypeArray(composite_type_metadata, type_array); + } +} + +// A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any +// caching, does not add any fields to the struct. This can be done later with +// set_members_of_composite_type(). +fn create_struct_stub(cx: &CrateContext, + struct_llvm_type: Type, + struct_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type); + + let unique_type_id_str = debug_context(cx).type_map + .borrow() + .get_unique_type_id_as_string(unique_type_id); + let metadata_stub = unsafe { + struct_type_name.with_c_str(|name| { + unique_type_id_str.as_slice().with_c_str(|unique_type_id| { + // LLVMDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + llvm::LLVMDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name, + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(struct_size), + bytes_to_bits(struct_align), + 0, + ptr::null_mut(), + empty_array, + 0, + ptr::null_mut(), + unique_type_id) + }) + }) + }; + + return metadata_stub; +} + +fn fixed_vec_metadata(cx: &CrateContext, + unique_type_id: UniqueTypeId, + element_type: ty::t, + len: uint, + span: Span) + -> MetadataCreationResult { + let element_type_metadata = type_metadata(cx, element_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let element_llvm_type = type_of::type_of(cx, element_type); + let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); + + let subrange = unsafe { + llvm::LLVMDIBuilderGetOrCreateSubrange( + DIB(cx), + 0, + len as i64) + }; + + let subscripts = create_DIArray(DIB(cx), &[subrange]); + let metadata = unsafe { + llvm::LLVMDIBuilderCreateArrayType( + DIB(cx), + bytes_to_bits(element_type_size * (len as u64)), + bytes_to_bits(element_type_align), + element_type_metadata, + subscripts) + }; + + return MetadataCreationResult::new(metadata, false); +} + +fn vec_slice_metadata(cx: &CrateContext, + vec_type: ty::t, + element_type: ty::t, + unique_type_id: UniqueTypeId, + span: Span) + -> MetadataCreationResult { + let data_ptr_type = ty::mk_ptr(cx.tcx(), ty::mt { + ty: element_type, + mutbl: ast::MutImmutable + }); + + let element_type_metadata = type_metadata(cx, data_ptr_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let slice_llvm_type = type_of::type_of(cx, vec_type); + let slice_type_name = compute_debuginfo_type_name(cx, vec_type, true); + + let member_llvm_types = slice_llvm_type.field_types(); + assert!(slice_layout_is_correct(cx, + member_llvm_types.as_slice(), + element_type)); + let member_descriptions = [ + MemberDescription { + name: "data_ptr".to_string(), + llvm_type: member_llvm_types[0], + type_metadata: element_type_metadata, + offset: ComputedMemberOffset, + flags: FLAGS_NONE + }, + MemberDescription { + name: "length".to_string(), + llvm_type: member_llvm_types[1], + type_metadata: type_metadata(cx, ty::mk_uint(), span), + offset: ComputedMemberOffset, + flags: FLAGS_NONE + }, + ]; + + assert!(member_descriptions.len() == member_llvm_types.len()); + + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, loc.file.name.as_slice()); + + let metadata = composite_type_metadata(cx, + slice_llvm_type, + slice_type_name.as_slice(), + unique_type_id, + &member_descriptions, + UNKNOWN_SCOPE_METADATA, + file_metadata, + span); + return MetadataCreationResult::new(metadata, false); + + fn slice_layout_is_correct(cx: &CrateContext, + member_llvm_types: &[Type], + element_type: ty::t) + -> bool { + member_llvm_types.len() == 2 && + member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && + member_llvm_types[1] == cx.int_type() + } +} + +fn subroutine_type_metadata(cx: &CrateContext, + unique_type_id: UniqueTypeId, + signature: &ty::FnSig, + span: Span) + -> MetadataCreationResult { + let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs.len() + 1); + + // return type + signature_metadata.push(match signature.output { + ty::FnConverging(ret_ty) => match ty::get(ret_ty).sty { + ty::ty_tup(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, ret_ty, span) + }, + ty::FnDiverging => diverging_type_metadata(cx) + }); + + // regular arguments + for &argument_type in signature.inputs.iter() { + signature_metadata.push(type_metadata(cx, argument_type, span)); + } + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + return MetadataCreationResult::new( + unsafe { + llvm::LLVMDIBuilderCreateSubroutineType( + DIB(cx), + UNKNOWN_FILE_METADATA, + create_DIArray(DIB(cx), signature_metadata.as_slice())) + }, + false); +} + +// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- +// defined concept. For the case of an actual trait pointer (i.e., Box, +// &Trait), trait_object_type should be the whole thing (e.g, Box) and +// trait_type should be the actual trait (e.g., Trait). Where the trait is part +// of a DST struct, there is no trait_object_type and the results of this +// function will be a little bit weird. +fn trait_pointer_metadata(cx: &CrateContext, + trait_type: ty::t, + trait_object_type: Option, + unique_type_id: UniqueTypeId) + -> DIType { + // The implementation provided here is a stub. It makes sure that the trait + // type is assigned the correct name, size, namespace, and source location. + // But it does not describe the trait's methods. + + let def_id = match ty::get(trait_type).sty { + ty::ty_trait(box ty::TyTrait { ref principal, .. }) => principal.def_id, + _ => { + let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type); + cx.sess().bug(format!("debuginfo: Unexpected trait-object type in \ + trait_pointer_metadata(): {}", + pp_type_name.as_slice()).as_slice()); + } + }; + + let trait_object_type = trait_object_type.unwrap_or(trait_type); + let trait_type_name = + compute_debuginfo_type_name(cx, trait_object_type, false); + + let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); + + let trait_llvm_type = type_of::type_of(cx, trait_object_type); + + composite_type_metadata(cx, + trait_llvm_type, + trait_type_name.as_slice(), + unique_type_id, + &[], + containing_scope, + UNKNOWN_FILE_METADATA, + codemap::DUMMY_SP) +} + +fn type_metadata(cx: &CrateContext, + t: ty::t, + usage_site_span: Span) + -> DIType { + // Get the unique type id of this type. + let unique_type_id = { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + // First, try to find the type in TypeMap. If we have seen it before, we + // can exit early here. + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + return metadata; + }, + None => { + // The ty::t is not in the TypeMap but maybe we have already seen + // an equivalent type (e.g. only differing in region arguments). + // In order to find out, generate the unique type id and look + // that up. + let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); + match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => { + // There is already an equivalent type in the TypeMap. + // Register this ty::t as an alias in the cache and + // return the cached metadata. + type_map.register_type_with_metadata(cx, t, metadata); + return metadata; + }, + None => { + // There really is no type metadata for this type, so + // proceed by creating it. + unique_type_id + } + } + } + } + }; + + debug!("type_metadata: {}", ty::get(t)); + + let sty = &ty::get(t).sty; + let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { + ty::ty_bool | + ty::ty_char | + ty::ty_int(_) | + ty::ty_uint(_) | + ty::ty_float(_) => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::ty_tup(ref elements) if elements.is_empty() => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::ty_enum(def_id, _) => { + prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx) + } + ty::ty_vec(typ, Some(len)) => { + fixed_vec_metadata(cx, unique_type_id, typ, len, usage_site_span) + } + // FIXME Can we do better than this for unsized vec/str fields? + ty::ty_vec(typ, None) => fixed_vec_metadata(cx, unique_type_id, typ, 0, usage_site_span), + ty::ty_str => fixed_vec_metadata(cx, unique_type_id, ty::mk_i8(), 0, usage_site_span), + ty::ty_trait(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, t, None, unique_type_id), + false) + } + ty::ty_uniq(ty) | ty::ty_ptr(ty::mt{ty, ..}) | ty::ty_rptr(_, ty::mt{ty, ..}) => { + match ty::get(ty).sty { + ty::ty_vec(typ, None) => { + vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span) + } + ty::ty_str => { + vec_slice_metadata(cx, t, ty::mk_u8(), unique_type_id, usage_site_span) + } + ty::ty_trait(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, ty, Some(t), unique_type_id), + false) + } + _ => { + let pointee_metadata = type_metadata(cx, ty, usage_site_span); + + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return metadata, + None => { /* proceed normally */ } + }; + + MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), + false) + } + } + } + ty::ty_bare_fn(ref barefnty) => { + subroutine_type_metadata(cx, unique_type_id, &barefnty.sig, usage_site_span) + } + ty::ty_closure(ref closurety) => { + subroutine_type_metadata(cx, unique_type_id, &closurety.sig, usage_site_span) + } + ty::ty_unboxed_closure(ref def_id, _, ref substs) => { + let sig = cx.tcx().unboxed_closures.borrow() + .get(def_id).unwrap().closure_type.sig.subst(cx.tcx(), substs); + subroutine_type_metadata(cx, unique_type_id, &sig, usage_site_span) + } + ty::ty_struct(def_id, ref substs) => { + prepare_struct_metadata(cx, + t, + def_id, + substs, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::ty_tup(ref elements) => { + prepare_tuple_metadata(cx, + t, + elements.as_slice(), + unique_type_id, + usage_site_span).finalize(cx) + } + _ => { + cx.sess().bug(format!("debuginfo: unexpected type in type_metadata: {}", + sty).as_slice()) + } + }; + + { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + + if already_stored_in_typemap { + // Also make sure that we already have a TypeMap entry entry for the unique type id. + let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => metadata, + None => { + let unique_type_id_str = + type_map.get_unique_type_id_as_string(unique_type_id); + let error_message = format!("Expected type metadata for unique \ + type id '{}' to already be in \ + the debuginfo::TypeMap but it \ + was not. (ty::t = {})", + unique_type_id_str.as_slice(), + ppaux::ty_to_string(cx.tcx(), t)); + cx.sess().span_bug(usage_site_span, error_message.as_slice()); + } + }; + + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + if metadata != metadata_for_uid { + let unique_type_id_str = + type_map.get_unique_type_id_as_string(unique_type_id); + let error_message = format!("Mismatch between ty::t and \ + UniqueTypeId maps in \ + debuginfo::TypeMap. \ + UniqueTypeId={}, ty::t={}", + unique_type_id_str.as_slice(), + ppaux::ty_to_string(cx.tcx(), t)); + cx.sess().span_bug(usage_site_span, error_message.as_slice()); + } + } + None => { + type_map.register_type_with_metadata(cx, t, metadata); + } + } + } else { + type_map.register_type_with_metadata(cx, t, metadata); + type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata); + } + } + + metadata +} + +struct MetadataCreationResult { + metadata: DIType, + already_stored_in_typemap: bool +} + +impl MetadataCreationResult { + fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { + MetadataCreationResult { + metadata: metadata, + already_stored_in_typemap: already_stored_in_typemap + } + } +} + +#[deriving(PartialEq)] +enum DebugLocation { + KnownLocation { scope: DIScope, line: uint, col: uint }, + UnknownLocation +} + +impl DebugLocation { + fn new(scope: DIScope, line: uint, col: uint) -> DebugLocation { + KnownLocation { + scope: scope, + line: line, + col: col, + } + } +} + +fn set_debug_location(cx: &CrateContext, debug_location: DebugLocation) { + if debug_location == debug_context(cx).current_debug_location.get() { + return; + } + + let metadata_node; + + match debug_location { + KnownLocation { scope, line, .. } => { + // Always set the column to zero like Clang and GCC + let col = UNKNOWN_COLUMN_NUMBER; + debug!("setting debug location to {} {}", line, col); + let elements = [C_i32(cx, line as i32), C_i32(cx, col as i32), + scope, ptr::null_mut()]; + unsafe { + metadata_node = llvm::LLVMMDNodeInContext(debug_context(cx).llcontext, + elements.as_ptr(), + elements.len() as c_uint); + } + } + UnknownLocation => { + debug!("clearing debug location "); + metadata_node = ptr::null_mut(); + } + }; + + unsafe { + llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); + } + + debug_context(cx).current_debug_location.set(debug_location); +} + +//=----------------------------------------------------------------------------- +// Utility Functions +//=----------------------------------------------------------------------------- + +fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { + attributes.iter().any(|attr| { + let meta_item: &ast::MetaItem = &*attr.node.value; + match meta_item.node { + ast::MetaWord(ref value) => value.get() == "no_debug", + _ => false + } + }) +} + +/// Return codemap::Loc corresponding to the beginning of the span +fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { + cx.sess().codemap().lookup_char_pos(span.lo) +} + +fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { + (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) +} + +fn bytes_to_bits(bytes: u64) -> u64 { + bytes * 8 +} + +#[inline] +fn debug_context<'a>(cx: &'a CrateContext) -> &'a CrateDebugContext { + let debug_context: &'a CrateDebugContext = cx.dbg_cx().as_ref().unwrap(); + debug_context +} + +#[inline] +#[allow(non_snake_case)] +fn DIB(cx: &CrateContext) -> DIBuilderRef { + cx.dbg_cx().as_ref().unwrap().builder +} + +fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { + match fcx.debug_context.repr { + DebugInfo(_) => false, + _ => true + } +} + +fn assert_type_for_node_id(cx: &CrateContext, + node_id: ast::NodeId, + error_reporting_span: Span) { + if !cx.tcx().node_types.borrow().contains_key(&node_id) { + cx.sess().span_bug(error_reporting_span, + "debuginfo: Could not find type for node id!"); + } +} + +fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) + -> (DIScope, Span) { + let containing_scope = namespace_for_item(cx, def_id).scope; + let definition_span = if def_id.krate == ast::LOCAL_CRATE { + cx.tcx().map.span(def_id.node) + } else { + // For external items there is no span information + codemap::DUMMY_SP + }; + + (containing_scope, definition_span) +} + +// This procedure builds the *scope map* for a given function, which maps any +// given ast::NodeId in the function's AST to the correct DIScope metadata instance. +// +// This builder procedure walks the AST in execution order and keeps track of +// what belongs to which scope, creating DIScope DIEs along the way, and +// introducing *artificial* lexical scope descriptors where necessary. These +// artificial scopes allow GDB to correctly handle name shadowing. +fn populate_scope_map(cx: &CrateContext, + args: &[ast::Arg], + fn_entry_block: &ast::Block, + fn_metadata: DISubprogram, + fn_ast_id: ast::NodeId, + scope_map: &mut NodeMap) { + let def_map = &cx.tcx().def_map; + + struct ScopeStackEntry { + scope_metadata: DIScope, + ident: Option + } + + let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, + ident: None }); + scope_map.insert(fn_ast_id, fn_metadata); + + // Push argument identifiers onto the stack so arguments integrate nicely + // with variable shadowing. + for arg in args.iter() { + pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { + scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, + ident: Some(path1.node) }); + scope_map.insert(node_id, fn_metadata); + }) + } + + // Clang creates a separate scope for function bodies, so let's do this too. + with_new_scope(cx, + fn_entry_block.span, + &mut scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, fn_entry_block, scope_stack, scope_map); + }); + + // local helper functions for walking the AST. + fn with_new_scope(cx: &CrateContext, + scope_span: Span, + scope_stack: &mut Vec , + scope_map: &mut NodeMap, + inner_walk: |&CrateContext, + &mut Vec , + &mut NodeMap|) { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); + let file_metadata = file_metadata(cx, loc.file.name.as_slice()); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_uint() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, + ident: None }); + + inner_walk(cx, scope_stack, scope_map); + + // pop artificial scopes + while scope_stack.last().unwrap().ident.is_some() { + scope_stack.pop(); + } + + if scope_stack.last().unwrap().scope_metadata != scope_metadata { + cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); + } + + scope_stack.pop(); + } + + fn walk_block(cx: &CrateContext, + block: &ast::Block, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); + + // The interesting things here are statements and the concluding expression. + for statement in block.stmts.iter() { + scope_map.insert(ast_util::stmt_id(&**statement), + scope_stack.last().unwrap().scope_metadata); + + match statement.node { + ast::StmtDecl(ref decl, _) => + walk_decl(cx, &**decl, scope_stack, scope_map), + ast::StmtExpr(ref exp, _) | + ast::StmtSemi(ref exp, _) => + walk_expr(cx, &**exp, scope_stack, scope_map), + ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). + } + } + + for exp in block.expr.iter() { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + + fn walk_decl(cx: &CrateContext, + decl: &ast::Decl, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + match *decl { + codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { + scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); + + walk_pattern(cx, &*local.pat, scope_stack, scope_map); + + for exp in local.init.iter() { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + _ => () + } + } + + fn walk_pattern(cx: &CrateContext, + pat: &ast::Pat, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + let def_map = &cx.tcx().def_map; + + // Unfortunately, we cannot just use pat_util::pat_bindings() or + // ast_util::walk_pat() here because we have to visit *all* nodes in + // order to put them into the scope map. The above functions don't do that. + match pat.node { + ast::PatIdent(_, ref path1, ref sub_pat_opt) => { + + // Check if this is a binding. If so we need to put it on the + // scope stack and maybe introduce an artificial scope + if pat_util::pat_is_binding(def_map, &*pat) { + + let ident = path1.node; + + // LLVM does not properly generate 'DW_AT_start_scope' fields + // for variable DIEs. For this reason we have to introduce + // an artificial scope at bindings whenever a variable with + // the same name is declared in *any* parent scope. + // + // Otherwise the following error occurs: + // + // let x = 10; + // + // do_something(); // 'gdb print x' correctly prints 10 + // + // { + // do_something(); // 'gdb print x' prints 0, because it + // // already reads the uninitialized 'x' + // // from the next line... + // let x = 100; + // do_something(); // 'gdb print x' correctly prints 100 + // } + + // Is there already a binding with that name? + // N.B.: this comparison must be UNhygienic... because + // gdb knows nothing about the context, so any two + // variables with the same name will cause the problem. + let need_new_scope = scope_stack + .iter() + .any(|entry| entry.ident.iter().any(|i| i.name == ident.name)); + + if need_new_scope { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); + let file_metadata = file_metadata(cx, + loc.file + .name + .as_slice()); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_uint() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { + scope_metadata: scope_metadata, + ident: Some(ident) + }); + + } else { + // Push a new entry anyway so the name can be found + let prev_metadata = scope_stack.last().unwrap().scope_metadata; + scope_stack.push(ScopeStackEntry { + scope_metadata: prev_metadata, + ident: Some(ident) + }); + } + } + + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in sub_pat_opt.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatWild(_) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + + ast::PatEnum(_, ref sub_pats_opt) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pats in sub_pats_opt.iter() { + for p in sub_pats.iter() { + walk_pattern(cx, &**p, scope_stack, scope_map); + } + } + } + + ast::PatStruct(_, ref field_pats, _) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for &codemap::Spanned { + node: ast::FieldPat { pat: ref sub_pat, .. }, + .. + } in field_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatTup(ref sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in sub_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + ast::PatLit(ref exp) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + ast::PatRange(ref exp1, ref exp2) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp1, scope_stack, scope_map); + walk_expr(cx, &**exp2, scope_stack, scope_map); + } + + ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in front_sub_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + for sub_pat in middle_sub_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + for sub_pat in back_sub_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatMac(_) => { + cx.sess().span_bug(pat.span, "debuginfo::populate_scope_map() - \ + Found unexpanded macro."); + } + } + } + + fn walk_expr(cx: &CrateContext, + exp: &ast::Expr, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); + + match exp.node { + ast::ExprLit(_) | + ast::ExprBreak(_) | + ast::ExprAgain(_) | + ast::ExprPath(_) => {} + + ast::ExprCast(ref sub_exp, _) | + ast::ExprAddrOf(_, ref sub_exp) | + ast::ExprField(ref sub_exp, _, _) | + ast::ExprTupField(ref sub_exp, _, _) | + ast::ExprParen(ref sub_exp) => + walk_expr(cx, &**sub_exp, scope_stack, scope_map), + + ast::ExprBox(ref place, ref sub_expr) => { + walk_expr(cx, &**place, scope_stack, scope_map); + walk_expr(cx, &**sub_expr, scope_stack, scope_map); + } + + ast::ExprRet(ref exp_opt) => match *exp_opt { + Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), + None => () + }, + + ast::ExprUnary(_, ref sub_exp) => { + walk_expr(cx, &**sub_exp, scope_stack, scope_map); + } + + ast::ExprAssignOp(_, ref lhs, ref rhs) | + ast::ExprIndex(ref lhs, ref rhs) | + ast::ExprBinary(_, ref lhs, ref rhs) => { + walk_expr(cx, &**lhs, scope_stack, scope_map); + walk_expr(cx, &**rhs, scope_stack, scope_map); + } + + ast::ExprSlice(ref base, ref start, ref end, _) => { + walk_expr(cx, &**base, scope_stack, scope_map); + start.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map)); + end.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map)); + } + + ast::ExprVec(ref init_expressions) | + ast::ExprTup(ref init_expressions) => { + for ie in init_expressions.iter() { + walk_expr(cx, &**ie, scope_stack, scope_map); + } + } + + ast::ExprAssign(ref sub_exp1, ref sub_exp2) | + ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { + walk_expr(cx, &**sub_exp1, scope_stack, scope_map); + walk_expr(cx, &**sub_exp2, scope_stack, scope_map); + } + + ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + then_block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**then_block, scope_stack, scope_map); + }); + + match *opt_else_exp { + Some(ref else_exp) => + walk_expr(cx, &**else_exp, scope_stack, scope_map), + _ => () + } + } + + ast::ExprIfLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded if-let."); + } + + ast::ExprWhile(ref cond_exp, ref loop_body, _) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + loop_body.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**loop_body, scope_stack, scope_map); + }) + } + + ast::ExprWhileLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded while-let."); + } + + ast::ExprForLoop(ref pattern, ref head, ref body, _) => { + walk_expr(cx, &**head, scope_stack, scope_map); + + with_new_scope(cx, + exp.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + scope_map.insert(exp.id, + scope_stack.last() + .unwrap() + .scope_metadata); + walk_pattern(cx, + &**pattern, + scope_stack, + scope_map); + walk_block(cx, &**body, scope_stack, scope_map); + }) + } + + ast::ExprMac(_) => { + cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded macro."); + } + + ast::ExprLoop(ref block, _) | + ast::ExprBlock(ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprFnBlock(_, ref decl, ref block) | + ast::ExprProc(ref decl, ref block) | + ast::ExprUnboxedFn(_, _, ref decl, ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for &ast::Arg { pat: ref pattern, .. } in decl.inputs.iter() { + walk_pattern(cx, &**pattern, scope_stack, scope_map); + } + + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprCall(ref fn_exp, ref args) => { + walk_expr(cx, &**fn_exp, scope_stack, scope_map); + + for arg_exp in args.iter() { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMethodCall(_, _, ref args) => { + for arg_exp in args.iter() { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMatch(ref discriminant_exp, ref arms, _) => { + walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); + + // For each arm we have to first walk the pattern as these might + // introduce new artificial scopes. It should be sufficient to + // walk only one pattern per arm, as they all must contain the + // same binding names. + + for arm_ref in arms.iter() { + let arm_span = arm_ref.pats[0].span; + + with_new_scope(cx, + arm_span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for pat in arm_ref.pats.iter() { + walk_pattern(cx, &**pat, scope_stack, scope_map); + } + + for guard_exp in arm_ref.guard.iter() { + walk_expr(cx, &**guard_exp, scope_stack, scope_map) + } + + walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); + }) + } + } + + ast::ExprStruct(_, ref fields, ref base_exp) => { + for &ast::Field { expr: ref exp, .. } in fields.iter() { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + match *base_exp { + Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), + None => () + } + } + + ast::ExprInlineAsm(ast::InlineAsm { ref inputs, + ref outputs, + .. }) => { + // inputs, outputs: Vec<(String, P)> + for &(_, ref exp) in inputs.iter() { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + for &(_, ref exp, _) in outputs.iter() { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + } + } +} + + +//=----------------------------------------------------------------------------- +// Type Names for Debug Info +//=----------------------------------------------------------------------------- + +// Compute the name of the type as it should be stored in debuginfo. Does not do +// any caching, i.e. calling the function twice with the same type will also do +// the work twice. The `qualified` parameter only affects the first level of the +// type name, further levels (i.e. type parameters) are always fully qualified. +fn compute_debuginfo_type_name(cx: &CrateContext, + t: ty::t, + qualified: bool) + -> String { + let mut result = String::with_capacity(64); + push_debuginfo_type_name(cx, t, qualified, &mut result); + result +} + +// Pushes the name of the type as it should be stored in debuginfo on the +// `output` String. See also compute_debuginfo_type_name(). +fn push_debuginfo_type_name(cx: &CrateContext, + t: ty::t, + qualified: bool, + output: &mut String) { + match ty::get(t).sty { + ty::ty_bool => output.push_str("bool"), + ty::ty_char => output.push_str("char"), + ty::ty_str => output.push_str("str"), + ty::ty_int(ast::TyI) => output.push_str("int"), + ty::ty_int(ast::TyI8) => output.push_str("i8"), + ty::ty_int(ast::TyI16) => output.push_str("i16"), + ty::ty_int(ast::TyI32) => output.push_str("i32"), + ty::ty_int(ast::TyI64) => output.push_str("i64"), + ty::ty_uint(ast::TyU) => output.push_str("uint"), + ty::ty_uint(ast::TyU8) => output.push_str("u8"), + ty::ty_uint(ast::TyU16) => output.push_str("u16"), + ty::ty_uint(ast::TyU32) => output.push_str("u32"), + ty::ty_uint(ast::TyU64) => output.push_str("u64"), + ty::ty_float(ast::TyF32) => output.push_str("f32"), + ty::ty_float(ast::TyF64) => output.push_str("f64"), + ty::ty_struct(def_id, ref substs) | + ty::ty_enum(def_id, ref substs) => { + push_item_name(cx, def_id, qualified, output); + push_type_params(cx, substs, output); + }, + ty::ty_tup(ref component_types) => { + output.push('('); + for &component_type in component_types.iter() { + push_debuginfo_type_name(cx, component_type, true, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::ty_uniq(inner_type) => { + output.push_str("Box<"); + push_debuginfo_type_name(cx, inner_type, true, output); + output.push('>'); + }, + ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + ast::MutImmutable => output.push_str("const "), + ast::MutMutable => output.push_str("mut "), + } + + push_debuginfo_type_name(cx, inner_type, true, output); + }, + ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == ast::MutMutable { + output.push_str("mut "); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + }, + ty::ty_vec(inner_type, optional_length) => { + output.push('['); + push_debuginfo_type_name(cx, inner_type, true, output); + + match optional_length { + Some(len) => { + output.push_str(format!(", ..{}", len).as_slice()); + } + None => { /* nothing to do */ } + }; + + output.push(']'); + }, + ty::ty_trait(ref trait_data) => { + push_item_name(cx, trait_data.principal.def_id, false, output); + push_type_params(cx, &trait_data.principal.substs, output); + }, + ty::ty_bare_fn(ty::BareFnTy{ fn_style, abi, ref sig } ) => { + if fn_style == ast::UnsafeFn { + output.push_str("unsafe "); + } + + if abi != ::syntax::abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + if sig.inputs.len() > 0 { + for ¶meter_type in sig.inputs.iter() { + push_debuginfo_type_name(cx, parameter_type, true, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if sig.inputs.len() > 0 { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + match sig.output { + ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_debuginfo_type_name(cx, result_type, true, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } + } + }, + ty::ty_closure(box ty::ClosureTy { fn_style, + onceness, + store, + ref sig, + .. // omitting bounds ... + }) => { + if fn_style == ast::UnsafeFn { + output.push_str("unsafe "); + } + + if onceness == ast::Once { + output.push_str("once "); + } + + let param_list_closing_char; + match store { + ty::UniqTraitStore => { + output.push_str("proc("); + param_list_closing_char = ')'; + } + ty::RegionTraitStore(_, ast::MutMutable) => { + output.push_str("&mut|"); + param_list_closing_char = '|'; + } + ty::RegionTraitStore(_, ast::MutImmutable) => { + output.push_str("&|"); + param_list_closing_char = '|'; + } + }; + + if sig.inputs.len() > 0 { + for ¶meter_type in sig.inputs.iter() { + push_debuginfo_type_name(cx, parameter_type, true, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if sig.inputs.len() > 0 { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(param_list_closing_char); + + match sig.output { + ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_debuginfo_type_name(cx, result_type, true, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } + } + }, + ty::ty_unboxed_closure(..) => { + output.push_str("closure"); + } + ty::ty_err | + ty::ty_infer(_) | + ty::ty_open(_) | + ty::ty_param(_) => { + cx.sess().bug(format!("debuginfo: Trying to create type name for \ + unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t)).as_slice()); + } + } + + fn push_item_name(cx: &CrateContext, + def_id: ast::DefId, + qualified: bool, + output: &mut String) { + ty::with_path(cx.tcx(), def_id, |mut path| { + if qualified { + if def_id.krate == ast::LOCAL_CRATE { + output.push_str(crate_root_namespace(cx)); + output.push_str("::"); + } + + let mut path_element_count = 0u; + for path_element in path { + let name = token::get_name(path_element.name()); + output.push_str(name.get()); + output.push_str("::"); + path_element_count += 1; + } + + if path_element_count == 0 { + cx.sess().bug("debuginfo: Encountered empty item path!"); + } + + output.pop(); + output.pop(); + } else { + let name = token::get_name(path.last() + .expect("debuginfo: Empty item path?") + .name()); + output.push_str(name.get()); + } + }); + } + + // Pushes the type parameters in the given `Substs` to the output string. + // This ignores region parameters, since they can't reliably be + // reconstructed for items from non-local crates. For local crates, this + // would be possible but with inlining and LTO we have to use the least + // common denominator - otherwise we would run into conflicts. + fn push_type_params(cx: &CrateContext, + substs: &subst::Substs, + output: &mut String) { + if substs.types.is_empty() { + return; + } + + output.push('<'); + + for &type_parameter in substs.types.iter() { + push_debuginfo_type_name(cx, type_parameter, true, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } +} + + +//=----------------------------------------------------------------------------- +// Namespace Handling +//=----------------------------------------------------------------------------- + +struct NamespaceTreeNode { + name: ast::Name, + scope: DIScope, + parent: Option>, +} + +impl NamespaceTreeNode { + fn mangled_name_of_contained_item(&self, item_name: &str) -> String { + fn fill_nested(node: &NamespaceTreeNode, output: &mut String) { + match node.parent { + Some(ref parent) => fill_nested(&*parent.upgrade().unwrap(), output), + None => {} + } + let string = token::get_name(node.name); + output.push_str(format!("{}", string.get().len()).as_slice()); + output.push_str(string.get()); + } + + let mut name = String::from_str("_ZN"); + fill_nested(self, &mut name); + name.push_str(format!("{}", item_name.len()).as_slice()); + name.push_str(item_name); + name.push('E'); + name + } +} + +fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str { + cx.link_meta().crate_name.as_slice() +} + +fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc { + ty::with_path(cx.tcx(), def_id, |path| { + // prepend crate name if not already present + let krate = if def_id.krate == ast::LOCAL_CRATE { + let crate_namespace_ident = token::str_to_ident(crate_root_namespace(cx)); + Some(ast_map::PathMod(crate_namespace_ident.name)) + } else { + None + }; + let mut path = krate.into_iter().chain(path).peekable(); + + let mut current_key = Vec::new(); + let mut parent_node: Option> = None; + + // Create/Lookup namespace for each element of the path. + loop { + // Emulate a for loop so we can use peek below. + let path_element = match path.next() { + Some(e) => e, + None => break + }; + // Ignore the name of the item (the last path element). + if path.peek().is_none() { + break; + } + + let name = path_element.name(); + current_key.push(name); + + let existing_node = debug_context(cx).namespace_map.borrow() + .get(¤t_key).cloned(); + let current_node = match existing_node { + Some(existing_node) => existing_node, + None => { + // create and insert + let parent_scope = match parent_node { + Some(ref node) => node.scope, + None => ptr::null_mut() + }; + let namespace_name = token::get_name(name); + let scope = namespace_name.get().with_c_str(|namespace_name| { + unsafe { + llvm::LLVMDIBuilderCreateNameSpace( + DIB(cx), + parent_scope, + namespace_name, + // cannot reconstruct file ... + ptr::null_mut(), + // ... or line information, but that's not so important. + 0) + } + }); + + let node = Rc::new(NamespaceTreeNode { + name: name, + scope: scope, + parent: parent_node.map(|parent| parent.downgrade()), + }); + + debug_context(cx).namespace_map.borrow_mut() + .insert(current_key.clone(), node.clone()); + + node + } + }; + + parent_node = Some(current_node); + } + + match parent_node { + Some(node) => node, + None => { + cx.sess().bug(format!("debuginfo::namespace_for_item(): \ + path too short for {}", + def_id).as_slice()); + } + } + }) +} diff --git a/src/librustc_trans/trans/doc.rs b/src/librustc_trans/trans/doc.rs new file mode 100644 index 00000000000..013483d0003 --- /dev/null +++ b/src/librustc_trans/trans/doc.rs @@ -0,0 +1,237 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +# Documentation for the trans module + +This module contains high-level summaries of how the various modules +in trans work. It is a work in progress. For detailed comments, +naturally, you can refer to the individual modules themselves. + +## The Expr module + +The expr module handles translation of expressions. The most general +translation routine is `trans()`, which will translate an expression +into a datum. `trans_into()` is also available, which will translate +an expression and write the result directly into memory, sometimes +avoiding the need for a temporary stack slot. Finally, +`trans_to_lvalue()` is available if you'd like to ensure that the +result has cleanup scheduled. + +Internally, each of these functions dispatches to various other +expression functions depending on the kind of expression. We divide +up expressions into: + +- **Datum expressions:** Those that most naturally yield values. + Examples would be `22`, `box x`, or `a + b` (when not overloaded). +- **DPS expressions:** Those that most naturally write into a location + in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`. +- **Statement expressions:** That that do not generate a meaningful + result. Examples would be `while { ... }` or `return 44`. + +## The Datum module + +A `Datum` encapsulates the result of evaluating a Rust expression. It +contains a `ValueRef` indicating the result, a `ty::t` describing +the Rust type, but also a *kind*. The kind indicates whether the datum +has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of +rvalues -- whether or not the value is "by ref" or "by value". + +The datum API is designed to try and help you avoid memory errors like +forgetting to arrange cleanup or duplicating a value. The type of the +datum incorporates the kind, and thus reflects whether it has cleanup +scheduled: + +- `Datum` -- by ref, cleanup scheduled +- `Datum` -- by value or by ref, no cleanup scheduled +- `Datum` -- either `Datum` or `Datum` + +Rvalue and expr datums are noncopyable, and most of the methods on +datums consume the datum itself (with some notable exceptions). This +reflects the fact that datums may represent affine values which ought +to be consumed exactly once, and if you were to try to (for example) +store an affine value multiple times, you would be duplicating it, +which would certainly be a bug. + +Some of the datum methods, however, are designed to work only on +copyable values such as ints or pointers. Those methods may borrow the +datum (`&self`) rather than consume it, but they always include +assertions on the type of the value represented to check that this +makes sense. An example is `shallow_copy()`, which duplicates +a datum value. + +Translating an expression always yields a `Datum` result, but +the methods `to_[lr]value_datum()` can be used to coerce a +`Datum` into a `Datum` or `Datum` as +needed. Coercing to an lvalue is fairly common, and generally occurs +whenever it is necessary to inspect a value and pull out its +subcomponents (for example, a match, or indexing expression). Coercing +to an rvalue is more unusual; it occurs when moving values from place +to place, such as in an assignment expression or parameter passing. + +### Lvalues in detail + +An lvalue datum is one for which cleanup has been scheduled. Lvalue +datums are always located in memory, and thus the `ValueRef` for an +LLVM value is always a pointer to the actual Rust value. This means +that if the Datum has a Rust type of `int`, then the LLVM type of the +`ValueRef` will be `int*` (pointer to int). + +Because lvalues already have cleanups scheduled, the memory must be +zeroed to prevent the cleanup from taking place (presuming that the +Rust type needs drop in the first place, otherwise it doesn't +matter). The Datum code automatically performs this zeroing when the +value is stored to a new location, for example. + +Lvalues usually result from evaluating lvalue expressions. For +example, evaluating a local variable `x` yields an lvalue, as does a +reference to a field like `x.f` or an index `x[i]`. + +Lvalue datums can also arise by *converting* an rvalue into an lvalue. +This is done with the `to_lvalue_datum` method defined on +`Datum`. Basically this method just schedules cleanup if the +datum is an rvalue, possibly storing the value into a stack slot first +if needed. Converting rvalues into lvalues occurs in constructs like +`&foo()` or `match foo() { ref x => ... }`, where the user is +implicitly requesting a temporary. + +Somewhat surprisingly, not all lvalue expressions yield lvalue datums +when trans'd. Ultimately the reason for this is to micro-optimize +the resulting LLVM. For example, consider the following code: + + fn foo() -> Box { ... } + let x = *foo(); + +The expression `*foo()` is an lvalue, but if you invoke `expr::trans`, +it will return an rvalue datum. See `deref_once` in expr.rs for +more details. + +### Rvalues in detail + +Rvalues datums are values with no cleanup scheduled. One must be +careful with rvalue datums to ensure that cleanup is properly +arranged, usually by converting to an lvalue datum or by invoking the +`add_clean` method. + +### Scratch datums + +Sometimes you need some temporary scratch space. The functions +`[lr]value_scratch_datum()` can be used to get temporary stack +space. As their name suggests, they yield lvalues and rvalues +respectively. That is, the slot from `lvalue_scratch_datum` will have +cleanup arranged, and the slot from `rvalue_scratch_datum` does not. + +## The Cleanup module + +The cleanup module tracks what values need to be cleaned up as scopes +are exited, either via panic or just normal control flow. The basic +idea is that the function context maintains a stack of cleanup scopes +that are pushed/popped as we traverse the AST tree. There is typically +at least one cleanup scope per AST node; some AST nodes may introduce +additional temporary scopes. + +Cleanup items can be scheduled into any of the scopes on the stack. +Typically, when a scope is popped, we will also generate the code for +each of its cleanups at that time. This corresponds to a normal exit +from a block (for example, an expression completing evaluation +successfully without panic). However, it is also possible to pop a +block *without* executing its cleanups; this is typically used to +guard intermediate values that must be cleaned up on panic, but not +if everything goes right. See the section on custom scopes below for +more details. + +Cleanup scopes come in three kinds: +- **AST scopes:** each AST node in a function body has a corresponding + AST scope. We push the AST scope when we start generate code for an AST + node and pop it once the AST node has been fully generated. +- **Loop scopes:** loops have an additional cleanup scope. Cleanups are + never scheduled into loop scopes; instead, they are used to record the + basic blocks that we should branch to when a `continue` or `break` statement + is encountered. +- **Custom scopes:** custom scopes are typically used to ensure cleanup + of intermediate values. + +### When to schedule cleanup + +Although the cleanup system is intended to *feel* fairly declarative, +it's still important to time calls to `schedule_clean()` correctly. +Basically, you should not schedule cleanup for memory until it has +been initialized, because if an unwind should occur before the memory +is fully initialized, then the cleanup will run and try to free or +drop uninitialized memory. If the initialization itself produces +byproducts that need to be freed, then you should use temporary custom +scopes to ensure that those byproducts will get freed on unwind. For +example, an expression like `box foo()` will first allocate a box in the +heap and then call `foo()` -- if `foo()` should panic, this box needs +to be *shallowly* freed. + +### Long-distance jumps + +In addition to popping a scope, which corresponds to normal control +flow exiting the scope, we may also *jump out* of a scope into some +earlier scope on the stack. This can occur in response to a `return`, +`break`, or `continue` statement, but also in response to panic. In +any of these cases, we will generate a series of cleanup blocks for +each of the scopes that is exited. So, if the stack contains scopes A +... Z, and we break out of a loop whose corresponding cleanup scope is +X, we would generate cleanup blocks for the cleanups in X, Y, and Z. +After cleanup is done we would branch to the exit point for scope X. +But if panic should occur, we would generate cleanups for all the +scopes from A to Z and then resume the unwind process afterwards. + +To avoid generating tons of code, we cache the cleanup blocks that we +create for breaks, returns, unwinds, and other jumps. Whenever a new +cleanup is scheduled, though, we must clear these cached blocks. A +possible improvement would be to keep the cached blocks but simply +generate a new block which performs the additional cleanup and then +branches to the existing cached blocks. + +### AST and loop cleanup scopes + +AST cleanup scopes are pushed when we begin and end processing an AST +node. They are used to house cleanups related to rvalue temporary that +get referenced (e.g., due to an expression like `&Foo()`). Whenever an +AST scope is popped, we always trans all the cleanups, adding the cleanup +code after the postdominator of the AST node. + +AST nodes that represent breakable loops also push a loop scope; the +loop scope never has any actual cleanups, it's just used to point to +the basic blocks where control should flow after a "continue" or +"break" statement. Popping a loop scope never generates code. + +### Custom cleanup scopes + +Custom cleanup scopes are used for a variety of purposes. The most +common though is to handle temporary byproducts, where cleanup only +needs to occur on panic. The general strategy is to push a custom +cleanup scope, schedule *shallow* cleanups into the custom scope, and +then pop the custom scope (without transing the cleanups) when +execution succeeds normally. This way the cleanups are only trans'd on +unwind, and only up until the point where execution succeeded, at +which time the complete value should be stored in an lvalue or some +other place where normal cleanup applies. + +To spell it out, here is an example. Imagine an expression `box expr`. +We would basically: + +1. Push a custom cleanup scope C. +2. Allocate the box. +3. Schedule a shallow free in the scope C. +4. Trans `expr` into the box. +5. Pop the scope C. +6. Return the box as an rvalue. + +This way, if a panic occurs while transing `expr`, the custom +cleanup scope C is pushed and hence the box will be freed. The trans +code for `expr` itself is responsible for freeing any other byproducts +that may be in play. + +*/ diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs new file mode 100644 index 00000000000..2b7c3d0c24f --- /dev/null +++ b/src/librustc_trans/trans/expr.rs @@ -0,0 +1,2248 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * # Translation of Expressions + * + * Public entry points: + * + * - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression, + * storing the result into `dest`. This is the preferred form, if you + * can manage it. + * + * - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding + * `Datum` with the result. You can then store the datum, inspect + * the value, etc. This may introduce temporaries if the datum is a + * structural type. + * + * - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an + * expression and ensures that the result has a cleanup associated with it, + * creating a temporary stack slot if necessary. + * + * - `trans_local_var -> Datum`: looks up a local variable or upvar. + * + * See doc.rs for more comments. + */ + +#![allow(non_camel_case_types)] + +pub use self::cast_kind::*; +pub use self::Dest::*; +use self::lazy_binop_ty::*; + +use back::abi; +use llvm; +use llvm::{ValueRef}; +use middle::def; +use middle::mem_categorization::Typer; +use middle::subst; +use middle::subst::Subst; +use trans::_match; +use trans::adt; +use trans::asm; +use trans::base::*; +use trans::base; +use trans::build::*; +use trans::callee; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::closure; +use trans::common::*; +use trans::consts; +use trans::controlflow; +use trans::datum::*; +use trans::debuginfo; +use trans::glue; +use trans::machine; +use trans::meth; +use trans::inline; +use trans::tvec; +use trans::type_of; +use middle::ty::{struct_fields, tup_fields}; +use middle::ty::{AdjustDerefRef, AdjustAddEnv, AutoUnsafe}; +use middle::ty::{AutoPtr}; +use middle::ty; +use middle::typeck; +use middle::typeck::MethodCall; +use util::common::indenter; +use util::ppaux::Repr; +use trans::machine::{llsize_of, llsize_of_alloc}; +use trans::type_::Type; + +use syntax::ast; +use syntax::codemap; +use syntax::print::pprust::{expr_to_string}; +use syntax::ptr::P; +use std::rc::Rc; + +// Destinations + +// These are passed around by the code generating functions to track the +// destination of a computation's value. + +#[deriving(PartialEq)] +pub enum Dest { + SaveIn(ValueRef), + Ignore, +} + +impl Dest { + pub fn to_string(&self, ccx: &CrateContext) -> String { + match *self { + SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)), + Ignore => "Ignore".to_string() + } + } +} + +pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + dest: Dest) + -> Block<'blk, 'tcx> { + /*! + * This function is equivalent to `trans(bcx, expr).store_to_dest(dest)` + * but it may generate better optimized LLVM code. + */ + + let mut bcx = bcx; + + if bcx.tcx().adjustments.borrow().contains_key(&expr.id) { + // use trans, which may be less efficient but + // which will perform the adjustments: + let datum = unpack_datum!(bcx, trans(bcx, expr)); + return datum.store_to_dest(bcx, dest, expr.id) + } + + debug!("trans_into() expr={}", expr.repr(bcx.tcx())); + + let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id, + expr.span, + false); + bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc); + + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + let kind = ty::expr_kind(bcx.tcx(), expr); + bcx = match kind { + ty::LvalueExpr | ty::RvalueDatumExpr => { + trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id) + } + ty::RvalueDpsExpr => { + trans_rvalue_dps_unadjusted(bcx, expr, dest) + } + ty::RvalueStmtExpr => { + trans_rvalue_stmt_unadjusted(bcx, expr) + } + }; + + bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id) +} + +pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + /*! + * Translates an expression, returning a datum (and new block) + * encapsulating the result. When possible, it is preferred to + * use `trans_into`, as that may avoid creating a temporary on + * the stack. + */ + + debug!("trans(expr={})", bcx.expr_to_string(expr)); + + let mut bcx = bcx; + let fcx = bcx.fcx; + + let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id, + expr.span, + false); + fcx.push_ast_cleanup_scope(cleanup_debug_loc); + let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr)); + let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id); + return DatumBlock::new(bcx, datum); +} + +pub fn get_len(bcx: Block, fat_ptr: ValueRef) -> ValueRef { + GEPi(bcx, fat_ptr, &[0u, abi::slice_elt_len]) +} + +pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef { + GEPi(bcx, fat_ptr, &[0u, abi::slice_elt_base]) +} + +fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'blk, 'tcx, Expr> { + /*! + * Helper for trans that apply adjustments from `expr` to `datum`, + * which should be the unadjusted translation of `expr`. + */ + + let mut bcx = bcx; + let mut datum = datum; + let adjustment = match bcx.tcx().adjustments.borrow().get(&expr.id).cloned() { + None => { + return DatumBlock::new(bcx, datum); + } + Some(adj) => { adj } + }; + debug!("unadjusted datum for expr {}: {}", + expr.id, datum.to_string(bcx.ccx())); + match adjustment { + AdjustAddEnv(..) => { + datum = unpack_datum!(bcx, add_env(bcx, expr, datum)); + } + AdjustDerefRef(ref adj) => { + let (autoderefs, use_autoref) = match adj.autoref { + // Extracting a value from a box counts as a deref, but if we are + // just converting Box<[T, ..n]> to Box<[T]> we aren't really doing + // a deref (and wouldn't if we could treat Box like a normal struct). + Some(ty::AutoUnsizeUniq(..)) => (adj.autoderefs - 1, true), + // We are a bit paranoid about adjustments and thus might have a re- + // borrow here which merely derefs and then refs again (it might have + // a different region or mutability, but we don't care here. It might + // also be just in case we need to unsize. But if there are no nested + // adjustments then it should be a no-op). + Some(ty::AutoPtr(_, _, None)) if adj.autoderefs == 1 => { + match ty::get(datum.ty).sty { + // Don't skip a conversion from Box to &T, etc. + ty::ty_rptr(..) => { + let method_call = MethodCall::autoderef(expr.id, adj.autoderefs-1); + let method = bcx.tcx().method_map.borrow().get(&method_call).is_some(); + if method { + // Don't skip an overloaded deref. + (adj.autoderefs, true) + } else { + (adj.autoderefs - 1, false) + } + } + _ => (adj.autoderefs, true), + } + } + _ => (adj.autoderefs, true) + }; + + if autoderefs > 0 { + // Schedule cleanup. + let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "auto_deref", expr.id)); + datum = unpack_datum!( + bcx, deref_multiple(bcx, expr, lval.to_expr_datum(), autoderefs)); + } + + // (You might think there is a more elegant way to do this than a + // use_autoref bool, but then you remember that the borrow checker exists). + match (use_autoref, &adj.autoref) { + (true, &Some(ref a)) => { + datum = unpack_datum!(bcx, apply_autoref(a, + bcx, + expr, + datum)); + } + _ => {} + } + } + } + debug!("after adjustments, datum={}", datum.to_string(bcx.ccx())); + return DatumBlock::new(bcx, datum); + + fn apply_autoref<'blk, 'tcx>(autoref: &ty::AutoRef, + bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let mut datum = datum; + + let datum = match autoref { + &AutoPtr(_, _, ref a) | &AutoUnsafe(_, ref a) => { + debug!(" AutoPtr"); + match a { + &Some(box ref a) => datum = unpack_datum!(bcx, + apply_autoref(a, bcx, expr, datum)), + _ => {} + } + unpack_datum!(bcx, ref_ptr(bcx, expr, datum)) + } + &ty::AutoUnsize(ref k) => { + debug!(" AutoUnsize"); + unpack_datum!(bcx, unsize_expr(bcx, expr, datum, k)) + } + + &ty::AutoUnsizeUniq(ty::UnsizeLength(len)) => { + debug!(" AutoUnsizeUniq(UnsizeLength)"); + unpack_datum!(bcx, unsize_unique_vec(bcx, expr, datum, len)) + } + &ty::AutoUnsizeUniq(ref k) => { + debug!(" AutoUnsizeUniq"); + unpack_datum!(bcx, unsize_unique_expr(bcx, expr, datum, k)) + } + }; + + DatumBlock::new(bcx, datum) + } + + fn ref_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'blk, 'tcx, Expr> { + if !ty::type_is_sized(bcx.tcx(), datum.ty) { + debug!("Taking address of unsized type {}", + bcx.ty_to_string(datum.ty)); + ref_fat_ptr(bcx, expr, datum) + } else { + debug!("Taking address of sized type {}", + bcx.ty_to_string(datum.ty)); + auto_ref(bcx, datum, expr) + } + } + + // Retrieve the information we are losing (making dynamic) in an unsizing + // adjustment. + // When making a dtor, we need to do different things depending on the + // ownership of the object.. mk_ty is a function for turning unsized_type + // into a type to be destructed. If we want to end up with a Box pointer, + // then mk_ty should make a Box pointer (T -> Box), if we want a + // borrowed reference then it should be T -> &T. + fn unsized_info<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + kind: &ty::UnsizeKind, + id: ast::NodeId, + unsized_ty: ty::t, + mk_ty: |ty::t| -> ty::t) -> ValueRef { + match kind { + &ty::UnsizeLength(len) => C_uint(bcx.ccx(), len), + &ty::UnsizeStruct(box ref k, tp_index) => match ty::get(unsized_ty).sty { + ty::ty_struct(_, ref substs) => { + let ty_substs = substs.types.get_slice(subst::TypeSpace); + // The dtor for a field treats it like a value, so mk_ty + // should just be the identity function. + unsized_info(bcx, k, id, ty_substs[tp_index], |t| t) + } + _ => bcx.sess().bug(format!("UnsizeStruct with bad sty: {}", + bcx.ty_to_string(unsized_ty)).as_slice()) + }, + &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => { + let substs = principal.substs.with_self_ty(unsized_ty); + let trait_ref = + Rc::new(ty::TraitRef { def_id: principal.def_id, + substs: substs }); + let trait_ref = + trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs); + let box_ty = mk_ty(unsized_ty); + PointerCast(bcx, + meth::get_vtable(bcx, box_ty, trait_ref), + Type::vtable_ptr(bcx.ccx())) + } + } + } + + fn unsize_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + k: &ty::UnsizeKind) + -> DatumBlock<'blk, 'tcx, Expr> { + let tcx = bcx.tcx(); + let datum_ty = datum.ty; + let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span); + let dest_ty = ty::mk_open(tcx, unsized_ty); + // Closures for extracting and manipulating the data and payload parts of + // the fat pointer. + let base = match k { + &ty::UnsizeStruct(..) => + |bcx, val| PointerCast(bcx, + val, + type_of::type_of(bcx.ccx(), unsized_ty).ptr_to()), + &ty::UnsizeLength(..) => + |bcx, val| GEPi(bcx, val, &[0u, 0u]), + &ty::UnsizeVtable(..) => + |_bcx, val| PointerCast(bcx, val, Type::i8p(bcx.ccx())) + }; + let info = |bcx, _val| unsized_info(bcx, + k, + expr.id, + ty::deref_or_dont(datum_ty), + |t| ty::mk_rptr(tcx, + ty::ReStatic, + ty::mt{ + ty: t, + mutbl: ast::MutImmutable + })); + into_fat_ptr(bcx, expr, datum, dest_ty, base, info) + } + + fn ref_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'blk, 'tcx, Expr> { + let tcx = bcx.tcx(); + let dest_ty = ty::close_type(tcx, datum.ty); + let base = |bcx, val| Load(bcx, get_dataptr(bcx, val)); + let len = |bcx, val| Load(bcx, get_len(bcx, val)); + into_fat_ptr(bcx, expr, datum, dest_ty, base, len) + } + + fn into_fat_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + dest_ty: ty::t, + base: |Block<'blk, 'tcx>, ValueRef| -> ValueRef, + info: |Block<'blk, 'tcx>, ValueRef| -> ValueRef) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + + // Arrange cleanup + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id)); + let base = base(bcx, lval.val); + let info = info(bcx, lval.val); + + let scratch = rvalue_scratch_datum(bcx, dest_ty, "__fat_ptr"); + Store(bcx, base, get_dataptr(bcx, scratch.val)); + Store(bcx, info, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + + fn unsize_unique_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + len: uint) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let tcx = bcx.tcx(); + + let datum_ty = datum.ty; + // Arrange cleanup + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "unsize_unique_vec", expr.id)); + + let ll_len = C_uint(bcx.ccx(), len); + let unit_ty = ty::sequence_element_type(tcx, ty::type_content(datum_ty)); + let vec_ty = ty::mk_uniq(tcx, ty::mk_vec(tcx, unit_ty, None)); + let scratch = rvalue_scratch_datum(bcx, vec_ty, "__unsize_unique"); + + let base = get_dataptr(bcx, scratch.val); + let base = PointerCast(bcx, + base, + type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); + bcx = lval.store_to(bcx, base); + + Store(bcx, ll_len, get_len(bcx, scratch.val)); + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + + fn unsize_unique_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + k: &ty::UnsizeKind) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let tcx = bcx.tcx(); + + let datum_ty = datum.ty; + let unboxed_ty = match ty::get(datum_ty).sty { + ty::ty_uniq(t) => t, + _ => bcx.sess().bug(format!("Expected ty_uniq, found {}", + bcx.ty_to_string(datum_ty)).as_slice()) + }; + let result_ty = ty::mk_uniq(tcx, ty::unsize_ty(tcx, unboxed_ty, k, expr.span)); + + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "unsize_unique_expr", expr.id)); + + let scratch = rvalue_scratch_datum(bcx, result_ty, "__uniq_fat_ptr"); + let llbox_ty = type_of::type_of(bcx.ccx(), datum_ty); + let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to()); + bcx = lval.store_to(bcx, base); + + let info = unsized_info(bcx, k, expr.id, unboxed_ty, |t| ty::mk_uniq(tcx, t)); + Store(bcx, info, get_len(bcx, scratch.val)); + + let scratch = unpack_datum!(bcx, + scratch.to_expr_datum().to_lvalue_datum(bcx, + "fresh_uniq_fat_ptr", + expr.id)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + + fn add_env<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum) + -> DatumBlock<'blk, 'tcx, Expr> { + // This is not the most efficient thing possible; since closures + // are two words it'd be better if this were compiled in + // 'dest' mode, but I can't find a nice way to structure the + // code and keep it DRY that accommodates that use case at the + // moment. + + let closure_ty = expr_ty_adjusted(bcx, expr); + let fn_ptr = datum.to_llscalarish(bcx); + let def = ty::resolve_expr(bcx.tcx(), expr); + closure::make_closure_from_bare_fn(bcx, closure_ty, def, fn_ptr) + } +} + +pub fn trans_to_lvalue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + name: &str) + -> DatumBlock<'blk, 'tcx, Lvalue> { + /*! + * Translates an expression in "lvalue" mode -- meaning that it + * returns a reference to the memory that the expr represents. + * + * If this expression is an rvalue, this implies introducing a + * temporary. In other words, something like `x().f` is + * translated into roughly the equivalent of + * + * { tmp = x(); tmp.f } + */ + + let mut bcx = bcx; + let datum = unpack_datum!(bcx, trans(bcx, expr)); + return datum.to_lvalue_datum(bcx, name, expr.id); +} + +fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + /*! + * A version of `trans` that ignores adjustments. You almost + * certainly do not want to call this directly. + */ + + let mut bcx = bcx; + + debug!("trans_unadjusted(expr={})", bcx.expr_to_string(expr)); + let _indenter = indenter(); + + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + + return match ty::expr_kind(bcx.tcx(), expr) { + ty::LvalueExpr | ty::RvalueDatumExpr => { + let datum = unpack_datum!(bcx, { + trans_datum_unadjusted(bcx, expr) + }); + + DatumBlock {bcx: bcx, datum: datum} + } + + ty::RvalueStmtExpr => { + bcx = trans_rvalue_stmt_unadjusted(bcx, expr); + nil(bcx, expr_ty(bcx, expr)) + } + + ty::RvalueDpsExpr => { + let ty = expr_ty(bcx, expr); + if type_is_zero_size(bcx.ccx(), ty) { + bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore); + nil(bcx, ty) + } else { + let scratch = rvalue_scratch_datum(bcx, ty, ""); + bcx = trans_rvalue_dps_unadjusted( + bcx, expr, SaveIn(scratch.val)); + + // Note: this is not obviously a good idea. It causes + // immediate values to be loaded immediately after a + // return from a call or other similar expression, + // which in turn leads to alloca's having shorter + // lifetimes and hence larger stack frames. However, + // in turn it can lead to more register pressure. + // Still, in practice it seems to increase + // performance, since we have fewer problems with + // morestack churn. + let scratch = unpack_datum!( + bcx, scratch.to_appropriate_datum(bcx)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + } + }; + + fn nil<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ty: ty::t) + -> DatumBlock<'blk, 'tcx, Expr> { + let llval = C_undef(type_of::type_of(bcx.ccx(), ty)); + let datum = immediate_rvalue(llval, ty); + DatumBlock::new(bcx, datum.to_expr_datum()) + } +} + +fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let fcx = bcx.fcx; + let _icx = push_ctxt("trans_datum_unadjusted"); + + match expr.node { + ast::ExprParen(ref e) => { + trans(bcx, &**e) + } + ast::ExprPath(_) => { + trans_def(bcx, expr, bcx.def(expr.id)) + } + ast::ExprField(ref base, ident, _) => { + trans_rec_field(bcx, &**base, ident.node) + } + ast::ExprTupField(ref base, idx, _) => { + trans_rec_tup_field(bcx, &**base, idx.node) + } + ast::ExprIndex(ref base, ref idx) => { + trans_index(bcx, expr, &**base, &**idx, MethodCall::expr(expr.id)) + } + ast::ExprSlice(ref base, ref start, ref end, _) => { + let _icx = push_ctxt("trans_slice"); + let ccx = bcx.ccx(); + + let method_call = MethodCall::expr(expr.id); + let method_ty = ccx.tcx() + .method_map + .borrow() + .get(&method_call) + .map(|method| method.ty); + let base_datum = unpack_datum!(bcx, trans(bcx, &**base)); + + let mut args = vec![]; + start.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); + end.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); + + let result_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty.unwrap())).unwrap(); + let scratch = rvalue_scratch_datum(bcx, result_ty, "trans_slice"); + + unpack_result!(bcx, + trans_overloaded_op(bcx, + expr, + method_call, + base_datum, + args, + Some(SaveIn(scratch.val)))); + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + ast::ExprBox(_, ref contents) => { + // Special case for `Box` + let box_ty = expr_ty(bcx, expr); + let contents_ty = expr_ty(bcx, &**contents); + match ty::get(box_ty).sty { + ty::ty_uniq(..) => { + trans_uniq_expr(bcx, box_ty, &**contents, contents_ty) + } + _ => bcx.sess().span_bug(expr.span, + "expected unique box") + } + + } + ast::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, &**lit), + ast::ExprBinary(op, ref lhs, ref rhs) => { + trans_binary(bcx, expr, op, &**lhs, &**rhs) + } + ast::ExprUnary(op, ref x) => { + trans_unary(bcx, expr, op, &**x) + } + ast::ExprAddrOf(_, ref x) => { + match x.node { + ast::ExprRepeat(..) | ast::ExprVec(..) => { + // Special case for slices. + let cleanup_debug_loc = + debuginfo::get_cleanup_debug_loc_for_ast_node(x.id, x.span, false); + fcx.push_ast_cleanup_scope(cleanup_debug_loc); + let datum = unpack_datum!( + bcx, tvec::trans_slice_vec(bcx, expr, &**x)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, x.id); + DatumBlock::new(bcx, datum) + } + _ => { + trans_addr_of(bcx, expr, &**x) + } + } + } + ast::ExprCast(ref val, _) => { + // Datum output mode means this is a scalar cast: + trans_imm_cast(bcx, &**val, expr.id) + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("trans_rvalue_datum_unadjusted reached \ + fall-through case: {}", + expr.node).as_slice()); + } + } +} + +fn trans_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + get_idx: |&'blk ty::ctxt<'tcx>, &[ty::field]| -> uint) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let _icx = push_ctxt("trans_rec_field"); + + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field")); + let bare_ty = ty::unopen_type(base_datum.ty); + let repr = adt::represent_type(bcx.ccx(), bare_ty); + with_field_tys(bcx.tcx(), bare_ty, None, |discr, field_tys| { + let ix = get_idx(bcx.tcx(), field_tys); + let d = base_datum.get_element( + bcx, + field_tys[ix].mt.ty, + |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix)); + + if ty::type_is_sized(bcx.tcx(), d.ty) { + DatumBlock { datum: d.to_expr_datum(), bcx: bcx } + } else { + let scratch = rvalue_scratch_datum(bcx, ty::mk_open(bcx.tcx(), d.ty), ""); + Store(bcx, d.val, get_dataptr(bcx, scratch.val)); + let info = Load(bcx, get_len(bcx, base_datum.val)); + Store(bcx, info, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + + } + }) + +} + +/// Translates `base.field`. +fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + field: ast::Ident) + -> DatumBlock<'blk, 'tcx, Expr> { + trans_field(bcx, base, |tcx, field_tys| ty::field_idx_strict(tcx, field.name, field_tys)) +} + +/// Translates `base.`. +fn trans_rec_tup_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + idx: uint) + -> DatumBlock<'blk, 'tcx, Expr> { + trans_field(bcx, base, |_, _| idx) +} + +fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + index_expr: &ast::Expr, + base: &ast::Expr, + idx: &ast::Expr, + method_call: MethodCall) + -> DatumBlock<'blk, 'tcx, Expr> { + //! Translates `base[idx]`. + + let _icx = push_ctxt("trans_index"); + let ccx = bcx.ccx(); + let mut bcx = bcx; + + // Check for overloaded index. + let method_ty = ccx.tcx() + .method_map + .borrow() + .get(&method_call) + .map(|method| method.ty); + let elt_datum = match method_ty { + Some(method_ty) => { + let base_datum = unpack_datum!(bcx, trans(bcx, base)); + + // Translate index expression. + let ix_datum = unpack_datum!(bcx, trans(bcx, idx)); + + let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap(); + let elt_ty = match ty::deref(ref_ty, true) { + None => { + bcx.tcx().sess.span_bug(index_expr.span, + "index method didn't return a \ + dereferenceable type?!") + } + Some(elt_tm) => elt_tm.ty, + }; + + // Overloaded. Evaluate `trans_overloaded_op`, which will + // invoke the user's index() method, which basically yields + // a `&T` pointer. We can then proceed down the normal + // path (below) to dereference that `&T`. + let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_index_elt"); + unpack_result!(bcx, + trans_overloaded_op(bcx, + index_expr, + method_call, + base_datum, + vec![(ix_datum, idx.id)], + Some(SaveIn(scratch.val)))); + let datum = scratch.to_expr_datum(); + if ty::type_is_sized(bcx.tcx(), elt_ty) { + Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr) + } else { + Datum::new(datum.val, ty::mk_open(bcx.tcx(), elt_ty), LvalueExpr) + } + } + None => { + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, + base, + "index")); + + // Translate index expression and cast to a suitable LLVM integer. + // Rust is less strict than LLVM in this regard. + let ix_datum = unpack_datum!(bcx, trans(bcx, idx)); + let ix_val = ix_datum.to_llscalarish(bcx); + let ix_size = machine::llbitsize_of_real(bcx.ccx(), + val_ty(ix_val)); + let int_size = machine::llbitsize_of_real(bcx.ccx(), + ccx.int_type()); + let ix_val = { + if ix_size < int_size { + if ty::type_is_signed(expr_ty(bcx, idx)) { + SExt(bcx, ix_val, ccx.int_type()) + } else { ZExt(bcx, ix_val, ccx.int_type()) } + } else if ix_size > int_size { + Trunc(bcx, ix_val, ccx.int_type()) + } else { + ix_val + } + }; + + let vt = + tvec::vec_types(bcx, + ty::sequence_element_type(bcx.tcx(), + base_datum.ty)); + base::maybe_name_value(bcx.ccx(), vt.llunit_size, "unit_sz"); + + let (base, len) = base_datum.get_vec_base_and_len(bcx); + + debug!("trans_index: base {}", bcx.val_to_string(base)); + debug!("trans_index: len {}", bcx.val_to_string(len)); + + let bounds_check = ICmp(bcx, llvm::IntUGE, ix_val, len); + let expect = ccx.get_intrinsic(&("llvm.expect.i1")); + let expected = Call(bcx, + expect, + &[bounds_check, C_bool(ccx, false)], + None); + bcx = with_cond(bcx, expected, |bcx| { + controlflow::trans_fail_bounds_check(bcx, + index_expr.span, + ix_val, + len) + }); + let elt = InBoundsGEP(bcx, base, &[ix_val]); + let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to()); + Datum::new(elt, vt.unit_ty, LvalueExpr) + } + }; + + DatumBlock::new(bcx, elt_datum) +} + +fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + ref_expr: &ast::Expr, + def: def::Def) + -> DatumBlock<'blk, 'tcx, Expr> { + //! Translates a reference to a path. + + let _icx = push_ctxt("trans_def_lvalue"); + match def { + def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) | + def::DefStruct(_) | def::DefVariant(..) => { + trans_def_fn_unadjusted(bcx, ref_expr, def) + } + def::DefStatic(did, _) => { + // There are two things that may happen here: + // 1) If the static item is defined in this crate, it will be + // translated using `get_item_val`, and we return a pointer to + // the result. + // 2) If the static item is defined in another crate then we add + // (or reuse) a declaration of an external global, and return a + // pointer to that. + let const_ty = expr_ty(bcx, ref_expr); + + fn get_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, did: ast::DefId, + const_ty: ty::t) -> ValueRef { + // For external constants, we don't inline. + if did.krate == ast::LOCAL_CRATE { + // Case 1. + + // The LLVM global has the type of its initializer, + // which may not be equal to the enum's type for + // non-C-like enums. + let val = base::get_item_val(bcx.ccx(), did.node); + let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to(); + PointerCast(bcx, val, pty) + } else { + // Case 2. + base::get_extern_const(bcx.ccx(), did, const_ty) + } + } + let val = get_val(bcx, did, const_ty); + DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr)) + } + def::DefConst(did) => { + // First, inline any external constants into the local crate so we + // can be sure to get the LLVM value corresponding to it. + let did = inline::maybe_instantiate_inline(bcx.ccx(), did); + if did.krate != ast::LOCAL_CRATE { + bcx.tcx().sess.span_bug(ref_expr.span, + "cross crate constant could not \ + be inlined"); + } + let val = base::get_item_val(bcx.ccx(), did.node); + + // Next, we need to crate a ByRef rvalue datum to return. We can't + // use the normal .to_ref_datum() function because the type of + // `val` is not actually the same as `const_ty`. + // + // To get around this, we make a custom alloca slot with the + // appropriate type (const_ty), and then we cast it to a pointer of + // typeof(val), store the value, and then hand this slot over to + // the datum infrastructure. + let const_ty = expr_ty(bcx, ref_expr); + let llty = type_of::type_of(bcx.ccx(), const_ty); + let slot = alloca(bcx, llty, "const"); + let pty = Type::from_ref(unsafe { llvm::LLVMTypeOf(val) }).ptr_to(); + Store(bcx, val, PointerCast(bcx, slot, pty)); + + let datum = Datum::new(slot, const_ty, Rvalue::new(ByRef)); + DatumBlock::new(bcx, datum.to_expr_datum()) + } + _ => { + DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum()) + } + } +} + +fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr) + -> Block<'blk, 'tcx> { + let mut bcx = bcx; + let _icx = push_ctxt("trans_rvalue_stmt"); + + if bcx.unreachable.get() { + return bcx; + } + + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + + match expr.node { + ast::ExprParen(ref e) => { + trans_into(bcx, &**e, Ignore) + } + ast::ExprBreak(label_opt) => { + controlflow::trans_break(bcx, expr.id, label_opt) + } + ast::ExprAgain(label_opt) => { + controlflow::trans_cont(bcx, expr.id, label_opt) + } + ast::ExprRet(ref ex) => { + controlflow::trans_ret(bcx, ex.as_ref().map(|e| &**e)) + } + ast::ExprWhile(ref cond, ref body, _) => { + controlflow::trans_while(bcx, expr.id, &**cond, &**body) + } + ast::ExprForLoop(ref pat, ref head, ref body, _) => { + controlflow::trans_for(bcx, + expr_info(expr), + &**pat, + &**head, + &**body) + } + ast::ExprLoop(ref body, _) => { + controlflow::trans_loop(bcx, expr.id, &**body) + } + ast::ExprAssign(ref dst, ref src) => { + let src_datum = unpack_datum!(bcx, trans(bcx, &**src)); + let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign")); + + if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) { + // If there are destructors involved, make sure we + // are copying from an rvalue, since that cannot possible + // alias an lvalue. We are concerned about code like: + // + // a = a + // + // but also + // + // a = a.b + // + // where e.g. a : Option and a.b : + // Option. In that case, freeing `a` before the + // assignment may also free `a.b`! + // + // We could avoid this intermediary with some analysis + // to determine whether `dst` may possibly own `src`. + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + let src_datum = unpack_datum!( + bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign")); + bcx = glue::drop_ty(bcx, + dst_datum.val, + dst_datum.ty, + Some(NodeInfo { id: expr.id, span: expr.span })); + src_datum.store_to(bcx, dst_datum.val) + } else { + src_datum.store_to(bcx, dst_datum.val) + } + } + ast::ExprAssignOp(op, ref dst, ref src) => { + trans_assign_op(bcx, expr, op, &**dst, &**src) + } + ast::ExprInlineAsm(ref a) => { + asm::trans_inline_asm(bcx, a) + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("trans_rvalue_stmt_unadjusted reached \ + fall-through case: {}", + expr.node).as_slice()); + } + } +} + +fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + dest: Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); + let mut bcx = bcx; + let tcx = bcx.tcx(); + + debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + + match expr.node { + ast::ExprParen(ref e) => { + trans_into(bcx, &**e, dest) + } + ast::ExprPath(_) => { + trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest) + } + ast::ExprIf(ref cond, ref thn, ref els) => { + controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest) + } + ast::ExprMatch(ref discr, ref arms, _) => { + _match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest) + } + ast::ExprBlock(ref blk) => { + controlflow::trans_block(bcx, &**blk, dest) + } + ast::ExprStruct(_, ref fields, ref base) => { + trans_struct(bcx, + fields.as_slice(), + base.as_ref().map(|e| &**e), + expr.span, + expr.id, + dest) + } + ast::ExprTup(ref args) => { + let numbered_fields: Vec<(uint, &ast::Expr)> = + args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect(); + trans_adt(bcx, + expr_ty(bcx, expr), + 0, + numbered_fields.as_slice(), + None, + dest, + Some(NodeInfo { id: expr.id, span: expr.span })) + } + ast::ExprLit(ref lit) => { + match lit.node { + ast::LitStr(ref s, _) => { + tvec::trans_lit_str(bcx, expr, (*s).clone(), dest) + } + _ => { + bcx.tcx() + .sess + .span_bug(expr.span, + "trans_rvalue_dps_unadjusted shouldn't be \ + translating this type of literal") + } + } + } + ast::ExprVec(..) | ast::ExprRepeat(..) => { + tvec::trans_fixed_vstore(bcx, expr, dest) + } + ast::ExprFnBlock(_, ref decl, ref body) | + ast::ExprProc(ref decl, ref body) => { + let expr_ty = expr_ty(bcx, expr); + let store = ty::ty_closure_store(expr_ty); + debug!("translating block function {} with type {}", + expr_to_string(expr), expr_ty.repr(tcx)); + closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest) + } + ast::ExprUnboxedFn(_, _, ref decl, ref body) => { + closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest) + } + ast::ExprCall(ref f, ref args) => { + if bcx.tcx().is_method_call(expr.id) { + trans_overloaded_call(bcx, + expr, + &**f, + args.as_slice(), + Some(dest)) + } else { + callee::trans_call(bcx, + expr, + &**f, + callee::ArgExprs(args.as_slice()), + dest) + } + } + ast::ExprMethodCall(_, _, ref args) => { + callee::trans_method_call(bcx, + expr, + &*args[0], + callee::ArgExprs(args.as_slice()), + dest) + } + ast::ExprBinary(_, ref lhs, ref rhs) => { + // if not overloaded, would be RvalueDatumExpr + let lhs = unpack_datum!(bcx, trans(bcx, &**lhs)); + let rhs_datum = unpack_datum!(bcx, trans(bcx, &**rhs)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs, + vec![(rhs_datum, rhs.id)], Some(dest)).bcx + } + ast::ExprUnary(_, ref subexpr) => { + // if not overloaded, would be RvalueDatumExpr + let arg = unpack_datum!(bcx, trans(bcx, &**subexpr)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), + arg, Vec::new(), Some(dest)).bcx + } + ast::ExprIndex(ref base, ref idx) => { + // if not overloaded, would be RvalueDatumExpr + let base = unpack_datum!(bcx, trans(bcx, &**base)); + let idx_datum = unpack_datum!(bcx, trans(bcx, &**idx)); + trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), base, + vec![(idx_datum, idx.id)], Some(dest)).bcx + } + ast::ExprCast(ref val, _) => { + // DPS output mode means this is a trait cast: + if ty::type_is_trait(node_id_type(bcx, expr.id)) { + let trait_ref = + bcx.tcx().object_cast_map.borrow() + .get(&expr.id) + .map(|t| (*t).clone()) + .unwrap(); + let trait_ref = + trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs); + let datum = unpack_datum!(bcx, trans(bcx, &**val)); + meth::trans_trait_cast(bcx, datum, expr.id, + trait_ref, dest) + } else { + bcx.tcx().sess.span_bug(expr.span, + "expr_cast of non-trait"); + } + } + ast::ExprAssignOp(op, ref dst, ref src) => { + trans_assign_op(bcx, expr, op, &**dst, &**src) + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("trans_rvalue_dps_unadjusted reached fall-through \ + case: {}", + expr.node).as_slice()); + } + } +} + +fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + ref_expr: &ast::Expr, + def: def::Def, + dest: Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_def_dps_unadjusted"); + + let lldest = match dest { + SaveIn(lldest) => lldest, + Ignore => { return bcx; } + }; + + match def { + def::DefVariant(tid, vid, _) => { + let variant_info = ty::enum_variant_with_id(bcx.tcx(), tid, vid); + if variant_info.args.len() > 0u { + // N-ary variant. + let llfn = callee::trans_fn_ref(bcx, vid, ExprId(ref_expr.id)); + Store(bcx, llfn, lldest); + return bcx; + } else { + // Nullary variant. + let ty = expr_ty(bcx, ref_expr); + let repr = adt::represent_type(bcx.ccx(), ty); + adt::trans_set_discr(bcx, &*repr, lldest, + variant_info.disr_val); + return bcx; + } + } + def::DefStruct(_) => { + let ty = expr_ty(bcx, ref_expr); + match ty::get(ty).sty { + ty::ty_struct(did, _) if ty::has_dtor(bcx.tcx(), did) => { + let repr = adt::represent_type(bcx.ccx(), ty); + adt::trans_set_discr(bcx, &*repr, lldest, 0); + } + _ => {} + } + bcx + } + _ => { + bcx.tcx().sess.span_bug(ref_expr.span, format!( + "Non-DPS def {} referened by {}", + def, bcx.node_id_to_string(ref_expr.id)).as_slice()); + } + } +} + +fn trans_def_fn_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + ref_expr: &ast::Expr, + def: def::Def) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_def_datum_unadjusted"); + + let llfn = match def { + def::DefFn(did, _) | + def::DefStruct(did) | def::DefVariant(_, did, _) | + def::DefStaticMethod(did, def::FromImpl(_)) | + def::DefMethod(did, _, def::FromImpl(_)) => { + callee::trans_fn_ref(bcx, did, ExprId(ref_expr.id)) + } + def::DefStaticMethod(impl_did, def::FromTrait(trait_did)) | + def::DefMethod(impl_did, _, def::FromTrait(trait_did)) => { + meth::trans_static_method_callee(bcx, impl_did, + trait_did, ref_expr.id) + } + _ => { + bcx.tcx().sess.span_bug(ref_expr.span, format!( + "trans_def_fn_unadjusted invoked on: {} for {}", + def, + ref_expr.repr(bcx.tcx())).as_slice()); + } + }; + + let fn_ty = expr_ty(bcx, ref_expr); + DatumBlock::new(bcx, Datum::new(llfn, fn_ty, RvalueExpr(Rvalue::new(ByValue)))) +} + +pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + def: def::Def) + -> Datum { + /*! + * Translates a reference to a local variable or argument. + * This always results in an lvalue datum. + */ + + let _icx = push_ctxt("trans_local_var"); + + match def { + def::DefUpvar(nid, _, _) => { + // Can't move upvars, so this is never a ZeroMemLastUse. + let local_ty = node_id_type(bcx, nid); + match bcx.fcx.llupvars.borrow().get(&nid) { + Some(&val) => Datum::new(val, local_ty, Lvalue), + None => { + bcx.sess().bug(format!( + "trans_local_var: no llval for upvar {} found", + nid).as_slice()); + } + } + } + def::DefLocal(nid) => { + let datum = match bcx.fcx.lllocals.borrow().get(&nid) { + Some(&v) => v, + None => { + bcx.sess().bug(format!( + "trans_local_var: no datum for local/arg {} found", + nid).as_slice()); + } + }; + debug!("take_local(nid={}, v={}, ty={})", + nid, bcx.val_to_string(datum.val), bcx.ty_to_string(datum.ty)); + datum + } + _ => { + bcx.sess().unimpl(format!( + "unsupported def type in trans_local_var: {}", + def).as_slice()); + } + } +} + +pub fn with_field_tys(tcx: &ty::ctxt, + ty: ty::t, + node_id_opt: Option, + op: |ty::Disr, (&[ty::field])| -> R) + -> R { + /*! + * Helper for enumerating the field types of structs, enums, or records. + * The optional node ID here is the node ID of the path identifying the enum + * variant in use. If none, this cannot possibly an enum variant (so, if it + * is and `node_id_opt` is none, this function panics). + */ + + match ty::get(ty).sty { + ty::ty_struct(did, ref substs) => { + op(0, struct_fields(tcx, did, substs).as_slice()) + } + + ty::ty_tup(ref v) => { + op(0, tup_fields(v.as_slice()).as_slice()) + } + + ty::ty_enum(_, ref substs) => { + // We want the *variant* ID here, not the enum ID. + match node_id_opt { + None => { + tcx.sess.bug(format!( + "cannot get field types from the enum type {} \ + without a node ID", + ty.repr(tcx)).as_slice()); + } + Some(node_id) => { + let def = tcx.def_map.borrow()[node_id].clone(); + match def { + def::DefVariant(enum_id, variant_id, _) => { + let variant_info = ty::enum_variant_with_id( + tcx, enum_id, variant_id); + op(variant_info.disr_val, + struct_fields(tcx, + variant_id, + substs).as_slice()) + } + _ => { + tcx.sess.bug("resolve didn't map this expr to a \ + variant ID") + } + } + } + } + } + + _ => { + tcx.sess.bug(format!( + "cannot get field types from the type {}", + ty.repr(tcx)).as_slice()); + } + } +} + +fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + fields: &[ast::Field], + base: Option<&ast::Expr>, + expr_span: codemap::Span, + expr_id: ast::NodeId, + dest: Dest) -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_rec"); + + let ty = node_id_type(bcx, expr_id); + let tcx = bcx.tcx(); + with_field_tys(tcx, ty, Some(expr_id), |discr, field_tys| { + let mut need_base = Vec::from_elem(field_tys.len(), true); + + let numbered_fields = fields.iter().map(|field| { + let opt_pos = + field_tys.iter().position(|field_ty| + field_ty.name == field.ident.node.name); + match opt_pos { + Some(i) => { + need_base[i] = false; + (i, &*field.expr) + } + None => { + tcx.sess.span_bug(field.span, + "Couldn't find field in struct type") + } + } + }).collect::>(); + let optbase = match base { + Some(base_expr) => { + let mut leftovers = Vec::new(); + for (i, b) in need_base.iter().enumerate() { + if *b { + leftovers.push((i, field_tys[i].mt.ty)) + } + } + Some(StructBaseInfo {expr: base_expr, + fields: leftovers }) + } + None => { + if need_base.iter().any(|b| *b) { + tcx.sess.span_bug(expr_span, "missing fields and no base expr") + } + None + } + }; + + trans_adt(bcx, + ty, + discr, + numbered_fields.as_slice(), + optbase, + dest, + Some(NodeInfo { id: expr_id, span: expr_span })) + }) +} + +/** + * Information that `trans_adt` needs in order to fill in the fields + * of a struct copied from a base struct (e.g., from an expression + * like `Foo { a: b, ..base }`. + * + * Note that `fields` may be empty; the base expression must always be + * evaluated for side-effects. + */ +pub struct StructBaseInfo<'a> { + /// The base expression; will be evaluated after all explicit fields. + expr: &'a ast::Expr, + /// The indices of fields to copy paired with their types. + fields: Vec<(uint, ty::t)> +} + +/** + * Constructs an ADT instance: + * + * - `fields` should be a list of field indices paired with the + * expression to store into that field. The initializers will be + * evaluated in the order specified by `fields`. + * + * - `optbase` contains information on the base struct (if any) from + * which remaining fields are copied; see comments on `StructBaseInfo`. + */ +pub fn trans_adt<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + ty: ty::t, + discr: ty::Disr, + fields: &[(uint, &ast::Expr)], + optbase: Option, + dest: Dest, + source_location: Option) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_adt"); + let fcx = bcx.fcx; + let repr = adt::represent_type(bcx.ccx(), ty); + + match source_location { + Some(src_loc) => debuginfo::set_source_location(bcx.fcx, + src_loc.id, + src_loc.span), + None => {} + }; + + // If we don't care about the result, just make a + // temporary stack slot + let addr = match dest { + SaveIn(pos) => pos, + Ignore => alloc_ty(bcx, ty, "temp"), + }; + + // This scope holds intermediates that must be cleaned should + // panic occur before the ADT as a whole is ready. + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + + // First we trans the base, if we have one, to the dest + for base in optbase.iter() { + assert_eq!(discr, 0); + + match ty::expr_kind(bcx.tcx(), &*base.expr) { + ty::RvalueDpsExpr | ty::RvalueDatumExpr if !ty::type_needs_drop(bcx.tcx(), ty) => { + bcx = trans_into(bcx, &*base.expr, SaveIn(addr)); + }, + ty::RvalueStmtExpr => bcx.tcx().sess.bug("unexpected expr kind for struct base expr"), + _ => { + let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base")); + for &(i, t) in base.fields.iter() { + let datum = base_datum.get_element( + bcx, t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); + assert!(ty::type_is_sized(bcx.tcx(), datum.ty)); + let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); + bcx = datum.store_to(bcx, dest); + } + } + } + } + + match source_location { + Some(src_loc) => debuginfo::set_source_location(bcx.fcx, + src_loc.id, + src_loc.span), + None => {} + }; + + if ty::type_is_simd(bcx.tcx(), ty) { + // This is the constructor of a SIMD type, such types are + // always primitive machine types and so do not have a + // destructor or require any clean-up. + let llty = type_of::type_of(bcx.ccx(), ty); + + // keep a vector as a register, and running through the field + // `insertelement`ing them directly into that register + // (i.e. avoid GEPi and `store`s to an alloca) . + let mut vec_val = C_undef(llty); + + for &(i, ref e) in fields.iter() { + let block_datum = trans(bcx, &**e); + bcx = block_datum.bcx; + let position = C_uint(bcx.ccx(), i); + let value = block_datum.datum.to_llscalarish(bcx); + vec_val = InsertElement(bcx, vec_val, value, position); + } + Store(bcx, vec_val, addr); + } else { + // Now, we just overwrite the fields we've explicitly specified + for &(i, ref e) in fields.iter() { + let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); + let e_ty = expr_ty_adjusted(bcx, &**e); + bcx = trans_into(bcx, &**e, SaveIn(dest)); + let scope = cleanup::CustomScope(custom_cleanup_scope); + fcx.schedule_lifetime_end(scope, dest); + fcx.schedule_drop_mem(scope, dest, e_ty); + } + } + + adt::trans_set_discr(bcx, &*repr, addr, discr); + + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + + // If we don't care about the result drop the temporary we made + match dest { + SaveIn(_) => bcx, + Ignore => { + bcx = glue::drop_ty(bcx, addr, ty, source_location); + base::call_lifetime_end(bcx, addr); + bcx + } + } +} + + +fn trans_immediate_lit<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + lit: &ast::Lit) + -> DatumBlock<'blk, 'tcx, Expr> { + // must not be a string constant, that is a RvalueDpsExpr + let _icx = push_ctxt("trans_immediate_lit"); + let ty = expr_ty(bcx, expr); + let v = consts::const_lit(bcx.ccx(), expr, lit); + immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock() +} + +fn trans_unary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + op: ast::UnOp, + sub_expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let ccx = bcx.ccx(); + let mut bcx = bcx; + let _icx = push_ctxt("trans_unary_datum"); + + let method_call = MethodCall::expr(expr.id); + + // The only overloaded operator that is translated to a datum + // is an overloaded deref, since it is always yields a `&T`. + // Otherwise, we should be in the RvalueDpsExpr path. + assert!( + op == ast::UnDeref || + !ccx.tcx().method_map.borrow().contains_key(&method_call)); + + let un_ty = expr_ty(bcx, expr); + + match op { + ast::UnNot => { + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + let llresult = Not(bcx, datum.to_llscalarish(bcx)); + immediate_rvalue_bcx(bcx, llresult, un_ty).to_expr_datumblock() + } + ast::UnNeg => { + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + let val = datum.to_llscalarish(bcx); + let llneg = { + if ty::type_is_fp(un_ty) { + FNeg(bcx, val) + } else { + Neg(bcx, val) + } + }; + immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock() + } + ast::UnUniq => { + trans_uniq_expr(bcx, un_ty, sub_expr, expr_ty(bcx, sub_expr)) + } + ast::UnDeref => { + let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); + deref_once(bcx, expr, datum, method_call) + } + } +} + +fn trans_uniq_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + box_ty: ty::t, + contents: &ast::Expr, + contents_ty: ty::t) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_uniq_expr"); + let fcx = bcx.fcx; + assert!(ty::type_is_sized(bcx.tcx(), contents_ty)); + let llty = type_of::type_of(bcx.ccx(), contents_ty); + let size = llsize_of(bcx.ccx(), llty); + let align = C_uint(bcx.ccx(), type_of::align_of(bcx.ccx(), contents_ty)); + let llty_ptr = llty.ptr_to(); + let Result { bcx, val } = malloc_raw_dyn(bcx, llty_ptr, box_ty, size, align); + // Unique boxes do not allocate for zero-size types. The standard library + // may assume that `free` is never called on the pointer returned for + // `Box`. + let bcx = if llsize_of_alloc(bcx.ccx(), llty) == 0 { + trans_into(bcx, contents, SaveIn(val)) + } else { + let custom_cleanup_scope = fcx.push_custom_cleanup_scope(); + fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope), + val, cleanup::HeapExchange, contents_ty); + let bcx = trans_into(bcx, contents, SaveIn(val)); + fcx.pop_custom_cleanup_scope(custom_cleanup_scope); + bcx + }; + immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock() +} + +fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + subexpr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_addr_of"); + let mut bcx = bcx; + let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of")); + match ty::get(sub_datum.ty).sty { + ty::ty_open(_) => { + // Opened DST value, close to a fat pointer + debug!("Closing fat pointer {}", bcx.ty_to_string(sub_datum.ty)); + + let scratch = rvalue_scratch_datum(bcx, + ty::close_type(bcx.tcx(), sub_datum.ty), + "fat_addr_of"); + let base = Load(bcx, get_dataptr(bcx, sub_datum.val)); + Store(bcx, base, get_dataptr(bcx, scratch.val)); + + let len = Load(bcx, get_len(bcx, sub_datum.val)); + Store(bcx, len, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + _ => { + // Sized value, ref to a thin pointer + let ty = expr_ty(bcx, expr); + immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock() + } + } +} + +// Important to get types for both lhs and rhs, because one might be _|_ +// and the other not. +fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + binop_expr: &ast::Expr, + binop_ty: ty::t, + op: ast::BinOp, + lhs_t: ty::t, + lhs: ValueRef, + rhs_t: ty::t, + rhs: ValueRef) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_eager_binop"); + + let tcx = bcx.tcx(); + let is_simd = ty::type_is_simd(tcx, lhs_t); + let intype = { + if is_simd { ty::simd_type(tcx, lhs_t) } + else { lhs_t } + }; + let is_float = ty::type_is_fp(intype); + let is_signed = ty::type_is_signed(intype); + + let rhs = base::cast_shift_expr_rhs(bcx, op, lhs, rhs); + + let mut bcx = bcx; + let val = match op { + ast::BiAdd => { + if is_float { FAdd(bcx, lhs, rhs) } + else { Add(bcx, lhs, rhs) } + } + ast::BiSub => { + if is_float { FSub(bcx, lhs, rhs) } + else { Sub(bcx, lhs, rhs) } + } + ast::BiMul => { + if is_float { FMul(bcx, lhs, rhs) } + else { Mul(bcx, lhs, rhs) } + } + ast::BiDiv => { + if is_float { + FDiv(bcx, lhs, rhs) + } else { + // Only zero-check integers; fp /0 is NaN + bcx = base::fail_if_zero_or_overflows(bcx, binop_expr.span, + op, lhs, rhs, rhs_t); + if is_signed { + SDiv(bcx, lhs, rhs) + } else { + UDiv(bcx, lhs, rhs) + } + } + } + ast::BiRem => { + if is_float { + FRem(bcx, lhs, rhs) + } else { + // Only zero-check integers; fp %0 is NaN + bcx = base::fail_if_zero_or_overflows(bcx, binop_expr.span, + op, lhs, rhs, rhs_t); + if is_signed { + SRem(bcx, lhs, rhs) + } else { + URem(bcx, lhs, rhs) + } + } + } + ast::BiBitOr => Or(bcx, lhs, rhs), + ast::BiBitAnd => And(bcx, lhs, rhs), + ast::BiBitXor => Xor(bcx, lhs, rhs), + ast::BiShl => Shl(bcx, lhs, rhs), + ast::BiShr => { + if is_signed { + AShr(bcx, lhs, rhs) + } else { LShr(bcx, lhs, rhs) } + } + ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { + if ty::type_is_scalar(rhs_t) { + unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op)) + } else if is_simd { + base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op) + } else { + bcx.tcx().sess.span_bug(binop_expr.span, "comparison operator unsupported for type") + } + } + _ => { + bcx.tcx().sess.span_bug(binop_expr.span, "unexpected binop"); + } + }; + + immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock() +} + +// refinement types would obviate the need for this +enum lazy_binop_ty { + lazy_and, + lazy_or, +} + +fn trans_lazy_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + binop_expr: &ast::Expr, + op: lazy_binop_ty, + a: &ast::Expr, + b: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_lazy_binop"); + let binop_ty = expr_ty(bcx, binop_expr); + let fcx = bcx.fcx; + + let DatumBlock {bcx: past_lhs, datum: lhs} = trans(bcx, a); + let lhs = lhs.to_llscalarish(past_lhs); + + if past_lhs.unreachable.get() { + return immediate_rvalue_bcx(past_lhs, lhs, binop_ty).to_expr_datumblock(); + } + + let join = fcx.new_id_block("join", binop_expr.id); + let before_rhs = fcx.new_id_block("before_rhs", b.id); + + match op { + lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb), + lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb) + } + + let DatumBlock {bcx: past_rhs, datum: rhs} = trans(before_rhs, b); + let rhs = rhs.to_llscalarish(past_rhs); + + if past_rhs.unreachable.get() { + return immediate_rvalue_bcx(join, lhs, binop_ty).to_expr_datumblock(); + } + + Br(past_rhs, join.llbb); + let phi = Phi(join, Type::i1(bcx.ccx()), &[lhs, rhs], + &[past_lhs.llbb, past_rhs.llbb]); + + return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock(); +} + +fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + op: ast::BinOp, + lhs: &ast::Expr, + rhs: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_binary"); + let ccx = bcx.ccx(); + + // if overloaded, would be RvalueDpsExpr + assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); + + match op { + ast::BiAnd => { + trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs) + } + ast::BiOr => { + trans_lazy_binop(bcx, expr, lazy_or, lhs, rhs) + } + _ => { + let mut bcx = bcx; + let lhs_datum = unpack_datum!(bcx, trans(bcx, lhs)); + let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs)); + let binop_ty = expr_ty(bcx, expr); + + debug!("trans_binary (expr {}): lhs_datum={}", + expr.id, + lhs_datum.to_string(ccx)); + let lhs_ty = lhs_datum.ty; + let lhs = lhs_datum.to_llscalarish(bcx); + + debug!("trans_binary (expr {}): rhs_datum={}", + expr.id, + rhs_datum.to_string(ccx)); + let rhs_ty = rhs_datum.ty; + let rhs = rhs_datum.to_llscalarish(bcx); + trans_eager_binop(bcx, expr, binop_ty, op, + lhs_ty, lhs, rhs_ty, rhs) + } + } +} + +fn trans_overloaded_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + method_call: MethodCall, + lhs: Datum, + rhs: Vec<(Datum, ast::NodeId)>, + dest: Option) + -> Result<'blk, 'tcx> { + let method_ty = (*bcx.tcx().method_map.borrow())[method_call].ty; + callee::trans_call_inner(bcx, + Some(expr_info(expr)), + monomorphize_type(bcx, method_ty), + |bcx, arg_cleanup_scope| { + meth::trans_method_callee(bcx, + method_call, + None, + arg_cleanup_scope) + }, + callee::ArgOverloadedOp(lhs, rhs), + dest) +} + +fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + callee: &'a ast::Expr, + args: &'a [P], + dest: Option) + -> Block<'blk, 'tcx> { + let method_call = MethodCall::expr(expr.id); + let method_type = (*bcx.tcx() + .method_map + .borrow())[method_call] + .ty; + let mut all_args = vec!(callee); + all_args.extend(args.iter().map(|e| &**e)); + unpack_result!(bcx, + callee::trans_call_inner(bcx, + Some(expr_info(expr)), + monomorphize_type(bcx, + method_type), + |bcx, arg_cleanup_scope| { + meth::trans_method_callee( + bcx, + method_call, + None, + arg_cleanup_scope) + }, + callee::ArgOverloadedCall(all_args), + dest)); + bcx +} + +fn int_cast(bcx: Block, + lldsttype: Type, + llsrctype: Type, + llsrc: ValueRef, + signed: bool) + -> ValueRef { + let _icx = push_ctxt("int_cast"); + unsafe { + let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype.to_ref()); + let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype.to_ref()); + return if dstsz == srcsz { + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { + ZExtOrBitCast(bcx, llsrc, lldsttype) + }; + } +} + +fn float_cast(bcx: Block, + lldsttype: Type, + llsrctype: Type, + llsrc: ValueRef) + -> ValueRef { + let _icx = push_ctxt("float_cast"); + let srcsz = llsrctype.float_width(); + let dstsz = lldsttype.float_width(); + return if dstsz > srcsz { + FPExt(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + FPTrunc(bcx, llsrc, lldsttype) + } else { llsrc }; +} + +#[deriving(PartialEq, Show)] +pub enum cast_kind { + cast_pointer, + cast_integral, + cast_float, + cast_enum, + cast_other, +} + +pub fn cast_type_kind(tcx: &ty::ctxt, t: ty::t) -> cast_kind { + match ty::get(t).sty { + ty::ty_char => cast_integral, + ty::ty_float(..) => cast_float, + ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => { + if ty::type_is_sized(tcx, mt.ty) { + cast_pointer + } else { + cast_other + } + } + ty::ty_bare_fn(..) => cast_pointer, + ty::ty_int(..) => cast_integral, + ty::ty_uint(..) => cast_integral, + ty::ty_bool => cast_integral, + ty::ty_enum(..) => cast_enum, + _ => cast_other + } +} + +fn cast_is_noop(t_in: ty::t, t_out: ty::t) -> bool { + match (ty::deref(t_in, true), ty::deref(t_out, true)) { + (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => { + t_in == t_out + } + _ => false + } +} + +fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + id: ast::NodeId) + -> DatumBlock<'blk, 'tcx, Expr> { + let _icx = push_ctxt("trans_cast"); + let mut bcx = bcx; + let ccx = bcx.ccx(); + + let t_in = expr_ty(bcx, expr); + let t_out = node_id_type(bcx, id); + let k_in = cast_type_kind(bcx.tcx(), t_in); + let k_out = cast_type_kind(bcx.tcx(), t_out); + let s_in = k_in == cast_integral && ty::type_is_signed(t_in); + let ll_t_in = type_of::arg_type_of(ccx, t_in); + let ll_t_out = type_of::arg_type_of(ccx, t_out); + + // Convert the value to be cast into a ValueRef, either by-ref or + // by-value as appropriate given its type: + let mut datum = unpack_datum!(bcx, trans(bcx, expr)); + + if cast_is_noop(datum.ty, t_out) { + datum.ty = t_out; + return DatumBlock::new(bcx, datum); + } + + let newval = match (k_in, k_out) { + (cast_integral, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) + } + (cast_float, cast_float) => { + let llexpr = datum.to_llscalarish(bcx); + float_cast(bcx, ll_t_out, ll_t_in, llexpr) + } + (cast_integral, cast_float) => { + let llexpr = datum.to_llscalarish(bcx); + if s_in { + SIToFP(bcx, llexpr, ll_t_out) + } else { UIToFP(bcx, llexpr, ll_t_out) } + } + (cast_float, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + if ty::type_is_signed(t_out) { + FPToSI(bcx, llexpr, ll_t_out) + } else { FPToUI(bcx, llexpr, ll_t_out) } + } + (cast_integral, cast_pointer) => { + let llexpr = datum.to_llscalarish(bcx); + IntToPtr(bcx, llexpr, ll_t_out) + } + (cast_pointer, cast_integral) => { + let llexpr = datum.to_llscalarish(bcx); + PtrToInt(bcx, llexpr, ll_t_out) + } + (cast_pointer, cast_pointer) => { + let llexpr = datum.to_llscalarish(bcx); + PointerCast(bcx, llexpr, ll_t_out) + } + (cast_enum, cast_integral) | + (cast_enum, cast_float) => { + let mut bcx = bcx; + let repr = adt::represent_type(ccx, t_in); + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id)); + let llexpr_ptr = datum.to_llref(); + let lldiscrim_a = + adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx))); + match k_out { + cast_integral => int_cast(bcx, ll_t_out, + val_ty(lldiscrim_a), + lldiscrim_a, true), + cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), + _ => { + ccx.sess().bug(format!("translating unsupported cast: \ + {} ({}) -> {} ({})", + t_in.repr(bcx.tcx()), + k_in, + t_out.repr(bcx.tcx()), + k_out).as_slice()) + } + } + } + _ => ccx.sess().bug(format!("translating unsupported cast: \ + {} ({}) -> {} ({})", + t_in.repr(bcx.tcx()), + k_in, + t_out.repr(bcx.tcx()), + k_out).as_slice()) + }; + return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock(); +} + +fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + op: ast::BinOp, + dst: &ast::Expr, + src: &ast::Expr) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_assign_op"); + let mut bcx = bcx; + + debug!("trans_assign_op(expr={})", bcx.expr_to_string(expr)); + + // User-defined operator methods cannot be used with `+=` etc right now + assert!(!bcx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id))); + + // Evaluate LHS (destination), which should be an lvalue + let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op")); + assert!(!ty::type_needs_drop(bcx.tcx(), dst_datum.ty)); + let dst_ty = dst_datum.ty; + let dst = load_ty(bcx, dst_datum.val, dst_datum.ty); + + // Evaluate RHS + let rhs_datum = unpack_datum!(bcx, trans(bcx, &*src)); + let rhs_ty = rhs_datum.ty; + let rhs = rhs_datum.to_llscalarish(bcx); + + // Perform computation and store the result + let result_datum = unpack_datum!( + bcx, trans_eager_binop(bcx, expr, dst_datum.ty, op, + dst_ty, dst, rhs_ty, rhs)); + return result_datum.store_to(bcx, dst_datum.val); +} + +fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + datum: Datum, + expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + + // Ensure cleanup of `datum` if not already scheduled and obtain + // a "by ref" pointer. + let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id)); + + // Compute final type. Note that we are loose with the region and + // mutability, since those things don't matter in trans. + let referent_ty = lv_datum.ty; + let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty); + + // Get the pointer. + let llref = lv_datum.to_llref(); + + // Construct the resulting datum, using what was the "by ref" + // ValueRef of type `referent_ty` to be the "by value" ValueRef + // of type `&referent_ty`. + DatumBlock::new(bcx, Datum::new(llref, ptr_ty, RvalueExpr(Rvalue::new(ByValue)))) +} + +fn deref_multiple<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + times: uint) + -> DatumBlock<'blk, 'tcx, Expr> { + let mut bcx = bcx; + let mut datum = datum; + for i in range(0, times) { + let method_call = MethodCall::autoderef(expr.id, i); + datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, method_call)); + } + DatumBlock { bcx: bcx, datum: datum } +} + +fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + method_call: MethodCall) + -> DatumBlock<'blk, 'tcx, Expr> { + let ccx = bcx.ccx(); + + debug!("deref_once(expr={}, datum={}, method_call={})", + expr.repr(bcx.tcx()), + datum.to_string(ccx), + method_call); + + let mut bcx = bcx; + + // Check for overloaded deref. + let method_ty = ccx.tcx().method_map.borrow() + .get(&method_call).map(|method| method.ty); + let datum = match method_ty { + Some(method_ty) => { + // Overloaded. Evaluate `trans_overloaded_op`, which will + // invoke the user's deref() method, which basically + // converts from the `Smaht` pointer that we have into + // a `&T` pointer. We can then proceed down the normal + // path (below) to dereference that `&T`. + let datum = match method_call.adjustment { + // Always perform an AutoPtr when applying an overloaded auto-deref + typeck::AutoDeref(_) => unpack_datum!(bcx, auto_ref(bcx, datum, expr)), + _ => datum + }; + + let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap(); + let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref"); + + unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call, + datum, Vec::new(), Some(SaveIn(scratch.val)))); + scratch.to_expr_datum() + } + None => { + // Not overloaded. We already have a pointer we know how to deref. + datum + } + }; + + let r = match ty::get(datum.ty).sty { + ty::ty_uniq(content_ty) => { + if ty::type_is_sized(bcx.tcx(), content_ty) { + deref_owned_pointer(bcx, expr, datum, content_ty) + } else { + // A fat pointer and an opened DST value have the same + // representation just different types. Since there is no + // temporary for `*e` here (because it is unsized), we cannot + // emulate the sized object code path for running drop glue and + // free. Instead, we schedule cleanup for `e`, turning it into + // an lvalue. + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); + + let datum = Datum::new(datum.val, ty::mk_open(bcx.tcx(), content_ty), LvalueExpr); + DatumBlock::new(bcx, datum) + } + } + + ty::ty_ptr(ty::mt { ty: content_ty, .. }) | + ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => { + if ty::type_is_sized(bcx.tcx(), content_ty) { + let ptr = datum.to_llscalarish(bcx); + + // Always generate an lvalue datum, even if datum.mode is + // an rvalue. This is because datum.mode is only an + // rvalue for non-owning pointers like &T or *T, in which + // case cleanup *is* scheduled elsewhere, by the true + // owner (or, in the case of *T, by the user). + DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) + } else { + // A fat pointer and an opened DST value have the same representation + // just different types. + DatumBlock::new(bcx, Datum::new(datum.val, + ty::mk_open(bcx.tcx(), content_ty), + LvalueExpr)) + } + } + + _ => { + bcx.tcx().sess.span_bug( + expr.span, + format!("deref invoked on expr of illegal type {}", + datum.ty.repr(bcx.tcx())).as_slice()); + } + }; + + debug!("deref_once(expr={}, method_call={}, result={})", + expr.id, method_call, r.datum.to_string(ccx)); + + return r; + + fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + datum: Datum, + content_ty: ty::t) + -> DatumBlock<'blk, 'tcx, Expr> { + /*! + * We microoptimize derefs of owned pointers a bit here. + * Basically, the idea is to make the deref of an rvalue + * result in an rvalue. This helps to avoid intermediate stack + * slots in the resulting LLVM. The idea here is that, if the + * `Box` pointer is an rvalue, then we can schedule a *shallow* + * free of the `Box` pointer, and then return a ByRef rvalue + * into the pointer. Because the free is shallow, it is legit + * to return an rvalue, because we know that the contents are + * not yet scheduled to be freed. The language rules ensure that the + * contents will be used (or moved) before the free occurs. + */ + + match datum.kind { + RvalueExpr(Rvalue { mode: ByRef }) => { + let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); + let ptr = Load(bcx, datum.val); + if !type_is_zero_size(bcx.ccx(), content_ty) { + bcx.fcx.schedule_free_value(scope, ptr, cleanup::HeapExchange, content_ty); + } + } + RvalueExpr(Rvalue { mode: ByValue }) => { + let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); + if !type_is_zero_size(bcx.ccx(), content_ty) { + bcx.fcx.schedule_free_value(scope, datum.val, cleanup::HeapExchange, + content_ty); + } + } + LvalueExpr => { } + } + + // If we had an rvalue in, we produce an rvalue out. + let (llptr, kind) = match datum.kind { + LvalueExpr => { + (Load(bcx, datum.val), LvalueExpr) + } + RvalueExpr(Rvalue { mode: ByRef }) => { + (Load(bcx, datum.val), RvalueExpr(Rvalue::new(ByRef))) + } + RvalueExpr(Rvalue { mode: ByValue }) => { + (datum.val, RvalueExpr(Rvalue::new(ByRef))) + } + }; + + let datum = Datum { ty: content_ty, val: llptr, kind: kind }; + DatumBlock { bcx: bcx, datum: datum } + } +} diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs new file mode 100644 index 00000000000..3ca37f9e355 --- /dev/null +++ b/src/librustc_trans/trans/foreign.rs @@ -0,0 +1,974 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use back::{link}; +use llvm::{ValueRef, CallConv, get_param}; +use llvm; +use middle::weak_lang_items; +use trans::base::{llvm_linkage_by_name, push_ctxt}; +use trans::base; +use trans::build::*; +use trans::cabi; +use trans::common::*; +use trans::machine; +use trans::type_::Type; +use trans::type_of::*; +use trans::type_of; +use middle::ty::FnSig; +use middle::ty; +use middle::subst::Subst; +use std::cmp; +use libc::c_uint; +use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi}; +use syntax::abi::{RustIntrinsic, Rust, RustCall, Stdcall, Fastcall, System}; +use syntax::codemap::Span; +use syntax::parse::token::{InternedString, special_idents}; +use syntax::parse::token; +use syntax::{ast}; +use syntax::{attr, ast_map}; +use util::ppaux::Repr; + +/////////////////////////////////////////////////////////////////////////// +// Type definitions + +struct ForeignTypes { + /// Rust signature of the function + fn_sig: ty::FnSig, + + /// Adapter object for handling native ABI rules (trust me, you + /// don't want to know) + fn_ty: cabi::FnType, + + /// LLVM types that will appear on the foreign function + llsig: LlvmSignature, +} + +struct LlvmSignature { + // LLVM versions of the types of this function's arguments. + llarg_tys: Vec , + + // LLVM version of the type that this function returns. Note that + // this *may not be* the declared return type of the foreign + // function, because the foreign function may opt to return via an + // out pointer. + llret_ty: Type, + + /// True if there is a return value (not bottom, not unit) + ret_def: bool, +} + + +/////////////////////////////////////////////////////////////////////////// +// Calls to external functions + +pub fn llvm_calling_convention(ccx: &CrateContext, + abi: Abi) -> CallConv { + match ccx.sess().target.target.adjust_abi(abi) { + RustIntrinsic => { + // Intrinsics are emitted at the call site + ccx.sess().bug("asked to register intrinsic fn"); + } + + Rust => { + // FIXME(#3678) Implement linking to foreign fns with Rust ABI + ccx.sess().unimpl("foreign functions with Rust ABI"); + } + + RustCall => { + // FIXME(#3678) Implement linking to foreign fns with Rust ABI + ccx.sess().unimpl("foreign functions with RustCall ABI"); + } + + // It's the ABI's job to select this, not us. + System => ccx.sess().bug("system abi should be selected elsewhere"), + + Stdcall => llvm::X86StdcallCallConv, + Fastcall => llvm::X86FastcallCallConv, + C => llvm::CCallConv, + Win64 => llvm::X86_64_Win64, + + // These API constants ought to be more specific... + Cdecl => llvm::CCallConv, + Aapcs => llvm::CCallConv, + } +} + +pub fn register_static(ccx: &CrateContext, + foreign_item: &ast::ForeignItem) -> ValueRef { + let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id); + let llty = type_of::type_of(ccx, ty); + + let ident = link_name(foreign_item); + match attr::first_attr_value_str_by_name(foreign_item.attrs.as_slice(), + "linkage") { + // If this is a static with a linkage specified, then we need to handle + // it a little specially. The typesystem prevents things like &T and + // extern "C" fn() from being non-null, so we can't just declare a + // static and call it a day. Some linkages (like weak) will make it such + // that the static actually has a null value. + Some(name) => { + let linkage = match llvm_linkage_by_name(name.get()) { + Some(linkage) => linkage, + None => { + ccx.sess().span_fatal(foreign_item.span, + "invalid linkage specified"); + } + }; + let llty2 = match ty::get(ty).sty { + ty::ty_ptr(ref mt) => type_of::type_of(ccx, mt.ty), + _ => { + ccx.sess().span_fatal(foreign_item.span, + "must have type `*T` or `*mut T`"); + } + }; + unsafe { + // Declare a symbol `foo` with the desired linkage. + let g1 = ident.get().with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(), buf) + }); + llvm::SetLinkage(g1, linkage); + + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. + let mut real_name = "_rust_extern_with_linkage_".to_string(); + real_name.push_str(ident.get()); + let g2 = real_name.with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) + }); + llvm::SetLinkage(g2, llvm::InternalLinkage); + llvm::LLVMSetInitializer(g2, g1); + g2 + } + } + None => unsafe { + // Generate an external declaration. + ident.get().with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf) + }) + } + } +} + +pub fn register_foreign_item_fn(ccx: &CrateContext, abi: Abi, fty: ty::t, + name: &str) -> ValueRef { + /*! + * Registers a foreign function found in a library. + * Just adds a LLVM global. + */ + + debug!("register_foreign_item_fn(abi={}, \ + ty={}, \ + name={})", + abi.repr(ccx.tcx()), + fty.repr(ccx.tcx()), + name); + + let cc = llvm_calling_convention(ccx, abi); + + // Register the function as a C extern fn + let tys = foreign_types_for_fn_ty(ccx, fty); + + // Make sure the calling convention is right for variadic functions + // (should've been caught if not in typeck) + if tys.fn_sig.variadic { + assert!(cc == llvm::CCallConv); + } + + // Create the LLVM value for the C extern fn + let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); + + let llfn = base::get_extern_fn(ccx, + &mut *ccx.externs().borrow_mut(), + name, + cc, + llfn_ty, + fty); + add_argument_attributes(&tys, llfn); + + llfn +} + +pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + callee_ty: ty::t, + llfn: ValueRef, + llretptr: ValueRef, + llargs_rust: &[ValueRef], + passed_arg_tys: Vec ) + -> Block<'blk, 'tcx> { + /*! + * Prepares a call to a native function. This requires adapting + * from the Rust argument passing rules to the native rules. + * + * # Parameters + * + * - `callee_ty`: Rust type for the function we are calling + * - `llfn`: the function pointer we are calling + * - `llretptr`: where to store the return value of the function + * - `llargs_rust`: a list of the argument values, prepared + * as they would be if calling a Rust function + * - `passed_arg_tys`: Rust type for the arguments. Normally we + * can derive these from callee_ty but in the case of variadic + * functions passed_arg_tys will include the Rust type of all + * the arguments including the ones not specified in the fn's signature. + */ + + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + + debug!("trans_native_call(callee_ty={}, \ + llfn={}, \ + llretptr={})", + callee_ty.repr(tcx), + ccx.tn().val_to_string(llfn), + ccx.tn().val_to_string(llretptr)); + + let (fn_abi, fn_sig) = match ty::get(callee_ty).sty { + ty::ty_bare_fn(ref fn_ty) => (fn_ty.abi, fn_ty.sig.clone()), + _ => ccx.sess().bug("trans_native_call called on non-function type") + }; + let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys.as_slice()); + let fn_type = cabi::compute_abi_info(ccx, + llsig.llarg_tys.as_slice(), + llsig.llret_ty, + llsig.ret_def); + + let arg_tys: &[cabi::ArgType] = fn_type.arg_tys.as_slice(); + + let mut llargs_foreign = Vec::new(); + + // If the foreign ABI expects return value by pointer, supply the + // pointer that Rust gave us. Sometimes we have to bitcast + // because foreign fns return slightly different (but equivalent) + // views on the same type (e.g., i64 in place of {i32,i32}). + if fn_type.ret_ty.is_indirect() { + match fn_type.ret_ty.cast { + Some(ty) => { + let llcastedretptr = + BitCast(bcx, llretptr, ty.ptr_to()); + llargs_foreign.push(llcastedretptr); + } + None => { + llargs_foreign.push(llretptr); + } + } + } + + for (i, &llarg_rust) in llargs_rust.iter().enumerate() { + let mut llarg_rust = llarg_rust; + + if arg_tys[i].is_ignore() { + continue; + } + + // Does Rust pass this argument by pointer? + let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]); + + debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}", + i, + ccx.tn().val_to_string(llarg_rust), + rust_indirect, + ccx.tn().type_to_string(arg_tys[i].ty)); + + // Ensure that we always have the Rust value indirectly, + // because it makes bitcasting easier. + if !rust_indirect { + let scratch = + base::alloca(bcx, + type_of::type_of(ccx, passed_arg_tys[i]), + "__arg"); + base::store_ty(bcx, llarg_rust, scratch, passed_arg_tys[i]); + llarg_rust = scratch; + } + + debug!("llarg_rust={} (after indirection)", + ccx.tn().val_to_string(llarg_rust)); + + // Check whether we need to do any casting + match arg_tys[i].cast { + Some(ty) => llarg_rust = BitCast(bcx, llarg_rust, ty.ptr_to()), + None => () + } + + debug!("llarg_rust={} (after casting)", + ccx.tn().val_to_string(llarg_rust)); + + // Finally, load the value if needed for the foreign ABI + let foreign_indirect = arg_tys[i].is_indirect(); + let llarg_foreign = if foreign_indirect { + llarg_rust + } else { + if ty::type_is_bool(passed_arg_tys[i]) { + let val = LoadRangeAssert(bcx, llarg_rust, 0, 2, llvm::False); + Trunc(bcx, val, Type::i1(bcx.ccx())) + } else { + Load(bcx, llarg_rust) + } + }; + + debug!("argument {}, llarg_foreign={}", + i, ccx.tn().val_to_string(llarg_foreign)); + + // fill padding with undef value + match arg_tys[i].pad { + Some(ty) => llargs_foreign.push(C_undef(ty)), + None => () + } + llargs_foreign.push(llarg_foreign); + } + + let cc = llvm_calling_convention(ccx, fn_abi); + + // A function pointer is called without the declaration available, so we have to apply + // any attributes with ABI implications directly to the call instruction. + let mut attrs = llvm::AttrBuilder::new(); + + // Add attributes that are always applicable, independent of the concrete foreign ABI + if fn_type.ret_ty.is_indirect() { + let llret_sz = machine::llsize_of_real(ccx, fn_type.ret_ty.ty); + + // The outptr can be noalias and nocapture because it's entirely + // invisible to the program. We also know it's nonnull as well + // as how many bytes we can dereference + attrs.arg(1, llvm::NoAliasAttribute) + .arg(1, llvm::NoCaptureAttribute) + .arg(1, llvm::DereferenceableAttribute(llret_sz)); + }; + + // Add attributes that depend on the concrete foreign ABI + let mut arg_idx = if fn_type.ret_ty.is_indirect() { 1 } else { 0 }; + match fn_type.ret_ty.attr { + Some(attr) => { attrs.arg(arg_idx, attr); }, + _ => () + } + + arg_idx += 1; + for arg_ty in fn_type.arg_tys.iter() { + if arg_ty.is_ignore() { + continue; + } + // skip padding + if arg_ty.pad.is_some() { arg_idx += 1; } + + match arg_ty.attr { + Some(attr) => { attrs.arg(arg_idx, attr); }, + _ => {} + } + + arg_idx += 1; + } + + let llforeign_retval = CallWithConv(bcx, + llfn, + llargs_foreign.as_slice(), + cc, + Some(attrs)); + + // If the function we just called does not use an outpointer, + // store the result into the rust outpointer. Cast the outpointer + // type to match because some ABIs will use a different type than + // the Rust type. e.g., a {u32,u32} struct could be returned as + // u64. + if llsig.ret_def && !fn_type.ret_ty.is_indirect() { + let llrust_ret_ty = llsig.llret_ty; + let llforeign_ret_ty = match fn_type.ret_ty.cast { + Some(ty) => ty, + None => fn_type.ret_ty.ty + }; + + debug!("llretptr={}", ccx.tn().val_to_string(llretptr)); + debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval)); + debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty)); + debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty)); + + if llrust_ret_ty == llforeign_ret_ty { + match fn_sig.output { + ty::FnConverging(result_ty) => { + base::store_ty(bcx, llforeign_retval, llretptr, result_ty) + } + ty::FnDiverging => {} + } + } else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); + Store(bcx, llforeign_retval, llscratch); + let llscratch_i8 = BitCast(bcx, llscratch, Type::i8(ccx).ptr_to()); + let llretptr_i8 = BitCast(bcx, llretptr, Type::i8(ccx).ptr_to()); + let llrust_size = machine::llsize_of_store(ccx, llrust_ret_ty); + let llforeign_align = machine::llalign_of_min(ccx, llforeign_ret_ty); + let llrust_align = machine::llalign_of_min(ccx, llrust_ret_ty); + let llalign = cmp::min(llforeign_align, llrust_align); + debug!("llrust_size={}", llrust_size); + base::call_memcpy(bcx, llretptr_i8, llscratch_i8, + C_uint(ccx, llrust_size), llalign as u32); + } + } + + return bcx; +} + +pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) { + let _icx = push_ctxt("foreign::trans_foreign_mod"); + for foreign_item in foreign_mod.items.iter() { + let lname = link_name(&**foreign_item); + + match foreign_item.node { + ast::ForeignItemFn(..) => { + match foreign_mod.abi { + Rust | RustIntrinsic => {} + abi => { + let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id); + register_foreign_item_fn(ccx, abi, ty, + lname.get().as_slice()); + // Unlike for other items, we shouldn't call + // `base::update_linkage` here. Foreign items have + // special linkage requirements, which are handled + // inside `foreign::register_*`. + } + } + } + _ => {} + } + + ccx.item_symbols().borrow_mut().insert(foreign_item.id, + lname.get().to_string()); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Rust functions with foreign ABIs +// +// These are normal Rust functions defined with foreign ABIs. For +// now, and perhaps forever, we translate these using a "layer of +// indirection". That is, given a Rust declaration like: +// +// extern "C" fn foo(i: u32) -> u32 { ... } +// +// we will generate a function like: +// +// S foo(T i) { +// S r; +// foo0(&r, NULL, i); +// return r; +// } +// +// #[inline_always] +// void foo0(uint32_t *r, void *env, uint32_t i) { ... } +// +// Here the (internal) `foo0` function follows the Rust ABI as normal, +// where the `foo` function follows the C ABI. We rely on LLVM to +// inline the one into the other. Of course we could just generate the +// correct code in the first place, but this is much simpler. + +pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext, + t: ty::t, + name: &str) + -> ValueRef { + let tys = foreign_types_for_fn_ty(ccx, t); + let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); + let cconv = match ty::get(t).sty { + ty::ty_bare_fn(ref fn_ty) => { + llvm_calling_convention(ccx, fn_ty.abi) + } + _ => panic!("expected bare fn in decl_rust_fn_with_foreign_abi") + }; + let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::FnConverging(ty::mk_nil(ccx.tcx()))); + add_argument_attributes(&tys, llfn); + debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", + ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); + llfn +} + +pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext, + sp: Span, + sym: String, + node_id: ast::NodeId) + -> ValueRef { + let _icx = push_ctxt("foreign::register_foreign_fn"); + + let tys = foreign_types_for_id(ccx, node_id); + let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys); + let t = ty::node_id_to_type(ccx.tcx(), node_id); + let cconv = match ty::get(t).sty { + ty::ty_bare_fn(ref fn_ty) => { + llvm_calling_convention(ccx, fn_ty.abi) + } + _ => panic!("expected bare fn in register_rust_fn_with_foreign_abi") + }; + let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty); + add_argument_attributes(&tys, llfn); + debug!("register_rust_fn_with_foreign_abi(node_id={}, llfn_ty={}, llfn={})", + node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); + llfn +} + +pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext, + decl: &ast::FnDecl, + body: &ast::Block, + attrs: &[ast::Attribute], + llwrapfn: ValueRef, + param_substs: ¶m_substs, + id: ast::NodeId, + hash: Option<&str>) { + let _icx = push_ctxt("foreign::build_foreign_fn"); + + let fnty = ty::node_id_to_type(ccx.tcx(), id); + let mty = fnty.subst(ccx.tcx(), ¶m_substs.substs); + let tys = foreign_types_for_fn_ty(ccx, mty); + + unsafe { // unsafe because we call LLVM operations + // Build up the Rust function (`foo0` above). + let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash); + + // Build up the foreign wrapper (`foo` above). + return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty); + } + + fn build_rust_fn(ccx: &CrateContext, + decl: &ast::FnDecl, + body: &ast::Block, + param_substs: ¶m_substs, + attrs: &[ast::Attribute], + id: ast::NodeId, + hash: Option<&str>) + -> ValueRef { + let _icx = push_ctxt("foreign::foreign::build_rust_fn"); + let tcx = ccx.tcx(); + let t = ty::node_id_to_type(tcx, id).subst( + ccx.tcx(), ¶m_substs.substs); + + let ps = ccx.tcx().map.with_path(id, |path| { + let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name)); + link::mangle(path.chain(abi.into_iter()), hash) + }); + + // Compute the type that the function would have if it were just a + // normal Rust function. This will be the type of the wrappee fn. + match ty::get(t).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.abi != Rust && f.abi != RustIntrinsic); + } + _ => { + ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \ + expected a bare fn ty", + ccx.tcx().map.path_to_string(id), + t.repr(tcx)).as_slice()); + } + }; + + debug!("build_rust_fn: path={} id={} t={}", + ccx.tcx().map.path_to_string(id), + id, t.repr(tcx)); + + let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice()); + base::set_llvm_fn_attrs(ccx, attrs, llfn); + base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]); + llfn + } + + unsafe fn build_wrap_fn(ccx: &CrateContext, + llrustfn: ValueRef, + llwrapfn: ValueRef, + tys: &ForeignTypes, + t: ty::t) { + let _icx = push_ctxt( + "foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn"); + let tcx = ccx.tcx(); + + debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})", + ccx.tn().val_to_string(llrustfn), + ccx.tn().val_to_string(llwrapfn), + t.repr(ccx.tcx())); + + // Avoid all the Rust generation stuff and just generate raw + // LLVM here. + // + // We want to generate code like this: + // + // S foo(T i) { + // S r; + // foo0(&r, NULL, i); + // return r; + // } + + let the_block = + "the block".with_c_str( + |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, s)); + + let builder = ccx.builder(); + builder.position_at_end(the_block); + + // Array for the arguments we will pass to the rust function. + let mut llrust_args = Vec::new(); + let mut next_foreign_arg_counter: c_uint = 0; + let next_foreign_arg: |pad: bool| -> c_uint = |pad: bool| { + next_foreign_arg_counter += if pad { + 2 + } else { + 1 + }; + next_foreign_arg_counter - 1 + }; + + // If there is an out pointer on the foreign function + let foreign_outptr = { + if tys.fn_ty.ret_ty.is_indirect() { + Some(get_param(llwrapfn, next_foreign_arg(false))) + } else { + None + } + }; + + // Push Rust return pointer, using null if it will be unused. + let rust_uses_outptr = match tys.fn_sig.output { + ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty), + ty::FnDiverging => false + }; + let return_alloca: Option; + let llrust_ret_ty = tys.llsig.llret_ty; + let llrust_retptr_ty = llrust_ret_ty.ptr_to(); + if rust_uses_outptr { + // Rust expects to use an outpointer. If the foreign fn + // also uses an outpointer, we can reuse it, but the types + // may vary, so cast first to the Rust type. If the + // foreign fn does NOT use an outpointer, we will have to + // alloca some scratch space on the stack. + match foreign_outptr { + Some(llforeign_outptr) => { + debug!("out pointer, foreign={}", + ccx.tn().val_to_string(llforeign_outptr)); + let llrust_retptr = + builder.bitcast(llforeign_outptr, llrust_retptr_ty); + debug!("out pointer, foreign={} (casted)", + ccx.tn().val_to_string(llrust_retptr)); + llrust_args.push(llrust_retptr); + return_alloca = None; + } + + None => { + let slot = builder.alloca(llrust_ret_ty, "return_alloca"); + debug!("out pointer, \ + allocad={}, \ + llrust_ret_ty={}, \ + return_ty={}", + ccx.tn().val_to_string(slot), + ccx.tn().type_to_string(llrust_ret_ty), + tys.fn_sig.output.repr(tcx)); + llrust_args.push(slot); + return_alloca = Some(slot); + } + } + } else { + // Rust does not expect an outpointer. If the foreign fn + // does use an outpointer, then we will do a store of the + // value that the Rust fn returns. + return_alloca = None; + }; + + // Build up the arguments to the call to the rust function. + // Careful to adapt for cases where the native convention uses + // a pointer and Rust does not or vice versa. + for i in range(0, tys.fn_sig.inputs.len()) { + let rust_ty = tys.fn_sig.inputs[i]; + let llrust_ty = tys.llsig.llarg_tys[i]; + let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty); + let llforeign_arg_ty = tys.fn_ty.arg_tys[i]; + let foreign_indirect = llforeign_arg_ty.is_indirect(); + + if llforeign_arg_ty.is_ignore() { + debug!("skipping ignored arg #{}", i); + llrust_args.push(C_undef(llrust_ty)); + continue; + } + + // skip padding + let foreign_index = next_foreign_arg(llforeign_arg_ty.pad.is_some()); + let mut llforeign_arg = get_param(llwrapfn, foreign_index); + + debug!("llforeign_arg {}{}: {}", "#", + i, ccx.tn().val_to_string(llforeign_arg)); + debug!("rust_indirect = {}, foreign_indirect = {}", + rust_indirect, foreign_indirect); + + // Ensure that the foreign argument is indirect (by + // pointer). It makes adapting types easier, since we can + // always just bitcast pointers. + if !foreign_indirect { + llforeign_arg = if ty::type_is_bool(rust_ty) { + let lltemp = builder.alloca(Type::bool(ccx), ""); + builder.store(builder.zext(llforeign_arg, Type::bool(ccx)), lltemp); + lltemp + } else { + let lltemp = builder.alloca(val_ty(llforeign_arg), ""); + builder.store(llforeign_arg, lltemp); + lltemp + } + } + + // If the types in the ABI and the Rust types don't match, + // bitcast the llforeign_arg pointer so it matches the types + // Rust expects. + if llforeign_arg_ty.cast.is_some() { + assert!(!foreign_indirect); + llforeign_arg = builder.bitcast(llforeign_arg, llrust_ty.ptr_to()); + } + + let llrust_arg = if rust_indirect { + llforeign_arg + } else { + if ty::type_is_bool(rust_ty) { + let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False); + builder.trunc(tmp, Type::i1(ccx)) + } else { + builder.load(llforeign_arg) + } + }; + + debug!("llrust_arg {}{}: {}", "#", + i, ccx.tn().val_to_string(llrust_arg)); + llrust_args.push(llrust_arg); + } + + // Perform the call itself + debug!("calling llrustfn = {}, t = {}", + ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx())); + let attributes = base::get_fn_llvm_attributes(ccx, t); + let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes)); + + // Get the return value where the foreign fn expects it. + let llforeign_ret_ty = match tys.fn_ty.ret_ty.cast { + Some(ty) => ty, + None => tys.fn_ty.ret_ty.ty + }; + match foreign_outptr { + None if !tys.llsig.ret_def => { + // Function returns `()` or `bot`, which in Rust is the LLVM + // type "{}" but in foreign ABIs is "Void". + builder.ret_void(); + } + + None if rust_uses_outptr => { + // Rust uses an outpointer, but the foreign ABI does not. Load. + let llrust_outptr = return_alloca.unwrap(); + let llforeign_outptr_casted = + builder.bitcast(llrust_outptr, llforeign_ret_ty.ptr_to()); + let llforeign_retval = builder.load(llforeign_outptr_casted); + builder.ret(llforeign_retval); + } + + None if llforeign_ret_ty != llrust_ret_ty => { + // Neither ABI uses an outpointer, but the types don't + // quite match. Must cast. Probably we should try and + // examine the types and use a concrete llvm cast, but + // right now we just use a temp memory location and + // bitcast the pointer, which is the same thing the + // old wrappers used to do. + let lltemp = builder.alloca(llforeign_ret_ty, ""); + let lltemp_casted = builder.bitcast(lltemp, llrust_ret_ty.ptr_to()); + builder.store(llrust_ret_val, lltemp_casted); + let llforeign_retval = builder.load(lltemp); + builder.ret(llforeign_retval); + } + + None => { + // Neither ABI uses an outpointer, and the types + // match. Easy peasy. + builder.ret(llrust_ret_val); + } + + Some(llforeign_outptr) if !rust_uses_outptr => { + // Foreign ABI requires an out pointer, but Rust doesn't. + // Store Rust return value. + let llforeign_outptr_casted = + builder.bitcast(llforeign_outptr, llrust_retptr_ty); + builder.store(llrust_ret_val, llforeign_outptr_casted); + builder.ret_void(); + } + + Some(_) => { + // Both ABIs use outpointers. Easy peasy. + builder.ret_void(); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// General ABI Support +// +// This code is kind of a confused mess and needs to be reworked given +// the massive simplifications that have occurred. + +pub fn link_name(i: &ast::ForeignItem) -> InternedString { + match attr::first_attr_value_str_by_name(i.attrs.as_slice(), "link_name") { + Some(ln) => ln.clone(), + None => match weak_lang_items::link_name(i.attrs.as_slice()) { + Some(name) => name, + None => token::get_ident(i.ident), + } + } +} + +fn foreign_signature(ccx: &CrateContext, fn_sig: &ty::FnSig, arg_tys: &[ty::t]) + -> LlvmSignature { + /*! + * The ForeignSignature is the LLVM types of the arguments/return type + * of a function. Note that these LLVM types are not quite the same + * as the LLVM types would be for a native Rust function because foreign + * functions just plain ignore modes. They also don't pass aggregate + * values by pointer like we do. + */ + + let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect(); + let (llret_ty, ret_def) = match fn_sig.output { + ty::FnConverging(ret_ty) => + (type_of::arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)), + ty::FnDiverging => + (Type::nil(ccx), false) + }; + LlvmSignature { + llarg_tys: llarg_tys, + llret_ty: llret_ty, + ret_def: ret_def + } +} + +fn foreign_types_for_id(ccx: &CrateContext, + id: ast::NodeId) -> ForeignTypes { + foreign_types_for_fn_ty(ccx, ty::node_id_to_type(ccx.tcx(), id)) +} + +fn foreign_types_for_fn_ty(ccx: &CrateContext, + ty: ty::t) -> ForeignTypes { + let fn_sig = match ty::get(ty).sty { + ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(), + _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type") + }; + let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs.as_slice()); + let fn_ty = cabi::compute_abi_info(ccx, + llsig.llarg_tys.as_slice(), + llsig.llret_ty, + llsig.ret_def); + debug!("foreign_types_for_fn_ty(\ + ty={}, \ + llsig={} -> {}, \ + fn_ty={} -> {}, \ + ret_def={}", + ty.repr(ccx.tcx()), + ccx.tn().types_to_str(llsig.llarg_tys.as_slice()), + ccx.tn().type_to_string(llsig.llret_ty), + ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::>().as_slice()), + ccx.tn().type_to_string(fn_ty.ret_ty.ty), + llsig.ret_def); + + ForeignTypes { + fn_sig: fn_sig, + llsig: llsig, + fn_ty: fn_ty + } +} + +fn lltype_for_fn_from_foreign_types(ccx: &CrateContext, tys: &ForeignTypes) -> Type { + let mut llargument_tys = Vec::new(); + + let ret_ty = tys.fn_ty.ret_ty; + let llreturn_ty = if ret_ty.is_indirect() { + llargument_tys.push(ret_ty.ty.ptr_to()); + Type::void(ccx) + } else { + match ret_ty.cast { + Some(ty) => ty, + None => ret_ty.ty + } + }; + + for &arg_ty in tys.fn_ty.arg_tys.iter() { + if arg_ty.is_ignore() { + continue; + } + // add padding + match arg_ty.pad { + Some(ty) => llargument_tys.push(ty), + None => () + } + + let llarg_ty = if arg_ty.is_indirect() { + arg_ty.ty.ptr_to() + } else { + match arg_ty.cast { + Some(ty) => ty, + None => arg_ty.ty + } + }; + + llargument_tys.push(llarg_ty); + } + + if tys.fn_sig.variadic { + Type::variadic_func(llargument_tys.as_slice(), &llreturn_ty) + } else { + Type::func(llargument_tys.as_slice(), &llreturn_ty) + } +} + +pub fn lltype_for_foreign_fn(ccx: &CrateContext, ty: ty::t) -> Type { + lltype_for_fn_from_foreign_types(ccx, &foreign_types_for_fn_ty(ccx, ty)) +} + +fn add_argument_attributes(tys: &ForeignTypes, + llfn: ValueRef) { + let mut i = if tys.fn_ty.ret_ty.is_indirect() { + 1i + } else { + 0i + }; + + match tys.fn_ty.ret_ty.attr { + Some(attr) => unsafe { + llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64); + }, + None => {} + } + + i += 1; + + for &arg_ty in tys.fn_ty.arg_tys.iter() { + if arg_ty.is_ignore() { + continue; + } + // skip padding + if arg_ty.pad.is_some() { i += 1; } + + match arg_ty.attr { + Some(attr) => unsafe { + llvm::LLVMAddFunctionAttribute(llfn, i as c_uint, attr.bits() as u64); + }, + None => () + } + + i += 1; + } +} diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs new file mode 100644 index 00000000000..cceea96d4c1 --- /dev/null +++ b/src/librustc_trans/trans/glue.rs @@ -0,0 +1,593 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! +// +// Code relating to taking, dropping, etc as well as type descriptors. + + +use back::abi; +use back::link::*; +use llvm::{ValueRef, True, get_param}; +use llvm; +use middle::lang_items::ExchangeFreeFnLangItem; +use middle::subst; +use middle::subst::Subst; +use trans::adt; +use trans::base::*; +use trans::build::*; +use trans::callee; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::common::*; +use trans::datum; +use trans::debuginfo; +use trans::expr; +use trans::machine::*; +use trans::tvec; +use trans::type_::Type; +use trans::type_of::{type_of, sizing_type_of, align_of}; +use middle::ty; +use util::ppaux::{ty_to_short_str, Repr}; +use util::ppaux; + +use arena::TypedArena; +use std::c_str::ToCStr; +use libc::c_uint; +use syntax::ast; +use syntax::parse::token; + +pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, + size: ValueRef, align: ValueRef) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("trans_exchange_free"); + let ccx = cx.ccx(); + callee::trans_lang_call(cx, + langcall(cx, None, "", ExchangeFreeFnLangItem), + &[PointerCast(cx, v, Type::i8p(ccx)), size, align], + Some(expr::Ignore)).bcx +} + +pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, + size: u64, align: u32) -> Block<'blk, 'tcx> { + trans_exchange_free_dyn(cx, v, C_uint(cx.ccx(), size), + C_uint(cx.ccx(), align)) +} + +pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ptr: ValueRef, + content_ty: ty::t) -> Block<'blk, 'tcx> { + assert!(ty::type_is_sized(bcx.ccx().tcx(), content_ty)); + let sizing_type = sizing_type_of(bcx.ccx(), content_ty); + let content_size = llsize_of_alloc(bcx.ccx(), sizing_type); + + // `Box` does not allocate. + if content_size != 0 { + let content_align = align_of(bcx.ccx(), content_ty); + trans_exchange_free(bcx, ptr, content_size, content_align) + } else { + bcx + } +} + +pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t { + let tcx = ccx.tcx(); + // Even if there is no dtor for t, there might be one deeper down and we + // might need to pass in the vtable ptr. + if !ty::type_is_sized(tcx, t) { + return t + } + if !ty::type_needs_drop(tcx, t) { + return ty::mk_i8(); + } + match ty::get(t).sty { + ty::ty_uniq(typ) if !ty::type_needs_drop(tcx, typ) + && ty::type_is_sized(tcx, typ) => { + let llty = sizing_type_of(ccx, typ); + // `Box` does not allocate. + if llsize_of_alloc(ccx, llty) == 0 { + ty::mk_i8() + } else { + t + } + } + _ => t + } +} + +pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + v: ValueRef, + t: ty::t, + source_location: Option) + -> Block<'blk, 'tcx> { + // NB: v is an *alias* of type t here, not a direct value. + debug!("drop_ty(t={})", t.repr(bcx.tcx())); + let _icx = push_ctxt("drop_ty"); + if ty::type_needs_drop(bcx.tcx(), t) { + let ccx = bcx.ccx(); + let glue = get_drop_glue(ccx, t); + let glue_type = get_drop_glue_type(ccx, t); + let ptr = if glue_type != t { + PointerCast(bcx, v, type_of(ccx, glue_type).ptr_to()) + } else { + v + }; + + match source_location { + Some(sl) => debuginfo::set_source_location(bcx.fcx, sl.id, sl.span), + None => debuginfo::clear_source_location(bcx.fcx) + }; + + Call(bcx, glue, &[ptr], None); + } + bcx +} + +pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + v: ValueRef, + t: ty::t, + source_location: Option) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("drop_ty_immediate"); + let vp = alloca(bcx, type_of(bcx.ccx(), t), ""); + Store(bcx, v, vp); + drop_ty(bcx, vp, t, source_location) +} + +pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { + debug!("make drop glue for {}", ppaux::ty_to_string(ccx.tcx(), t)); + let t = get_drop_glue_type(ccx, t); + debug!("drop glue type {}", ppaux::ty_to_string(ccx.tcx(), t)); + match ccx.drop_glues().borrow().get(&t) { + Some(&glue) => return glue, + _ => { } + } + + let llty = if ty::type_is_sized(ccx.tcx(), t) { + type_of(ccx, t).ptr_to() + } else { + type_of(ccx, ty::mk_uniq(ccx.tcx(), t)).ptr_to() + }; + + let llfnty = Type::glue_fn(ccx, llty); + + let (glue, new_sym) = match ccx.available_drop_glues().borrow().get(&t) { + Some(old_sym) => { + let glue = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil(ccx.tcx())); + (glue, None) + }, + None => { + let (sym, glue) = declare_generic_glue(ccx, t, llfnty, "drop"); + (glue, Some(sym)) + }, + }; + + ccx.drop_glues().borrow_mut().insert(t, glue); + + // To avoid infinite recursion, don't `make_drop_glue` until after we've + // added the entry to the `drop_glues` cache. + match new_sym { + Some(sym) => { + ccx.available_drop_glues().borrow_mut().insert(t, sym); + // We're creating a new drop glue, so also generate a body. + make_generic_glue(ccx, t, glue, make_drop_glue, "drop"); + }, + None => {}, + } + + glue +} + +fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, + t: ty::t, + v0: ValueRef, + dtor_did: ast::DefId, + class_did: ast::DefId, + substs: &subst::Substs) + -> Block<'blk, 'tcx> { + let repr = adt::represent_type(bcx.ccx(), t); + let struct_data = if ty::type_is_sized(bcx.tcx(), t) { + v0 + } else { + let llval = GEPi(bcx, v0, &[0, abi::slice_elt_base]); + Load(bcx, llval) + }; + let drop_flag = unpack_datum!(bcx, adt::trans_drop_flag_ptr(bcx, &*repr, struct_data)); + with_cond(bcx, load_ty(bcx, drop_flag.val, ty::mk_bool()), |cx| { + trans_struct_drop(cx, t, v0, dtor_did, class_did, substs) + }) +} + +fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + t: ty::t, + v0: ValueRef, + dtor_did: ast::DefId, + class_did: ast::DefId, + substs: &subst::Substs) + -> Block<'blk, 'tcx> { + let repr = adt::represent_type(bcx.ccx(), t); + + // Find and call the actual destructor + let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, t, + class_did, substs); + + // The first argument is the "self" argument for drop + let params = unsafe { + let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr)); + ty.element_type().func_params() + }; + + let fty = ty::lookup_item_type(bcx.tcx(), dtor_did).ty.subst(bcx.tcx(), substs); + let self_ty = match ty::get(fty).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.sig.inputs.len() == 1); + f.sig.inputs[0] + } + _ => bcx.sess().bug(format!("Expected function type, found {}", + bcx.ty_to_string(fty)).as_slice()) + }; + + let (struct_data, info) = if ty::type_is_sized(bcx.tcx(), t) { + (v0, None) + } else { + let data = GEPi(bcx, v0, &[0, abi::slice_elt_base]); + let info = GEPi(bcx, v0, &[0, abi::slice_elt_len]); + (Load(bcx, data), Some(Load(bcx, info))) + }; + + adt::fold_variants(bcx, &*repr, struct_data, |variant_cx, st, value| { + // Be sure to put all of the fields into a scope so we can use an invoke + // instruction to call the user destructor but still call the field + // destructors if the user destructor panics. + let field_scope = variant_cx.fcx.push_custom_cleanup_scope(); + + // Class dtors have no explicit args, so the params should + // just consist of the environment (self). + assert_eq!(params.len(), 1); + let self_arg = if ty::type_is_fat_ptr(bcx.tcx(), self_ty) { + // The dtor expects a fat pointer, so make one, even if we have to fake it. + let boxed_ty = ty::mk_open(bcx.tcx(), t); + let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_self"); + Store(bcx, value, GEPi(bcx, scratch.val, &[0, abi::slice_elt_base])); + Store(bcx, + // If we just had a thin pointer, make a fat pointer by sticking + // null where we put the unsizing info. This works because t + // is a sized type, so we will only unpack the fat pointer, never + // use the fake info. + info.unwrap_or(C_null(Type::i8p(bcx.ccx()))), + GEPi(bcx, scratch.val, &[0, abi::slice_elt_len])); + PointerCast(variant_cx, scratch.val, params[0]) + } else { + PointerCast(variant_cx, value, params[0]) + }; + let args = vec!(self_arg); + + // Add all the fields as a value which needs to be cleaned at the end of + // this scope. Iterate in reverse order so a Drop impl doesn't reverse + // the order in which fields get dropped. + for (i, ty) in st.fields.iter().enumerate().rev() { + let llfld_a = adt::struct_field_ptr(variant_cx, &*st, value, i, false); + + let val = if ty::type_is_sized(bcx.tcx(), *ty) { + llfld_a + } else { + let boxed_ty = ty::mk_open(bcx.tcx(), *ty); + let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_field"); + Store(bcx, llfld_a, GEPi(bcx, scratch.val, &[0, abi::slice_elt_base])); + Store(bcx, info.unwrap(), GEPi(bcx, scratch.val, &[0, abi::slice_elt_len])); + scratch.val + }; + variant_cx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope), + val, *ty); + } + + let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID, + &[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil(bcx.tcx())); + let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false); + + variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope); + variant_cx + }) +} + +fn size_and_align_of_dst(bcx: Block, t :ty::t, info: ValueRef) -> (ValueRef, ValueRef) { + debug!("calculate size of DST: {}; with lost info: {}", + bcx.ty_to_string(t), bcx.val_to_string(info)); + if ty::type_is_sized(bcx.tcx(), t) { + let sizing_type = sizing_type_of(bcx.ccx(), t); + let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type)); + let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t)); + return (size, align); + } + match ty::get(t).sty { + ty::ty_struct(id, ref substs) => { + let ccx = bcx.ccx(); + // First get the size of all statically known fields. + // Don't use type_of::sizing_type_of because that expects t to be sized. + assert!(!ty::type_is_simd(bcx.tcx(), t)); + let repr = adt::represent_type(ccx, t); + let sizing_type = adt::sizing_type_of(ccx, &*repr, true); + let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type)); + let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type)); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let fields = ty::struct_fields(bcx.tcx(), id, substs); + let last_field = fields[fields.len()-1]; + let field_ty = last_field.mt.ty; + let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); + + // Return the sum of sizes and max of aligns. + let size = Add(bcx, sized_size, unsized_size); + let align = Select(bcx, + ICmp(bcx, llvm::IntULT, sized_align, unsized_align), + sized_align, + unsized_align); + (size, align) + } + ty::ty_trait(..) => { + // info points to the vtable and the second entry in the vtable is the + // dynamic size of the object. + let info = PointerCast(bcx, info, Type::int(bcx.ccx()).ptr_to()); + let size_ptr = GEPi(bcx, info, &[1u]); + let align_ptr = GEPi(bcx, info, &[2u]); + (Load(bcx, size_ptr), Load(bcx, align_ptr)) + } + ty::ty_vec(unit_ty, None) => { + // The info in this case is the length of the vec, so the size is that + // times the unit size. + let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); + let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); + (Mul(bcx, info, C_uint(bcx.ccx(), unit_size)), C_uint(bcx.ccx(), 8u)) + } + _ => bcx.sess().bug(format!("Unexpected unsized type, found {}", + bcx.ty_to_string(t)).as_slice()) + } +} + +fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t) + -> Block<'blk, 'tcx> { + // NB: v0 is an *alias* of type t here, not a direct value. + let _icx = push_ctxt("make_drop_glue"); + match ty::get(t).sty { + ty::ty_uniq(content_ty) => { + match ty::get(content_ty).sty { + ty::ty_vec(ty, None) => { + tvec::make_drop_glue_unboxed(bcx, v0, ty, true) + } + ty::ty_str => { + let unit_ty = ty::sequence_element_type(bcx.tcx(), content_ty); + tvec::make_drop_glue_unboxed(bcx, v0, unit_ty, true) + } + ty::ty_trait(..) => { + let lluniquevalue = GEPi(bcx, v0, &[0, abi::trt_field_box]); + // Only drop the value when it is non-null + let concrete_ptr = Load(bcx, lluniquevalue); + with_cond(bcx, IsNotNull(bcx, concrete_ptr), |bcx| { + let dtor_ptr = Load(bcx, GEPi(bcx, v0, &[0, abi::trt_field_vtable])); + let dtor = Load(bcx, dtor_ptr); + Call(bcx, + dtor, + &[PointerCast(bcx, lluniquevalue, Type::i8p(bcx.ccx()))], + None); + bcx + }) + } + ty::ty_struct(..) if !ty::type_is_sized(bcx.tcx(), content_ty) => { + let llval = GEPi(bcx, v0, &[0, abi::slice_elt_base]); + let llbox = Load(bcx, llval); + let not_null = IsNotNull(bcx, llbox); + with_cond(bcx, not_null, |bcx| { + let bcx = drop_ty(bcx, v0, content_ty, None); + let info = GEPi(bcx, v0, &[0, abi::slice_elt_len]); + let info = Load(bcx, info); + let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info); + trans_exchange_free_dyn(bcx, llbox, llsize, llalign) + }) + } + _ => { + assert!(ty::type_is_sized(bcx.tcx(), content_ty)); + let llval = v0; + let llbox = Load(bcx, llval); + let not_null = IsNotNull(bcx, llbox); + with_cond(bcx, not_null, |bcx| { + let bcx = drop_ty(bcx, llbox, content_ty, None); + trans_exchange_free_ty(bcx, llbox, content_ty) + }) + } + } + } + ty::ty_struct(did, ref substs) | ty::ty_enum(did, ref substs) => { + let tcx = bcx.tcx(); + match ty::ty_dtor(tcx, did) { + ty::TraitDtor(dtor, true) => { + // FIXME(16758) Since the struct is unsized, it is hard to + // find the drop flag (which is at the end of the struct). + // Lets just ignore the flag and pretend everything will be + // OK. + if ty::type_is_sized(bcx.tcx(), t) { + trans_struct_drop_flag(bcx, t, v0, dtor, did, substs) + } else { + // Give the user a heads up that we are doing something + // stupid and dangerous. + bcx.sess().warn(format!("Ignoring drop flag in destructor for {}\ + because the struct is unsized. See issue\ + #16758", + bcx.ty_to_string(t)).as_slice()); + trans_struct_drop(bcx, t, v0, dtor, did, substs) + } + } + ty::TraitDtor(dtor, false) => { + trans_struct_drop(bcx, t, v0, dtor, did, substs) + } + ty::NoDtor => { + // No dtor? Just the default case + iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) + } + } + } + ty::ty_unboxed_closure(..) => iter_structural_ty(bcx, + v0, + t, + |bb, vv, tt| drop_ty(bb, vv, tt, None)), + ty::ty_closure(ref f) if f.store == ty::UniqTraitStore => { + let box_cell_v = GEPi(bcx, v0, &[0u, abi::fn_field_box]); + let env = Load(bcx, box_cell_v); + let env_ptr_ty = Type::at_box(bcx.ccx(), Type::i8(bcx.ccx())).ptr_to(); + let env = PointerCast(bcx, env, env_ptr_ty); + with_cond(bcx, IsNotNull(bcx, env), |bcx| { + let dtor_ptr = GEPi(bcx, env, &[0u, abi::box_field_drop_glue]); + let dtor = Load(bcx, dtor_ptr); + Call(bcx, dtor, &[PointerCast(bcx, box_cell_v, Type::i8p(bcx.ccx()))], None); + bcx + }) + } + ty::ty_trait(..) => { + // No need to do a null check here (as opposed to the Box tvec::make_drop_glue_unboxed(bcx, v0, ty, false), + _ => { + assert!(ty::type_is_sized(bcx.tcx(), t)); + if ty::type_needs_drop(bcx.tcx(), t) && + ty::type_is_structural(t) { + iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) + } else { + bcx + } + } + } +} + +// Generates the declaration for (but doesn't emit) a type descriptor. +pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info { + // If emit_tydescs already ran, then we shouldn't be creating any new + // tydescs. + assert!(!ccx.finished_tydescs().get()); + + let llty = type_of(ccx, t); + + if ccx.sess().count_type_sizes() { + println!("{}\t{}", llsize_of_real(ccx, llty), + ppaux::ty_to_string(ccx.tcx(), t)); + } + + let llsize = llsize_of(ccx, llty); + let llalign = llalign_of(ccx, llty); + let name = mangle_internal_name_by_type_and_seq(ccx, t, "tydesc"); + debug!("+++ declare_tydesc {} {}", ppaux::ty_to_string(ccx.tcx(), t), name); + let gvar = name.as_slice().with_c_str(|buf| { + unsafe { + llvm::LLVMAddGlobal(ccx.llmod(), ccx.tydesc_type().to_ref(), buf) + } + }); + note_unique_llvm_symbol(ccx, name); + + let ty_name = token::intern_and_get_ident( + ppaux::ty_to_string(ccx.tcx(), t).as_slice()); + let ty_name = C_str_slice(ccx, ty_name); + + debug!("--- declare_tydesc {}", ppaux::ty_to_string(ccx.tcx(), t)); + tydesc_info { + ty: t, + tydesc: gvar, + size: llsize, + align: llalign, + name: ty_name, + } +} + +fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type, + name: &str) -> (String, ValueRef) { + let _icx = push_ctxt("declare_generic_glue"); + let fn_nm = mangle_internal_name_by_type_and_seq( + ccx, + t, + format!("glue_{}", name).as_slice()); + let llfn = decl_cdecl_fn(ccx, fn_nm.as_slice(), llfnty, ty::mk_nil(ccx.tcx())); + note_unique_llvm_symbol(ccx, fn_nm.clone()); + return (fn_nm, llfn); +} + +fn make_generic_glue(ccx: &CrateContext, + t: ty::t, + llfn: ValueRef, + helper: for<'blk, 'tcx> |Block<'blk, 'tcx>, ValueRef, ty::t| + -> Block<'blk, 'tcx>, + name: &str) + -> ValueRef { + let _icx = push_ctxt("make_generic_glue"); + let glue_name = format!("glue {} {}", name, ty_to_short_str(ccx.tcx(), t)); + let _s = StatRecorder::new(ccx, glue_name); + + let arena = TypedArena::new(); + let empty_param_substs = param_substs::empty(); + let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, + ty::FnConverging(ty::mk_nil(ccx.tcx())), + &empty_param_substs, None, &arena); + + let bcx = init_function(&fcx, false, ty::FnConverging(ty::mk_nil(ccx.tcx()))); + + update_linkage(ccx, llfn, None, OriginalTranslation); + + ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u); + // All glue functions take values passed *by alias*; this is a + // requirement since in many contexts glue is invoked indirectly and + // the caller has no idea if it's dealing with something that can be + // passed by value. + // + // llfn is expected be declared to take a parameter of the appropriate + // type, so we don't need to explicitly cast the function parameter. + + let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint); + let bcx = helper(bcx, llrawptr0, t); + finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil(ccx.tcx()))); + + llfn +} + +pub fn emit_tydescs(ccx: &CrateContext) { + let _icx = push_ctxt("emit_tydescs"); + // As of this point, allow no more tydescs to be created. + ccx.finished_tydescs().set(true); + let glue_fn_ty = Type::generic_glue_fn(ccx).ptr_to(); + for (_, ti) in ccx.tydescs().borrow().iter() { + // Each of the glue functions needs to be cast to a generic type + // before being put into the tydesc because we only have a singleton + // tydesc type. Then we'll recast each function to its real type when + // calling it. + let drop_glue = unsafe { + llvm::LLVMConstPointerCast(get_drop_glue(ccx, ti.ty), glue_fn_ty.to_ref()) + }; + ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() + 1); + + let tydesc = C_named_struct(ccx.tydesc_type(), + &[ti.size, // size + ti.align, // align + drop_glue, // drop_glue + ti.name]); // name + + unsafe { + let gvar = ti.tydesc; + llvm::LLVMSetInitializer(gvar, tydesc); + llvm::LLVMSetGlobalConstant(gvar, True); + llvm::SetLinkage(gvar, llvm::InternalLinkage); + } + }; +} diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs new file mode 100644 index 00000000000..51d839c3bee --- /dev/null +++ b/src/librustc_trans/trans/inline.rs @@ -0,0 +1,197 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{AvailableExternallyLinkage, InternalLinkage, SetLinkage}; +use metadata::csearch; +use middle::astencode; +use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn}; +use trans::common::*; +use middle::ty; + +use syntax::ast; +use syntax::ast_util::{local_def, PostExpansionMethod}; + +fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) + -> Option { + let _icx = push_ctxt("maybe_instantiate_inline"); + match ccx.external().borrow().get(&fn_id) { + Some(&Some(node_id)) => { + // Already inline + debug!("maybe_instantiate_inline({}): already inline as node id {}", + ty::item_path_str(ccx.tcx(), fn_id), node_id); + return Some(local_def(node_id)); + } + Some(&None) => { + return None; // Not inlinable + } + None => { + // Not seen yet + } + } + + let csearch_result = + csearch::maybe_get_item_ast( + ccx.tcx(), fn_id, + |a,b,c,d| astencode::decode_inlined_item(a, b, c, d)); + + let inline_def = match csearch_result { + csearch::not_found => { + ccx.external().borrow_mut().insert(fn_id, None); + return None; + } + csearch::found(&ast::IIItem(ref item)) => { + ccx.external().borrow_mut().insert(fn_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, fn_id); + + ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); + trans_item(ccx, &**item); + + let linkage = match item.node { + ast::ItemFn(_, _, _, ref generics, _) => { + if generics.is_type_parameterized() { + // Generics have no symbol, so they can't be given any + // linkage. + None + } else { + if ccx.sess().opts.cg.codegen_units == 1 { + // We could use AvailableExternallyLinkage here, + // but InternalLinkage allows LLVM to optimize more + // aggressively (at the cost of sometimes + // duplicating code). + Some(InternalLinkage) + } else { + // With multiple compilation units, duplicated code + // is more of a problem. Also, `codegen_units > 1` + // means the user is okay with losing some + // performance. + Some(AvailableExternallyLinkage) + } + } + } + ast::ItemConst(..) => None, + _ => unreachable!(), + }; + + match linkage { + Some(linkage) => { + let g = get_item_val(ccx, item.id); + SetLinkage(g, linkage); + } + None => {} + } + + local_def(item.id) + } + csearch::found(&ast::IIForeign(ref item)) => { + ccx.external().borrow_mut().insert(fn_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, fn_id); + local_def(item.id) + } + csearch::found_parent(parent_id, &ast::IIItem(ref item)) => { + ccx.external().borrow_mut().insert(parent_id, Some(item.id)); + ccx.external_srcs().borrow_mut().insert(item.id, parent_id); + + let mut my_id = 0; + match item.node { + ast::ItemEnum(_, _) => { + let vs_here = ty::enum_variants(ccx.tcx(), local_def(item.id)); + let vs_there = ty::enum_variants(ccx.tcx(), parent_id); + for (here, there) in vs_here.iter().zip(vs_there.iter()) { + if there.id == fn_id { my_id = here.id.node; } + ccx.external().borrow_mut().insert(there.id, Some(here.id.node)); + } + } + ast::ItemStruct(ref struct_def, _) => { + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + ccx.external().borrow_mut().insert(fn_id, Some(ctor_id)); + my_id = ctor_id; + } + } + } + _ => ccx.sess().bug("maybe_instantiate_inline: item has a \ + non-enum, non-struct parent") + } + trans_item(ccx, &**item); + local_def(my_id) + } + csearch::found_parent(_, _) => { + ccx.sess().bug("maybe_get_item_ast returned a found_parent \ + with a non-item parent"); + } + csearch::found(&ast::IITraitItem(_, ref trait_item)) => { + match *trait_item { + ast::RequiredMethod(_) => ccx.sess().bug("found RequiredMethod IITraitItem"), + ast::ProvidedMethod(ref mth) => { + ccx.external().borrow_mut().insert(fn_id, Some(mth.id)); + ccx.external_srcs().borrow_mut().insert(mth.id, fn_id); + + ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); + + // If this is a default method, we can't look up the + // impl type. But we aren't going to translate anyways, so + // don't. + local_def(mth.id) + } + ast::TypeTraitItem(_) => { + ccx.sess().bug("found TypeTraitItem IITraitItem") + } + } + } + csearch::found(&ast::IIImplItem(impl_did, ref impl_item)) => { + match *impl_item { + ast::MethodImplItem(ref mth) => { + ccx.external().borrow_mut().insert(fn_id, Some(mth.id)); + ccx.external_srcs().borrow_mut().insert(mth.id, fn_id); + + ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); + + let impl_tpt = ty::lookup_item_type(ccx.tcx(), impl_did); + let unparameterized = impl_tpt.generics.types.is_empty() && + mth.pe_generics().ty_params.is_empty(); + + if unparameterized { + let llfn = get_item_val(ccx, mth.id); + trans_fn(ccx, + &*mth.pe_fn_decl(), + &*mth.pe_body(), + llfn, + ¶m_substs::empty(), + mth.id, + &[]); + // Use InternalLinkage so LLVM can optimize more + // aggressively. + SetLinkage(llfn, InternalLinkage); + } + local_def(mth.id) + } + ast::TypeImplItem(_) => { + ccx.sess().bug("found TypeImplItem IIImplItem") + } + } + } + }; + + return Some(inline_def); +} + +pub fn get_local_instance(ccx: &CrateContext, fn_id: ast::DefId) + -> Option { + if fn_id.krate == ast::LOCAL_CRATE { + Some(fn_id) + } else { + instantiate_inline(ccx, fn_id) + } +} + +pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) -> ast::DefId { + get_local_instance(ccx, fn_id).unwrap_or(fn_id) +} diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs new file mode 100644 index 00000000000..3bc6c30f8d6 --- /dev/null +++ b/src/librustc_trans/trans/intrinsic.rs @@ -0,0 +1,623 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use llvm; +use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef}; +use middle::subst; +use middle::subst::FnSpace; +use trans::base::*; +use trans::build::*; +use trans::callee; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::common::*; +use trans::datum::*; +use trans::expr; +use trans::glue; +use trans::type_of::*; +use trans::type_of; +use trans::machine; +use trans::machine::llsize_of; +use trans::type_::Type; +use middle::ty; +use syntax::abi::RustIntrinsic; +use syntax::ast; +use syntax::parse::token; +use util::ppaux::ty_to_string; + +pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Option { + let name = match token::get_ident(item.ident).get() { + "sqrtf32" => "llvm.sqrt.f32", + "sqrtf64" => "llvm.sqrt.f64", + "powif32" => "llvm.powi.f32", + "powif64" => "llvm.powi.f64", + "sinf32" => "llvm.sin.f32", + "sinf64" => "llvm.sin.f64", + "cosf32" => "llvm.cos.f32", + "cosf64" => "llvm.cos.f64", + "powf32" => "llvm.pow.f32", + "powf64" => "llvm.pow.f64", + "expf32" => "llvm.exp.f32", + "expf64" => "llvm.exp.f64", + "exp2f32" => "llvm.exp2.f32", + "exp2f64" => "llvm.exp2.f64", + "logf32" => "llvm.log.f32", + "logf64" => "llvm.log.f64", + "log10f32" => "llvm.log10.f32", + "log10f64" => "llvm.log10.f64", + "log2f32" => "llvm.log2.f32", + "log2f64" => "llvm.log2.f64", + "fmaf32" => "llvm.fma.f32", + "fmaf64" => "llvm.fma.f64", + "fabsf32" => "llvm.fabs.f32", + "fabsf64" => "llvm.fabs.f64", + "copysignf32" => "llvm.copysign.f32", + "copysignf64" => "llvm.copysign.f64", + "floorf32" => "llvm.floor.f32", + "floorf64" => "llvm.floor.f64", + "ceilf32" => "llvm.ceil.f32", + "ceilf64" => "llvm.ceil.f64", + "truncf32" => "llvm.trunc.f32", + "truncf64" => "llvm.trunc.f64", + "rintf32" => "llvm.rint.f32", + "rintf64" => "llvm.rint.f64", + "nearbyintf32" => "llvm.nearbyint.f32", + "nearbyintf64" => "llvm.nearbyint.f64", + "roundf32" => "llvm.round.f32", + "roundf64" => "llvm.round.f64", + "ctpop8" => "llvm.ctpop.i8", + "ctpop16" => "llvm.ctpop.i16", + "ctpop32" => "llvm.ctpop.i32", + "ctpop64" => "llvm.ctpop.i64", + "bswap16" => "llvm.bswap.i16", + "bswap32" => "llvm.bswap.i32", + "bswap64" => "llvm.bswap.i64", + "assume" => "llvm.assume", + _ => return None + }; + Some(ccx.get_intrinsic(&name)) +} + +/// Performs late verification that intrinsics are used correctly. At present, +/// the only intrinsic that needs such verification is `transmute`. +pub fn check_intrinsics(ccx: &CrateContext) { + for transmute_restriction in ccx.tcx() + .transmute_restrictions + .borrow() + .iter() { + let llfromtype = type_of::sizing_type_of(ccx, + transmute_restriction.from); + let lltotype = type_of::sizing_type_of(ccx, + transmute_restriction.to); + let from_type_size = machine::llbitsize_of_real(ccx, llfromtype); + let to_type_size = machine::llbitsize_of_real(ccx, lltotype); + if from_type_size != to_type_size { + ccx.sess() + .span_err(transmute_restriction.span, + format!("transmute called on types with different sizes: \ + {} ({} bit{}) to {} ({} bit{})", + ty_to_string(ccx.tcx(), transmute_restriction.from), + from_type_size as uint, + if from_type_size == 1 { + "" + } else { + "s" + }, + ty_to_string(ccx.tcx(), transmute_restriction.to), + to_type_size as uint, + if to_type_size == 1 { + "" + } else { + "s" + }).as_slice()); + } + if ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || + ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { + ccx.sess() + .add_lint(::lint::builtin::FAT_PTR_TRANSMUTES, + transmute_restriction.id, + transmute_restriction.span, + format!("Transmuting fat pointer types; {} to {}.\ + Beware of relying on the compiler's representation", + ty_to_string(ccx.tcx(), transmute_restriction.from), + ty_to_string(ccx.tcx(), transmute_restriction.to))); + } + } + ccx.sess().abort_if_errors(); +} + +pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::NodeId, + callee_ty: ty::t, cleanup_scope: cleanup::CustomScopeIndex, + args: callee::CallArgs, dest: expr::Dest, + substs: subst::Substs, call_info: NodeInfo) + -> Result<'blk, 'tcx> { + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let tcx = bcx.tcx(); + + let ret_ty = match ty::get(callee_ty).sty { + ty::ty_bare_fn(ref f) => f.sig.output, + _ => panic!("expected bare_fn in trans_intrinsic_call") + }; + let foreign_item = tcx.map.expect_foreign_item(node); + let name = token::get_ident(foreign_item.ident); + + // For `transmute` we can just trans the input expr directly into dest + if name.get() == "transmute" { + let llret_ty = type_of::type_of(ccx, ret_ty.unwrap()); + match args { + callee::ArgExprs(arg_exprs) => { + assert_eq!(arg_exprs.len(), 1); + + let (in_type, out_type) = (*substs.types.get(FnSpace, 0), + *substs.types.get(FnSpace, 1)); + let llintype = type_of::type_of(ccx, in_type); + let llouttype = type_of::type_of(ccx, out_type); + + let in_type_size = machine::llbitsize_of_real(ccx, llintype); + let out_type_size = machine::llbitsize_of_real(ccx, llouttype); + + // This should be caught by the intrinsicck pass + assert_eq!(in_type_size, out_type_size); + + // We need to cast the dest so the types work out + let dest = match dest { + expr::SaveIn(d) => expr::SaveIn(PointerCast(bcx, d, llintype.ptr_to())), + expr::Ignore => expr::Ignore + }; + bcx = expr::trans_into(bcx, &*arg_exprs[0], dest); + + fcx.pop_custom_cleanup_scope(cleanup_scope); + + return match dest { + expr::SaveIn(d) => Result::new(bcx, d), + expr::Ignore => Result::new(bcx, C_undef(llret_ty.ptr_to())) + }; + + } + + _ => { + ccx.sess().bug("expected expr as argument for transmute"); + } + } + } + + // Push the arguments. + let mut llargs = Vec::new(); + bcx = callee::trans_args(bcx, + args, + callee_ty, + &mut llargs, + cleanup::CustomScope(cleanup_scope), + false, + RustIntrinsic); + + fcx.pop_custom_cleanup_scope(cleanup_scope); + + // These are the only intrinsic functions that diverge. + if name.get() == "abort" { + let llfn = ccx.get_intrinsic(&("llvm.trap")); + Call(bcx, llfn, &[], None); + Unreachable(bcx); + return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to())); + } else if name.get() == "unreachable" { + Unreachable(bcx); + return Result::new(bcx, C_nil(ccx)); + } + + let ret_ty = match ret_ty { + ty::FnConverging(ret_ty) => ret_ty, + ty::FnDiverging => unreachable!() + }; + + let llret_ty = type_of::type_of(ccx, ret_ty); + + // Get location to store the result. If the user does + // not care about the result, just make a stack slot + let llresult = match dest { + expr::SaveIn(d) => d, + expr::Ignore => { + if !type_is_zero_size(ccx, ret_ty) { + alloc_ty(bcx, ret_ty, "intrinsic_result") + } else { + C_undef(llret_ty.ptr_to()) + } + } + }; + + let simple = get_simple_intrinsic(ccx, &*foreign_item); + let llval = match (simple, name.get()) { + (Some(llfn), _) => { + Call(bcx, llfn, llargs.as_slice(), None) + } + (_, "breakpoint") => { + let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); + Call(bcx, llfn, &[], None) + } + (_, "size_of") => { + let tp_ty = *substs.types.get(FnSpace, 0); + let lltp_ty = type_of::type_of(ccx, tp_ty); + C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)) + } + (_, "min_align_of") => { + let tp_ty = *substs.types.get(FnSpace, 0); + C_uint(ccx, type_of::align_of(ccx, tp_ty)) + } + (_, "pref_align_of") => { + let tp_ty = *substs.types.get(FnSpace, 0); + let lltp_ty = type_of::type_of(ccx, tp_ty); + C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)) + } + (_, "move_val_init") => { + // Create a datum reflecting the value being moved. + // Use `appropriate_mode` so that the datum is by ref + // if the value is non-immediate. Note that, with + // intrinsics, there are no argument cleanups to + // concern ourselves with, so we can use an rvalue datum. + let tp_ty = *substs.types.get(FnSpace, 0); + let mode = appropriate_rvalue_mode(ccx, tp_ty); + let src = Datum { + val: llargs[1], + ty: tp_ty, + kind: Rvalue::new(mode) + }; + bcx = src.store_to(bcx, llargs[0]); + C_nil(ccx) + } + (_, "get_tydesc") => { + let tp_ty = *substs.types.get(FnSpace, 0); + let static_ti = get_tydesc(ccx, tp_ty); + + // FIXME (#3730): ideally this shouldn't need a cast, + // but there's a circularity between translating rust types to llvm + // types and having a tydesc type available. So I can't directly access + // the llvm type of intrinsic::TyDesc struct. + PointerCast(bcx, static_ti.tydesc, llret_ty) + } + (_, "type_id") => { + let hash = ty::hash_crate_independent( + ccx.tcx(), + *substs.types.get(FnSpace, 0), + &ccx.link_meta().crate_hash); + // NB: This needs to be kept in lockstep with the TypeId struct in + // the intrinsic module + C_named_struct(llret_ty, &[C_u64(ccx, hash)]) + } + (_, "init") => { + let tp_ty = *substs.types.get(FnSpace, 0); + let lltp_ty = type_of::type_of(ccx, tp_ty); + if return_type_is_void(ccx, tp_ty) { + C_nil(ccx) + } else { + C_null(lltp_ty) + } + } + // Effectively no-ops + (_, "uninit") | (_, "forget") => { + C_nil(ccx) + } + (_, "needs_drop") => { + let tp_ty = *substs.types.get(FnSpace, 0); + C_bool(ccx, ty::type_needs_drop(ccx.tcx(), tp_ty)) + } + (_, "owns_managed") => { + let tp_ty = *substs.types.get(FnSpace, 0); + C_bool(ccx, ty::type_contents(ccx.tcx(), tp_ty).owns_managed()) + } + (_, "offset") => { + let ptr = llargs[0]; + let offset = llargs[1]; + InBoundsGEP(bcx, ptr, &[offset]) + } + + (_, "copy_nonoverlapping_memory") => { + copy_intrinsic(bcx, false, false, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + (_, "copy_memory") => { + copy_intrinsic(bcx, true, false, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + (_, "set_memory") => { + memset_intrinsic(bcx, false, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + + (_, "volatile_copy_nonoverlapping_memory") => { + copy_intrinsic(bcx, false, true, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + (_, "volatile_copy_memory") => { + copy_intrinsic(bcx, true, true, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + (_, "volatile_set_memory") => { + memset_intrinsic(bcx, true, *substs.types.get(FnSpace, 0), + llargs[0], llargs[1], llargs[2]) + } + (_, "volatile_load") => { + VolatileLoad(bcx, llargs[0]) + }, + (_, "volatile_store") => { + VolatileStore(bcx, llargs[1], llargs[0]); + C_nil(ccx) + }, + + (_, "ctlz8") => count_zeros_intrinsic(bcx, "llvm.ctlz.i8", llargs[0]), + (_, "ctlz16") => count_zeros_intrinsic(bcx, "llvm.ctlz.i16", llargs[0]), + (_, "ctlz32") => count_zeros_intrinsic(bcx, "llvm.ctlz.i32", llargs[0]), + (_, "ctlz64") => count_zeros_intrinsic(bcx, "llvm.ctlz.i64", llargs[0]), + (_, "cttz8") => count_zeros_intrinsic(bcx, "llvm.cttz.i8", llargs[0]), + (_, "cttz16") => count_zeros_intrinsic(bcx, "llvm.cttz.i16", llargs[0]), + (_, "cttz32") => count_zeros_intrinsic(bcx, "llvm.cttz.i32", llargs[0]), + (_, "cttz64") => count_zeros_intrinsic(bcx, "llvm.cttz.i64", llargs[0]), + + (_, "i8_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "i16_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "i32_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "i64_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.sadd.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "u8_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "u16_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "u32_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "u64_add_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.uadd.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "i8_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "i16_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "i32_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "i64_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.ssub.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "u8_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "u16_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "u32_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "u64_sub_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.usub.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "i8_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "i16_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "i32_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "i64_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.smul.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "u8_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i8", ret_ty, + llargs[0], llargs[1]), + (_, "u16_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i16", ret_ty, + llargs[0], llargs[1]), + (_, "u32_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i32", ret_ty, + llargs[0], llargs[1]), + (_, "u64_mul_with_overflow") => + with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i64", ret_ty, + llargs[0], llargs[1]), + + (_, "return_address") => { + if !fcx.caller_expects_out_pointer { + tcx.sess.span_err(call_info.span, + "invalid use of `return_address` intrinsic: function \ + does not use out pointer"); + C_null(Type::i8p(ccx)) + } else { + PointerCast(bcx, llvm::get_param(fcx.llfn, 0), Type::i8p(ccx)) + } + } + + // This requires that atomic intrinsics follow a specific naming pattern: + // "atomic_[_]", and no ordering means SeqCst + (_, name) if name.starts_with("atomic_") => { + let split: Vec<&str> = name.split('_').collect(); + assert!(split.len() >= 2, "Atomic intrinsic not correct format"); + + let order = if split.len() == 2 { + llvm::SequentiallyConsistent + } else { + match split[2] { + "relaxed" => llvm::Monotonic, + "acq" => llvm::Acquire, + "rel" => llvm::Release, + "acqrel" => llvm::AcquireRelease, + _ => ccx.sess().fatal("unknown ordering in atomic intrinsic") + } + }; + + match split[1] { + "cxchg" => { + // See include/llvm/IR/Instructions.h for their implementation + // of this, I assume that it's good enough for us to use for + // now. + let strongest_failure_ordering = match order { + llvm::NotAtomic | llvm::Unordered => + ccx.sess().fatal("cmpxchg must be atomic"), + + llvm::Monotonic | llvm::Release => + llvm::Monotonic, + + llvm::Acquire | llvm::AcquireRelease => + llvm::Acquire, + + llvm::SequentiallyConsistent => + llvm::SequentiallyConsistent + }; + + let res = AtomicCmpXchg(bcx, llargs[0], llargs[1], + llargs[2], order, + strongest_failure_ordering); + if unsafe { llvm::LLVMVersionMinor() >= 5 } { + ExtractValue(bcx, res, 0) + } else { + res + } + } + + "load" => { + AtomicLoad(bcx, llargs[0], order) + } + "store" => { + AtomicStore(bcx, llargs[1], llargs[0], order); + C_nil(ccx) + } + + "fence" => { + AtomicFence(bcx, order); + C_nil(ccx) + } + + // These are all AtomicRMW ops + op => { + let atom_op = match op { + "xchg" => llvm::AtomicXchg, + "xadd" => llvm::AtomicAdd, + "xsub" => llvm::AtomicSub, + "and" => llvm::AtomicAnd, + "nand" => llvm::AtomicNand, + "or" => llvm::AtomicOr, + "xor" => llvm::AtomicXor, + "max" => llvm::AtomicMax, + "min" => llvm::AtomicMin, + "umax" => llvm::AtomicUMax, + "umin" => llvm::AtomicUMin, + _ => ccx.sess().fatal("unknown atomic operation") + }; + + AtomicRMW(bcx, atom_op, llargs[0], llargs[1], order) + } + } + + } + + (_, _) => ccx.sess().span_bug(foreign_item.span, "unknown intrinsic") + }; + + if val_ty(llval) != Type::void(ccx) && + machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { + store_ty(bcx, llval, llresult, ret_ty); + } + + // If we made a temporary stack slot, let's clean it up + match dest { + expr::Ignore => { + bcx = glue::drop_ty(bcx, llresult, ret_ty, Some(call_info)); + } + expr::SaveIn(_) => {} + } + + Result::new(bcx, llresult) +} + +fn copy_intrinsic(bcx: Block, allow_overlap: bool, volatile: bool, + tp_ty: ty::t, dst: ValueRef, src: ValueRef, count: ValueRef) -> ValueRef { + let ccx = bcx.ccx(); + let lltp_ty = type_of::type_of(ccx, tp_ty); + let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); + let size = machine::llsize_of(ccx, lltp_ty); + let int_size = machine::llbitsize_of_real(ccx, ccx.int_type()); + let name = if allow_overlap { + if int_size == 32 { + "llvm.memmove.p0i8.p0i8.i32" + } else { + "llvm.memmove.p0i8.p0i8.i64" + } + } else { + if int_size == 32 { + "llvm.memcpy.p0i8.p0i8.i32" + } else { + "llvm.memcpy.p0i8.p0i8.i64" + } + }; + + let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); + let src_ptr = PointerCast(bcx, src, Type::i8p(ccx)); + let llfn = ccx.get_intrinsic(&name); + + Call(bcx, llfn, &[dst_ptr, src_ptr, Mul(bcx, size, count), align, + C_bool(ccx, volatile)], None) +} + +fn memset_intrinsic(bcx: Block, volatile: bool, tp_ty: ty::t, + dst: ValueRef, val: ValueRef, count: ValueRef) -> ValueRef { + let ccx = bcx.ccx(); + let lltp_ty = type_of::type_of(ccx, tp_ty); + let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); + let size = machine::llsize_of(ccx, lltp_ty); + let name = if machine::llbitsize_of_real(ccx, ccx.int_type()) == 32 { + "llvm.memset.p0i8.i32" + } else { + "llvm.memset.p0i8.i64" + }; + + let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); + let llfn = ccx.get_intrinsic(&name); + + Call(bcx, llfn, &[dst_ptr, val, Mul(bcx, size, count), align, + C_bool(ccx, volatile)], None) +} + +fn count_zeros_intrinsic(bcx: Block, name: &'static str, val: ValueRef) -> ValueRef { + let y = C_bool(bcx.ccx(), false); + let llfn = bcx.ccx().get_intrinsic(&name); + Call(bcx, llfn, &[val, y], None) +} + +fn with_overflow_intrinsic(bcx: Block, name: &'static str, t: ty::t, + a: ValueRef, b: ValueRef) -> ValueRef { + let llfn = bcx.ccx().get_intrinsic(&name); + + // Convert `i1` to a `bool`, and write it to the out parameter + let val = Call(bcx, llfn, &[a, b], None); + let result = ExtractValue(bcx, val, 0); + let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); + let ret = C_undef(type_of::type_of(bcx.ccx(), t)); + let ret = InsertValue(bcx, ret, result, 0); + let ret = InsertValue(bcx, ret, overflow, 1); + + ret +} diff --git a/src/librustc_trans/trans/llrepr.rs b/src/librustc_trans/trans/llrepr.rs new file mode 100644 index 00000000000..dcf3a532157 --- /dev/null +++ b/src/librustc_trans/trans/llrepr.rs @@ -0,0 +1,38 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use trans::context::CrateContext; +use trans::type_::Type; +use llvm::ValueRef; + +pub trait LlvmRepr for Sized? { + fn llrepr(&self, ccx: &CrateContext) -> String; +} + +impl LlvmRepr for [T] { + fn llrepr(&self, ccx: &CrateContext) -> String { + let reprs: Vec = self.iter().map(|t| t.llrepr(ccx)).collect(); + format!("[{}]", reprs.connect(",")) + } +} + +impl LlvmRepr for Type { + fn llrepr(&self, ccx: &CrateContext) -> String { + ccx.tn().type_to_string(*self) + } +} + +impl LlvmRepr for ValueRef { + fn llrepr(&self, ccx: &CrateContext) -> String { + ccx.tn().val_to_string(*self) + } +} + + diff --git a/src/librustc_trans/trans/machine.rs b/src/librustc_trans/trans/machine.rs new file mode 100644 index 00000000000..41738f1e58f --- /dev/null +++ b/src/librustc_trans/trans/machine.rs @@ -0,0 +1,128 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Information concerning the machine representation of various types. + +#![allow(non_camel_case_types)] + +use llvm; +use llvm::{ValueRef}; +use llvm::False; +use trans::common::*; + +use trans::type_::Type; + +pub type llbits = u64; +pub type llsize = u64; +pub type llalign = u32; + +// ______________________________________________________________________ +// compute sizeof / alignof + +// Returns the number of bytes clobbered by a Store to this type. +pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> llsize { + unsafe { + return llvm::LLVMStoreSizeOfType(cx.td().lltd, ty.to_ref()); + } +} + +// Returns the number of bytes between successive elements of type T in an +// array of T. This is the "ABI" size. It includes any ABI-mandated padding. +pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> llsize { + unsafe { + return llvm::LLVMABISizeOfType(cx.td().lltd, ty.to_ref()); + } +} + +// Returns, as near as we can figure, the "real" size of a type. As in, the +// bits in this number of bytes actually carry data related to the datum +// with the type. Not junk, padding, accidentally-damaged words, or +// whatever. Rounds up to the nearest byte though, so if you have a 1-bit +// value, we return 1 here, not 0. Most of rustc works in bytes. Be warned +// that LLVM *does* distinguish between e.g. a 1-bit value and an 8-bit value +// at the codegen level! In general you should prefer `llbitsize_of_real` +// below. +pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> llsize { + unsafe { + let nbits = llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()); + if nbits & 7 != 0 { + // Not an even number of bytes, spills into "next" byte. + 1 + (nbits >> 3) + } else { + nbits >> 3 + } + } +} + +/// Returns the "real" size of the type in bits. +pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> llbits { + unsafe { + llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) + } +} + +/// Returns the size of the type as an LLVM constant integer value. +pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { + // Once upon a time, this called LLVMSizeOf, which does a + // getelementptr(1) on a null pointer and casts to an int, in + // order to obtain the type size as a value without requiring the + // target data layout. But we have the target data layout, so + // there's no need for that contrivance. The instruction + // selection DAG generator would flatten that GEP(1) node into a + // constant of the type's alloc size, so let's save it some work. + return C_uint(cx, llsize_of_alloc(cx, ty)); +} + +// Returns the "default" size of t (see above), or 1 if the size would +// be zero. This is important for things like vectors that expect +// space to be consumed. +pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { + if llbitsize_of_real(cx, ty) == 0 { + unsafe { llvm::LLVMConstInt(cx.int_type().to_ref(), 1, False) } + } else { + llsize_of(cx, ty) + } +} + +// Returns the preferred alignment of the given type for the current target. +// The preferred alignment may be larger than the alignment used when +// packing the type into structs. This will be used for things like +// allocations inside a stack frame, which LLVM has a free hand in. +pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> llalign { + unsafe { + return llvm::LLVMPreferredAlignmentOfType(cx.td().lltd, ty.to_ref()); + } +} + +// Returns the minimum alignment of a type required by the platform. +// This is the alignment that will be used for struct fields, arrays, +// and similar ABI-mandated things. +pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> llalign { + unsafe { + return llvm::LLVMABIAlignmentOfType(cx.td().lltd, ty.to_ref()); + } +} + +// Returns the "default" alignment of t, which is calculated by casting +// null to a record containing a single-bit followed by a t value, then +// doing gep(0,1) to get at the trailing (and presumably padded) t cell. +pub fn llalign_of(cx: &CrateContext, ty: Type) -> ValueRef { + unsafe { + return llvm::LLVMConstIntCast( + llvm::LLVMAlignOf(ty.to_ref()), cx.int_type().to_ref(), False); + } +} + +pub fn llelement_offset(cx: &CrateContext, struct_ty: Type, element: uint) -> u64 { + unsafe { + return llvm::LLVMOffsetOfElement(cx.td().lltd, struct_ty.to_ref(), + element as u32); + } +} diff --git a/src/librustc_trans/trans/macros.rs b/src/librustc_trans/trans/macros.rs new file mode 100644 index 00000000000..313280cb7a8 --- /dev/null +++ b/src/librustc_trans/trans/macros.rs @@ -0,0 +1,31 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![macro_escape] + +macro_rules! unpack_datum( + ($bcx: ident, $inp: expr) => ( + { + let db = $inp; + $bcx = db.bcx; + db.datum + } + ) +) + +macro_rules! unpack_result( + ($bcx: ident, $inp: expr) => ( + { + let db = $inp; + $bcx = db.bcx; + db.val + } + ) +) diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs new file mode 100644 index 00000000000..55ecd8a6554 --- /dev/null +++ b/src/librustc_trans/trans/meth.rs @@ -0,0 +1,778 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use back::abi; +use llvm; +use llvm::ValueRef; +use metadata::csearch; +use middle::subst::{Subst,Substs}; +use middle::subst::VecPerParamSpace; +use middle::subst; +use middle::traits; +use trans::base::*; +use trans::build::*; +use trans::callee::*; +use trans::callee; +use trans::cleanup; +use trans::common::*; +use trans::datum::*; +use trans::expr::{SaveIn, Ignore}; +use trans::expr; +use trans::glue; +use trans::machine; +use trans::type_::Type; +use trans::type_of::*; +use middle::ty; +use middle::typeck; +use middle::typeck::MethodCall; +use util::ppaux::Repr; + +use std::c_str::ToCStr; +use std::rc::Rc; +use syntax::abi::{Rust, RustCall}; +use syntax::parse::token; +use syntax::{ast, ast_map, attr, visit}; +use syntax::ast_util::PostExpansionMethod; +use syntax::codemap::DUMMY_SP; + +// drop_glue pointer, size, align. +static VTABLE_OFFSET: uint = 3; + +/** +The main "translation" pass for methods. Generates code +for non-monomorphized methods only. Other methods will +be generated once they are invoked with specific type parameters, +see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`. +*/ +pub fn trans_impl(ccx: &CrateContext, + name: ast::Ident, + impl_items: &[ast::ImplItem], + generics: &ast::Generics, + id: ast::NodeId) { + let _icx = push_ctxt("meth::trans_impl"); + let tcx = ccx.tcx(); + + debug!("trans_impl(name={}, id={})", name.repr(tcx), id); + + // Both here and below with generic methods, be sure to recurse and look for + // items that we need to translate. + if !generics.ty_params.is_empty() { + let mut v = TransItemVisitor{ ccx: ccx }; + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + visit::walk_method_helper(&mut v, &**method); + } + ast::TypeImplItem(_) => {} + } + } + return; + } + for impl_item in impl_items.iter() { + match *impl_item { + ast::MethodImplItem(ref method) => { + if method.pe_generics().ty_params.len() == 0u { + let trans_everywhere = attr::requests_inline(method.attrs.as_slice()); + for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) { + let llfn = get_item_val(ccx, method.id); + trans_fn(ccx, + method.pe_fn_decl(), + method.pe_body(), + llfn, + ¶m_substs::empty(), + method.id, + &[]); + update_linkage(ccx, + llfn, + Some(method.id), + if is_origin { OriginalTranslation } else { InlinedCopy }); + } + } + let mut v = TransItemVisitor { + ccx: ccx, + }; + visit::walk_method_helper(&mut v, &**method); + } + ast::TypeImplItem(_) => {} + } + } +} + +pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + method_call: MethodCall, + self_expr: Option<&ast::Expr>, + arg_cleanup_scope: cleanup::ScopeId) + -> Callee<'blk, 'tcx> { + let _icx = push_ctxt("meth::trans_method_callee"); + + let (origin, method_ty) = + bcx.tcx().method_map + .borrow() + .get(&method_call) + .map(|method| (method.origin.clone(), method.ty)) + .unwrap(); + + match origin { + typeck::MethodStatic(did) | + typeck::MethodStaticUnboxedClosure(did) => { + Callee { + bcx: bcx, + data: Fn(callee::trans_fn_ref(bcx, + did, + MethodCall(method_call))), + } + } + + typeck::MethodTypeParam(typeck::MethodParam { + ref trait_ref, + method_num + }) => { + let trait_ref = + Rc::new(trait_ref.subst(bcx.tcx(), + &bcx.fcx.param_substs.substs)); + let span = bcx.tcx().map.span(method_call.expr_id); + let origin = fulfill_obligation(bcx.ccx(), + span, + (*trait_ref).clone()); + debug!("origin = {}", origin.repr(bcx.tcx())); + trans_monomorphized_callee(bcx, method_call, trait_ref.def_id, + method_num, origin) + } + + typeck::MethodTraitObject(ref mt) => { + let self_expr = match self_expr { + Some(self_expr) => self_expr, + None => { + bcx.sess().span_bug(bcx.tcx().map.span(method_call.expr_id), + "self expr wasn't provided for trait object \ + callee (trying to call overloaded op?)") + } + }; + trans_trait_callee(bcx, + monomorphize_type(bcx, method_ty), + mt.real_index, + self_expr, + arg_cleanup_scope) + } + } +} + +pub fn trans_static_method_callee(bcx: Block, + method_id: ast::DefId, + trait_id: ast::DefId, + expr_id: ast::NodeId) + -> ValueRef +{ + let _icx = push_ctxt("meth::trans_static_method_callee"); + let ccx = bcx.ccx(); + + debug!("trans_static_method_callee(method_id={}, trait_id={}, \ + expr_id={})", + method_id, + ty::item_path_str(bcx.tcx(), trait_id), + expr_id); + + let mname = if method_id.krate == ast::LOCAL_CRATE { + match bcx.tcx().map.get(method_id.node) { + ast_map::NodeTraitItem(method) => { + let ident = match *method { + ast::RequiredMethod(ref m) => m.ident, + ast::ProvidedMethod(ref m) => m.pe_ident(), + ast::TypeTraitItem(_) => { + bcx.tcx().sess.bug("trans_static_method_callee() on \ + an associated type?!") + } + }; + ident.name + } + _ => panic!("callee is not a trait method") + } + } else { + csearch::get_item_path(bcx.tcx(), method_id).last().unwrap().name() + }; + debug!("trans_static_method_callee: method_id={}, expr_id={}, \ + name={}", method_id, expr_id, token::get_name(mname)); + + // Find the substitutions for the fn itself. This includes + // type parameters that belong to the trait but also some that + // belong to the method: + let rcvr_substs = node_id_substs(bcx, ExprId(expr_id)); + let subst::SeparateVecsPerParamSpace { + types: rcvr_type, + selfs: rcvr_self, + assocs: rcvr_assoc, + fns: rcvr_method + } = rcvr_substs.types.split(); + + // Lookup the precise impl being called. To do that, we need to + // create a trait reference identifying the self type and other + // input type parameters. To create that trait reference, we have + // to pick apart the type parameters to identify just those that + // pertain to the trait. This is easiest to explain by example: + // + // trait Convert { + // fn from(n: U) -> Option; + // } + // ... + // let f = as Convert>::from::(...) + // + // Here, in this call, which I've written with explicit UFCS + // notation, the set of type parameters will be: + // + // rcvr_type: [] <-- nothing declared on the trait itself + // rcvr_self: [Vec] <-- the self type + // rcvr_method: [String] <-- method type parameter + // + // So we create a trait reference using the first two, + // basically corresponding to ` as Convert>`. + // The remaining type parameters (`rcvr_method`) will be used below. + let trait_substs = + Substs::erased(VecPerParamSpace::new(rcvr_type, + rcvr_self, + rcvr_assoc, + Vec::new())); + debug!("trait_substs={}", trait_substs.repr(bcx.tcx())); + let trait_ref = Rc::new(ty::TraitRef { def_id: trait_id, + substs: trait_substs }); + let vtbl = fulfill_obligation(bcx.ccx(), + DUMMY_SP, + trait_ref); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(traits::VtableImplData { + impl_def_id: impl_did, + substs: impl_substs, + nested: _ }) => + { + assert!(impl_substs.types.all(|t| !ty::type_needs_infer(*t))); + + // Create the substitutions that are in scope. This combines + // the type parameters from the impl with those declared earlier. + // To see what I mean, consider a possible impl: + // + // impl Convert for Vec { + // fn from(n: U) { ... } + // } + // + // Recall that we matched ` as Convert>`. Trait + // resolution will have given us a substitution + // containing `impl_substs=[[T=int],[],[]]` (the type + // parameters defined on the impl). We combine + // that with the `rcvr_method` from before, which tells us + // the type parameters from the *method*, to yield + // `callee_substs=[[T=int],[],[U=String]]`. + let subst::SeparateVecsPerParamSpace { + types: impl_type, + selfs: impl_self, + assocs: impl_assoc, + fns: _ + } = impl_substs.types.split(); + let callee_substs = + Substs::erased(VecPerParamSpace::new(impl_type, + impl_self, + impl_assoc, + rcvr_method)); + + let mth_id = method_with_name(ccx, impl_did, mname); + let llfn = trans_fn_ref_with_substs(bcx, mth_id, ExprId(expr_id), + callee_substs); + + let callee_ty = node_id_type(bcx, expr_id); + let llty = type_of_fn_from_ty(ccx, callee_ty).ptr_to(); + PointerCast(bcx, llfn, llty) + } + _ => { + bcx.tcx().sess.bug( + format!("static call to invalid vtable: {}", + vtbl.repr(bcx.tcx())).as_slice()); + } + } +} + +fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name) + -> ast::DefId { + match ccx.impl_method_cache().borrow().get(&(impl_id, name)).cloned() { + Some(m) => return m, + None => {} + } + + let impl_items = ccx.tcx().impl_items.borrow(); + let impl_items = + impl_items.get(&impl_id) + .expect("could not find impl while translating"); + let meth_did = impl_items.iter() + .find(|&did| { + ty::impl_or_trait_item(ccx.tcx(), did.def_id()).name() == name + }).expect("could not find method while \ + translating"); + + ccx.impl_method_cache().borrow_mut().insert((impl_id, name), + meth_did.def_id()); + meth_did.def_id() +} + +fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + method_call: MethodCall, + trait_id: ast::DefId, + n_method: uint, + vtable: traits::Vtable<()>) + -> Callee<'blk, 'tcx> { + let _icx = push_ctxt("meth::trans_monomorphized_callee"); + match vtable { + traits::VtableImpl(vtable_impl) => { + let ccx = bcx.ccx(); + let impl_did = vtable_impl.impl_def_id; + let mname = match ty::trait_item(ccx.tcx(), trait_id, n_method) { + ty::MethodTraitItem(method) => method.name, + ty::TypeTraitItem(_) => { + bcx.tcx().sess.bug("can't monomorphize an associated \ + type") + } + }; + let mth_id = method_with_name(bcx.ccx(), impl_did, mname); + + // create a concatenated set of substitutions which includes + // those from the impl and those from the method: + let callee_substs = + combine_impl_and_methods_tps( + bcx, MethodCall(method_call), vtable_impl.substs); + + // translate the function + let llfn = trans_fn_ref_with_substs(bcx, + mth_id, + MethodCall(method_call), + callee_substs); + + Callee { bcx: bcx, data: Fn(llfn) } + } + traits::VtableUnboxedClosure(closure_def_id, substs) => { + // The substitutions should have no type parameters remaining + // after passing through fulfill_obligation + let llfn = trans_fn_ref_with_substs(bcx, + closure_def_id, + MethodCall(method_call), + substs); + + Callee { + bcx: bcx, + data: Fn(llfn), + } + } + _ => { + bcx.tcx().sess.bug( + "vtable_param left in monomorphized function's vtable substs"); + } + } +} + +fn combine_impl_and_methods_tps(bcx: Block, + node: ExprOrMethodCall, + rcvr_substs: subst::Substs) + -> subst::Substs +{ + /*! + * Creates a concatenated set of substitutions which includes + * those from the impl and those from the method. This are + * some subtle complications here. Statically, we have a list + * of type parameters like `[T0, T1, T2, M1, M2, M3]` where + * `Tn` are type parameters that appear on the receiver. For + * example, if the receiver is a method parameter `A` with a + * bound like `trait` then `Tn` would be `[B,C,D]`. + * + * The weird part is that the type `A` might now be bound to + * any other type, such as `foo`. In that case, the vector + * we want is: `[X, M1, M2, M3]`. Therefore, what we do now is + * to slice off the method type parameters and append them to + * the type parameters from the type that the receiver is + * mapped to. + */ + + let ccx = bcx.ccx(); + + let node_substs = node_id_substs(bcx, node); + + debug!("rcvr_substs={}", rcvr_substs.repr(ccx.tcx())); + debug!("node_substs={}", node_substs.repr(ccx.tcx())); + + // Break apart the type parameters from the node and type + // parameters from the receiver. + let node_method = node_substs.types.split().fns; + let subst::SeparateVecsPerParamSpace { + types: rcvr_type, + selfs: rcvr_self, + assocs: rcvr_assoc, + fns: rcvr_method + } = rcvr_substs.types.clone().split(); + assert!(rcvr_method.is_empty()); + subst::Substs { + regions: subst::ErasedRegions, + types: subst::VecPerParamSpace::new(rcvr_type, rcvr_self, rcvr_assoc, node_method) + } +} + +fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + method_ty: ty::t, + n_method: uint, + self_expr: &ast::Expr, + arg_cleanup_scope: cleanup::ScopeId) + -> Callee<'blk, 'tcx> { + /*! + * Create a method callee where the method is coming from a trait + * object (e.g., Box type). In this case, we must pull the fn + * pointer out of the vtable that is packaged up with the object. + * Objects are represented as a pair, so we first evaluate the self + * expression and then extract the self data and vtable out of the + * pair. + */ + + let _icx = push_ctxt("meth::trans_trait_callee"); + let mut bcx = bcx; + + // Translate self_datum and take ownership of the value by + // converting to an rvalue. + let self_datum = unpack_datum!( + bcx, expr::trans(bcx, self_expr)); + + let llval = if ty::type_needs_drop(bcx.tcx(), self_datum.ty) { + let self_datum = unpack_datum!( + bcx, self_datum.to_rvalue_datum(bcx, "trait_callee")); + + // Convert to by-ref since `trans_trait_callee_from_llval` wants it + // that way. + let self_datum = unpack_datum!( + bcx, self_datum.to_ref_datum(bcx)); + + // Arrange cleanup in case something should go wrong before the + // actual call occurs. + self_datum.add_clean(bcx.fcx, arg_cleanup_scope) + } else { + // We don't have to do anything about cleanups for &Trait and &mut Trait. + assert!(self_datum.kind.is_by_ref()); + self_datum.val + }; + + trans_trait_callee_from_llval(bcx, method_ty, n_method, llval) +} + +pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + callee_ty: ty::t, + n_method: uint, + llpair: ValueRef) + -> Callee<'blk, 'tcx> { + /*! + * Same as `trans_trait_callee()` above, except that it is given + * a by-ref pointer to the object pair. + */ + + let _icx = push_ctxt("meth::trans_trait_callee"); + let ccx = bcx.ccx(); + + // Load the data pointer from the object. + debug!("(translating trait callee) loading second index from pair"); + let llboxptr = GEPi(bcx, llpair, &[0u, abi::trt_field_box]); + let llbox = Load(bcx, llboxptr); + let llself = PointerCast(bcx, llbox, Type::i8p(ccx)); + + // Load the function from the vtable and cast it to the expected type. + debug!("(translating trait callee) loading method"); + // Replace the self type (&Self or Box) with an opaque pointer. + let llcallee_ty = match ty::get(callee_ty).sty { + ty::ty_bare_fn(ref f) if f.abi == Rust || f.abi == RustCall => { + type_of_rust_fn(ccx, + Some(Type::i8p(ccx)), + f.sig.inputs.slice_from(1), + f.sig.output, + f.abi) + } + _ => { + ccx.sess().bug("meth::trans_trait_callee given non-bare-rust-fn"); + } + }; + let llvtable = Load(bcx, + PointerCast(bcx, + GEPi(bcx, llpair, + &[0u, abi::trt_field_vtable]), + Type::vtable(ccx).ptr_to().ptr_to())); + let mptr = Load(bcx, GEPi(bcx, llvtable, &[0u, n_method + VTABLE_OFFSET])); + let mptr = PointerCast(bcx, mptr, llcallee_ty.ptr_to()); + + return Callee { + bcx: bcx, + data: TraitItem(MethodData { + llfn: mptr, + llself: llself, + }) + }; +} + +/// Creates a returns a dynamic vtable for the given type and vtable origin. +/// This is used only for objects. +/// +/// The `trait_ref` encodes the erased self type. Hence if we are +/// making an object `Foo` from a value of type `Foo`, then +/// `trait_ref` would map `T:Trait`, but `box_ty` would be +/// `Foo`. This `box_ty` is primarily used to encode the destructor. +/// This will hopefully change now that DST is underway. +pub fn get_vtable(bcx: Block, + box_ty: ty::t, + trait_ref: Rc) + -> ValueRef +{ + debug!("get_vtable(box_ty={}, trait_ref={})", + box_ty.repr(bcx.tcx()), + trait_ref.repr(bcx.tcx())); + + let tcx = bcx.tcx(); + let ccx = bcx.ccx(); + let _icx = push_ctxt("meth::get_vtable"); + + // Check the cache. + let cache_key = (box_ty, trait_ref.clone()); + match ccx.vtables().borrow().get(&cache_key) { + Some(&val) => { return val } + None => { } + } + + // Not in the cache. Build it. + let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| { + let vtable = fulfill_obligation(bcx.ccx(), + DUMMY_SP, + trait_ref.clone()); + match vtable { + traits::VtableBuiltin(_) => { + Vec::new().into_iter() + } + traits::VtableImpl( + traits::VtableImplData { + impl_def_id: id, + substs, + nested: _ }) => { + emit_vtable_methods(bcx, id, substs).into_iter() + } + traits::VtableUnboxedClosure(closure_def_id, substs) => { + // Look up closure type + let self_ty = ty::node_id_to_type(bcx.tcx(), closure_def_id.node); + // Apply substitutions from closure param environment. + // The substitutions should have no type parameters + // remaining after passing through fulfill_obligation + let self_ty = self_ty.subst(bcx.tcx(), &substs); + + let mut llfn = trans_fn_ref_with_substs( + bcx, + closure_def_id, + ExprId(0), + substs.clone()); + + { + let unboxed_closures = bcx.tcx() + .unboxed_closures + .borrow(); + let closure_info = + unboxed_closures.get(&closure_def_id) + .expect("get_vtable(): didn't find \ + unboxed closure"); + if closure_info.kind == ty::FnOnceUnboxedClosureKind { + // Untuple the arguments and create an unboxing shim. + let (new_inputs, new_output) = match ty::get(self_ty).sty { + ty::ty_unboxed_closure(_, _, ref substs) => { + let mut new_inputs = vec![self_ty.clone()]; + match ty::get(closure_info.closure_type + .sig + .inputs[0]).sty { + ty::ty_tup(ref elements) => { + for element in elements.iter() { + new_inputs.push(element.subst(bcx.tcx(), substs)); + } + } + _ => { + bcx.tcx().sess.bug("get_vtable(): closure \ + type wasn't a tuple") + } + } + (new_inputs, + closure_info.closure_type.sig.output.subst(bcx.tcx(), substs)) + }, + _ => bcx.tcx().sess.bug("get_vtable(): def wasn't an unboxed closure") + }; + + let closure_type = ty::BareFnTy { + fn_style: closure_info.closure_type.fn_style, + abi: Rust, + sig: ty::FnSig { + binder_id: closure_info.closure_type + .sig + .binder_id, + inputs: new_inputs, + output: new_output, + variadic: false, + }, + }; + debug!("get_vtable(): closure type is {}", + closure_type.repr(bcx.tcx())); + llfn = trans_unboxing_shim(bcx, + llfn, + &closure_type, + closure_def_id, + &substs); + } + } + + (vec!(llfn)).into_iter() + } + traits::VtableParam(..) => { + bcx.sess().bug( + format!("resolved vtable for {} to bad vtable {} in trans", + trait_ref.repr(bcx.tcx()), + vtable.repr(bcx.tcx())).as_slice()); + } + } + }); + + let size_ty = sizing_type_of(ccx, trait_ref.self_ty()); + let size = machine::llsize_of_alloc(ccx, size_ty); + let ll_size = C_uint(ccx, size); + let align = align_of(ccx, trait_ref.self_ty()); + let ll_align = C_uint(ccx, align); + + // Generate a destructor for the vtable. + let drop_glue = glue::get_drop_glue(ccx, box_ty); + let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods); + + ccx.vtables().borrow_mut().insert(cache_key, vtable); + vtable +} + +/// Helper function to declare and initialize the vtable. +pub fn make_vtable>(ccx: &CrateContext, + drop_glue: ValueRef, + size: ValueRef, + align: ValueRef, + ptrs: I) + -> ValueRef { + let _icx = push_ctxt("meth::make_vtable"); + + let head = vec![drop_glue, size, align]; + let components: Vec<_> = head.into_iter().chain(ptrs).collect(); + + unsafe { + let tbl = C_struct(ccx, components.as_slice(), false); + let sym = token::gensym("vtable"); + let vt_gvar = format!("vtable{}", sym.uint()).with_c_str(|buf| { + llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), buf) + }); + llvm::LLVMSetInitializer(vt_gvar, tbl); + llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True); + llvm::SetLinkage(vt_gvar, llvm::InternalLinkage); + vt_gvar + } +} + +fn emit_vtable_methods(bcx: Block, + impl_id: ast::DefId, + substs: subst::Substs) + -> Vec { + let ccx = bcx.ccx(); + let tcx = ccx.tcx(); + + let trt_id = match ty::impl_trait_ref(tcx, impl_id) { + Some(t_id) => t_id.def_id, + None => ccx.sess().bug("make_impl_vtable: don't know how to \ + make a vtable for a type impl!") + }; + + ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trt_id); + + let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id); + trait_item_def_ids.iter().flat_map(|method_def_id| { + let method_def_id = method_def_id.def_id(); + let name = ty::impl_or_trait_item(tcx, method_def_id).name(); + // The substitutions we have are on the impl, so we grab + // the method type from the impl to substitute into. + let m_id = method_with_name(ccx, impl_id, name); + let ti = ty::impl_or_trait_item(tcx, m_id); + match ti { + ty::MethodTraitItem(m) => { + debug!("(making impl vtable) emitting method {} at subst {}", + m.repr(tcx), + substs.repr(tcx)); + if m.generics.has_type_params(subst::FnSpace) || + ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) { + debug!("(making impl vtable) method has self or type \ + params: {}", + token::get_name(name)); + Some(C_null(Type::nil(ccx).ptr_to())).into_iter() + } else { + let mut fn_ref = trans_fn_ref_with_substs( + bcx, + m_id, + ExprId(0), + substs.clone()); + if m.explicit_self == ty::ByValueExplicitSelfCategory { + fn_ref = trans_unboxing_shim(bcx, + fn_ref, + &m.fty, + m_id, + &substs); + } + Some(fn_ref).into_iter() + } + } + ty::TypeTraitItem(_) => { + None.into_iter() + } + } + }).collect() +} + +pub fn trans_trait_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + datum: Datum, + id: ast::NodeId, + trait_ref: Rc, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + /*! + * Generates the code to convert from a pointer (`Box`, `&T`, etc) + * into an object (`Box`, `&Trait`, etc). This means creating a + * pair where the first word is the vtable and the second word is + * the pointer. + */ + + let mut bcx = bcx; + let _icx = push_ctxt("meth::trans_trait_cast"); + + let lldest = match dest { + Ignore => { + return datum.clean(bcx, "trait_trait_cast", id); + } + SaveIn(dest) => dest + }; + + debug!("trans_trait_cast: trait_ref={}", + trait_ref.repr(bcx.tcx())); + + let datum_ty = datum.ty; + let llbox_ty = type_of(bcx.ccx(), datum_ty); + + // Store the pointer into the first half of pair. + let llboxdest = GEPi(bcx, lldest, &[0u, abi::trt_field_box]); + let llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to()); + bcx = datum.store_to(bcx, llboxdest); + + // Store the vtable into the second half of pair. + let vtable = get_vtable(bcx, datum_ty, trait_ref); + let llvtabledest = GEPi(bcx, lldest, &[0u, abi::trt_field_vtable]); + let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to()); + Store(bcx, vtable, llvtabledest); + + bcx +} diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs new file mode 100644 index 00000000000..fe7697447ac --- /dev/null +++ b/src/librustc_trans/trans/mod.rs @@ -0,0 +1,47 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod doc; +pub mod macros; +pub mod inline; +pub mod monomorphize; +pub mod controlflow; +pub mod glue; +pub mod datum; +pub mod callee; +pub mod expr; +pub mod common; +pub mod context; +pub mod consts; +pub mod type_of; +pub mod build; +pub mod builder; +pub mod base; +pub mod _match; +pub mod closure; +pub mod tvec; +pub mod meth; +pub mod cabi; +pub mod cabi_x86; +pub mod cabi_x86_64; +pub mod cabi_x86_win64; +pub mod cabi_arm; +pub mod cabi_mips; +pub mod foreign; +pub mod intrinsic; +pub mod debuginfo; +pub mod machine; +pub mod adt; +pub mod asm; +pub mod type_; +pub mod value; +pub mod basic_block; +pub mod llrepr; +pub mod cleanup; diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs new file mode 100644 index 00000000000..077c1337a44 --- /dev/null +++ b/src/librustc_trans/trans/monomorphize.rs @@ -0,0 +1,291 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::link::exported_name; +use session; +use llvm::ValueRef; +use llvm; +use middle::subst; +use middle::subst::Subst; +use trans::base::{set_llvm_fn_attrs, set_inline_hint}; +use trans::base::{trans_enum_variant, push_ctxt, get_item_val}; +use trans::base::{trans_fn, decl_internal_rust_fn}; +use trans::base; +use trans::common::*; +use trans::foreign; +use middle::ty; +use util::ppaux::Repr; + +use syntax::abi; +use syntax::ast; +use syntax::ast_map; +use syntax::ast_util::{local_def, PostExpansionMethod}; +use syntax::attr; +use std::hash::{sip, Hash}; + +pub fn monomorphic_fn(ccx: &CrateContext, + fn_id: ast::DefId, + real_substs: &subst::Substs, + ref_id: Option) + -> (ValueRef, bool) { + debug!("monomorphic_fn(\ + fn_id={}, \ + real_substs={}, \ + ref_id={})", + fn_id.repr(ccx.tcx()), + real_substs.repr(ccx.tcx()), + ref_id); + + assert!(real_substs.types.all(|t| { + !ty::type_needs_infer(*t) && !ty::type_has_params(*t) + })); + + let _icx = push_ctxt("monomorphic_fn"); + + let hash_id = MonoId { + def: fn_id, + params: real_substs.types.clone() + }; + + match ccx.monomorphized().borrow().get(&hash_id) { + Some(&val) => { + debug!("leaving monomorphic fn {}", + ty::item_path_str(ccx.tcx(), fn_id)); + return (val, false); + } + None => () + } + + let psubsts = param_substs { + substs: (*real_substs).clone(), + }; + + debug!("monomorphic_fn(\ + fn_id={}, \ + psubsts={}, \ + hash_id={})", + fn_id.repr(ccx.tcx()), + psubsts.repr(ccx.tcx()), + hash_id); + + let tpt = ty::lookup_item_type(ccx.tcx(), fn_id); + let llitem_ty = tpt.ty; + + let map_node = session::expect( + ccx.sess(), + ccx.tcx().map.find(fn_id.node), + || { + format!("while monomorphizing {}, couldn't find it in \ + the item map (may have attempted to monomorphize \ + an item defined in a different crate?)", + fn_id) + }); + + match map_node { + ast_map::NodeForeignItem(_) => { + if ccx.tcx().map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic { + // Foreign externs don't have to be monomorphized. + return (get_item_val(ccx, fn_id.node), true); + } + } + _ => {} + } + + debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx())); + let mono_ty = llitem_ty.subst(ccx.tcx(), real_substs); + + ccx.stats().n_monos.set(ccx.stats().n_monos.get() + 1); + + let depth; + { + let mut monomorphizing = ccx.monomorphizing().borrow_mut(); + depth = match monomorphizing.get(&fn_id) { + Some(&d) => d, None => 0 + }; + + // Random cut-off -- code that needs to instantiate the same function + // recursively more than thirty times can probably safely be assumed + // to be causing an infinite expansion. + if depth > ccx.sess().recursion_limit.get() { + ccx.sess().span_fatal(ccx.tcx().map.span(fn_id.node), + "reached the recursion limit during monomorphization"); + } + + monomorphizing.insert(fn_id, depth + 1); + } + + let hash; + let s = { + let mut state = sip::SipState::new(); + hash_id.hash(&mut state); + mono_ty.hash(&mut state); + + hash = format!("h{}", state.result()); + ccx.tcx().map.with_path(fn_id.node, |path| { + exported_name(path, hash.as_slice()) + }) + }; + + debug!("monomorphize_fn mangled to {}", s); + + // This shouldn't need to option dance. + let mut hash_id = Some(hash_id); + let mk_lldecl = |abi: abi::Abi| { + let lldecl = if abi != abi::Rust { + foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, s.as_slice()) + } else { + decl_internal_rust_fn(ccx, mono_ty, s.as_slice()) + }; + + ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); + lldecl + }; + let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| { + base::update_linkage(ccx, lldecl, None, base::OriginalTranslation); + set_llvm_fn_attrs(ccx, attrs, lldecl); + + let is_first = !ccx.available_monomorphizations().borrow().contains(&s); + if is_first { + ccx.available_monomorphizations().borrow_mut().insert(s.clone()); + } + + let trans_everywhere = attr::requests_inline(attrs); + if trans_everywhere && !is_first { + llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage); + } + + // If `true`, then `lldecl` should be given a function body. + // Otherwise, it should be left as a declaration of an external + // function, with no definition in the current compilation unit. + trans_everywhere || is_first + }; + + let lldecl = match map_node { + ast_map::NodeItem(i) => { + match *i { + ast::Item { + node: ast::ItemFn(ref decl, _, abi, _, ref body), + .. + } => { + let d = mk_lldecl(abi); + let needs_body = setup_lldecl(d, i.attrs.as_slice()); + if needs_body { + if abi != abi::Rust { + foreign::trans_rust_fn_with_foreign_abi( + ccx, &**decl, &**body, &[], d, &psubsts, fn_id.node, + Some(hash.as_slice())); + } else { + trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, &[]); + } + } + + d + } + _ => { + ccx.sess().bug("Can't monomorphize this kind of item") + } + } + } + ast_map::NodeVariant(v) => { + let parent = ccx.tcx().map.get_parent(fn_id.node); + let tvs = ty::enum_variants(ccx.tcx(), local_def(parent)); + let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap(); + let d = mk_lldecl(abi::Rust); + set_inline_hint(d); + match v.node.kind { + ast::TupleVariantKind(ref args) => { + trans_enum_variant(ccx, + parent, + &*v, + args.as_slice(), + this_tv.disr_val, + &psubsts, + d); + } + ast::StructVariantKind(_) => + ccx.sess().bug("can't monomorphize struct variants"), + } + d + } + ast_map::NodeImplItem(ii) => { + match *ii { + ast::MethodImplItem(ref mth) => { + let d = mk_lldecl(abi::Rust); + let needs_body = setup_lldecl(d, mth.attrs.as_slice()); + if needs_body { + trans_fn(ccx, + mth.pe_fn_decl(), + mth.pe_body(), + d, + &psubsts, + mth.id, + &[]); + } + d + } + ast::TypeImplItem(_) => { + ccx.sess().bug("can't monomorphize an associated type") + } + } + } + ast_map::NodeTraitItem(method) => { + match *method { + ast::ProvidedMethod(ref mth) => { + let d = mk_lldecl(abi::Rust); + let needs_body = setup_lldecl(d, mth.attrs.as_slice()); + if needs_body { + trans_fn(ccx, mth.pe_fn_decl(), mth.pe_body(), d, + &psubsts, mth.id, &[]); + } + d + } + _ => { + ccx.sess().bug(format!("can't monomorphize a {}", + map_node).as_slice()) + } + } + } + ast_map::NodeStructCtor(struct_def) => { + let d = mk_lldecl(abi::Rust); + set_inline_hint(d); + base::trans_tuple_struct(ccx, + struct_def.fields.as_slice(), + struct_def.ctor_id.expect("ast-mapped tuple struct \ + didn't have a ctor id"), + &psubsts, + d); + d + } + + // Ugh -- but this ensures any new variants won't be forgotten + ast_map::NodeForeignItem(..) | + ast_map::NodeLifetime(..) | + ast_map::NodeExpr(..) | + ast_map::NodeStmt(..) | + ast_map::NodeArg(..) | + ast_map::NodeBlock(..) | + ast_map::NodePat(..) | + ast_map::NodeLocal(..) => { + ccx.sess().bug(format!("can't monomorphize a {}", + map_node).as_slice()) + } + }; + + ccx.monomorphizing().borrow_mut().insert(fn_id, depth); + + debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id)); + (lldecl, true) +} + +#[deriving(PartialEq, Eq, Hash, Show)] +pub struct MonoId { + pub def: ast::DefId, + pub params: subst::VecPerParamSpace +} diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs new file mode 100644 index 00000000000..0590d7c785a --- /dev/null +++ b/src/librustc_trans/trans/tvec.rs @@ -0,0 +1,537 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types)] + +use back::abi; +use llvm; +use llvm::{ValueRef}; +use trans::base::*; +use trans::base; +use trans::build::*; +use trans::cleanup; +use trans::cleanup::CleanupMethods; +use trans::common::*; +use trans::datum::*; +use trans::expr::{Dest, Ignore, SaveIn}; +use trans::expr; +use trans::glue; +use trans::machine; +use trans::machine::{nonzero_llsize_of, llsize_of_alloc}; +use trans::type_::Type; +use trans::type_of; +use middle::ty; +use util::ppaux::ty_to_string; + +use syntax::ast; +use syntax::parse::token::InternedString; + +fn get_len(bcx: Block, vptr: ValueRef) -> ValueRef { + let _icx = push_ctxt("tvec::get_lenl"); + Load(bcx, expr::get_len(bcx, vptr)) +} + +fn get_dataptr(bcx: Block, vptr: ValueRef) -> ValueRef { + let _icx = push_ctxt("tvec::get_dataptr"); + Load(bcx, expr::get_dataptr(bcx, vptr)) +} + +pub fn pointer_add_byte(bcx: Block, ptr: ValueRef, bytes: ValueRef) -> ValueRef { + let _icx = push_ctxt("tvec::pointer_add_byte"); + let old_ty = val_ty(ptr); + let bptr = PointerCast(bcx, ptr, Type::i8p(bcx.ccx())); + return PointerCast(bcx, InBoundsGEP(bcx, bptr, &[bytes]), old_ty); +} + +pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + vptr: ValueRef, + unit_ty: ty::t, + should_deallocate: bool) + -> Block<'blk, 'tcx> { + let not_null = IsNotNull(bcx, vptr); + with_cond(bcx, not_null, |bcx| { + let ccx = bcx.ccx(); + let tcx = bcx.tcx(); + let _icx = push_ctxt("tvec::make_drop_glue_unboxed"); + + let dataptr = get_dataptr(bcx, vptr); + let bcx = if ty::type_needs_drop(tcx, unit_ty) { + let len = get_len(bcx, vptr); + iter_vec_raw(bcx, dataptr, unit_ty, len, |bb, vv, tt| glue::drop_ty(bb, vv, tt, None)) + } else { + bcx + }; + + if should_deallocate { + let llty = type_of::type_of(ccx, unit_ty); + let unit_size = llsize_of_alloc(ccx, llty); + if unit_size != 0 { + let len = get_len(bcx, vptr); + let not_empty = ICmp(bcx, llvm::IntNE, len, C_uint(ccx, 0u)); + with_cond(bcx, not_empty, |bcx| { + let llalign = C_uint(ccx, machine::llalign_of_min(ccx, llty)); + let size = Mul(bcx, C_uint(ccx, unit_size), len); + glue::trans_exchange_free_dyn(bcx, dataptr, size, llalign) + }) + } else { + bcx + } + } else { + bcx + } + }) +} + +pub struct VecTypes { + pub unit_ty: ty::t, + pub llunit_ty: Type, + pub llunit_size: ValueRef, + pub llunit_alloc_size: u64 +} + +impl VecTypes { + pub fn to_string(&self, ccx: &CrateContext) -> String { + format!("VecTypes {{unit_ty={}, llunit_ty={}, \ + llunit_size={}, llunit_alloc_size={}}}", + ty_to_string(ccx.tcx(), self.unit_ty), + ccx.tn().type_to_string(self.llunit_ty), + ccx.tn().val_to_string(self.llunit_size), + self.llunit_alloc_size) + } +} + +pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &ast::Expr, + dest: expr::Dest) + -> Block<'blk, 'tcx> { + //! + // + // [...] allocates a fixed-size array and moves it around "by value". + // In this case, it means that the caller has already given us a location + // to store the array of the suitable size, so all we have to do is + // generate the content. + + debug!("trans_fixed_vstore(expr={}, dest={})", + bcx.expr_to_string(expr), dest.to_string(bcx.ccx())); + + let vt = vec_types_from_expr(bcx, expr); + + return match dest { + Ignore => write_content(bcx, &vt, expr, expr, dest), + SaveIn(lldest) => { + // lldest will have type *[T x N], but we want the type *T, + // so use GEP to convert: + let lldest = GEPi(bcx, lldest, &[0, 0]); + write_content(bcx, &vt, expr, expr, SaveIn(lldest)) + } + }; +} + +pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + slice_expr: &ast::Expr, + content_expr: &ast::Expr) + -> DatumBlock<'blk, 'tcx, Expr> { + /*! + * &[...] allocates memory on the stack and writes the values into it, + * returning the vector (the caller must make the reference). "..." is + * similar except that the memory can be statically allocated and we return + * a reference (strings are always by-ref). + */ + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let mut bcx = bcx; + + debug!("trans_slice_vec(slice_expr={})", + bcx.expr_to_string(slice_expr)); + + let vec_ty = node_id_type(bcx, slice_expr.id); + + // Handle the "..." case (returns a slice since strings are always unsized): + match content_expr.node { + ast::ExprLit(ref lit) => { + match lit.node { + ast::LitStr(ref s, _) => { + let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); + bcx = trans_lit_str(bcx, + content_expr, + s.clone(), + SaveIn(scratch.val)); + return DatumBlock::new(bcx, scratch.to_expr_datum()); + } + _ => {} + } + } + _ => {} + } + + // Handle the &[...] case: + let vt = vec_types_from_expr(bcx, content_expr); + let count = elements_required(bcx, content_expr); + debug!(" vt={}, count={}", vt.to_string(ccx), count); + let llcount = C_uint(ccx, count); + + let fixed_ty = ty::mk_vec(bcx.tcx(), + vt.unit_ty, + Some(count)); + let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); + + let llfixed = if count == 0 { + // Just create a zero-sized alloca to preserve + // the non-null invariant of the inner slice ptr + let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + BitCast(bcx, llfixed, llfixed_ty) + } else { + // Make a fixed-length backing array and allocate it on the stack. + let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + + // Arrange for the backing array to be cleaned up. + let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty); + let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id); + fcx.schedule_lifetime_end(cleanup_scope, llfixed_casted); + fcx.schedule_drop_mem(cleanup_scope, llfixed_casted, fixed_ty); + + // Generate the content into the backing array. + bcx = write_content(bcx, &vt, slice_expr, + content_expr, SaveIn(llfixed)); + + llfixed_casted + }; + + immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock() +} + +pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lit_expr: &ast::Expr, + str_lit: InternedString, + dest: Dest) + -> Block<'blk, 'tcx> { + /*! + * Literal strings translate to slices into static memory. This is + * different from trans_slice_vstore() above because it doesn't need to copy + * the content anywhere. + */ + + debug!("trans_lit_str(lit_expr={}, dest={})", + bcx.expr_to_string(lit_expr), + dest.to_string(bcx.ccx())); + + match dest { + Ignore => bcx, + SaveIn(lldest) => { + unsafe { + let bytes = str_lit.get().len(); + let llbytes = C_uint(bcx.ccx(), bytes); + let llcstr = C_cstr(bcx.ccx(), str_lit, false); + let llcstr = llvm::LLVMConstPointerCast(llcstr, Type::i8p(bcx.ccx()).to_ref()); + Store(bcx, llcstr, GEPi(bcx, lldest, &[0u, abi::slice_elt_base])); + Store(bcx, llbytes, GEPi(bcx, lldest, &[0u, abi::slice_elt_len])); + bcx + } + } + } +} + +pub fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + vt: &VecTypes, + vstore_expr: &ast::Expr, + content_expr: &ast::Expr, + dest: Dest) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("tvec::write_content"); + let fcx = bcx.fcx; + let mut bcx = bcx; + + debug!("write_content(vt={}, dest={}, vstore_expr={})", + vt.to_string(bcx.ccx()), + dest.to_string(bcx.ccx()), + bcx.expr_to_string(vstore_expr)); + + match content_expr.node { + ast::ExprLit(ref lit) => { + match lit.node { + ast::LitStr(ref s, _) => { + match dest { + Ignore => return bcx, + SaveIn(lldest) => { + let bytes = s.get().len(); + let llbytes = C_uint(bcx.ccx(), bytes); + let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false); + base::call_memcpy(bcx, + lldest, + llcstr, + llbytes, + 1); + return bcx; + } + } + } + _ => { + bcx.tcx().sess.span_bug(content_expr.span, + "unexpected evec content"); + } + } + } + ast::ExprVec(ref elements) => { + match dest { + Ignore => { + for element in elements.iter() { + bcx = expr::trans_into(bcx, &**element, Ignore); + } + } + + SaveIn(lldest) => { + let temp_scope = fcx.push_custom_cleanup_scope(); + for (i, element) in elements.iter().enumerate() { + let lleltptr = GEPi(bcx, lldest, &[i]); + debug!("writing index {} with lleltptr={}", + i, bcx.val_to_string(lleltptr)); + bcx = expr::trans_into(bcx, &**element, + SaveIn(lleltptr)); + let scope = cleanup::CustomScope(temp_scope); + fcx.schedule_lifetime_end(scope, lleltptr); + fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty); + } + fcx.pop_custom_cleanup_scope(temp_scope); + } + } + return bcx; + } + ast::ExprRepeat(ref element, ref count_expr) => { + match dest { + Ignore => { + return expr::trans_into(bcx, &**element, Ignore); + } + SaveIn(lldest) => { + match ty::eval_repeat_count(bcx.tcx(), &**count_expr) { + 0 => bcx, + 1 => expr::trans_into(bcx, &**element, SaveIn(lldest)), + count => { + let elem = unpack_datum!(bcx, expr::trans(bcx, &**element)); + assert!(!ty::type_moves_by_default(bcx.tcx(), elem.ty)); + + let bcx = iter_vec_loop(bcx, lldest, vt, + C_uint(bcx.ccx(), count), + |set_bcx, lleltptr, _| { + elem.shallow_copy(set_bcx, lleltptr) + }); + + elem.add_clean_if_rvalue(bcx, element.id); + bcx + } + } + } + } + } + _ => { + bcx.tcx().sess.span_bug(content_expr.span, + "unexpected vec content"); + } + } +} + +pub fn vec_types_from_expr(bcx: Block, vec_expr: &ast::Expr) -> VecTypes { + let vec_ty = node_id_type(bcx, vec_expr.id); + vec_types(bcx, ty::sequence_element_type(bcx.tcx(), vec_ty)) +} + +pub fn vec_types(bcx: Block, unit_ty: ty::t) -> VecTypes { + let ccx = bcx.ccx(); + let llunit_ty = type_of::type_of(ccx, unit_ty); + let llunit_size = nonzero_llsize_of(ccx, llunit_ty); + let llunit_alloc_size = llsize_of_alloc(ccx, llunit_ty); + + VecTypes { + unit_ty: unit_ty, + llunit_ty: llunit_ty, + llunit_size: llunit_size, + llunit_alloc_size: llunit_alloc_size + } +} + +pub fn elements_required(bcx: Block, content_expr: &ast::Expr) -> uint { + //! Figure out the number of elements we need to store this content + + match content_expr.node { + ast::ExprLit(ref lit) => { + match lit.node { + ast::LitStr(ref s, _) => s.get().len(), + _ => { + bcx.tcx().sess.span_bug(content_expr.span, + "unexpected evec content") + } + } + }, + ast::ExprVec(ref es) => es.len(), + ast::ExprRepeat(_, ref count_expr) => { + ty::eval_repeat_count(bcx.tcx(), &**count_expr) + } + _ => bcx.tcx().sess.span_bug(content_expr.span, + "unexpected vec content") + } +} + +pub fn get_fixed_base_and_len(bcx: Block, + llval: ValueRef, + vec_length: uint) + -> (ValueRef, ValueRef) { + /*! + * Converts a fixed-length vector into the slice pair. + * The vector should be stored in `llval` which should be by ref. + */ + + let ccx = bcx.ccx(); + + let base = expr::get_dataptr(bcx, llval); + let len = C_uint(ccx, vec_length); + (base, len) +} + +fn get_slice_base_and_len(bcx: Block, + llval: ValueRef) + -> (ValueRef, ValueRef) { + let base = Load(bcx, GEPi(bcx, llval, &[0u, abi::slice_elt_base])); + let len = Load(bcx, GEPi(bcx, llval, &[0u, abi::slice_elt_len])); + (base, len) +} + +pub fn get_base_and_len(bcx: Block, + llval: ValueRef, + vec_ty: ty::t) + -> (ValueRef, ValueRef) { + /*! + * Converts a vector into the slice pair. The vector should be + * stored in `llval` which should be by-reference. If you have a + * datum, you would probably prefer to call + * `Datum::get_base_and_len()` which will handle any conversions + * for you. + */ + + let ccx = bcx.ccx(); + + match ty::get(vec_ty).sty { + ty::ty_vec(_, Some(n)) => get_fixed_base_and_len(bcx, llval, n), + ty::ty_open(ty) => match ty::get(ty).sty { + ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), + _ => ccx.sess().bug("unexpected type in get_base_and_len") + }, + + // Only used for pattern matching. + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { + ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), + ty::ty_vec(_, Some(n)) => { + let base = GEPi(bcx, Load(bcx, llval), &[0u, 0u]); + (base, C_uint(ccx, n)) + } + _ => ccx.sess().bug("unexpected type in get_base_and_len"), + }, + _ => ccx.sess().bug("unexpected type in get_base_and_len"), + } +} + +pub type iter_vec_block<'a, 'blk, 'tcx> = + |Block<'blk, 'tcx>, ValueRef, ty::t|: 'a -> Block<'blk, 'tcx>; + +pub fn iter_vec_loop<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + data_ptr: ValueRef, + vt: &VecTypes, + count: ValueRef, + f: iter_vec_block<'a, 'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("tvec::iter_vec_loop"); + let fcx = bcx.fcx; + + let next_bcx = fcx.new_temp_block("expr_repeat: while next"); + let loop_bcx = fcx.new_temp_block("expr_repeat"); + let cond_bcx = fcx.new_temp_block("expr_repeat: loop cond"); + let body_bcx = fcx.new_temp_block("expr_repeat: body: set"); + let inc_bcx = fcx.new_temp_block("expr_repeat: body: inc"); + Br(bcx, loop_bcx.llbb); + + let loop_counter = { + // i = 0 + let i = alloca(loop_bcx, bcx.ccx().int_type(), "__i"); + Store(loop_bcx, C_uint(bcx.ccx(), 0u), i); + + Br(loop_bcx, cond_bcx.llbb); + i + }; + + { // i < count + let lhs = Load(cond_bcx, loop_counter); + let rhs = count; + let cond_val = ICmp(cond_bcx, llvm::IntULT, lhs, rhs); + + CondBr(cond_bcx, cond_val, body_bcx.llbb, next_bcx.llbb); + } + + { // loop body + let i = Load(body_bcx, loop_counter); + let lleltptr = if vt.llunit_alloc_size == 0 { + data_ptr + } else { + InBoundsGEP(body_bcx, data_ptr, &[i]) + }; + let body_bcx = f(body_bcx, lleltptr, vt.unit_ty); + + Br(body_bcx, inc_bcx.llbb); + } + + { // i += 1 + let i = Load(inc_bcx, loop_counter); + let plusone = Add(inc_bcx, i, C_uint(bcx.ccx(), 1u)); + Store(inc_bcx, plusone, loop_counter); + + Br(inc_bcx, cond_bcx.llbb); + } + + next_bcx +} + +pub fn iter_vec_raw<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + data_ptr: ValueRef, + unit_ty: ty::t, + len: ValueRef, + f: iter_vec_block<'a, 'blk, 'tcx>) + -> Block<'blk, 'tcx> { + let _icx = push_ctxt("tvec::iter_vec_raw"); + let fcx = bcx.fcx; + + let vt = vec_types(bcx, unit_ty); + let fill = Mul(bcx, len, vt.llunit_size); + + if vt.llunit_alloc_size == 0 { + // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) + iter_vec_loop(bcx, data_ptr, &vt, fill, f) + } else { + // Calculate the last pointer address we want to handle. + // FIXME (#3729): Optimize this when the size of the unit type is + // statically known to not use pointer casts, which tend to confuse + // LLVM. + let data_end_ptr = pointer_add_byte(bcx, data_ptr, fill); + + // Now perform the iteration. + let header_bcx = fcx.new_temp_block("iter_vec_loop_header"); + Br(bcx, header_bcx.llbb); + let data_ptr = + Phi(header_bcx, val_ty(data_ptr), &[data_ptr], &[bcx.llbb]); + let not_yet_at_end = + ICmp(header_bcx, llvm::IntULT, data_ptr, data_end_ptr); + let body_bcx = fcx.new_temp_block("iter_vec_loop_body"); + let next_bcx = fcx.new_temp_block("iter_vec_next"); + CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb); + let body_bcx = f(body_bcx, data_ptr, vt.unit_ty); + AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr, + &[C_int(bcx.ccx(), 1i)]), + body_bcx.llbb); + Br(body_bcx, header_bcx.llbb); + next_bcx + } +} diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs new file mode 100644 index 00000000000..0662909e40f --- /dev/null +++ b/src/librustc_trans/trans/type_.rs @@ -0,0 +1,358 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use llvm; +use llvm::{TypeRef, Bool, False, True, TypeKind, ValueRef}; +use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; + +use trans::context::CrateContext; +use util::nodemap::FnvHashMap; + +use syntax::ast; + +use std::c_str::ToCStr; +use std::mem; +use std::cell::RefCell; + +use libc::c_uint; + +#[deriving(Clone, PartialEq, Show)] +#[repr(C)] +pub struct Type { + rf: TypeRef +} + +macro_rules! ty ( + ($e:expr) => ( Type::from_ref(unsafe { $e })) +) + +/** + * Wrapper for LLVM TypeRef + */ +impl Type { + #[inline(always)] + pub fn from_ref(r: TypeRef) -> Type { + Type { + rf: r + } + } + + #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler + pub fn to_ref(&self) -> TypeRef { + self.rf + } + + pub fn void(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMVoidTypeInContext(ccx.llcx())) + } + + pub fn nil(ccx: &CrateContext) -> Type { + Type::empty_struct(ccx) + } + + pub fn metadata(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx())) + } + + pub fn i1(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMInt1TypeInContext(ccx.llcx())) + } + + pub fn i8(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMInt8TypeInContext(ccx.llcx())) + } + + pub fn i16(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMInt16TypeInContext(ccx.llcx())) + } + + pub fn i32(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMInt32TypeInContext(ccx.llcx())) + } + + pub fn i64(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMInt64TypeInContext(ccx.llcx())) + } + + pub fn f32(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMFloatTypeInContext(ccx.llcx())) + } + + pub fn f64(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx())) + } + + pub fn bool(ccx: &CrateContext) -> Type { + Type::i8(ccx) + } + + pub fn char(ccx: &CrateContext) -> Type { + Type::i32(ccx) + } + + pub fn i8p(ccx: &CrateContext) -> Type { + Type::i8(ccx).ptr_to() + } + + pub fn int(ccx: &CrateContext) -> Type { + match ccx.tcx().sess.target.target.target_word_size.as_slice() { + "32" => Type::i32(ccx), + "64" => Type::i64(ccx), + tws => panic!("Unsupported target word size for int: {}", tws), + } + } + + pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type { + match t { + ast::TyI => ccx.int_type(), + ast::TyI8 => Type::i8(ccx), + ast::TyI16 => Type::i16(ccx), + ast::TyI32 => Type::i32(ccx), + ast::TyI64 => Type::i64(ccx) + } + } + + pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type { + match t { + ast::TyU => ccx.int_type(), + ast::TyU8 => Type::i8(ccx), + ast::TyU16 => Type::i16(ccx), + ast::TyU32 => Type::i32(ccx), + ast::TyU64 => Type::i64(ccx) + } + } + + pub fn float_from_ty(ccx: &CrateContext, t: ast::FloatTy) -> Type { + match t { + ast::TyF32 => Type::f32(ccx), + ast::TyF64 => Type::f64(ccx), + } + } + + pub fn func(args: &[Type], ret: &Type) -> Type { + let vec : &[TypeRef] = unsafe { mem::transmute(args) }; + ty!(llvm::LLVMFunctionType(ret.to_ref(), vec.as_ptr(), + args.len() as c_uint, False)) + } + + pub fn variadic_func(args: &[Type], ret: &Type) -> Type { + let vec : &[TypeRef] = unsafe { mem::transmute(args) }; + ty!(llvm::LLVMFunctionType(ret.to_ref(), vec.as_ptr(), + args.len() as c_uint, True)) + } + + pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type { + let els : &[TypeRef] = unsafe { mem::transmute(els) }; + ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(), + els.len() as c_uint, + packed as Bool)) + } + + pub fn named_struct(ccx: &CrateContext, name: &str) -> Type { + ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx(), s))) + } + + pub fn empty_struct(ccx: &CrateContext) -> Type { + Type::struct_(ccx, &[], false) + } + + pub fn vtable(ccx: &CrateContext) -> Type { + Type::array(&Type::i8p(ccx).ptr_to(), 1) + } + + pub fn generic_glue_fn(cx: &CrateContext) -> Type { + match cx.tn().find_type("glue_fn") { + Some(ty) => return ty, + None => () + } + + let ty = Type::glue_fn(cx, Type::i8p(cx)); + cx.tn().associate_type("glue_fn", &ty); + + ty + } + + pub fn glue_fn(ccx: &CrateContext, t: Type) -> Type { + Type::func(&[t], &Type::void(ccx)) + } + + pub fn tydesc(ccx: &CrateContext, str_slice_ty: Type) -> Type { + let mut tydesc = Type::named_struct(ccx, "tydesc"); + let glue_fn_ty = Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(); + + let int_ty = Type::int(ccx); + + // Must mirror: + // + // std::unstable::intrinsics::TyDesc + + let elems = [int_ty, // size + int_ty, // align + glue_fn_ty, // drop + str_slice_ty]; // name + tydesc.set_struct_body(&elems, false); + + tydesc + } + + pub fn array(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) + } + + pub fn vector(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) + } + + pub fn vec(ccx: &CrateContext, ty: &Type) -> Type { + Type::struct_(ccx, + &[Type::array(ty, 0), Type::int(ccx)], + false) + } + + pub fn opaque_vec(ccx: &CrateContext) -> Type { + Type::vec(ccx, &Type::i8(ccx)) + } + + // The box pointed to by @T. + pub fn at_box(ccx: &CrateContext, ty: Type) -> Type { + Type::struct_(ccx, &[ + ccx.int_type(), Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(), + Type::i8p(ccx), Type::i8p(ccx), ty + ], false) + } + + pub fn vtable_ptr(ccx: &CrateContext) -> Type { + Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to() + } + + pub fn opaque_trait(ccx: &CrateContext) -> Type { + Type::struct_(ccx, &[Type::opaque_trait_data(ccx).ptr_to(), Type::vtable_ptr(ccx)], false) + } + + pub fn opaque_trait_data(ccx: &CrateContext) -> Type { + Type::i8(ccx) + } + + pub fn kind(&self) -> TypeKind { + unsafe { + llvm::LLVMGetTypeKind(self.to_ref()) + } + } + + pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { + unsafe { + let vec : &[TypeRef] = mem::transmute(els); + llvm::LLVMStructSetBody(self.to_ref(), vec.as_ptr(), + els.len() as c_uint, packed as Bool) + } + } + + pub fn ptr_to(&self) -> Type { + ty!(llvm::LLVMPointerType(self.to_ref(), 0)) + } + + pub fn is_packed(&self) -> bool { + unsafe { + llvm::LLVMIsPackedStruct(self.to_ref()) == True + } + } + + pub fn element_type(&self) -> Type { + unsafe { + Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) + } + } + + pub fn array_length(&self) -> uint { + unsafe { + llvm::LLVMGetArrayLength(self.to_ref()) as uint + } + } + + pub fn field_types(&self) -> Vec { + unsafe { + let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint; + if n_elts == 0 { + return Vec::new(); + } + let mut elts = Vec::from_elem(n_elts, Type { rf: 0 as TypeRef }); + llvm::LLVMGetStructElementTypes(self.to_ref(), + elts.as_mut_ptr() as *mut TypeRef); + elts + } + } + + pub fn return_type(&self) -> Type { + ty!(llvm::LLVMGetReturnType(self.to_ref())) + } + + pub fn func_params(&self) -> Vec { + unsafe { + let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as uint; + let mut args = Vec::from_elem(n_args, Type { rf: 0 as TypeRef }); + llvm::LLVMGetParamTypes(self.to_ref(), + args.as_mut_ptr() as *mut TypeRef); + args + } + } + + pub fn float_width(&self) -> uint { + match self.kind() { + Float => 32, + Double => 64, + X86_FP80 => 80, + FP128 | PPC_FP128 => 128, + _ => panic!("llvm_float_width called on a non-float type") + } + } +} + + +/* Memory-managed object interface to type handles. */ + +pub struct TypeNames { + named_types: RefCell>, +} + +impl TypeNames { + pub fn new() -> TypeNames { + TypeNames { + named_types: RefCell::new(FnvHashMap::new()) + } + } + + pub fn associate_type(&self, s: &str, t: &Type) { + assert!(self.named_types.borrow_mut().insert(s.to_string(), + t.to_ref()).is_none()); + } + + pub fn find_type(&self, s: &str) -> Option { + self.named_types.borrow().get(s).map(|x| Type::from_ref(*x)) + } + + pub fn type_to_string(&self, ty: Type) -> String { + llvm::build_string(|s| unsafe { + llvm::LLVMWriteTypeToString(ty.to_ref(), s); + }).expect("non-UTF8 type description from LLVM") + } + + pub fn types_to_str(&self, tys: &[Type]) -> String { + let strs: Vec = tys.iter().map(|t| self.type_to_string(*t)).collect(); + format!("[{}]", strs.connect(",")) + } + + pub fn val_to_string(&self, val: ValueRef) -> String { + llvm::build_string(|s| unsafe { + llvm::LLVMWriteValueToString(val, s); + }).expect("nun-UTF8 value description from LLVM") + } +} diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs new file mode 100644 index 00000000000..5fa21984637 --- /dev/null +++ b/src/librustc_trans/trans/type_of.rs @@ -0,0 +1,478 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types)] + +pub use self::named_ty::*; + +use middle::subst; +use trans::adt; +use trans::common::*; +use trans::foreign; +use trans::machine; +use middle::ty; +use util::ppaux; +use util::ppaux::Repr; + +use trans::type_::Type; + +use std::num::Int; +use syntax::abi; +use syntax::ast; + +// LLVM doesn't like objects that are too big. Issue #17913 +fn ensure_array_fits_in_address_space(ccx: &CrateContext, + llet: Type, + size: machine::llsize, + scapegoat: ty::t) { + let esz = machine::llsize_of_alloc(ccx, llet); + match esz.checked_mul(size) { + Some(n) if n < ccx.max_obj_size() => {} + _ => { ccx.report_overbig_object(scapegoat) } + } +} + +pub fn arg_is_indirect(ccx: &CrateContext, arg_ty: ty::t) -> bool { + !type_is_immediate(ccx, arg_ty) +} + +pub fn return_uses_outptr(ccx: &CrateContext, ty: ty::t) -> bool { + !type_is_immediate(ccx, ty) +} + +pub fn type_of_explicit_arg(ccx: &CrateContext, arg_ty: ty::t) -> Type { + let llty = arg_type_of(ccx, arg_ty); + if arg_is_indirect(ccx, arg_ty) { + llty.ptr_to() + } else { + llty + } +} + +/// Yields the types of the "real" arguments for this function. For most +/// functions, these are simply the types of the arguments. For functions with +/// the `RustCall` ABI, however, this untuples the arguments of the function. +pub fn untuple_arguments_if_necessary(ccx: &CrateContext, + inputs: &[ty::t], + abi: abi::Abi) + -> Vec { + if abi != abi::RustCall { + return inputs.iter().map(|x| (*x).clone()).collect() + } + + if inputs.len() == 0 { + return Vec::new() + } + + let mut result = Vec::new(); + for (i, &arg_prior_to_tuple) in inputs.iter().enumerate() { + if i < inputs.len() - 1 { + result.push(arg_prior_to_tuple); + } + } + + match ty::get(inputs[inputs.len() - 1]).sty { + ty::ty_tup(ref tupled_arguments) => { + debug!("untuple_arguments_if_necessary(): untupling arguments"); + for &tupled_argument in tupled_arguments.iter() { + result.push(tupled_argument); + } + } + _ => { + ccx.tcx().sess.bug("argument to function with \"rust-call\" ABI \ + is neither a tuple nor unit") + } + } + + result +} + +pub fn type_of_rust_fn(cx: &CrateContext, + llenvironment_type: Option, + inputs: &[ty::t], + output: ty::FnOutput, + abi: abi::Abi) + -> Type { + let mut atys: Vec = Vec::new(); + + // First, munge the inputs, if this has the `rust-call` ABI. + let inputs = untuple_arguments_if_necessary(cx, inputs, abi); + + // Arg 0: Output pointer. + // (if the output type is non-immediate) + let lloutputtype = match output { + ty::FnConverging(output) => { + let use_out_pointer = return_uses_outptr(cx, output); + let lloutputtype = arg_type_of(cx, output); + // Use the output as the actual return value if it's immediate. + if use_out_pointer { + atys.push(lloutputtype.ptr_to()); + Type::void(cx) + } else if return_type_is_void(cx, output) { + Type::void(cx) + } else { + lloutputtype + } + } + ty::FnDiverging => Type::void(cx) + }; + + // Arg 1: Environment + match llenvironment_type { + None => {} + Some(llenvironment_type) => atys.push(llenvironment_type), + } + + // ... then explicit args. + let input_tys = inputs.iter().map(|&arg_ty| type_of_explicit_arg(cx, arg_ty)); + atys.extend(input_tys); + + Type::func(atys.as_slice(), &lloutputtype) +} + +// Given a function type and a count of ty params, construct an llvm type +pub fn type_of_fn_from_ty(cx: &CrateContext, fty: ty::t) -> Type { + match ty::get(fty).sty { + ty::ty_closure(ref f) => { + type_of_rust_fn(cx, + Some(Type::i8p(cx)), + f.sig.inputs.as_slice(), + f.sig.output, + f.abi) + } + ty::ty_bare_fn(ref f) => { + if f.abi == abi::Rust || f.abi == abi::RustCall { + type_of_rust_fn(cx, + None, + f.sig.inputs.as_slice(), + f.sig.output, + f.abi) + } else { + foreign::lltype_for_foreign_fn(cx, fty) + } + } + _ => { + cx.sess().bug("type_of_fn_from_ty given non-closure, non-bare-fn") + } + } +} + +// A "sizing type" is an LLVM type, the size and alignment of which are +// guaranteed to be equivalent to what you would get out of `type_of()`. It's +// useful because: +// +// (1) It may be cheaper to compute the sizing type than the full type if all +// you're interested in is the size and/or alignment; +// +// (2) It won't make any recursive calls to determine the structure of the +// type behind pointers. This can help prevent infinite loops for +// recursive types. For example, enum types rely on this behavior. + +pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { + match cx.llsizingtypes().borrow().get(&t).cloned() { + Some(t) => return t, + None => () + } + + let llsizingty = match ty::get(t).sty { + _ if !ty::lltype_is_sized(cx.tcx(), t) => { + cx.sess().bug(format!("trying to take the sizing type of {}, an unsized type", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) + } + + ty::ty_bool => Type::bool(cx), + ty::ty_char => Type::char(cx), + ty::ty_int(t) => Type::int_from_ty(cx, t), + ty::ty_uint(t) => Type::uint_from_ty(cx, t), + ty::ty_float(t) => Type::float_from_ty(cx, t), + + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) | ty::ty_ptr(ty::mt{ty, ..}) => { + if ty::type_is_sized(cx.tcx(), ty) { + Type::i8p(cx) + } else { + Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false) + } + } + + ty::ty_bare_fn(..) => Type::i8p(cx), + ty::ty_closure(..) => Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false), + + ty::ty_vec(ty, Some(size)) => { + let llty = sizing_type_of(cx, ty); + let size = size as u64; + ensure_array_fits_in_address_space(cx, llty, size, t); + Type::array(&llty, size) + } + + ty::ty_tup(ref tys) if tys.is_empty() => { + Type::nil(cx) + } + + ty::ty_tup(..) | ty::ty_enum(..) | ty::ty_unboxed_closure(..) => { + let repr = adt::represent_type(cx, t); + adt::sizing_type_of(cx, &*repr, false) + } + + ty::ty_struct(..) => { + if ty::type_is_simd(cx.tcx(), t) { + let llet = type_of(cx, ty::simd_type(cx.tcx(), t)); + let n = ty::simd_size(cx.tcx(), t) as u64; + ensure_array_fits_in_address_space(cx, llet, n, t); + Type::vector(&llet, n) + } else { + let repr = adt::represent_type(cx, t); + adt::sizing_type_of(cx, &*repr, false) + } + } + + ty::ty_open(_) => { + Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false) + } + + ty::ty_infer(..) | ty::ty_param(..) | ty::ty_err(..) => { + cx.sess().bug(format!("fictitious type {} in sizing_type_of()", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) + } + ty::ty_vec(_, None) | ty::ty_trait(..) | ty::ty_str => panic!("unreachable") + }; + + cx.llsizingtypes().borrow_mut().insert(t, llsizingty); + llsizingty +} + +pub fn arg_type_of(cx: &CrateContext, t: ty::t) -> Type { + if ty::type_is_bool(t) { + Type::i1(cx) + } else { + type_of(cx, t) + } +} + +// NB: If you update this, be sure to update `sizing_type_of()` as well. +pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { + fn type_of_unsize_info(cx: &CrateContext, t: ty::t) -> Type { + // It is possible to end up here with a sized type. This happens with a + // struct which might be unsized, but is monomorphised to a sized type. + // In this case we'll fake a fat pointer with no unsize info (we use 0). + // However, its still a fat pointer, so we need some type use. + if ty::type_is_sized(cx.tcx(), t) { + return Type::i8p(cx); + } + + match ty::get(ty::unsized_part_of_type(cx.tcx(), t)).sty { + ty::ty_str | ty::ty_vec(..) => Type::uint_from_ty(cx, ast::TyU), + ty::ty_trait(_) => Type::vtable_ptr(cx), + _ => panic!("Unexpected type returned from unsized_part_of_type : {}", + t.repr(cx.tcx())) + } + } + + // Check the cache. + match cx.lltypes().borrow().get(&t) { + Some(&llty) => return llty, + None => () + } + + debug!("type_of {} {}", t.repr(cx.tcx()), ty::get(t).sty); + + // Replace any typedef'd types with their equivalent non-typedef + // type. This ensures that all LLVM nominal types that contain + // Rust types are defined as the same LLVM types. If we don't do + // this then, e.g. `Option<{myfield: bool}>` would be a different + // type than `Option`. + let t_norm = ty::normalize_ty(cx.tcx(), t); + + if t != t_norm { + let llty = type_of(cx, t_norm); + debug!("--> normalized {} {} to {} {} llty={}", + t.repr(cx.tcx()), + t, + t_norm.repr(cx.tcx()), + t_norm, + cx.tn().type_to_string(llty)); + cx.lltypes().borrow_mut().insert(t, llty); + return llty; + } + + let mut llty = match ty::get(t).sty { + ty::ty_bool => Type::bool(cx), + ty::ty_char => Type::char(cx), + ty::ty_int(t) => Type::int_from_ty(cx, t), + ty::ty_uint(t) => Type::uint_from_ty(cx, t), + ty::ty_float(t) => Type::float_from_ty(cx, t), + ty::ty_enum(did, ref substs) => { + // Only create the named struct, but don't fill it in. We + // fill it in *after* placing it into the type cache. This + // avoids creating more than one copy of the enum when one + // of the enum's variants refers to the enum itself. + let repr = adt::represent_type(cx, t); + let tps = substs.types.get_slice(subst::TypeSpace); + let name = llvm_type_name(cx, an_enum, did, tps); + adt::incomplete_type_of(cx, &*repr, name.as_slice()) + } + ty::ty_unboxed_closure(did, _, ref substs) => { + // Only create the named struct, but don't fill it in. We + // fill it in *after* placing it into the type cache. + let repr = adt::represent_type(cx, t); + // Unboxed closures can have substitutions in all spaces + // inherited from their environment, so we use entire + // contents of the VecPerParamSpace to to construct the llvm + // name + let name = llvm_type_name(cx, an_unboxed_closure, did, substs.types.as_slice()); + adt::incomplete_type_of(cx, &*repr, name.as_slice()) + } + + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) | ty::ty_ptr(ty::mt{ty, ..}) => { + match ty::get(ty).sty { + ty::ty_str => { + // This means we get a nicer name in the output (str is always + // unsized). + cx.tn().find_type("str_slice").unwrap() + } + ty::ty_trait(..) => Type::opaque_trait(cx), + _ if !ty::type_is_sized(cx.tcx(), ty) => { + let p_ty = type_of(cx, ty).ptr_to(); + Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, ty)], false) + } + _ => type_of(cx, ty).ptr_to(), + } + } + + ty::ty_vec(ty, Some(size)) => { + let size = size as u64; + let llty = type_of(cx, ty); + ensure_array_fits_in_address_space(cx, llty, size, t); + Type::array(&llty, size) + } + ty::ty_vec(ty, None) => { + type_of(cx, ty) + } + + ty::ty_trait(..) => { + Type::opaque_trait_data(cx) + } + + ty::ty_str => Type::i8(cx), + + ty::ty_bare_fn(_) => { + type_of_fn_from_ty(cx, t).ptr_to() + } + ty::ty_closure(_) => { + let fn_ty = type_of_fn_from_ty(cx, t).ptr_to(); + Type::struct_(cx, &[fn_ty, Type::i8p(cx)], false) + } + ty::ty_tup(ref tys) if tys.is_empty() => Type::nil(cx), + ty::ty_tup(..) => { + let repr = adt::represent_type(cx, t); + adt::type_of(cx, &*repr) + } + ty::ty_struct(did, ref substs) => { + if ty::type_is_simd(cx.tcx(), t) { + let llet = type_of(cx, ty::simd_type(cx.tcx(), t)); + let n = ty::simd_size(cx.tcx(), t) as u64; + ensure_array_fits_in_address_space(cx, llet, n, t); + Type::vector(&llet, n) + } else { + // Only create the named struct, but don't fill it in. We fill it + // in *after* placing it into the type cache. This prevents + // infinite recursion with recursive struct types. + let repr = adt::represent_type(cx, t); + let tps = substs.types.get_slice(subst::TypeSpace); + let name = llvm_type_name(cx, a_struct, did, tps); + adt::incomplete_type_of(cx, &*repr, name.as_slice()) + } + } + + ty::ty_open(t) => match ty::get(t).sty { + ty::ty_struct(..) => { + let p_ty = type_of(cx, t).ptr_to(); + Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_vec(ty, None) => { + let p_ty = type_of(cx, ty).ptr_to(); + Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_str => { + let p_ty = Type::i8p(cx); + Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_trait(..) => Type::opaque_trait(cx), + _ => cx.sess().bug(format!("ty_open with sized type: {}", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) + }, + + ty::ty_infer(..) => cx.sess().bug("type_of with ty_infer"), + ty::ty_param(..) => cx.sess().bug("type_of with ty_param"), + ty::ty_err(..) => cx.sess().bug("type_of with ty_err"), + }; + + debug!("--> mapped t={} {} to llty={}", + t.repr(cx.tcx()), + t, + cx.tn().type_to_string(llty)); + + cx.lltypes().borrow_mut().insert(t, llty); + + // If this was an enum or struct, fill in the type now. + match ty::get(t).sty { + ty::ty_enum(..) | ty::ty_struct(..) | ty::ty_unboxed_closure(..) + if !ty::type_is_simd(cx.tcx(), t) => { + let repr = adt::represent_type(cx, t); + adt::finish_type_of(cx, &*repr, &mut llty); + } + _ => () + } + + return llty; +} + +pub fn align_of(cx: &CrateContext, t: ty::t) -> machine::llalign { + let llty = sizing_type_of(cx, t); + machine::llalign_of_min(cx, llty) +} + +// Want refinements! (Or case classes, I guess +pub enum named_ty { + a_struct, + an_enum, + an_unboxed_closure, +} + +pub fn llvm_type_name(cx: &CrateContext, + what: named_ty, + did: ast::DefId, + tps: &[ty::t]) + -> String +{ + let name = match what { + a_struct => "struct", + an_enum => "enum", + an_unboxed_closure => return "closure".to_string(), + }; + + let base = ty::item_path_str(cx.tcx(), did); + let strings: Vec = tps.iter().map(|t| t.repr(cx.tcx())).collect(); + let tstr = if strings.is_empty() { + base + } else { + format!("{}<{}>", base, strings) + }; + + if did.krate == 0 { + format!("{}.{}", name, tstr) + } else { + format!("{}.{}[{}{}]", name, tstr, "#", did.krate) + } +} + +pub fn type_of_dtor(ccx: &CrateContext, self_ty: ty::t) -> Type { + let self_ty = type_of(ccx, self_ty).ptr_to(); + Type::func(&[self_ty], &Type::void(ccx)) +} diff --git a/src/librustc_trans/trans/value.rs b/src/librustc_trans/trans/value.rs new file mode 100644 index 00000000000..8d74275c92a --- /dev/null +++ b/src/librustc_trans/trans/value.rs @@ -0,0 +1,167 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; +use llvm::{UseRef, ValueRef}; +use trans::basic_block::BasicBlock; +use trans::common::Block; +use libc::c_uint; + +pub struct Value(pub ValueRef); + +macro_rules! opt_val ( ($e:expr) => ( + unsafe { + match $e { + p if p.is_not_null() => Some(Value(p)), + _ => None + } + } +)) + +/** + * Wrapper for LLVM ValueRef + */ +impl Value { + /// Returns the native ValueRef + pub fn get(&self) -> ValueRef { + let Value(v) = *self; v + } + + /// Returns the BasicBlock that contains this value + pub fn get_parent(self) -> Option { + unsafe { + match llvm::LLVMGetInstructionParent(self.get()) { + p if p.is_not_null() => Some(BasicBlock(p)), + _ => None + } + } + } + + /// Removes this value from its containing BasicBlock + pub fn erase_from_parent(self) { + unsafe { + llvm::LLVMInstructionEraseFromParent(self.get()); + } + } + + /// Returns the single dominating store to this value, if any + /// This only performs a search for a trivially dominating store. The store + /// must be the only user of this value, and there must not be any conditional + /// branches between the store and the given block. + pub fn get_dominating_store(self, bcx: Block) -> Option { + match self.get_single_user().and_then(|user| user.as_store_inst()) { + Some(store) => { + store.get_parent().and_then(|store_bb| { + let mut bb = BasicBlock(bcx.llbb); + let mut ret = Some(store); + while bb.get() != store_bb.get() { + match bb.get_single_predecessor() { + Some(pred) => bb = pred, + None => { ret = None; break } + } + } + ret + }) + } + _ => None + } + } + + /// Returns the first use of this value, if any + pub fn get_first_use(self) -> Option { + unsafe { + match llvm::LLVMGetFirstUse(self.get()) { + u if u.is_not_null() => Some(Use(u)), + _ => None + } + } + } + + /// Tests if there are no uses of this value + pub fn has_no_uses(self) -> bool { + self.get_first_use().is_none() + } + + /// Returns the single user of this value + /// If there are no users or multiple users, this returns None + pub fn get_single_user(self) -> Option { + let mut iter = self.user_iter(); + match (iter.next(), iter.next()) { + (Some(first), None) => Some(first), + _ => None + } + } + + /// Returns an iterator for the users of this value + pub fn user_iter(self) -> Users { + Users { + next: self.get_first_use() + } + } + + /// Returns the requested operand of this instruction + /// Returns None, if there's no operand at the given index + pub fn get_operand(self, i: uint) -> Option { + opt_val!(llvm::LLVMGetOperand(self.get(), i as c_uint)) + } + + /// Returns the Store represent by this value, if any + pub fn as_store_inst(self) -> Option { + opt_val!(llvm::LLVMIsAStoreInst(self.get())) + } + + /// Tests if this value is a terminator instruction + pub fn is_a_terminator_inst(self) -> bool { + unsafe { + llvm::LLVMIsATerminatorInst(self.get()).is_not_null() + } + } +} + +pub struct Use(UseRef); + +/** + * Wrapper for LLVM UseRef + */ +impl Use { + pub fn get(&self) -> UseRef { + let Use(v) = *self; v + } + + pub fn get_user(self) -> Value { + unsafe { + Value(llvm::LLVMGetUser(self.get())) + } + } + + pub fn get_next_use(self) -> Option { + unsafe { + match llvm::LLVMGetNextUse(self.get()) { + u if u.is_not_null() => Some(Use(u)), + _ => None + } + } + } +} + +/// Iterator for the users of a value +pub struct Users { + next: Option +} + +impl Iterator for Users { + fn next(&mut self) -> Option { + let current = self.next; + + self.next = current.and_then(|u| u.get_next_use()); + + current.map(|u| u.get_user()) + } +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 98eeada6f5d..d622965dac7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -38,8 +38,8 @@ use syntax::parse::token::InternedString; use syntax::parse::token; use syntax::ptr::P; -use rustc::back::link; -use rustc::driver::driver; +use rustc_trans::back::link; +use rustc_trans::driver::driver; use rustc::metadata::cstore; use rustc::metadata::csearch; use rustc::metadata::decoder; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 0a748f7e798..21242e6f1e4 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -9,10 +9,11 @@ // except according to those terms. pub use self::MaybeTyped::*; -use rustc::driver::{config, driver, session}; +use rustc_trans::driver::driver; +use rustc::session::{mod, config}; use rustc::middle::{privacy, ty}; use rustc::lint; -use rustc::back::link; +use rustc_trans::back::link; use syntax::{ast, ast_map, codemap, diagnostic}; @@ -93,7 +94,7 @@ pub fn run_core(libs: Vec, cfgs: Vec, externs: Externs, crate_types: vec!(config::CrateTypeRlib), lint_opts: vec!((warning_lint, lint::Allow)), externs: externs, - target_triple: triple.unwrap_or(driver::host_triple().to_string()), + target_triple: triple.unwrap_or(config::host_triple().to_string()), cfg: config::parse_cfgspecs(cfgs), ..config::basic_options().clone() }; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7343d674972..69da6285b6d 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -22,6 +22,7 @@ extern crate arena; extern crate getopts; extern crate libc; extern crate rustc; +extern crate rustc_trans; extern crate serialize; extern crate syntax; extern crate "test" as testing; @@ -155,7 +156,7 @@ pub fn main_args(args: &[String]) -> int { usage(args[0].as_slice()); return 0; } else if matches.opt_present("version") { - match rustc::driver::version("rustdoc", &matches) { + match rustc_trans::driver::version("rustdoc", &matches) { Some(err) => { println!("{}", err); return 1 diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 93662b3b63e..2dc1bcf776e 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -19,10 +19,8 @@ use std::string::String; use std::collections::{HashSet, HashMap}; use testing; -use rustc::back::write; -use rustc::driver::config; -use rustc::driver::driver; -use rustc::driver::session; +use rustc::session::{mod, config}; +use rustc_trans::driver::driver; use syntax::ast; use syntax::codemap::{CodeMap, dummy_spanned}; use syntax::diagnostic; @@ -119,7 +117,7 @@ fn runtest(test: &str, cratename: &str, libs: Vec, externs: core::Externs, maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), addl_lib_search_paths: RefCell::new(libs), crate_types: vec!(config::CrateTypeExecutable), - output_types: vec!(write::OutputTypeExe), + output_types: vec!(config::OutputTypeExe), no_trans: no_run, externs: externs, cg: config::CodegenOptions { -- cgit 1.4.1-3-g733a5