From 9585c5dc1fa3cef34ebdc5a5d39af88db60c6f15 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 7 Aug 2018 16:03:57 +0200 Subject: [PATCH 1/2] Introduce const_cstr!() macro and use it where applicable. --- src/librustc_codegen_llvm/attributes.rs | 14 +++---- src/librustc_codegen_llvm/base.rs | 4 +- src/librustc_codegen_llvm/builder.rs | 6 +-- src/librustc_codegen_llvm/consts.rs | 2 +- .../debuginfo/metadata.rs | 4 +- src/librustc_data_structures/const_cstr.rs | 42 +++++++++++++++++++ src/librustc_data_structures/lib.rs | 1 + 7 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 src/librustc_data_structures/const_cstr.rs diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 714e8914e48c5..74f7e6f2ce752 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -9,7 +9,7 @@ // except according to those terms. //! Set and unset common attributes on LLVM values. -use std::ffi::{CStr, CString}; +use std::ffi::CString; use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; @@ -75,7 +75,7 @@ pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) if cx.sess().must_not_eliminate_frame_pointers() { llvm::AddFunctionAttrStringValue( llfn, llvm::AttributePlace::Function, - cstr("no-frame-pointer-elim\0"), cstr("true\0")); + const_cstr!("no-frame-pointer-elim"), const_cstr!("true")); } } @@ -108,7 +108,7 @@ pub fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // This is defined in the `compiler-builtins` crate for each architecture. llvm::AddFunctionAttrStringValue( llfn, llvm::AttributePlace::Function, - cstr("probe-stack\0"), cstr("__rust_probestack\0")); + const_cstr!("probe-stack"), const_cstr!("__rust_probestack")); } pub fn llvm_target_features(sess: &Session) -> impl Iterator { @@ -202,7 +202,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value, id: DefId) { let val = CString::new(features).unwrap(); llvm::AddFunctionAttrStringValue( llfn, llvm::AttributePlace::Function, - cstr("target-features\0"), &val); + const_cstr!("target-features"), &val); } // Note that currently the `wasm-import-module` doesn't do anything, but @@ -213,17 +213,13 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value, id: DefId) { llvm::AddFunctionAttrStringValue( llfn, llvm::AttributePlace::Function, - cstr("wasm-import-module\0"), + const_cstr!("wasm-import-module"), &module, ); } } } -fn cstr(s: &'static str) -> &CStr { - CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") -} - pub fn provide(providers: &mut Providers) { providers.target_features_whitelist = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 54ff5d219b39a..4415adc27d6dd 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -1255,8 +1255,8 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Create the llvm.used variable // This variable has type [N x i8*] and is stored in the llvm.metadata section if !cx.used_statics.borrow().is_empty() { - let name = CString::new("llvm.used").unwrap(); - let section = CString::new("llvm.metadata").unwrap(); + let name = const_cstr!("llvm.used"); + let section = const_cstr!("llvm.metadata"); let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); unsafe { diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index b174cd8c7ac76..bc82886714ae1 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -975,7 +975,7 @@ impl Builder<'a, 'll, 'tcx> { parent: Option<&'ll Value>, args: &[&'ll Value]) -> &'ll Value { self.count_insn("cleanuppad"); - let name = CString::new("cleanuppad").unwrap(); + let name = const_cstr!("cleanuppad"); let ret = unsafe { llvm::LLVMRustBuildCleanupPad(self.llbuilder, parent, @@ -1001,7 +1001,7 @@ impl Builder<'a, 'll, 'tcx> { parent: &'ll Value, args: &[&'ll Value]) -> &'ll Value { self.count_insn("catchpad"); - let name = CString::new("catchpad").unwrap(); + let name = const_cstr!("catchpad"); let ret = unsafe { llvm::LLVMRustBuildCatchPad(self.llbuilder, parent, args.len() as c_uint, args.as_ptr(), @@ -1025,7 +1025,7 @@ impl Builder<'a, 'll, 'tcx> { num_handlers: usize, ) -> &'ll Value { self.count_insn("catchswitch"); - let name = CString::new("catchswitch").unwrap(); + let name = const_cstr!("catchswitch"); let ret = unsafe { llvm::LLVMRustBuildCatchSwitch(self.llbuilder, parent, unwind, num_handlers as c_uint, diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index fafc0e723225d..5501e915976a3 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -328,7 +328,7 @@ pub fn codegen_static<'a, 'tcx>( } else { // If we created the global with the wrong type, // correct the type. - let empty_string = CString::new("").unwrap(); + let empty_string = const_cstr!(""); let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); llvm::LLVMSetValueName(g, empty_string.as_ptr()); diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 8ee2404e10cdf..3bd1a1519b6b4 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -883,7 +883,7 @@ pub fn compile_unit_metadata(tcx: TyCtxt, gcov_cu_info.as_ptr(), gcov_cu_info.len() as c_uint); - let llvm_gcov_ident = CString::new("llvm.gcov").unwrap(); + let llvm_gcov_ident = const_cstr!("llvm.gcov"); llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), gcov_metadata); @@ -1780,7 +1780,7 @@ pub fn create_vtable_metadata( // later on in llvm/lib/IR/Value.cpp. let empty_array = create_DIArray(DIB(cx), &[]); - let name = CString::new("vtable").unwrap(); + let name = const_cstr!("vtable"); // Create a new one each time. We don't want metadata caching // here, because each vtable will refer to a unique containing diff --git a/src/librustc_data_structures/const_cstr.rs b/src/librustc_data_structures/const_cstr.rs new file mode 100644 index 0000000000000..4589d973b6a41 --- /dev/null +++ b/src/librustc_data_structures/const_cstr.rs @@ -0,0 +1,42 @@ +// Copyright 2018 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. + +/// This macro creates a zero-overhead &CStr by adding a NUL terminator to +/// the string literal passed into it at compile-time. Use it like: +/// +/// ``` +/// let some_const_cstr = const_cstr!("abc"); +/// ``` +/// +/// The above is roughly equivalent to: +/// +/// ``` +/// let some_const_cstr = CStr::from_bytes_with_nul(b"abc\0").unwrap() +/// ``` +/// +/// Note that macro only checks the string literal for internal NULs if +/// debug-assertions are enabled in order to avoid runtime overhead in release +/// builds. +#[macro_export] +macro_rules! const_cstr { + ($s:expr) => ({ + use std::ffi::CStr; + + let str_plus_nul = concat!($s, "\0"); + + if cfg!(debug_assertions) { + CStr::from_bytes_with_nul(str_plus_nul.as_bytes()).unwrap() + } else { + unsafe { + CStr::from_bytes_with_nul_unchecked(str_plus_nul.as_bytes()) + } + } + }) +} diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 3aa15f472a274..bd11a2977ffb5 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -60,6 +60,7 @@ pub mod array_vec; pub mod base_n; pub mod bitslice; pub mod bitvec; +pub mod const_cstr; pub mod flock; pub mod fx; pub mod graph; From 88d84b38f19eff3c25ec88931f04bf2640edf2b5 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 7 Aug 2018 16:04:34 +0200 Subject: [PATCH 2/2] Introduce SmallCStr and use it where applicable. --- src/librustc_codegen_llvm/attributes.rs | 2 +- src/librustc_codegen_llvm/back/write.rs | 10 +- src/librustc_codegen_llvm/base.rs | 5 +- src/librustc_codegen_llvm/builder.rs | 8 +- src/librustc_codegen_llvm/context.rs | 10 +- .../debuginfo/metadata.rs | 74 +++++----- src/librustc_codegen_llvm/debuginfo/mod.rs | 7 +- .../debuginfo/namespace.rs | 4 +- src/librustc_codegen_llvm/declare.rs | 14 +- src/librustc_codegen_llvm/llvm/mod.rs | 5 +- src/librustc_codegen_llvm/type_.rs | 4 +- src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/small_c_str.rs | 131 ++++++++++++++++++ 13 files changed, 200 insertions(+), 75 deletions(-) create mode 100644 src/librustc_data_structures/small_c_str.rs diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 74f7e6f2ce752..816242129f338 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -128,7 +128,7 @@ pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { llvm::AddFunctionAttrStringValue( llfn, llvm::AttributePlace::Function, - cstr("target-cpu\0"), + const_cstr!("target-cpu"), target_cpu.as_c_str()); } diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index cdfa874b1772a..ebe8b79741438 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -31,6 +31,7 @@ use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passes_entry}; use rustc_fs_util::{path2cstr, link_or_copy}; +use rustc_data_structures::small_c_str::SmallCStr; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use errors::emitter::{Emitter}; use syntax::attr; @@ -170,11 +171,8 @@ pub fn target_machine_factory(sess: &Session, find_features: bool) let singlethread = sess.target.target.options.singlethread; - let triple = &sess.target.target.llvm_target; - - let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = sess.target_cpu(); - let cpu = CString::new(cpu.as_bytes()).unwrap(); + let triple = SmallCStr::new(&sess.target.target.llvm_target); + let cpu = SmallCStr::new(sess.target_cpu()); let features = attributes::llvm_target_features(sess) .collect::>() .join(","); @@ -522,7 +520,7 @@ unsafe fn optimize(cgcx: &CodegenContext, // If we're verifying or linting, add them to the function pass // manager. let addpass = |pass_name: &str| { - let pass_name = CString::new(pass_name).unwrap(); + let pass_name = SmallCStr::new(pass_name); let pass = match llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()) { Some(pass) => pass, None => return false, diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 4415adc27d6dd..7ca6a89dd9b57 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -73,6 +73,7 @@ use type_::Type; use type_of::LayoutLlvmExt; use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; use CrateInfo; +use rustc_data_structures::small_c_str::SmallCStr; use rustc_data_structures::sync::Lrc; use std::any::Any; @@ -533,7 +534,7 @@ pub fn set_link_section(llval: &Value, attrs: &CodegenFnAttrs) { None => return, }; unsafe { - let buf = CString::new(sect.as_str().as_bytes()).unwrap(); + let buf = SmallCStr::new(§.as_str()); llvm::LLVMSetSection(llval, buf.as_ptr()); } } @@ -681,7 +682,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, unsafe { llvm::LLVMSetInitializer(llglobal, llconst); let section_name = metadata::metadata_section_name(&tcx.sess.target.target); - let name = CString::new(section_name).unwrap(); + let name = SmallCStr::new(section_name); llvm::LLVMSetSection(llglobal, name.as_ptr()); // Also generate a .section directive to force no diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index bc82886714ae1..b0f88bd4189d3 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -18,9 +18,9 @@ use libc::{c_uint, c_char}; use rustc::ty::TyCtxt; use rustc::ty::layout::{Align, Size}; use rustc::session::{config, Session}; +use rustc_data_structures::small_c_str::SmallCStr; use std::borrow::Cow; -use std::ffi::CString; use std::ops::Range; use std::ptr; @@ -58,7 +58,7 @@ impl Builder<'a, 'll, 'tcx> { pub fn new_block<'b>(cx: &'a CodegenCx<'ll, 'tcx>, llfn: &'ll Value, name: &'b str) -> Self { let bx = Builder::with_cx(cx); let llbb = unsafe { - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(name); llvm::LLVMAppendBasicBlockInContext( cx.llcx, llfn, @@ -118,7 +118,7 @@ impl Builder<'a, 'll, 'tcx> { } pub fn set_value_name(&self, value: &'ll Value, name: &str) { - let cname = CString::new(name.as_bytes()).unwrap(); + let cname = SmallCStr::new(name); unsafe { llvm::LLVMSetValueName(value, cname.as_ptr()); } @@ -436,7 +436,7 @@ impl Builder<'a, 'll, 'tcx> { let alloca = if name.is_empty() { llvm::LLVMBuildAlloca(self.llbuilder, ty, noname()) } else { - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(name); llvm::LLVMBuildAlloca(self.llbuilder, ty, name.as_ptr()) }; diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 7a308bb6e8823..781f8ebbb78c1 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -26,6 +26,7 @@ use type_::Type; use type_of::PointeeInfo; use rustc_data_structures::base_n; +use rustc_data_structures::small_c_str::SmallCStr; use rustc::mir::mono::Stats; use rustc::session::config::{self, DebugInfo}; use rustc::session::Session; @@ -34,7 +35,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; use rustc_target::spec::{HasTargetSpec, Target}; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::cell::{Cell, RefCell}; use std::iter; use std::str; @@ -161,7 +162,7 @@ pub unsafe fn create_module( llcx: &'ll llvm::Context, mod_name: &str, ) -> &'ll llvm::Module { - let mod_name = CString::new(mod_name).unwrap(); + let mod_name = SmallCStr::new(mod_name); let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); // Ensure the data-layout values hardcoded remain the defaults. @@ -201,11 +202,10 @@ pub unsafe fn create_module( } } - let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); + let data_layout = SmallCStr::new(&sess.target.target.data_layout); llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); - let llvm_target = sess.target.target.llvm_target.as_bytes(); - let llvm_target = CString::new(llvm_target).unwrap(); + let llvm_target = SmallCStr::new(&sess.target.target.llvm_target); llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); if is_pie_binary(sess) { diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 3bd1a1519b6b4..223fa75723cf9 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -37,6 +37,7 @@ use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; use rustc::session::config; use rustc::util::nodemap::FxHashMap; use rustc_fs_util::path2cstr; +use rustc_data_structures::small_c_str::SmallCStr; use libc::{c_uint, c_longlong}; use std::ffi::CString; @@ -274,7 +275,7 @@ impl RecursiveTypeDescription<'ll, 'tcx> { // ... and attach them to the stub to complete it. set_members_of_composite_type(cx, metadata_stub, - &member_descriptions[..]); + member_descriptions); return MetadataCreationResult::new(metadata_stub, true); } } @@ -349,7 +350,7 @@ fn vec_slice_metadata( let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); - let member_descriptions = [ + let member_descriptions = vec![ MemberDescription { name: "data_ptr".to_string(), type_metadata: data_ptr_metadata, @@ -374,7 +375,7 @@ fn vec_slice_metadata( slice_ptr_type, &slice_type_name[..], unique_type_id, - &member_descriptions, + member_descriptions, NO_SCOPE_METADATA, file_metadata, span); @@ -460,7 +461,7 @@ fn trait_pointer_metadata( let data_ptr_field = layout.field(cx, 0); let vtable_field = layout.field(cx, 1); - let member_descriptions = [ + let member_descriptions = vec![ MemberDescription { name: "pointer".to_string(), type_metadata: type_metadata(cx, @@ -485,7 +486,7 @@ fn trait_pointer_metadata( trait_object_type, &trait_type_name[..], unique_type_id, - &member_descriptions, + member_descriptions, containing_scope, file_metadata, syntax_pos::DUMMY_SP) @@ -746,8 +747,8 @@ fn file_metadata_raw(cx: &CodegenCx<'ll, '_>, debug!("file_metadata: file_name: {}, directory: {}", file_name, directory); - let file_name = CString::new(file_name).unwrap(); - let directory = CString::new(directory).unwrap(); + let file_name = SmallCStr::new(file_name); + let directory = SmallCStr::new(directory); let file_metadata = unsafe { llvm::LLVMRustDIBuilderCreateFile(DIB(cx), @@ -782,7 +783,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { }; let (size, align) = cx.size_and_align_of(t); - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(name); let ty_metadata = unsafe { llvm::LLVMRustDIBuilderCreateBasicType( DIB(cx), @@ -813,7 +814,7 @@ fn pointer_type_metadata( ) -> &'ll DIType { let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); let name = compute_debuginfo_type_name(cx, pointer_type, false); - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(&name); unsafe { llvm::LLVMRustDIBuilderCreatePointerType( DIB(cx), @@ -847,9 +848,9 @@ pub fn compile_unit_metadata(tcx: TyCtxt, let producer = format!("clang LLVM (rustc version {})", (option_env!("CFG_VERSION")).expect("CFG_VERSION")); - let name_in_debuginfo = name_in_debuginfo.to_string_lossy().into_owned(); - let name_in_debuginfo = CString::new(name_in_debuginfo).unwrap(); - let work_dir = CString::new(&tcx.sess.working_dir.0.to_string_lossy()[..]).unwrap(); + let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); + let name_in_debuginfo = SmallCStr::new(&name_in_debuginfo); + let work_dir = SmallCStr::new(&tcx.sess.working_dir.0.to_string_lossy()); let producer = CString::new(producer).unwrap(); let flags = "\0"; let split_name = "\0"; @@ -1187,7 +1188,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { set_members_of_composite_type(cx, variant_type_metadata, - &member_descriptions[..]); + member_descriptions); vec![ MemberDescription { name: "".to_string(), @@ -1217,7 +1218,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { set_members_of_composite_type(cx, variant_type_metadata, - &member_descriptions); + member_descriptions); MemberDescription { name: "".to_string(), type_metadata: variant_type_metadata, @@ -1244,7 +1245,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { set_members_of_composite_type(cx, variant_type_metadata, - &variant_member_descriptions[..]); + variant_member_descriptions); // Encode the information about the null variant in the union // member's name. @@ -1416,8 +1417,7 @@ fn prepare_enum_metadata( let enumerators_metadata: Vec<_> = def.discriminants(cx.tcx) .zip(&def.variants) .map(|(discr, v)| { - let token = v.name.as_str(); - let name = CString::new(token.as_bytes()).unwrap(); + let name = SmallCStr::new(&v.name.as_str()); unsafe { Some(llvm::LLVMRustDIBuilderCreateEnumerator( DIB(cx), @@ -1442,7 +1442,7 @@ fn prepare_enum_metadata( type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str(); - let name = CString::new(discriminant_name.as_bytes()).unwrap(); + let name = SmallCStr::new(&discriminant_name); let discriminant_type_metadata = unsafe { llvm::LLVMRustDIBuilderCreateEnumerationType( DIB(cx), @@ -1482,10 +1482,10 @@ fn prepare_enum_metadata( let (enum_type_size, enum_type_align) = layout.size_and_align(); - let enum_name = CString::new(enum_name).unwrap(); - let unique_type_id_str = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); + let enum_name = SmallCStr::new(&enum_name); + let unique_type_id_str = SmallCStr::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id) + ); let enum_metadata = unsafe { llvm::LLVMRustDIBuilderCreateUnionType( DIB(cx), @@ -1531,7 +1531,7 @@ fn composite_type_metadata( composite_type: Ty<'tcx>, composite_type_name: &str, composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription<'ll>], + member_descriptions: Vec>, containing_scope: Option<&'ll DIScope>, // Ignore source location information as long as it @@ -1555,7 +1555,7 @@ fn composite_type_metadata( fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>, composite_type_metadata: &'ll DICompositeType, - member_descriptions: &[MemberDescription<'ll>]) { + member_descriptions: Vec>) { // 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 @@ -1574,10 +1574,9 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>, } let member_metadata: Vec<_> = member_descriptions - .iter() + .into_iter() .map(|member_description| { - let member_name = member_description.name.as_bytes(); - let member_name = CString::new(member_name).unwrap(); + let member_name = CString::new(member_description.name).unwrap(); unsafe { Some(llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), @@ -1613,10 +1612,10 @@ fn create_struct_stub( ) -> &'ll DICompositeType { let (struct_size, struct_align) = cx.size_and_align_of(struct_type); - let name = CString::new(struct_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); + let name = SmallCStr::new(struct_type_name); + let unique_type_id = SmallCStr::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id) + ); let metadata_stub = unsafe { // LLVMRustDIBuilderCreateStructType() wants an empty array. A null // pointer will lead to hard to trace and debug LLVM assertions @@ -1651,10 +1650,10 @@ fn create_union_stub( ) -> &'ll DICompositeType { let (union_size, union_align) = cx.size_and_align_of(union_type); - let name = CString::new(union_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); + let name = SmallCStr::new(union_type_name); + let unique_type_id = SmallCStr::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id) + ); let metadata_stub = unsafe { // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null // pointer will lead to hard to trace and debug LLVM assertions @@ -1713,13 +1712,12 @@ pub fn create_global_var_metadata( let is_local_to_unit = is_node_local_to_unit(cx, def_id); let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx); let type_metadata = type_metadata(cx, variable_type, span); - let var_name = tcx.item_name(def_id).to_string(); - let var_name = CString::new(var_name).unwrap(); + let var_name = SmallCStr::new(&tcx.item_name(def_id).as_str()); let linkage_name = if no_mangle { None } else { let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)); - Some(CString::new(linkage_name.to_string()).unwrap()) + Some(SmallCStr::new(&linkage_name.as_str())) }; let global_align = cx.align_of(variable_type); diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index d4fb2549a752c..fcb8bc3fe2b21 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -34,6 +34,7 @@ use rustc::ty::{self, ParamEnv, Ty, InstanceDef}; use rustc::mir; use rustc::session::config::{self, DebugInfo}; use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; +use rustc_data_structures::small_c_str::SmallCStr; use value::Value; use libc::c_uint; @@ -265,7 +266,7 @@ pub fn create_function_debug_context( let is_local_to_unit = is_node_local_to_unit(cx, def_id); let function_name = CString::new(name).unwrap(); - let linkage_name = CString::new(linkage_name.to_string()).unwrap(); + let linkage_name = SmallCStr::new(&linkage_name.as_str()); let mut flags = DIFlags::FlagPrototyped; @@ -407,7 +408,7 @@ pub fn create_function_debug_context( let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); - let name = CString::new(name.as_str().as_bytes()).unwrap(); + let name = SmallCStr::new(&name.as_str()); Some(unsafe { Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( DIB(cx), @@ -510,7 +511,7 @@ pub fn declare_local( }; let align = cx.align_of(variable_type); - let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); + let name = SmallCStr::new(&variable_name.as_str()); match (variable_access, &[][..]) { (DirectVariable { alloca }, address_operations) | (IndirectVariable {alloca, address_operations}, _) => { diff --git a/src/librustc_codegen_llvm/debuginfo/namespace.rs b/src/librustc_codegen_llvm/debuginfo/namespace.rs index 9f1141a7e7db0..06f8a4b131b60 100644 --- a/src/librustc_codegen_llvm/debuginfo/namespace.rs +++ b/src/librustc_codegen_llvm/debuginfo/namespace.rs @@ -21,7 +21,7 @@ use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use common::CodegenCx; -use std::ffi::CString; +use rustc_data_structures::small_c_str::SmallCStr; pub fn mangled_name_of_instance<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, @@ -49,7 +49,7 @@ pub fn item_namespace(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { data => data.as_interned_str().as_str() }; - let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); + let namespace_name = SmallCStr::new(&namespace_name); let scope = unsafe { llvm::LLVMRustDIBuilderCreateNameSpace( diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index a0310eecd591d..5e743ac51bc61 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -25,6 +25,7 @@ use llvm::AttributePlace::Function; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, LayoutOf}; use rustc::session::config::Sanitizer; +use rustc_data_structures::small_c_str::SmallCStr; use rustc_target::spec::PanicStrategy; use abi::{Abi, FnType, FnTypeExt}; use attributes; @@ -33,7 +34,6 @@ use common; use type_::Type; use value::Value; -use std::ffi::CString; /// Declare a global value. /// @@ -41,9 +41,7 @@ use std::ffi::CString; /// return its Value instead. pub fn declare_global(cx: &CodegenCx<'ll, '_>, name: &str, ty: &'ll Type) -> &'ll Value { debug!("declare_global(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); + let namebuf = SmallCStr::new(name); unsafe { llvm::LLVMRustGetOrInsertGlobal(cx.llmod, namebuf.as_ptr(), ty) } @@ -61,9 +59,7 @@ fn declare_raw_fn( ty: &'ll Type, ) -> &'ll Value { debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); + let namebuf = SmallCStr::new(name); let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty) }; @@ -214,9 +210,7 @@ pub fn define_internal_fn( /// Get declared value by name. pub fn get_declared_value(cx: &CodegenCx<'ll, '_>, name: &str) -> Option<&'ll Value> { debug!("get_declared_value(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); + let namebuf = SmallCStr::new(name); unsafe { llvm::LLVMRustGetNamedValue(cx.llmod, namebuf.as_ptr()) } } diff --git a/src/librustc_codegen_llvm/llvm/mod.rs b/src/librustc_codegen_llvm/llvm/mod.rs index 558d2a2bc87b3..4343c8c184ecf 100644 --- a/src/librustc_codegen_llvm/llvm/mod.rs +++ b/src/librustc_codegen_llvm/llvm/mod.rs @@ -24,9 +24,10 @@ pub use self::Linkage::*; use std::str::FromStr; use std::string::FromUtf8Error; use std::slice; -use std::ffi::{CString, CStr}; +use std::ffi::CStr; use std::cell::RefCell; use libc::{self, c_uint, c_char, size_t}; +use rustc_data_structures::small_c_str::SmallCStr; pub mod archive_ro; pub mod diagnostic; @@ -264,7 +265,7 @@ pub struct OperandBundleDef<'a> { impl OperandBundleDef<'a> { pub fn new(name: &str, vals: &[&'a Value]) -> Self { - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(name); let def = unsafe { LLVMRustBuildOperandBundleDef(name.as_ptr(), vals.as_ptr(), vals.len() as c_uint) }; diff --git a/src/librustc_codegen_llvm/type_.rs b/src/librustc_codegen_llvm/type_.rs index 9fa7cc46aee5e..51a233d791625 100644 --- a/src/librustc_codegen_llvm/type_.rs +++ b/src/librustc_codegen_llvm/type_.rs @@ -19,8 +19,8 @@ use context::CodegenCx; use syntax::ast; use rustc::ty::layout::{self, Align, Size}; +use rustc_data_structures::small_c_str::SmallCStr; -use std::ffi::CString; use std::fmt; use libc::c_uint; @@ -201,7 +201,7 @@ impl Type { } pub fn named_struct(cx: &CodegenCx<'ll, '_>, name: &str) -> &'ll Type { - let name = CString::new(name).unwrap(); + let name = SmallCStr::new(name); unsafe { llvm::LLVMStructCreateNamed(cx.llcx, name.as_ptr()) } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index bd11a2977ffb5..8a586d8c2337b 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -70,6 +70,7 @@ pub mod obligation_forest; pub mod owning_ref; pub mod ptr_key; pub mod sip128; +pub mod small_c_str; pub mod small_vec; pub mod snapshot_map; pub use ena::snapshot_vec; diff --git a/src/librustc_data_structures/small_c_str.rs b/src/librustc_data_structures/small_c_str.rs new file mode 100644 index 0000000000000..b0ad83e497938 --- /dev/null +++ b/src/librustc_data_structures/small_c_str.rs @@ -0,0 +1,131 @@ +// Copyright 2018 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 std::ffi; +use std::ops::Deref; + +const SIZE: usize = 38; + +/// Like SmallVec but for C strings. +#[derive(Clone)] +pub enum SmallCStr { + OnStack { + data: [u8; SIZE], + len_with_nul: u8, + }, + OnHeap { + data: ffi::CString, + } +} + +impl SmallCStr { + #[inline] + pub fn new(s: &str) -> SmallCStr { + if s.len() < SIZE { + let mut data = [0; SIZE]; + data[.. s.len()].copy_from_slice(s.as_bytes()); + let len_with_nul = s.len() + 1; + + // Make sure once that this is a valid CStr + if let Err(e) = ffi::CStr::from_bytes_with_nul(&data[.. len_with_nul]) { + panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e); + } + + SmallCStr::OnStack { + data, + len_with_nul: len_with_nul as u8, + } + } else { + SmallCStr::OnHeap { + data: ffi::CString::new(s).unwrap() + } + } + } + + #[inline] + pub fn as_c_str(&self) -> &ffi::CStr { + match *self { + SmallCStr::OnStack { ref data, len_with_nul } => { + unsafe { + let slice = &data[.. len_with_nul as usize]; + ffi::CStr::from_bytes_with_nul_unchecked(slice) + } + } + SmallCStr::OnHeap { ref data } => { + data.as_c_str() + } + } + } + + #[inline] + pub fn len_with_nul(&self) -> usize { + match *self { + SmallCStr::OnStack { len_with_nul, .. } => { + len_with_nul as usize + } + SmallCStr::OnHeap { ref data } => { + data.as_bytes_with_nul().len() + } + } + } +} + +impl Deref for SmallCStr { + type Target = ffi::CStr; + + fn deref(&self) -> &ffi::CStr { + self.as_c_str() + } +} + + +#[test] +fn short() { + const TEXT: &str = "abcd"; + let reference = ffi::CString::new(TEXT.to_string()).unwrap(); + + let scs = SmallCStr::new(TEXT); + + assert_eq!(scs.len_with_nul(), TEXT.len() + 1); + assert_eq!(scs.as_c_str(), reference.as_c_str()); + assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false }); +} + +#[test] +fn empty() { + const TEXT: &str = ""; + let reference = ffi::CString::new(TEXT.to_string()).unwrap(); + + let scs = SmallCStr::new(TEXT); + + assert_eq!(scs.len_with_nul(), TEXT.len() + 1); + assert_eq!(scs.as_c_str(), reference.as_c_str()); + assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false }); +} + +#[test] +fn long() { + const TEXT: &str = "01234567890123456789012345678901234567890123456789\ + 01234567890123456789012345678901234567890123456789\ + 01234567890123456789012345678901234567890123456789"; + let reference = ffi::CString::new(TEXT.to_string()).unwrap(); + + let scs = SmallCStr::new(TEXT); + + assert_eq!(scs.len_with_nul(), TEXT.len() + 1); + assert_eq!(scs.as_c_str(), reference.as_c_str()); + assert!(if let SmallCStr::OnHeap { .. } = scs { true } else { false }); +} + +#[test] +#[should_panic] +fn internal_nul() { + let _ = SmallCStr::new("abcd\0def"); +}