Directly construct lvalue datums for function arguments
The current split between create_datums_for_fn_args, copy_args_to_allocas and store_arg involves a detour via rvalue datums which cause additional work in form of insertvalue/extractvalue pairs for fat pointer arguments, and an extra alloca and memcpy for tupled args in rust-call functions. By merging those three functions into just one that actually covers the whole process of creating the final argument datums, we can skip all that. Also, this allows to easily merge in the handling of rust-call functions, allowing to make create_datum_for_fn_args_under_call_abi obsolete. cc #26600 -- The insertvalue instructions kicked us off of fast-isel.
This commit is contained in:
parent
6b5edd24f5
commit
a04784f7f9
3 changed files with 102 additions and 163 deletions
|
@ -215,7 +215,7 @@ use trans::monomorphize;
|
||||||
use trans::tvec;
|
use trans::tvec;
|
||||||
use trans::type_of;
|
use trans::type_of;
|
||||||
use middle::ty::{self, Ty};
|
use middle::ty::{self, Ty};
|
||||||
use session::config::{NoDebugInfo, FullDebugInfo};
|
use session::config::NoDebugInfo;
|
||||||
use util::common::indenter;
|
use util::common::indenter;
|
||||||
use util::nodemap::FnvHashMap;
|
use util::nodemap::FnvHashMap;
|
||||||
use util::ppaux;
|
use util::ppaux;
|
||||||
|
@ -1600,54 +1600,6 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code for argument patterns like `fn foo(<pat>: 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.
|
|
||||||
pub fn store_arg<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
|
|
||||||
pat: &ast::Pat,
|
|
||||||
arg: Datum<'tcx, Rvalue>,
|
|
||||||
arg_scope: cleanup::ScopeId)
|
|
||||||
-> Block<'blk, 'tcx> {
|
|
||||||
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.name, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
|
fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
|
||||||
p_id: ast::NodeId,
|
p_id: ast::NodeId,
|
||||||
name: ast::Name,
|
name: ast::Name,
|
||||||
|
@ -1687,7 +1639,7 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
|
||||||
/// - bcx: starting basic block context
|
/// - bcx: starting basic block context
|
||||||
/// - pat: the irrefutable pattern being matched.
|
/// - pat: the irrefutable pattern being matched.
|
||||||
/// - val: the value being matched -- must be an lvalue (by ref, with cleanup)
|
/// - val: the value being matched -- must be an lvalue (by ref, with cleanup)
|
||||||
fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
pat: &ast::Pat,
|
pat: &ast::Pat,
|
||||||
val: ValueRef,
|
val: ValueRef,
|
||||||
cleanup_scope: cleanup::ScopeId)
|
cleanup_scope: cleanup::ScopeId)
|
||||||
|
|
|
@ -40,10 +40,11 @@ use middle::cfg;
|
||||||
use middle::infer;
|
use middle::infer;
|
||||||
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
|
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
|
||||||
use middle::weak_lang_items;
|
use middle::weak_lang_items;
|
||||||
|
use middle::pat_util::simple_identifier;
|
||||||
use middle::subst::Substs;
|
use middle::subst::Substs;
|
||||||
use middle::ty::{self, Ty, HasTypeFlags};
|
use middle::ty::{self, Ty, HasTypeFlags};
|
||||||
use rustc::ast_map;
|
use rustc::ast_map;
|
||||||
use session::config::{self, NoDebugInfo};
|
use session::config::{self, NoDebugInfo, FullDebugInfo};
|
||||||
use session::Session;
|
use session::Session;
|
||||||
use trans::_match;
|
use trans::_match;
|
||||||
use trans::adt;
|
use trans::adt;
|
||||||
|
@ -1035,6 +1036,13 @@ pub fn alloca_no_lifetime(cx: Block, ty: Type, name: &str) -> ValueRef {
|
||||||
Alloca(cx, ty, name)
|
Alloca(cx, ty, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_value_name(val: ValueRef, name: &str) {
|
||||||
|
unsafe {
|
||||||
|
let name = CString::new(name).unwrap();
|
||||||
|
llvm::LLVMSetValueName(val, name.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Creates the alloca slot which holds the pointer to the slot for the final return value
|
// Creates the alloca slot which holds the pointer to the slot for the final return value
|
||||||
pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
||||||
output_type: Ty<'tcx>) -> ValueRef {
|
output_type: Ty<'tcx>) -> ValueRef {
|
||||||
|
@ -1297,78 +1305,70 @@ pub fn arg_kind<'a, 'tcx>(cx: &FunctionContext<'a, 'tcx>, t: Ty<'tcx>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// work around bizarre resolve errors
|
// create_datums_for_fn_args: creates lvalue datums for each of the
|
||||||
pub type RvalueDatum<'tcx> = datum::Datum<'tcx, datum::Rvalue>;
|
// incoming function arguments.
|
||||||
|
pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
|
||||||
// create_datums_for_fn_args: creates rvalue datums for each of the
|
args: &[ast::Arg],
|
||||||
// incoming function arguments. These will later be stored into
|
arg_tys: &[Ty<'tcx>],
|
||||||
// appropriate lvalue datums.
|
has_tupled_arg: bool,
|
||||||
pub fn create_datums_for_fn_args<'a, 'tcx>(bcx: Block<'a, 'tcx>,
|
arg_scope: cleanup::CustomScopeIndex)
|
||||||
arg_tys: &[Ty<'tcx>])
|
-> Block<'a, 'tcx> {
|
||||||
-> Vec<RvalueDatum<'tcx>> {
|
|
||||||
let _icx = push_ctxt("create_datums_for_fn_args");
|
let _icx = push_ctxt("create_datums_for_fn_args");
|
||||||
let fcx = bcx.fcx;
|
let fcx = bcx.fcx;
|
||||||
|
let arg_scope_id = cleanup::CustomScope(arg_scope);
|
||||||
|
|
||||||
// Return an array wrapping the ValueRefs that we get from `get_param` for
|
// Return an array wrapping the ValueRefs that we get from `get_param` for
|
||||||
// each argument into datums.
|
// each argument into datums.
|
||||||
let mut i = fcx.arg_offset() as c_uint;
|
//
|
||||||
arg_tys.iter().map(|&arg_ty| {
|
// For certain mode/type combinations, the raw llarg values are passed
|
||||||
if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
|
// by value. However, within the fn body itself, we want to always
|
||||||
let llty = type_of::type_of(bcx.ccx(), arg_ty);
|
// have all locals and arguments be by-ref so that we can cancel the
|
||||||
let data = get_param(fcx.llfn, i);
|
// cleanup and for better interaction with LLVM's debug info. So, if
|
||||||
let extra = get_param(fcx.llfn, i + 1);
|
// the argument would be passed by value, we store it into an alloca.
|
||||||
let fat_ptr = expr::make_fat_ptr(bcx, llty, data, extra);
|
// This alloca should be optimized away by LLVM's mem-to-reg pass in
|
||||||
i += 2;
|
// the event it's not truly needed.
|
||||||
datum::Datum::new(fat_ptr, arg_ty, datum::Rvalue { mode: datum::ByValue })
|
let mut idx = fcx.arg_offset() as c_uint;
|
||||||
} else {
|
|
||||||
let llarg = get_param(fcx.llfn, i);
|
|
||||||
i += 1;
|
|
||||||
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<'blk, 'tcx>(
|
|
||||||
mut bcx: Block<'blk, 'tcx>,
|
|
||||||
arg_scope: cleanup::CustomScopeIndex,
|
|
||||||
arg_tys: &[Ty<'tcx>])
|
|
||||||
-> Vec<RvalueDatum<'tcx>> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let mut idx = bcx.fcx.arg_offset() as c_uint;
|
|
||||||
for (i, &arg_ty) in arg_tys.iter().enumerate() {
|
for (i, &arg_ty) in arg_tys.iter().enumerate() {
|
||||||
if i < arg_tys.len() - 1 {
|
let arg_datum = if !has_tupled_arg || i < arg_tys.len() - 1 {
|
||||||
// Regular argument.
|
if type_of::arg_is_indirect(bcx.ccx(), arg_ty)
|
||||||
result.push(if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
|
&& bcx.sess().opts.debuginfo != FullDebugInfo {
|
||||||
let llty = type_of::type_of(bcx.ccx(), arg_ty);
|
// Don't copy an indirect argument to an alloca, the caller
|
||||||
let data = get_param(bcx.fcx.llfn, idx);
|
// already put it in a temporary alloca and gave it up, unless
|
||||||
let extra = get_param(bcx.fcx.llfn, idx + 1);
|
// we emit extra-debug-info, which requires local allocas :(.
|
||||||
idx += 2;
|
let llarg = get_param(fcx.llfn, idx);
|
||||||
let fat_ptr = expr::make_fat_ptr(bcx, llty, data, extra);
|
|
||||||
datum::Datum::new(fat_ptr, arg_ty, datum::Rvalue { mode: datum::ByValue })
|
|
||||||
} else {
|
|
||||||
let val = get_param(bcx.fcx.llfn, idx);
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
datum::Datum::new(val, arg_ty, arg_kind(bcx.fcx, arg_ty))
|
bcx.fcx.schedule_lifetime_end(arg_scope_id, llarg);
|
||||||
});
|
bcx.fcx.schedule_drop_mem(arg_scope_id, llarg, arg_ty);
|
||||||
|
|
||||||
continue
|
datum::Datum::new(llarg, arg_ty, datum::Lvalue)
|
||||||
}
|
} else if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
|
||||||
|
let data = get_param(fcx.llfn, idx);
|
||||||
// This is the last argument. Tuple it.
|
let extra = get_param(fcx.llfn, idx + 1);
|
||||||
match arg_ty.sty {
|
idx += 2;
|
||||||
ty::TyTuple(ref tupled_arg_tys) => {
|
unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
|
||||||
let tuple_args_scope_id = cleanup::CustomScope(arg_scope);
|
arg_scope_id, (data, extra),
|
||||||
let tuple =
|
|(data, extra), bcx, dst| {
|
||||||
|
Store(bcx, data, expr::get_dataptr(bcx, dst));
|
||||||
|
Store(bcx, extra, expr::get_len(bcx, dst));
|
||||||
|
bcx
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
let llarg = get_param(fcx.llfn, idx);
|
||||||
|
idx += 1;
|
||||||
|
let tmp = datum::Datum::new(llarg, arg_ty, arg_kind(fcx, arg_ty));
|
||||||
|
unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
|
||||||
|
arg_scope_id, tmp,
|
||||||
|
|tmp, bcx, dst| tmp.store_to(bcx, dst)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
|
||||||
|
match arg_ty.sty {
|
||||||
|
ty::TyTuple(ref tupled_arg_tys) => {
|
||||||
unpack_datum!(bcx,
|
unpack_datum!(bcx,
|
||||||
datum::lvalue_scratch_datum(bcx,
|
datum::lvalue_scratch_datum(bcx,
|
||||||
arg_ty,
|
arg_ty,
|
||||||
"tupled_args",
|
"tupled_args",
|
||||||
tuple_args_scope_id,
|
arg_scope_id,
|
||||||
(),
|
(),
|
||||||
|(),
|
|(),
|
||||||
mut bcx,
|
mut bcx,
|
||||||
|
@ -1392,46 +1392,27 @@ fn create_datums_for_fn_args_under_call_abi<'blk, 'tcx>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
bcx
|
bcx
|
||||||
}));
|
}))
|
||||||
let tuple = unpack_datum!(bcx,
|
}
|
||||||
tuple.to_expr_datum()
|
_ => {
|
||||||
.to_rvalue_datum(bcx,
|
bcx.tcx().sess.bug("last argument of a function with \
|
||||||
"argtuple"));
|
`rust-call` ABI isn't a tuple?!")
|
||||||
result.push(tuple);
|
}
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
bcx.tcx().sess.bug("last argument of a function with \
|
|
||||||
`rust-call` ABI isn't a tuple?!")
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
let pat = &*args[i].pat;
|
||||||
|
bcx = if let Some(ident) = simple_identifier(&*pat) {
|
||||||
result
|
// Generate nicer LLVM for the common case of fn a pattern
|
||||||
}
|
// like `x: T`
|
||||||
|
set_value_name(arg_datum.val, &bcx.name(ident.name));
|
||||||
fn copy_args_to_allocas<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
bcx.fcx.lllocals.borrow_mut().insert(pat.id, arg_datum);
|
||||||
arg_scope: cleanup::CustomScopeIndex,
|
bcx
|
||||||
args: &[ast::Arg],
|
} else {
|
||||||
arg_datums: Vec<RvalueDatum<'tcx>>)
|
// General path. Copy out the values that are used in the
|
||||||
-> Block<'blk, 'tcx> {
|
// pattern.
|
||||||
debug!("copy_args_to_allocas");
|
_match::bind_irrefutable_pat(bcx, pat, arg_datum.val, arg_scope_id)
|
||||||
|
};
|
||||||
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);
|
|
||||||
debuginfo::create_argument_metadata(bcx, &args[i]);
|
debuginfo::create_argument_metadata(bcx, &args[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1585,16 +1566,13 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
debug!("trans_closure: function lltype: {}",
|
debug!("trans_closure: function lltype: {}",
|
||||||
bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn));
|
bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn));
|
||||||
|
|
||||||
let arg_datums = match closure_env {
|
let has_tupled_arg = match closure_env {
|
||||||
closure::ClosureEnv::NotClosure if abi == RustCall => {
|
closure::ClosureEnv::NotClosure => abi == RustCall,
|
||||||
create_datums_for_fn_args_under_call_abi(bcx, arg_scope, &monomorphized_arg_types[..])
|
_ => false
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
create_datums_for_fn_args(bcx, &monomorphized_arg_types)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bcx = copy_args_to_allocas(bcx, arg_scope, &decl.inputs, arg_datums);
|
bcx = create_datums_for_fn_args(bcx, &decl.inputs, &monomorphized_arg_types,
|
||||||
|
has_tupled_arg, arg_scope);
|
||||||
|
|
||||||
bcx = closure_env.load(bcx, cleanup::CustomScope(arg_scope));
|
bcx = closure_env.load(bcx, cleanup::CustomScope(arg_scope));
|
||||||
|
|
||||||
|
@ -1795,18 +1773,30 @@ fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx
|
||||||
|
|
||||||
let arg_tys = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_args());
|
let arg_tys = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_args());
|
||||||
|
|
||||||
let arg_datums = create_datums_for_fn_args(bcx, &arg_tys[..]);
|
|
||||||
|
|
||||||
if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) {
|
if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) {
|
||||||
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
|
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
|
||||||
let repr = adt::represent_type(ccx, result_ty.unwrap());
|
let repr = adt::represent_type(ccx, result_ty.unwrap());
|
||||||
for (i, arg_datum) in arg_datums.into_iter().enumerate() {
|
let mut llarg_idx = fcx.arg_offset() as c_uint;
|
||||||
|
for (i, arg_ty) in arg_tys.into_iter().enumerate() {
|
||||||
let lldestptr = adt::trans_field_ptr(bcx,
|
let lldestptr = adt::trans_field_ptr(bcx,
|
||||||
&*repr,
|
&*repr,
|
||||||
dest,
|
dest,
|
||||||
disr,
|
disr,
|
||||||
i);
|
i);
|
||||||
arg_datum.store_to(bcx, lldestptr);
|
if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
|
||||||
|
Store(bcx, get_param(fcx.llfn, llarg_idx), expr::get_dataptr(bcx, lldestptr));
|
||||||
|
Store(bcx, get_param(fcx.llfn, llarg_idx + 1), expr::get_len(bcx, lldestptr));
|
||||||
|
llarg_idx += 2;
|
||||||
|
} else {
|
||||||
|
let arg = get_param(fcx.llfn, llarg_idx);
|
||||||
|
llarg_idx += 1;
|
||||||
|
|
||||||
|
if arg_is_indirect(ccx, arg_ty) {
|
||||||
|
memcpy_ty(bcx, lldestptr, arg, arg_ty);
|
||||||
|
} else {
|
||||||
|
store_ty(bcx, arg, lldestptr, arg_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
adt::trans_set_discr(bcx, &*repr, dest, disr);
|
adt::trans_set_discr(bcx, &*repr, dest, disr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,9 +291,6 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
|
||||||
GEPi(bcx, fat_ptr, &[0, abi::FAT_PTR_ADDR])
|
GEPi(bcx, fat_ptr, &[0, abi::FAT_PTR_ADDR])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_fat_ptr(bcx: Block, ty: Type, data: ValueRef, extra: ValueRef) -> ValueRef {
|
|
||||||
InsertValue(bcx, InsertValue(bcx, C_undef(ty), data, 0), extra, 1)
|
|
||||||
}
|
|
||||||
pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
|
pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
|
||||||
Store(bcx, Load(bcx, get_dataptr(bcx, src_ptr)), get_dataptr(bcx, dst_ptr));
|
Store(bcx, Load(bcx, get_dataptr(bcx, src_ptr)), get_dataptr(bcx, dst_ptr));
|
||||||
Store(bcx, Load(bcx, get_len(bcx, src_ptr)), get_len(bcx, dst_ptr));
|
Store(bcx, Load(bcx, get_len(bcx, src_ptr)), get_len(bcx, dst_ptr));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue